@@ -0,0 +1,104 @@
|
||||
import type {
|
||||
InputHTMLAttributes,
|
||||
ReactNode,
|
||||
SelectHTMLAttributes,
|
||||
TextareaHTMLAttributes,
|
||||
} from 'react';
|
||||
import Icon from '@/components/site/icon';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const controlClasses =
|
||||
'w-full rounded-xl border border-outline-variant/60 bg-surface-container-lowest px-4 py-2.5 text-sm text-on-surface transition-colors placeholder:text-on-surface-variant/50 focus:border-primary focus:outline-hidden';
|
||||
|
||||
type FieldProps = {
|
||||
id: string;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function Field({
|
||||
id,
|
||||
label,
|
||||
required,
|
||||
className,
|
||||
children,
|
||||
}: FieldProps) {
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-1.5', className)}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="text-on-surface font-mono text-xs font-medium"
|
||||
>
|
||||
{label}
|
||||
{required && ' *'}
|
||||
</label>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TextInput({
|
||||
className,
|
||||
...props
|
||||
}: InputHTMLAttributes<HTMLInputElement>) {
|
||||
return <input className={cn(controlClasses, className)} {...props} />;
|
||||
}
|
||||
|
||||
export function SelectInput({
|
||||
options,
|
||||
placeholder,
|
||||
className,
|
||||
...props
|
||||
}: SelectHTMLAttributes<HTMLSelectElement> & {
|
||||
options: ReadonlyArray<{ value: string; label: string }>;
|
||||
placeholder?: string;
|
||||
}) {
|
||||
return (
|
||||
<select
|
||||
className={cn(controlClasses, 'cursor-pointer', className)}
|
||||
defaultValue={placeholder ? '' : undefined}
|
||||
{...props}
|
||||
>
|
||||
{placeholder && (
|
||||
<option value="" disabled>
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
export function TextArea({
|
||||
className,
|
||||
...props
|
||||
}: TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
||||
return <textarea className={cn(controlClasses, className)} {...props} />;
|
||||
}
|
||||
|
||||
export function FormSuccess({
|
||||
title,
|
||||
message,
|
||||
}: {
|
||||
title: string;
|
||||
message: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
className="border-primary/40 bg-surface-container-lowest flex flex-col items-center gap-2 rounded-xl border p-6 text-center"
|
||||
>
|
||||
<Icon name="check_circle" className="text-primary text-4xl" />
|
||||
<span className="font-display text-on-surface text-base font-bold">
|
||||
{title}
|
||||
</span>
|
||||
<p className="text-on-surface-variant text-xs">{message}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user