mirror of
https://github.com/idanoo/autobrr
synced 2025-07-25 01:39:13 +00:00
enhancement(web): ui overhaul (#1155)
* Various WebUI changes and fixes. * feat(tooltip): make tooltip display upwards * fix(tooltip): place tooltip to the right * fix(web): add missing ml-px to SwitchGroup header current: https://i.imgur.com/2WXstPV.png new: https://i.imgur.com/QGQ49mP.png * fix(web): collapse sections * fix(web): improve freeleech section * fix(web): rename action to action_components Renamed the 'action' folder to 'action_components' to resolve import issues due to case sensitivity. * fix(web): align CollapsibleSection Old Advanced tab: https://i.imgur.com/MXaJ5eJ.png New Advanced tab: https://i.imgur.com/4nPJJRw.png Music tab for comparison: https://i.imgur.com/I59X7ot.png * fix(web): remove invalid CSS class * revert: vertical padding on switchgroup added py-0 on the freeleech part instead * feat(settings): add back log files * fix(settings): irc channels and font sizes * fix(components): radio select roundness * fix(styling): various minor changes * fix(filters): remove jitter fields --------- Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com> Co-authored-by: soup <soup@r4tio.dev> Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
parent
a274d9ddce
commit
e842a7bd42
84 changed files with 4378 additions and 4361 deletions
|
@ -4,36 +4,82 @@
|
|||
*/
|
||||
|
||||
import { Switch } from "@headlessui/react";
|
||||
import { classNames } from "@utils";
|
||||
|
||||
interface CheckboxProps {
|
||||
value: boolean;
|
||||
setValue: (newValue: boolean) => void;
|
||||
label: string;
|
||||
description?: string;
|
||||
value: boolean;
|
||||
setValue: (newValue: boolean) => void;
|
||||
label?: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const Checkbox = ({ label, description, value, setValue }: CheckboxProps) => (
|
||||
<Switch.Group as="li" className="py-4 flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Switch.Label as="p" className="text-sm font-medium whitespace-nowrap text-gray-900 dark:text-white" passive>
|
||||
{label}
|
||||
</Switch.Label>
|
||||
{description === undefined ? null : (
|
||||
<Switch.Description className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{description}
|
||||
</Switch.Description>
|
||||
)}
|
||||
</div>
|
||||
export const Checkbox = ({
|
||||
label,
|
||||
description,
|
||||
value,
|
||||
className,
|
||||
setValue,
|
||||
disabled
|
||||
}: CheckboxProps) => (
|
||||
<Switch.Group
|
||||
as="div"
|
||||
className={classNames(className ?? "py-2", "flex items-center justify-between")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
}}
|
||||
>
|
||||
{(label || description) ? (
|
||||
<div className="flex flex-col mr-4">
|
||||
{label ? (
|
||||
<Switch.Label as="p" className="text-sm font-medium whitespace-nowrap text-gray-900 dark:text-white" passive>
|
||||
{label}
|
||||
</Switch.Label>
|
||||
) : null}
|
||||
{description ? (
|
||||
<Switch.Description className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{description}
|
||||
</Switch.Description>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<Switch
|
||||
checked={value}
|
||||
onChange={setValue}
|
||||
className={
|
||||
`${value ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-700"
|
||||
} ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500`}
|
||||
onChange={(newValue) => {
|
||||
!disabled && setValue(newValue);
|
||||
}}
|
||||
className={classNames(
|
||||
disabled
|
||||
? "cursor-not-allowed bg-gray-450 dark:bg-gray-700 border-gray-375 dark:border-gray-800"
|
||||
: (
|
||||
value
|
||||
? "cursor-pointer bg-blue-600 border-blue-525"
|
||||
: "cursor-pointer bg-gray-300 dark:bg-gray-700 border-gray-375 dark:border-gray-600"
|
||||
),
|
||||
"border relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={`${value ? "translate-x-5" : "translate-x-0"} inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200`}
|
||||
/>
|
||||
className={classNames(
|
||||
value ? "translate-x-6" : "translate-x-[0.15rem]",
|
||||
disabled ? "bg-gray-650 dark:bg-gray-800" : "bg-white",
|
||||
"inline-flex items-center align-center h-4 w-4 transform rounded-full transition ring-0 shadow"
|
||||
)}
|
||||
>
|
||||
{value
|
||||
? (
|
||||
<svg className={classNames(
|
||||
disabled ? "text-white dark:text-gray-300" : "text-blue-500", "w-4 h-4"
|
||||
)} fill="currentColor" viewBox="0 0 12 12"><path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"></path></svg>
|
||||
)
|
||||
: (
|
||||
<svg className={classNames(
|
||||
disabled ? "text-white dark:text-gray-300" : "text-gray-600", "w-4 h-4"
|
||||
)} fill="none" viewBox="0 0 12 12"><path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"></path></svg>
|
||||
)}
|
||||
</span>
|
||||
</Switch>
|
||||
</Switch.Group>
|
||||
);
|
||||
);
|
||||
|
|
|
@ -16,5 +16,5 @@ export const ExternalLink = ({ href, className, children }: ExternalLinkProps) =
|
|||
);
|
||||
|
||||
export const DocsLink = ({ href }: { href: string; }) => (
|
||||
<ExternalLink href={href} className="text-blue-400 visited:text-blue-400">{href}</ExternalLink>
|
||||
<ExternalLink href={href} className="text-blue-700 dark:text-blue-400 visited:text-blue-700 dark:visited:text-blue-400">{href}</ExternalLink>
|
||||
);
|
||||
|
|
|
@ -29,4 +29,4 @@ export const SectionLoader = ({ $size }: SectionLoaderProps) => {
|
|||
<RingResizeSpinner className={classNames(SIZE[$size], "text-blue-500")} />
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,10 +20,10 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
|
|||
|
||||
const parseTitle = () => {
|
||||
switch (error?.cause) {
|
||||
case "OFFLINE":
|
||||
return "Connection to Autobrr failed! Check the application state and verify your connectivity.";
|
||||
default:
|
||||
return "We caught an unrecoverable error!";
|
||||
case "OFFLINE":
|
||||
return "Connection to Autobrr failed! Check the application state and verify your connectivity.";
|
||||
default:
|
||||
return "We caught an unrecoverable error!";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
35
web/src/components/alerts/Warning.tsx
Normal file
35
web/src/components/alerts/Warning.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2023, Ludvig Lundgren and the autobrr contributors.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import { classNames } from "@utils";
|
||||
|
||||
interface WarningAlertProps {
|
||||
text: string | JSX.Element;
|
||||
alert?: string;
|
||||
colors?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WarningAlert = ({ text, alert, colors, className }: WarningAlertProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
className ?? "",
|
||||
"col-span-12 flex items-center px-4 py-3 text-md font-medium rounded-lg",
|
||||
colors ?? "text-amber-800 bg-amber-100 border border-amber-700 dark:border-none dark:bg-amber-200 dark:text-amber-800"
|
||||
)}
|
||||
role="alert">
|
||||
<svg aria-hidden="true" className="flex-shrink-0 inline w-5 h-5 mr-3" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fillRule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||
clipRule="evenodd"></path>
|
||||
</svg>
|
||||
<span className="sr-only">Info</span>
|
||||
<div>
|
||||
<span className="font-extrabold">{alert ?? "Warning!"}</span>
|
||||
{" "}{text}
|
||||
</div>
|
||||
</div>
|
||||
);
|
|
@ -3,34 +3,5 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
interface props {
|
||||
title?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function AlertWarning({ title, text }: props) {
|
||||
return (
|
||||
<div className="my-4 rounded-md bg-yellow-50 dark:bg-yellow-100 p-4 border border-yellow-300 dark:border-none">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-5 w-5 text-yellow-400 dark:text-yellow-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{title ? (
|
||||
<h3 className="mb-1 text-md font-medium text-yellow-800">{title}</h3>
|
||||
) : null}
|
||||
<div className="text-sm text-yellow-800">
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ErrorPage } from "./ErrorPage";
|
||||
export * from "./ErrorPage";
|
||||
export * from "./Warning";
|
||||
|
|
|
@ -32,11 +32,14 @@ export const PageButton = ({ children, className, disabled, onClick }: ButtonPro
|
|||
type="button"
|
||||
className={classNames(
|
||||
className ?? "",
|
||||
"cursor-pointer inline-flex items-center p-1.5 border border-gray-300 dark:border-gray-700 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||
disabled
|
||||
? "cursor-not-allowed text-gray-500 dark:text-gray-500 border-gray-300 dark:border-gray-700 dark:bg-gray-800"
|
||||
: "cursor-pointer text-gray-500 dark:text-gray-350 border-gray-300 dark:border-gray-700 dark:bg-gray-850 hover:bg-gray-100 dark:hover:bg-gray-700",
|
||||
"inline-flex items-center p-1.5 border text-sm font-medium transition"
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import { toast } from "react-hot-toast";
|
|||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowPathIcon, CheckIcon } from "@heroicons/react/24/solid";
|
||||
import { ClockIcon, ExclamationCircleIcon, NoSymbolIcon } from "@heroicons/react/24/outline";
|
||||
import { ClockIcon, XMarkIcon, NoSymbolIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
import { APIClient } from "@api/APIClient";
|
||||
import { classNames, simplifyDate } from "@utils";
|
||||
|
@ -95,7 +95,7 @@ const RetryActionButton = ({ status }: RetryActionButtonProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<button className="flex items-center px-1.5 py-1 ml-2 border-gray-500 bg-gray-700 rounded hover:bg-gray-600" onClick={replayAction}>
|
||||
<button className="flex items-center px-1.5 py-1 ml-2 rounded transition border-gray-500 bg-gray-250 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600" onClick={replayAction}>
|
||||
<span className="mr-1.5">Retry</span>
|
||||
{mutation.isLoading
|
||||
? <RingResizeSpinner className="text-blue-500 w-4 h-4 iconHeight" aria-hidden="true" />
|
||||
|
@ -117,8 +117,8 @@ interface StatusCellMapEntry {
|
|||
|
||||
const StatusCellMap: Record<string, StatusCellMapEntry> = {
|
||||
"PUSH_ERROR": {
|
||||
colors: "bg-pink-100 text-pink-800 hover:bg-pink-300",
|
||||
icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />,
|
||||
colors: "bg-red-100 text-red-800 hover:bg-red-275",
|
||||
icon: <XMarkIcon className="h-5 w-5" aria-hidden="true" />,
|
||||
textFormatter: (status: ReleaseActionStatus) => (
|
||||
<>
|
||||
<span>
|
||||
|
@ -159,7 +159,7 @@ const StatusCellMap: Record<string, StatusCellMapEntry> = {
|
|||
)
|
||||
},
|
||||
"PUSH_APPROVED": {
|
||||
colors: "bg-green-100 text-green-800 hover:bg-green-300",
|
||||
colors: "bg-green-175 text-green-900 hover:bg-green-300",
|
||||
icon: <CheckIcon className="h-5 w-5" aria-hidden="true" />,
|
||||
textFormatter: (status: ReleaseActionStatus) => (
|
||||
<>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { PlusIcon } from "@heroicons/react/24/solid";
|
|||
|
||||
interface EmptySimpleProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
subtitle?: string;
|
||||
buttonText?: string;
|
||||
buttonAction?: () => void;
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ export const EmptySimple = ({
|
|||
}: EmptySimpleProps) => (
|
||||
<div className="text-center py-8">
|
||||
<h3 className="mt-2 text-sm font-medium text-gray-900 dark:text-white">{title}</h3>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-200">{subtitle}</p>
|
||||
{subtitle ? (
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-200">{subtitle}</p>
|
||||
) : null}
|
||||
{buttonText && buttonAction ? (
|
||||
<div className="mt-6">
|
||||
<button
|
||||
|
@ -57,4 +59,4 @@ export function EmptyListState({ text, buttonText, buttonOnClick }: EmptyListSta
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,12 +67,12 @@ export const KeyField = ({ value }: KeyFieldProps) => {
|
|||
type={isVisible ? "text" : "password"}
|
||||
value={value}
|
||||
readOnly={true}
|
||||
className="focus:outline-none dark:focus:border-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
|
||||
className="focus:outline-none dark:focus:border-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 block w-full rounded-none rounded-l-md sm:text-sm border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 dark:border-gray-700 hover:bg-gray-100 text-sm font-medium text-gray-700 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none"
|
||||
className="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 dark:border-gray-700 hover:bg-gray-100 text-sm font-medium text-gray-700 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 focus:outline-none"
|
||||
onClick={toggleVisibility}
|
||||
title="show"
|
||||
>
|
||||
|
@ -80,7 +80,7 @@ export const KeyField = ({ value }: KeyFieldProps) => {
|
|||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 dark:border-gray-700 hover:bg-gray-100 text-sm font-medium rounded-r-md text-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none"
|
||||
className="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 dark:border-gray-700 hover:bg-gray-100 text-sm font-medium rounded-r-md text-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 focus:outline-none"
|
||||
onClick={handleCopyClick}
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
|
|
|
@ -48,12 +48,12 @@ export const Header = () => {
|
|||
return (
|
||||
<Disclosure
|
||||
as="nav"
|
||||
className="bg-gradient-to-b from-gray-100 dark:from-[#141414]"
|
||||
className="bg-gradient-to-b from-gray-100 dark:from-gray-925"
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="max-w-screen-xl mx-auto sm:px-6 lg:px-8">
|
||||
<div className="border-b border-gray-300 dark:border-gray-700">
|
||||
<div className="border-b border-gray-300 dark:border-gray-775">
|
||||
<div className="flex items-center justify-between h-16 px-4 sm:px-0">
|
||||
<LeftNav />
|
||||
<RightNav logoutMutation={logoutMutation.mutate} />
|
||||
|
|
|
@ -12,6 +12,7 @@ import { classNames } from "@utils";
|
|||
import { AuthContext } from "@utils/Context";
|
||||
|
||||
import { RightNavProps } from "./_shared";
|
||||
import { Cog6ToothIcon, ArrowLeftOnRectangleIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export const RightNav = (props: RightNavProps) => {
|
||||
const authContext = AuthContext.useValue();
|
||||
|
@ -23,10 +24,10 @@ export const RightNav = (props: RightNavProps) => {
|
|||
<>
|
||||
<Menu.Button
|
||||
className={classNames(
|
||||
open ? "bg-gray-200 dark:bg-gray-800" : "",
|
||||
"text-gray-600 dark:text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-2xl text-sm font-medium",
|
||||
open ? "bg-gray-200 dark:bg-gray-800 text-gray-900 dark:text-white" : "hover:text-gray-900 dark:hover:text-white",
|
||||
"text-gray-600 dark:text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-800 px-3 py-2 rounded-2xl text-sm font-medium",
|
||||
"max-w-xs rounded-full flex items-center text-sm px-3 py-2",
|
||||
"transition-colors duration-200"
|
||||
"transition duration-200"
|
||||
)}
|
||||
>
|
||||
<span className="hidden text-sm font-medium sm:block">
|
||||
|
@ -52,7 +53,7 @@ export const RightNav = (props: RightNavProps) => {
|
|||
>
|
||||
<Menu.Items
|
||||
static
|
||||
className="origin-top-right absolute right-0 mt-2 w-48 z-10 rounded-md shadow-lg py-1 bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
className="origin-top-right absolute right-0 mt-2 w-48 z-10 divide-y divide-gray-100 dark:divide-gray-750 rounded-md shadow-lg bg-white dark:bg-gray-800 border border-gray-250 dark:border-gray-775 focus:outline-none"
|
||||
>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
|
@ -62,9 +63,13 @@ export const RightNav = (props: RightNavProps) => {
|
|||
active
|
||||
? "bg-gray-100 dark:bg-gray-600"
|
||||
: "",
|
||||
"block px-4 py-2 text-sm text-gray-900 dark:text-gray-200"
|
||||
"flex items-center transition rounded-t-md px-2 py-2 text-sm text-gray-900 dark:text-gray-200"
|
||||
)}
|
||||
>
|
||||
<Cog6ToothIcon
|
||||
className="w-5 h-5 mr-1 text-gray-700 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Settings
|
||||
</Link>
|
||||
)}
|
||||
|
@ -80,9 +85,13 @@ export const RightNav = (props: RightNavProps) => {
|
|||
active
|
||||
? "bg-gray-100 dark:bg-gray-600"
|
||||
: "",
|
||||
"block w-full px-4 py-2 text-sm text-gray-900 dark:text-gray-200 text-left"
|
||||
"flex items-center transition rounded-b-md w-full px-2 py-2 text-sm text-gray-900 dark:text-gray-200 text-left"
|
||||
)}
|
||||
>
|
||||
<ArrowLeftOnRectangleIcon
|
||||
className="w-5 h-5 mr-1 text-gray-700 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Log out
|
||||
</button>
|
||||
)}
|
||||
|
@ -95,4 +104,4 @@ export const RightNav = (props: RightNavProps) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,16 +3,15 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import { FC } from "react";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
title: string;
|
||||
subtitle: string | React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TitleSubtitle: FC<Props> = ({ title, subtitle }) => (
|
||||
<div>
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">{title}</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
|
||||
export const TitleSubtitle = ({ title, subtitle, className }: Props) => (
|
||||
<div className={className}>
|
||||
<h2 className="text-lg leading-5 capitalize font-bold text-gray-900 dark:text-gray-100">{title}</h2>
|
||||
<p className="mt-0.5 text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
|
|
|
@ -4,73 +4,87 @@
|
|||
*/
|
||||
|
||||
import { Field, FieldProps } from "formik";
|
||||
import { components } from "react-select";
|
||||
import type {
|
||||
InputProps,
|
||||
ControlProps,
|
||||
MenuProps,
|
||||
OptionProps,
|
||||
IndicatorSeparatorProps,
|
||||
DropdownIndicatorProps
|
||||
} from "react-select";
|
||||
|
||||
import { classNames } from "@utils";
|
||||
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
|
||||
|
||||
interface ErrorFieldProps {
|
||||
name: string;
|
||||
classNames?: string;
|
||||
name: string;
|
||||
classNames?: string;
|
||||
}
|
||||
|
||||
const ErrorField = ({ name, classNames }: ErrorFieldProps) => (
|
||||
<div>
|
||||
<Field name={name} subscribe={{ touched: true, error: true }}>
|
||||
{({ meta: { touched, error } }: FieldProps) =>
|
||||
touched && error ? <span className={classNames}>{error}</span> : null
|
||||
}
|
||||
</Field>
|
||||
</div>
|
||||
export const ErrorField = ({ name, classNames }: ErrorFieldProps) => (
|
||||
<Field name={name} subscribe={{ touched: true, error: true }}>
|
||||
{({ meta: { touched, error } }: FieldProps) =>
|
||||
touched && error ? <span className={classNames}>{error}</span> : null
|
||||
}
|
||||
</Field>
|
||||
);
|
||||
|
||||
interface RequiredFieldProps {
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const RequiredField = ({ required }: RequiredFieldProps) => (
|
||||
export const RequiredField = ({ required }: RequiredFieldProps) => (
|
||||
<>
|
||||
{required && <span className="ml-1 text-red-500">*</span>}
|
||||
</>
|
||||
);
|
||||
|
||||
interface CheckboxFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
sublabel?: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
}
|
||||
|
||||
const CheckboxField = ({
|
||||
name,
|
||||
label,
|
||||
sublabel,
|
||||
tooltip,
|
||||
disabled
|
||||
}: CheckboxFieldProps) => (
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex items-center h-5">
|
||||
<Field
|
||||
id={name}
|
||||
name={name}
|
||||
type="checkbox"
|
||||
className={classNames(
|
||||
"focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded",
|
||||
disabled ? "bg-gray-200 dark:bg-gray-700 dark:border-gray-700" : ""
|
||||
)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label htmlFor={name} className="flex mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
</label>
|
||||
<p className="text-gray-500">{sublabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
export const SelectInput = (props: InputProps) => (
|
||||
<components.Input
|
||||
{...props}
|
||||
inputClassName="outline-none border-none shadow-none focus:ring-transparent"
|
||||
className="text-gray-400 dark:text-gray-100"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
|
||||
export { ErrorField, RequiredField, CheckboxField };
|
||||
export const SelectControl = (props: ControlProps) => (
|
||||
<components.Control
|
||||
{...props}
|
||||
className="p-1 block w-full !bg-gray-100 dark:!bg-gray-850 border border-gray-300 dark:border-gray-775 dark:hover:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
|
||||
export const SelectMenu = (props: MenuProps) => (
|
||||
<components.Menu
|
||||
{...props}
|
||||
className="dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-400 rounded-md shadow-sm cursor-pointer"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
|
||||
export const SelectOption = (props: OptionProps) => (
|
||||
<components.Option
|
||||
{...props}
|
||||
className={classNames(
|
||||
"transition dark:hover:bg-gray-900 dark:focus:bg-gray-900",
|
||||
props.isSelected ? "dark:bg-gray-875 dark:text-gray-200" : "dark:bg-gray-800 dark:text-gray-400"
|
||||
)}
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
|
||||
export const IndicatorSeparator = (props: IndicatorSeparatorProps) => (
|
||||
<components.IndicatorSeparator
|
||||
{...props}
|
||||
className="!bg-gray-400 dark:!bg-gray-700"
|
||||
/>
|
||||
);
|
||||
|
||||
export const DropdownIndicator = (props: DropdownIndicatorProps) => (
|
||||
<components.DropdownIndicator
|
||||
{...props}
|
||||
className="!text-gray-400 dark:!text-gray-300"
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
export { ErrorField, CheckboxField } from "./common";
|
||||
export { TextField, NumberField, PasswordField, RegexField } from "./input";
|
||||
export { NumberFieldWide, PasswordFieldWide, SwitchGroupWide, SwitchGroupWideRed, TextFieldWide } from "./input_wide";
|
||||
export { RadioFieldsetWide } from "./radio";
|
||||
export { MultiSelect, Select, SelectWide, DownloadClientSelect, IndexerMultiSelect } from "./select";
|
||||
export { SwitchGroup } from "./switch";
|
||||
export * from "./common";
|
||||
export * from "./input";
|
||||
export * from "./input_wide";
|
||||
export * from "./radio";
|
||||
export * from "./select";
|
||||
export * from "./switch";
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ interface TextFieldProps {
|
|||
name: string;
|
||||
defaultValue?: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
columns?: COL_WIDTHS;
|
||||
autoComplete?: string;
|
||||
|
@ -30,6 +31,7 @@ export const TextField = ({
|
|||
name,
|
||||
defaultValue,
|
||||
label,
|
||||
required,
|
||||
placeholder,
|
||||
columns,
|
||||
autoComplete,
|
||||
|
@ -39,25 +41,27 @@ export const TextField = ({
|
|||
}: TextFieldProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-12",
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
<label htmlFor={name} className="flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
{required ? (
|
||||
<span className="ml-1 text-red-500">*</span>
|
||||
) : null}
|
||||
</label>
|
||||
)}
|
||||
<Field name={name}>
|
||||
<Field name={name} defaultValue={defaultValue}>
|
||||
{({
|
||||
field,
|
||||
meta
|
||||
}: FieldProps) => (
|
||||
<div>
|
||||
<>
|
||||
<input
|
||||
{...field}
|
||||
name={name}
|
||||
|
@ -65,9 +69,13 @@ export const TextField = ({
|
|||
defaultValue={defaultValue}
|
||||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md"
|
||||
meta.touched && meta.error
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
disabled
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100",
|
||||
"mt-1 block border w-full dark:text-gray-100 rounded-md"
|
||||
)}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
|
@ -76,7 +84,7 @@ export const TextField = ({
|
|||
{meta.touched && meta.error && (
|
||||
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
|
@ -174,20 +182,19 @@ export const RegexField = ({
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-12",
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"
|
||||
className="flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</label>
|
||||
)}
|
||||
<Field
|
||||
|
@ -204,15 +211,15 @@ export const RegexField = ({
|
|||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
useRegex && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
disabled
|
||||
? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed"
|
||||
: "dark:bg-gray-800",
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100",
|
||||
useRegex
|
||||
? "pr-10"
|
||||
: "",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md"
|
||||
"mt-1 block w-full dark:text-gray-100 rounded-md"
|
||||
)}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
|
@ -221,9 +228,9 @@ export const RegexField = ({
|
|||
<div className="relative">
|
||||
<div className="flex float-right items-center">
|
||||
{!meta.error ? (
|
||||
<CheckCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
<CheckCircleIcon className="h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
) : (
|
||||
<XCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
<XCircleIcon className="h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -315,20 +322,22 @@ export const RegexTextAreaField = ({
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-12",
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"
|
||||
className={classNames(
|
||||
tooltip ? "z-10" : "",
|
||||
"flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide"
|
||||
)}
|
||||
>
|
||||
<div className="flex z-10">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</label>
|
||||
)}
|
||||
<Field
|
||||
|
@ -346,15 +355,15 @@ export const RegexTextAreaField = ({
|
|||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
useRegex && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
disabled
|
||||
? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed"
|
||||
: "dark:bg-gray-800",
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100",
|
||||
useRegex
|
||||
? "pr-10"
|
||||
: "",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md"
|
||||
"mt-1 block w-full dark:text-gray-100 rounded-md"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
|
@ -364,9 +373,9 @@ export const RegexTextAreaField = ({
|
|||
<div className="relative">
|
||||
<div className="flex float-right items-center">
|
||||
{!meta.error ? (
|
||||
<CheckCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
<CheckCircleIcon className="h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
) : (
|
||||
<XCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
<XCircleIcon className="h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -406,17 +415,16 @@ export const TextArea = ({
|
|||
}: TextAreaProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-12",
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
<label htmlFor={name} className="flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</label>
|
||||
)}
|
||||
<Field name={name}>
|
||||
|
@ -432,9 +440,13 @@ export const TextArea = ({
|
|||
defaultValue={defaultValue}
|
||||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md"
|
||||
meta.touched && meta.error
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
disabled
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100",
|
||||
"mt-1 block border w-full dark:text-gray-100 rounded-md"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
|
@ -460,7 +472,7 @@ interface TextAreaAutoResizeProps {
|
|||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TextAreaAutoResize = ({
|
||||
|
@ -473,21 +485,22 @@ export const TextAreaAutoResize = ({
|
|||
autoComplete,
|
||||
hidden,
|
||||
tooltip,
|
||||
disabled
|
||||
disabled,
|
||||
className = ""
|
||||
}: TextAreaAutoResizeProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
"col-span-12",
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
<label htmlFor={name} className="flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</label>
|
||||
)}
|
||||
<Field name={name}>
|
||||
|
@ -504,9 +517,13 @@ export const TextAreaAutoResize = ({
|
|||
defaultValue={defaultValue}
|
||||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md"
|
||||
meta.touched && meta.error
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
disabled
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100",
|
||||
"mt-1 block w-full dark:text-gray-100 rounded-md"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
|
@ -548,11 +565,12 @@ export const PasswordField = ({
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
"col-span-12",
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<label htmlFor={name} className="block ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{label} {required && <span className="text-gray-500">*</span>}
|
||||
</label>
|
||||
)}
|
||||
|
@ -571,9 +589,9 @@ export const PasswordField = ({
|
|||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
meta.touched && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
"mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md"
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
"mt-1 block w-full rounded-md bg-gray-100 dark:bg-gray-850 dark:text-gray-100"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
|
@ -608,6 +626,7 @@ interface NumberFieldProps {
|
|||
min?: number;
|
||||
max?: number;
|
||||
tooltip?: JSX.Element;
|
||||
className?: string;
|
||||
isDecimal?: boolean;
|
||||
}
|
||||
|
||||
|
@ -621,18 +640,17 @@ export const NumberField = ({
|
|||
tooltip,
|
||||
disabled,
|
||||
required,
|
||||
isDecimal
|
||||
isDecimal,
|
||||
className = ""
|
||||
}: NumberFieldProps) => (
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<div className={classNames(className, "col-span-12 sm:col-span-6")}>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"
|
||||
className="flex ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</label>
|
||||
|
||||
<Field name={name} type="number">
|
||||
|
@ -648,10 +666,12 @@ export const NumberField = ({
|
|||
required={required}
|
||||
className={classNames(
|
||||
meta.touched && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300",
|
||||
"mt-2 block w-full border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800"
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
"mt-1 block w-full border rounded-md",
|
||||
disabled
|
||||
? "bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
||||
: "bg-gray-100 dark:bg-gray-815 dark:text-gray-100"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -3,16 +3,20 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import type { FieldProps, FieldValidator } from "formik";
|
||||
import { Field } from "formik";
|
||||
import Select from "react-select";
|
||||
import { Switch } from "@headlessui/react";
|
||||
import type { FieldProps, FieldValidator } from "formik";
|
||||
|
||||
import { classNames } from "@utils";
|
||||
import { useToggle } from "@hooks/hooks";
|
||||
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
|
||||
import { Switch } from "@headlessui/react";
|
||||
import { ErrorField, RequiredField } from "./common";
|
||||
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
||||
|
||||
import { SelectFieldProps } from "./select";
|
||||
import * as common from "./common";
|
||||
|
||||
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
|
||||
import { Checkbox } from "@components/Checkbox";
|
||||
|
||||
interface TextFieldWideProps {
|
||||
name: string;
|
||||
|
@ -41,12 +45,12 @@ export const TextFieldWide = ({
|
|||
}: TextFieldWideProps) => (
|
||||
<div hidden={hidden} className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
|
||||
<label htmlFor={name} className="flex ml-px text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
<RequiredField required={required} />
|
||||
<common.RequiredField required={required} />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -64,7 +68,12 @@ export const TextFieldWide = ({
|
|||
type="text"
|
||||
value={field.value ? field.value : defaultValue ?? ""}
|
||||
onChange={field.onChange}
|
||||
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md")}
|
||||
className={classNames(
|
||||
meta.touched && meta.error
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
"block w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
hidden={hidden}
|
||||
required={required}
|
||||
|
@ -76,22 +85,22 @@ export const TextFieldWide = ({
|
|||
{help && (
|
||||
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
|
||||
)}
|
||||
<ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
<common.ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface PasswordFieldWideProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
help?: string;
|
||||
required?: boolean;
|
||||
autoComplete?: string;
|
||||
defaultVisible?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
validate?: FieldValidator;
|
||||
name: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
help?: string;
|
||||
required?: boolean;
|
||||
autoComplete?: string;
|
||||
defaultVisible?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
validate?: FieldValidator;
|
||||
}
|
||||
|
||||
export const PasswordFieldWide = ({
|
||||
|
@ -111,12 +120,12 @@ export const PasswordFieldWide = ({
|
|||
return (
|
||||
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
|
||||
<label htmlFor={name} className="flex ml-px text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
<RequiredField required={required} />
|
||||
<common.RequiredField required={required} />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -134,7 +143,12 @@ export const PasswordFieldWide = ({
|
|||
value={field.value ? field.value : defaultValue ?? ""}
|
||||
onChange={field.onChange}
|
||||
type={isVisible ? "text" : "password"}
|
||||
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full pr-10 dark:bg-gray-800 shadow-sm dark:text-gray-100 sm:text-sm rounded-md")}
|
||||
className={classNames(
|
||||
meta.touched && meta.error
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
"block w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100"
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
autoComplete={autoComplete}
|
||||
|
@ -149,20 +163,20 @@ export const PasswordFieldWide = ({
|
|||
{help && (
|
||||
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
|
||||
)}
|
||||
<ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
<common.ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface NumberFieldWideProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
help?: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: number;
|
||||
required?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
name: string;
|
||||
label?: string;
|
||||
help?: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: number;
|
||||
required?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
}
|
||||
|
||||
export const NumberFieldWide = ({
|
||||
|
@ -178,13 +192,13 @@ export const NumberFieldWide = ({
|
|||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
|
||||
className="block ml-px text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
<RequiredField required={required} />
|
||||
<common.RequiredField required={required} />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -202,9 +216,9 @@ export const NumberFieldWide = ({
|
|||
onChange={(e) => { form.setFieldValue(field.name, parseInt(e.target.value)); }}
|
||||
className={classNames(
|
||||
meta.touched && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
"block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md"
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||
"block w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100"
|
||||
)}
|
||||
onWheel={(event) => {
|
||||
if (event.currentTarget === document.activeElement) {
|
||||
|
@ -219,18 +233,18 @@ export const NumberFieldWide = ({
|
|||
{help && (
|
||||
<p className="mt-2 text-sm text-gray-500 dark:text-gray-500" id={`${name}-description`}>{help}</p>
|
||||
)}
|
||||
<ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
<common.ErrorField name={name} classNames="block text-red-500 mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface SwitchGroupWideProps {
|
||||
name: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
defaultValue?: boolean;
|
||||
className?: string;
|
||||
tooltip?: JSX.Element;
|
||||
name: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
defaultValue?: boolean;
|
||||
className?: string;
|
||||
tooltip?: JSX.Element;
|
||||
}
|
||||
|
||||
export const SwitchGroupWide = ({
|
||||
|
@ -262,138 +276,23 @@ export const SwitchGroupWide = ({
|
|||
defaultValue={defaultValue as boolean}
|
||||
type="checkbox"
|
||||
>
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Switch
|
||||
{({
|
||||
field,
|
||||
form: { setFieldValue }
|
||||
}: FieldProps) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
type="button"
|
||||
value={field.value}
|
||||
checked={field.checked ?? false}
|
||||
onChange={(value: unknown) => {
|
||||
form.setFieldValue(field?.name ?? "", value);
|
||||
value={!!field.checked}
|
||||
setValue={(value) => {
|
||||
setFieldValue(field?.name ?? "", value);
|
||||
}}
|
||||
className={classNames(
|
||||
field.value ? "bg-blue-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-500",
|
||||
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Use setting</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
field.value ? "translate-x-5" : "translate-x-0",
|
||||
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Switch.Group>
|
||||
</ul>
|
||||
);
|
||||
|
||||
interface SwitchGroupWideRedProps {
|
||||
name: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
defaultValue?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const SwitchGroupWideRed = ({
|
||||
name,
|
||||
label,
|
||||
description,
|
||||
defaultValue
|
||||
}: SwitchGroupWideRedProps) => (
|
||||
<ul className="mt-2 px-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<Switch.Group as="li" className="py-4 flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
|
||||
passive>
|
||||
{label}
|
||||
</Switch.Label>
|
||||
{description && (
|
||||
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
|
||||
{description}
|
||||
</Switch.Description>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Field
|
||||
name={name}
|
||||
defaultValue={defaultValue as boolean}
|
||||
type="checkbox"
|
||||
>
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Switch
|
||||
{...field}
|
||||
type="button"
|
||||
value={field.value}
|
||||
checked={field.checked ?? false}
|
||||
onChange={(value: unknown) => {
|
||||
form.setFieldValue(field?.name ?? "", value);
|
||||
}}
|
||||
className={classNames(
|
||||
field.value ? "bg-blue-500 dark:bg-blue-500" : "bg-red-500 dark:bg-red-500",
|
||||
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Use setting</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
field.value ? "translate-x-5" : "translate-x-0",
|
||||
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
)}
|
||||
</Field>
|
||||
</Switch.Group>
|
||||
</ul>
|
||||
);
|
||||
|
||||
const Input = (props: InputProps) => {
|
||||
return (
|
||||
<components.Input
|
||||
{...props}
|
||||
inputClassName="outline-none border-none shadow-none focus:ring-transparent"
|
||||
className="text-gray-400 dark:text-gray-100"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Control = (props: ControlProps) => {
|
||||
return (
|
||||
<components.Control
|
||||
{...props}
|
||||
className="p-1 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = (props: MenuProps) => {
|
||||
return (
|
||||
<components.Menu
|
||||
{...props}
|
||||
className="dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-400 rounded-md shadow-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Option = (props: OptionProps) => {
|
||||
return (
|
||||
<components.Option
|
||||
{...props}
|
||||
className="dark:text-gray-400 dark:bg-gray-800 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SelectFieldWide = ({
|
||||
name,
|
||||
label,
|
||||
|
@ -405,7 +304,7 @@ export const SelectFieldWide = ({
|
|||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="flex text-sm font-medium text-gray-900 dark:text-white"
|
||||
className="flex ml-px text-sm font-medium text-gray-900 dark:text-white"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
|
@ -426,10 +325,12 @@ export const SelectFieldWide = ({
|
|||
isClearable={true}
|
||||
isSearchable={true}
|
||||
components={{
|
||||
Input,
|
||||
Control,
|
||||
Menu,
|
||||
Option
|
||||
Input: common.SelectInput,
|
||||
Control: common.SelectControl,
|
||||
Menu: common.SelectMenu,
|
||||
Option: common.SelectOption,
|
||||
IndicatorSeparator: common.IndicatorSeparator,
|
||||
DropdownIndicator: common.DropdownIndicator
|
||||
}}
|
||||
placeholder={optionDefaultText}
|
||||
styles={{
|
||||
|
|
|
@ -77,7 +77,7 @@ function RadioFieldsetWide({ name, legend, options }: props) {
|
|||
checked
|
||||
? "bg-blue-600 dark:bg-blue-500 border-transparent"
|
||||
: "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-300",
|
||||
"h-6 w-6 mt-1 cursor-pointer rounded-full border flex items-center justify-center"
|
||||
"h-6 w-6 mt-1 cursor-pointer rounded-full border flex items-center justify-center flex-shrink-0"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
@ -10,24 +10,23 @@ import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/solid";
|
|||
import { MultiSelect as RMSC } from "react-multi-select-component";
|
||||
|
||||
import { classNames, COL_WIDTHS } from "@utils";
|
||||
import { SettingsContext } from "@utils/Context";
|
||||
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
|
||||
|
||||
export interface MultiSelectOption {
|
||||
value: string | number;
|
||||
label: string;
|
||||
key?: string;
|
||||
disabled?: boolean;
|
||||
value: string | number;
|
||||
label: string;
|
||||
key?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface MultiSelectProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
options: MultiSelectOption[];
|
||||
columns?: COL_WIDTHS;
|
||||
creatable?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
name: string;
|
||||
label?: string;
|
||||
options: MultiSelectOption[];
|
||||
columns?: COL_WIDTHS;
|
||||
creatable?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
}
|
||||
|
||||
export const MultiSelect = ({
|
||||
|
@ -39,8 +38,6 @@ export const MultiSelect = ({
|
|||
tooltip,
|
||||
disabled
|
||||
}: MultiSelectProps) => {
|
||||
const settingsContext = SettingsContext.useValue();
|
||||
|
||||
const handleNewField = (value: string) => ({
|
||||
value: value.toUpperCase(),
|
||||
label: value.toUpperCase(),
|
||||
|
@ -50,11 +47,12 @@ export const MultiSelect = ({
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
"col-span-12",
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
<label
|
||||
htmlFor={label} className="flex mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200">
|
||||
htmlFor={label} className="flex ml-px mb-1 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-100">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
|
@ -83,7 +81,6 @@ export const MultiSelect = ({
|
|||
|
||||
setFieldValue(field.name, am);
|
||||
}}
|
||||
className={settingsContext.darkTheme ? "dark" : ""}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
@ -93,8 +90,8 @@ export const MultiSelect = ({
|
|||
|
||||
|
||||
interface IndexerMultiSelectOption {
|
||||
id: number;
|
||||
name: string;
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const IndexerMultiSelect = ({
|
||||
|
@ -102,55 +99,52 @@ export const IndexerMultiSelect = ({
|
|||
label,
|
||||
options,
|
||||
columns
|
||||
}: MultiSelectProps) => {
|
||||
const settingsContext = SettingsContext.useValue();
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
)}
|
||||
}: MultiSelectProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-12",
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
<label
|
||||
className="block ml-px mb-1 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
|
||||
htmlFor={label}
|
||||
>
|
||||
<label
|
||||
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
|
||||
htmlFor={label}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
{label}
|
||||
</label>
|
||||
|
||||
<Field name={name} type="select" multiple={true}>
|
||||
{({
|
||||
field,
|
||||
meta,
|
||||
form: { setFieldValue }
|
||||
}: FieldProps) => (
|
||||
<>
|
||||
<RMSC
|
||||
{...field}
|
||||
options={options}
|
||||
labelledBy={name}
|
||||
value={field.value && field.value.map((item: IndexerMultiSelectOption) => ({
|
||||
value: item.id, label: item.name
|
||||
}))}
|
||||
onChange={(values: MultiSelectOption[]) => {
|
||||
const item = values && values.map((i) => ({ id: i.value, name: i.label }));
|
||||
setFieldValue(field.name, item);
|
||||
}}
|
||||
className={settingsContext.darkTheme ? "dark" : ""}
|
||||
/>
|
||||
{meta.touched && meta.error && (
|
||||
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<Field name={name} type="select" multiple={true}>
|
||||
{({
|
||||
field,
|
||||
meta,
|
||||
form: { setFieldValue }
|
||||
}: FieldProps) => (
|
||||
<>
|
||||
<RMSC
|
||||
{...field}
|
||||
options={options}
|
||||
labelledBy={name}
|
||||
value={field.value && field.value.map((item: IndexerMultiSelectOption) => ({
|
||||
value: item.id, label: item.name
|
||||
}))}
|
||||
onChange={(values: MultiSelectOption[]) => {
|
||||
const item = values && values.map((i) => ({ id: i.value, name: i.label }));
|
||||
setFieldValue(field.name, item);
|
||||
}}
|
||||
/>
|
||||
{meta.touched && meta.error && (
|
||||
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface DownloadClientSelectProps {
|
||||
name: string;
|
||||
action: Action;
|
||||
clients: DownloadClient[];
|
||||
name: string;
|
||||
action: Action;
|
||||
clients: DownloadClient[];
|
||||
}
|
||||
|
||||
export function DownloadClientSelect({
|
||||
|
@ -159,7 +153,7 @@ export function DownloadClientSelect({
|
|||
clients
|
||||
}: DownloadClientSelectProps) {
|
||||
return (
|
||||
<div className="col-span-6 sm:col-span-6">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<Field name={name} type="select">
|
||||
{({
|
||||
field,
|
||||
|
@ -172,11 +166,11 @@ export function DownloadClientSelect({
|
|||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<Listbox.Label className="block text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
Client
|
||||
</Listbox.Label>
|
||||
<div className="mt-2 relative">
|
||||
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
|
||||
<div className="mt-1 relative">
|
||||
<Listbox.Button className="block w-full shadow-sm sm:text-sm rounded-md border py-2 pl-3 pr-10 text-left focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
|
||||
<span className="block truncate">
|
||||
{field.value
|
||||
? clients.find((c) => c.id === field.value)?.name
|
||||
|
@ -198,7 +192,7 @@ export function DownloadClientSelect({
|
|||
>
|
||||
<Listbox.Options
|
||||
static
|
||||
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
className="absolute z-10 mt-1 w-full border border-gray-400 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-lg max-h-60 rounded-md py-1 text-base overflow-auto focus:outline-none sm:text-sm"
|
||||
>
|
||||
{clients
|
||||
.filter((c) => c.type === action.type)
|
||||
|
@ -207,7 +201,7 @@ export function DownloadClientSelect({
|
|||
key={client.id}
|
||||
className={({ active }) => classNames(
|
||||
active
|
||||
? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-800"
|
||||
? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-950"
|
||||
: "text-gray-900 dark:text-gray-300",
|
||||
"cursor-default select-none relative py-2 pl-3 pr-9"
|
||||
)}
|
||||
|
@ -227,7 +221,7 @@ export function DownloadClientSelect({
|
|||
{selected ? (
|
||||
<span
|
||||
className={classNames(
|
||||
active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-gray-700",
|
||||
active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-blue-500",
|
||||
"absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
)}
|
||||
>
|
||||
|
@ -256,17 +250,17 @@ export function DownloadClientSelect({
|
|||
}
|
||||
|
||||
export interface SelectFieldOption {
|
||||
label: string;
|
||||
value: string;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface SelectFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
optionDefaultText: string;
|
||||
options: SelectFieldOption[];
|
||||
columns?: COL_WIDTHS;
|
||||
tooltip?: JSX.Element;
|
||||
name: string;
|
||||
label: string;
|
||||
optionDefaultText: string;
|
||||
options: SelectFieldOption[];
|
||||
columns?: COL_WIDTHS;
|
||||
tooltip?: JSX.Element;
|
||||
}
|
||||
|
||||
export const Select = ({
|
||||
|
@ -275,12 +269,13 @@ export const Select = ({
|
|||
tooltip,
|
||||
optionDefaultText,
|
||||
options,
|
||||
columns
|
||||
columns = 6
|
||||
}: SelectFieldProps) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-6"
|
||||
"col-span-12",
|
||||
columns ? `sm:col-span-${columns}` : ""
|
||||
)}
|
||||
>
|
||||
<Field name={name} type="select">
|
||||
|
@ -289,20 +284,21 @@ export const Select = ({
|
|||
form: { setFieldValue }
|
||||
}: FieldProps) => (
|
||||
<Listbox
|
||||
value={field.value}
|
||||
onChange={(value) => setFieldValue(field?.name, value)}
|
||||
// ?? null is required here otherwise React throws:
|
||||
// "console.js:213 A component is changing from uncontrolled to controlled.
|
||||
// This may be caused by the value changing from undefined to a defined value, which should not happen."
|
||||
value={field.value ?? null}
|
||||
onChange={(value) => setFieldValue(field.name, value)}
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Listbox.Label className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</div>
|
||||
<Listbox.Label className="flex text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{tooltip ? (
|
||||
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
|
||||
) : label}
|
||||
</Listbox.Label>
|
||||
<div className="mt-2 relative">
|
||||
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2.5 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
|
||||
<div className="mt-1 relative">
|
||||
<Listbox.Button className="block w-full relative shadow-sm sm:text-sm text-left rounded-md border pl-3 pr-10 py-2.5 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
|
||||
<span className="block truncate">
|
||||
{field.value
|
||||
? options.find((c) => c.value === field.value)?.label
|
||||
|
@ -326,45 +322,39 @@ export const Select = ({
|
|||
>
|
||||
<Listbox.Options
|
||||
static
|
||||
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
className="absolute z-10 mt-1 w-full shadow-lg max-h-60 rounded-md py-1 text-base overflow-auto border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100 focus:outline-none sm:text-sm"
|
||||
>
|
||||
{options.map((opt) => (
|
||||
<Listbox.Option
|
||||
key={opt.value}
|
||||
className={({ active }) =>
|
||||
className={({ active: hovered, selected }) =>
|
||||
classNames(
|
||||
active
|
||||
? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-800"
|
||||
: "text-gray-900 dark:text-gray-300",
|
||||
"cursor-default select-none relative py-2 pl-3 pr-9"
|
||||
selected
|
||||
? "font-bold text-black dark:text-white bg-gray-300 dark:bg-gray-950"
|
||||
: (
|
||||
hovered
|
||||
? "text-black dark:text-gray-100 font-normal"
|
||||
: "text-gray-700 dark:text-gray-300 font-normal"
|
||||
),
|
||||
hovered ? "bg-gray-200 dark:bg-gray-800" : "",
|
||||
"transition-colors cursor-default select-none relative py-2 pl-3 pr-9"
|
||||
)
|
||||
}
|
||||
value={opt.value}
|
||||
>
|
||||
{({ selected, active }) => (
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
selected ? "font-semibold" : "font-normal",
|
||||
"block truncate"
|
||||
)}
|
||||
>
|
||||
<span className="block truncate">
|
||||
{opt.label}
|
||||
</span>
|
||||
|
||||
{selected ? (
|
||||
<span
|
||||
className={classNames(
|
||||
active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-gray-700",
|
||||
"absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
)}
|
||||
>
|
||||
<CheckIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
className={classNames(
|
||||
selected ? "visible" : "invisible",
|
||||
"absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5 text-blue-600 dark:text-blue-500" aria-hidden="true" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import type { FieldProps } from "formik";
|
||||
import { Field } from "formik";
|
||||
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
||||
import { OptionBasicTyped } from "@domain/constants";
|
||||
import Select from "react-select";
|
||||
import CreatableSelect from "react-select/creatable";
|
||||
import type { FieldProps } from "formik";
|
||||
|
||||
import { OptionBasicTyped } from "@domain/constants";
|
||||
import * as common from "@components/inputs/common";
|
||||
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
|
||||
|
||||
interface SelectFieldProps<T> {
|
||||
|
@ -26,7 +28,7 @@ export function SelectFieldCreatable<T>({ name, label, help, placeholder, toolti
|
|||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
className="block ml-px text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
|
@ -47,10 +49,12 @@ export function SelectFieldCreatable<T>({ name, label, help, placeholder, toolti
|
|||
isClearable={true}
|
||||
isSearchable={true}
|
||||
components={{
|
||||
Input,
|
||||
Control,
|
||||
Menu,
|
||||
Option
|
||||
Input: common.SelectInput,
|
||||
Control: common.SelectControl,
|
||||
Menu: common.SelectMenu,
|
||||
Option: common.SelectOption,
|
||||
IndicatorSeparator: common.IndicatorSeparator,
|
||||
DropdownIndicator: common.DropdownIndicator
|
||||
}}
|
||||
placeholder={placeholder ?? "Choose an option"}
|
||||
styles={{
|
||||
|
@ -89,54 +93,13 @@ export function SelectFieldCreatable<T>({ name, label, help, placeholder, toolti
|
|||
);
|
||||
}
|
||||
|
||||
const Input = (props: InputProps) => {
|
||||
return (
|
||||
<components.Input
|
||||
{...props}
|
||||
inputClassName="outline-none border-none shadow-none focus:ring-transparent"
|
||||
className="text-gray-400 dark:text-gray-100"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Control = (props: ControlProps) => {
|
||||
return (
|
||||
<components.Control
|
||||
{...props}
|
||||
className="p-1 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = (props: MenuProps) => {
|
||||
return (
|
||||
<components.Menu
|
||||
{...props}
|
||||
className="dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-400 rounded-md shadow-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Option = (props: OptionProps) => {
|
||||
return (
|
||||
<components.Option
|
||||
{...props}
|
||||
className="dark:text-gray-400 dark:bg-gray-800 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export function SelectField<T>({ name, label, help, placeholder, options }: SelectFieldProps<T>) {
|
||||
return (
|
||||
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
className="block ml-px text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
|
@ -151,10 +114,12 @@ export function SelectField<T>({ name, label, help, placeholder, options }: Sele
|
|||
{...field}
|
||||
id={name}
|
||||
components={{
|
||||
Input,
|
||||
Control,
|
||||
Menu,
|
||||
Option
|
||||
Input: common.SelectInput,
|
||||
Control: common.SelectControl,
|
||||
Menu: common.SelectMenu,
|
||||
Option: common.SelectOption,
|
||||
IndicatorSeparator: common.IndicatorSeparator,
|
||||
DropdownIndicator: common.DropdownIndicator
|
||||
}}
|
||||
placeholder={placeholder ?? "Choose an option"}
|
||||
styles={{
|
||||
|
@ -199,7 +164,7 @@ export function SelectFieldBasic<T>({ name, label, help, placeholder, tooltip, d
|
|||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
className="block ml-px text-sm font-medium text-gray-900 dark:text-white sm:pt-2"
|
||||
>
|
||||
<div className="flex">
|
||||
{tooltip ? (
|
||||
|
@ -218,10 +183,12 @@ export function SelectFieldBasic<T>({ name, label, help, placeholder, tooltip, d
|
|||
{...field}
|
||||
id={name}
|
||||
components={{
|
||||
Input,
|
||||
Control,
|
||||
Menu,
|
||||
Option
|
||||
Input: common.SelectInput,
|
||||
Control: common.SelectControl,
|
||||
Menu: common.SelectMenu,
|
||||
Option: common.SelectOption,
|
||||
IndicatorSeparator: common.IndicatorSeparator,
|
||||
DropdownIndicator: common.DropdownIndicator
|
||||
}}
|
||||
placeholder={placeholder ?? "Choose an option"}
|
||||
styles={{
|
||||
|
|
|
@ -3,80 +3,22 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import type { FieldInputProps, FieldMetaProps, FieldProps, FormikProps, FormikValues } from "formik";
|
||||
import type { FieldProps } from "formik";
|
||||
import { Field } from "formik";
|
||||
import { Switch as HeadlessSwitch } from "@headlessui/react";
|
||||
|
||||
import { classNames } from "@utils";
|
||||
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
|
||||
|
||||
type SwitchProps<V = unknown> = {
|
||||
label?: string
|
||||
checked: boolean
|
||||
value: boolean
|
||||
disabled?: boolean
|
||||
onChange: (value: boolean) => void
|
||||
field?: FieldInputProps<V>
|
||||
form?: FormikProps<FormikValues>
|
||||
meta?: FieldMetaProps<V>
|
||||
children: React.ReactNode
|
||||
className: string
|
||||
};
|
||||
|
||||
export const Switch = ({
|
||||
label,
|
||||
checked: $checked,
|
||||
disabled = false,
|
||||
onChange: $onChange,
|
||||
field,
|
||||
form
|
||||
}: SwitchProps) => {
|
||||
const checked = field?.checked ?? $checked;
|
||||
|
||||
return (
|
||||
<HeadlessSwitch.Group as="div" className="flex items-center space-x-4">
|
||||
<HeadlessSwitch.Label>{label}</HeadlessSwitch.Label>
|
||||
<HeadlessSwitch
|
||||
as="button"
|
||||
name={field?.name}
|
||||
disabled={disabled}
|
||||
checked={checked}
|
||||
onChange={value => {
|
||||
form?.setFieldValue(field?.name ?? "", value);
|
||||
$onChange && $onChange(value);
|
||||
}}
|
||||
|
||||
className={classNames(
|
||||
checked ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
|
||||
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
)}
|
||||
>
|
||||
{({ checked }) => (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
checked ? "translate-x-5" : "translate-x-0",
|
||||
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</HeadlessSwitch>
|
||||
</HeadlessSwitch.Group>
|
||||
);
|
||||
};
|
||||
|
||||
export type SwitchFormikProps = SwitchProps & FieldProps & React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
export const SwitchFormik = (props: SwitchProps) => <Switch {...props} children={props.children}/>;
|
||||
import { Checkbox } from "@components/Checkbox";
|
||||
|
||||
interface SwitchGroupProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
heading?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
name: string;
|
||||
label?: string;
|
||||
description?: string | React.ReactNode;
|
||||
heading?: boolean;
|
||||
tooltip?: JSX.Element;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SwitchGroup = ({
|
||||
|
@ -84,15 +26,23 @@ const SwitchGroup = ({
|
|||
label,
|
||||
description,
|
||||
tooltip,
|
||||
heading
|
||||
heading,
|
||||
disabled,
|
||||
className
|
||||
}: SwitchGroupProps) => (
|
||||
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
|
||||
<HeadlessSwitch.Group
|
||||
as="div"
|
||||
className={classNames(
|
||||
className ?? "py-2",
|
||||
"flex items-center justify-between"
|
||||
)}
|
||||
>
|
||||
{label && <div className="flex flex-col">
|
||||
<HeadlessSwitch.Label
|
||||
passive
|
||||
as={heading ? "h2" : "span"}
|
||||
className={classNames(
|
||||
"flex float-left cursor-default mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide",
|
||||
"flex float-left ml-px cursor-default text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide",
|
||||
heading ? "text-lg" : "text-sm"
|
||||
)}
|
||||
>
|
||||
|
@ -103,7 +53,7 @@ const SwitchGroup = ({
|
|||
</div>
|
||||
</HeadlessSwitch.Label>
|
||||
{description && (
|
||||
<HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400">
|
||||
<HeadlessSwitch.Description as="span" className="text-sm mt-1 pr-4 text-gray-500 dark:text-gray-400">
|
||||
{description}
|
||||
</HeadlessSwitch.Description>
|
||||
)}
|
||||
|
@ -115,28 +65,15 @@ const SwitchGroup = ({
|
|||
field,
|
||||
form: { setFieldValue }
|
||||
}: FieldProps) => (
|
||||
<Switch
|
||||
<Checkbox
|
||||
{...field}
|
||||
// type="button"
|
||||
value={field.value}
|
||||
checked={field.checked ?? false}
|
||||
onChange={value => {
|
||||
className=""
|
||||
value={!!field.checked}
|
||||
setValue={(value) => {
|
||||
setFieldValue(field?.name ?? "", value);
|
||||
}}
|
||||
className={classNames(
|
||||
field.value ? "bg-blue-500" : "bg-gray-200",
|
||||
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
field.value ? "translate-x-5" : "translate-x-0",
|
||||
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</HeadlessSwitch.Group>
|
||||
|
|
|
@ -112,7 +112,7 @@ export const TextInput = <TFormValues extends Record<string, unknown>>({
|
|||
)}
|
||||
>
|
||||
{props.label && (
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<label htmlFor={name} className="block ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{props.label}
|
||||
</label>
|
||||
)}
|
||||
|
@ -121,8 +121,10 @@ export const TextInput = <TFormValues extends Record<string, unknown>>({
|
|||
name={name}
|
||||
aria-invalid={hasError}
|
||||
className={classNames(
|
||||
"mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md",
|
||||
hasError ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700"
|
||||
"block mt-1 w-full shadow-sm sm:text-sm rounded-md py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100",
|
||||
hasError
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500"
|
||||
)}
|
||||
{...props}
|
||||
{...(register && register(name, rules))}
|
||||
|
@ -162,7 +164,7 @@ export const PasswordInput = <TFormValues extends Record<string, unknown>>({
|
|||
)}
|
||||
>
|
||||
{props.label && (
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
<label htmlFor={name} className="block ml-px text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
|
||||
{props.label}
|
||||
</label>
|
||||
)}
|
||||
|
@ -173,8 +175,10 @@ export const PasswordInput = <TFormValues extends Record<string, unknown>>({
|
|||
aria-invalid={hasError}
|
||||
type={isVisible ? "text" : "password"}
|
||||
className={classNames(
|
||||
"mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md",
|
||||
hasError ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700"
|
||||
"block mt-1 w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100",
|
||||
hasError
|
||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500"
|
||||
)}
|
||||
{...props}
|
||||
{...(register && register(name, rules))}
|
||||
|
|
|
@ -15,9 +15,12 @@ type Props = {
|
|||
};
|
||||
|
||||
const Toast: FC<Props> = ({ type, body, t }) => (
|
||||
<div className={classNames(
|
||||
t?.visible ? "animate-enter" : "animate-leave",
|
||||
"max-w-sm w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden transition-all")}>
|
||||
<div
|
||||
className={classNames(
|
||||
t?.visible ? "animate-enter" : "animate-leave",
|
||||
"max-w-sm w-full bg-white dark:bg-gray-800 whitespace-pre-wrap shadow-2xl rounded-lg pointer-events-auto border border-gray-250 dark:border-gray-775 overflow-hidden transition-all"
|
||||
)}
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="flex items-start">
|
||||
<div className="flex-shrink-0">
|
||||
|
@ -51,4 +54,4 @@ const Toast: FC<Props> = ({ type, body, t }) => (
|
|||
</div>
|
||||
);
|
||||
|
||||
export default Toast;
|
||||
export default Toast;
|
||||
|
|
|
@ -96,7 +96,7 @@ function SlideOver<DataType extends FormikValues>({
|
|||
>
|
||||
{({ handleSubmit, values }) => (
|
||||
<Form
|
||||
className="h-full flex flex-col bg-white dark:bg-gray-800 shadow-xl overflow-y-scroll"
|
||||
className="h-full flex flex-col bg-white dark:bg-gray-800 shadow-xl overflow-y-auto"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit(e);
|
||||
|
|
|
@ -37,8 +37,9 @@ export const Tooltip = ({
|
|||
visible
|
||||
} = usePopperTooltip({
|
||||
trigger: requiresClick ? ["click"] : ["click", "hover"],
|
||||
interactive: !requiresClick,
|
||||
delayHide: 200
|
||||
interactive: true,
|
||||
delayHide: 200,
|
||||
placement: "right"
|
||||
});
|
||||
|
||||
if (!children || Array.isArray(children) && !children.length) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue