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:
stacksmash76 2023-11-18 13:46:16 +00:00 committed by GitHub
parent a274d9ddce
commit e842a7bd42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
84 changed files with 4378 additions and 4361 deletions

View file

@ -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>
);
);

View file

@ -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>
);

View file

@ -29,4 +29,4 @@ export const SectionLoader = ({ $size }: SectionLoaderProps) => {
<RingResizeSpinner className={classNames(SIZE[$size], "text-blue-500")} />
);
}
}
};

View file

@ -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!";
}
};

View 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>
);

View file

@ -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";

View file

@ -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>
);
);

View file

@ -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) => (
<>

View file

@ -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>
);
}
}

View file

@ -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"
>

View file

@ -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} />

View file

@ -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>
);
}
};

View file

@ -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>
);
);

View file

@ -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"
/>
);

View file

@ -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";

View file

@ -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}

View file

@ -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={{

View file

@ -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"
/>

View file

@ -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>

View file

@ -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={{

View file

@ -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>

View file

@ -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))}

View file

@ -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;

View file

@ -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);

View file

@ -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) {