2.0 Overhaul start
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import type React from "react";
|
||||
import { forwardRef, InputHTMLAttributes } from "react";
|
||||
|
||||
export interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label: string;
|
||||
description?: string;
|
||||
options?: string[];
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
action?: {
|
||||
icon: JSX.Element;
|
||||
action: () => void;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
function Input(
|
||||
{
|
||||
label,
|
||||
description,
|
||||
options,
|
||||
prefix,
|
||||
suffix,
|
||||
action,
|
||||
error,
|
||||
children,
|
||||
...rest
|
||||
}: CheckboxProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex h-5 items-center">
|
||||
<input
|
||||
ref={ref}
|
||||
type="checkbox"
|
||||
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label htmlFor="comments" className="font-medium text-gray-700">
|
||||
{label}
|
||||
</label>
|
||||
<p id="comments-description" className="text-gray-500">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1,15 +1,29 @@
|
||||
import type React from "react";
|
||||
import type { HTMLProps } from "react";
|
||||
|
||||
import { Button, majorScale, Pane, SavedIcon, Spinner } from "evergreen-ui";
|
||||
import { FiSave } from "react-icons/fi";
|
||||
|
||||
import { Button } from "@components/Button.js";
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
ChevronRightIcon,
|
||||
HomeIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
export interface FormProps extends HTMLProps<HTMLFormElement> {
|
||||
title: string;
|
||||
breadcrumbs: string[];
|
||||
reset: () => void;
|
||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
|
||||
loading: boolean;
|
||||
dirty: boolean;
|
||||
}
|
||||
|
||||
export const Form = ({
|
||||
title,
|
||||
breadcrumbs,
|
||||
reset,
|
||||
loading,
|
||||
dirty,
|
||||
children,
|
||||
@@ -18,31 +32,48 @@ export const Form = ({
|
||||
}: FormProps): JSX.Element => {
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
<form onSubmit={onSubmit} style={{ position: "relative" }} {...props}>
|
||||
<form className="w-full" onSubmit={onSubmit} {...props}>
|
||||
{loading && (
|
||||
<Pane
|
||||
position="absolute"
|
||||
display="flex"
|
||||
width="100%"
|
||||
height="100%"
|
||||
backgroundColor="rgba(67, 90, 111, 0.2)"
|
||||
zIndex={10}
|
||||
borderRadius={majorScale(1)}
|
||||
>
|
||||
<Spinner margin="auto" />
|
||||
</Pane>
|
||||
<div className="absolute flex w-full h-full bg-slate-600 rounded-md z-10">
|
||||
<ArrowPathIcon className="h-8 animate-spin m-auto" />
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
<Pane display="flex" marginTop={majorScale(2)}>
|
||||
<Button
|
||||
type="submit"
|
||||
marginLeft="auto"
|
||||
disabled={!dirty}
|
||||
iconBefore={<SavedIcon />}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Pane>
|
||||
<div className="select-none rounded-md p-4 bg-gray-700">
|
||||
<ol className="flex gap-4">
|
||||
<li className="text-gray-400 hover:text-gray-200 cursor-pointer">
|
||||
<HomeIcon className="h-5 w-5 flex-shrink-0 text-gray-400" />
|
||||
</li>
|
||||
{breadcrumbs.map((breadcrumb, index) => (
|
||||
<li key={index} className="flex gap-4">
|
||||
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-500" />
|
||||
<span className="text-sm font-medium text-gray-400 hover:text-gray-200 cursor-pointer">
|
||||
{breadcrumb}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<div className="mt-2 flex items-center">
|
||||
<h2 className="font-bold text-white truncate text-3xl tracking-tight">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="flex gap-2 ml-auto">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
reset();
|
||||
}}
|
||||
variant="secondary"
|
||||
iconBefore={<ArrowUturnLeftIcon className="w-4" />}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<Button disabled={!dirty} iconBefore={<FiSave className="w-4" />}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col p-2 gap-3">{children}</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import type React from "react";
|
||||
import { forwardRef, InputHTMLAttributes } from "react";
|
||||
|
||||
import { ExclamationCircleIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label: string;
|
||||
description?: string;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
action?: {
|
||||
icon: JSX.Element;
|
||||
action: () => void;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
{ label, description, prefix, suffix, action, error, ...rest }: InputProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div>
|
||||
{/* Label */}
|
||||
<label className="block text-sm font-medium text-gray-700">{label}</label>
|
||||
{/* */}
|
||||
<div className="relative flex rounded-md shadow-sm">
|
||||
{prefix && (
|
||||
<span className="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm">
|
||||
{prefix}
|
||||
</span>
|
||||
)}
|
||||
<input
|
||||
ref={ref}
|
||||
className={`block w-full min-w-0 flex-1 rounded-md border border-gray-300 px-3 h-10 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm ${
|
||||
prefix ? "rounded-l-none" : ""
|
||||
} ${action ? "rounded-r-none" : ""}`}
|
||||
{...rest}
|
||||
/>
|
||||
{suffix && (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<span className="text-gray-500 sm:text-sm" id="price-currency">
|
||||
{suffix}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{action && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={action.action}
|
||||
className="relative -ml-px inline-flex items-center space-x-2 rounded-r-md border border-gray-300 bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||
>
|
||||
{action.icon}
|
||||
{/* <span>Sort</span> */}
|
||||
</button>
|
||||
)}
|
||||
{error && (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<ExclamationCircleIcon className="h-5 w-5 text-red-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{description && (
|
||||
<p className="mt-2 text-sm text-gray-500" id="email-description">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
{error && (
|
||||
<p className="mt-2 text-sm text-red-600" id="email-error">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import type React from "react";
|
||||
import { forwardRef, SelectHTMLAttributes } from "react";
|
||||
|
||||
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||
label: string;
|
||||
description?: string;
|
||||
options?: string[];
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
action?: {
|
||||
icon: JSX.Element;
|
||||
action: () => void;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Input(
|
||||
{
|
||||
label,
|
||||
description,
|
||||
options,
|
||||
prefix,
|
||||
suffix,
|
||||
action,
|
||||
error,
|
||||
children,
|
||||
...rest
|
||||
}: SelectProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="location"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<div className="flex">
|
||||
<select
|
||||
ref={ref}
|
||||
className={`block w-full min-w-0 flex-1 rounded-md border border-gray-300 px-3 py-2 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm ${
|
||||
prefix ? "rounded-l-none" : ""
|
||||
} ${action ? "rounded-r-none" : ""}`}
|
||||
{...rest}
|
||||
>
|
||||
{options &&
|
||||
options.map((option, index) => (
|
||||
<option key={index}>{option}</option>
|
||||
))}
|
||||
{children}
|
||||
</select>
|
||||
{action && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={action.action}
|
||||
className="relative -ml-px inline-flex items-center space-x-2 rounded-r-md border border-gray-300 bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||
>
|
||||
{action.icon}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import type React from "react";
|
||||
|
||||
import { Switch } from "@headlessui/react";
|
||||
|
||||
export interface ToggleProps {
|
||||
label: string;
|
||||
description: string;
|
||||
checked: boolean;
|
||||
onChange?: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
export const Toggle = ({
|
||||
label,
|
||||
description,
|
||||
checked,
|
||||
onChange,
|
||||
}: ToggleProps): JSX.Element => {
|
||||
return (
|
||||
<Switch.Group as="div" className="flex items-center justify-between">
|
||||
<span className="flex flex-grow flex-col">
|
||||
<Switch.Label
|
||||
as="span"
|
||||
className="text-sm font-medium text-gray-900"
|
||||
passive
|
||||
>
|
||||
{label}
|
||||
</Switch.Label>
|
||||
<Switch.Description as="span" className="text-sm text-gray-500">
|
||||
{description}
|
||||
</Switch.Description>
|
||||
</span>
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${
|
||||
checked ? "bg-indigo-600" : "bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
|
||||
checked ? "translate-x-5" : "translate-x-0"
|
||||
}`}
|
||||
/>
|
||||
</Switch>
|
||||
</Switch.Group>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user