Ahojte,
chtěl jsem promyslet a navrhnout lepší architekturu form komponent, protože momentální development verze mi přišla pro používání zbytečně komplikovaná.
Cíle:
-
Zjednodušit primitives a jejich api + pokud možno mít je
by default SSR-friendly -
Přizpůsobit architekturu po vzoru
shadcnhlavně z hlediska dx (implementace) a zároveň maximálně využít již existující vlastnosti / apiheadless-uikomponent (e.g. accessibility, předdefinované form komponenty) které si wrappujeme.
Momentální stav (příklad na input + jeho submission form)
- Input
import { Input as InputHeadless, type InputProps } from '@headlessui/react';
import React from 'react';
type InputCompProps = React.ComponentProps<'input'> & InputProps;
export function InputComp({ invalid, type, name, placeholder, ...props }: InputCompProps, ref: React.Ref<HTMLInputElement>) {
return (
<InputHeadless invalid={invalid} ref={ref} {...props} type={type} placeholder={placeholder} className="ko:w-full ko:border ko:border-black ko:rounded-2xl ko:rounded-br-none ko:p-4" name={name} />
);
}
export const Input = React.forwardRef<HTMLInputElement, InputCompProps>(InputComp);
- Form
*tato komponent vznikla jako rychlý test, zbytek headless-ui ještě přebranduji, ale víceméně tyto asi nemá smysl moc customizovat, validace je teď na rychlo (v produkci počítám se zodem), ještě mrknu na nějaké ty nativní headless-ui api (pokud budou pro nás využitelné)
import { Description, Field, Label } from '@headlessui/react';
import { Input } from '@repo/design-system/components';
import { type SubmitHandler, useForm } from 'react-hook-form';
type EmailSubmissionFormProps = {
email: string;
};
export function EmailSubmissionForm() {
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<EmailSubmissionFormProps>();
const onSubmit: SubmitHandler<EmailSubmissionFormProps> = (data) => {
console.log('Form submitted:', data);
};
return (
<div className="ko:w-1/2">
<form onSubmit={handleSubmit(onSubmit)} className="ko:flex">
<Field className="ko:relative">
<Label className="ko:hidden">Jméno</Label>
<Input
type="email"
placeholder="Zadejte e-mail"
invalid={!isValid}
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address',
},
})}
/>
{errors.email && <Description className="ko:absolute ko:-bottom-1.5 ko:right-4 ko:px-1 ko:bg-white ko:text-xs ko:text-red-500">{errors.email.message}</Description>}
</Field>
<button className="ko:w-fit ko:px-4 ko:border-black ko:rounded-2xl ko:rounded-tr-none ko:border-2 " type="submit">
Submit
</button>
</form>
</div>
);
}
a) Ten přístup by z toho měl být jasný (separace a zjednodušení primitives, zbytek delegovat na specifickou form komponentu)
b) Dejte vědět @martinwenisch , @krystof-k
c) Ještě by stálo za zvážení postavení custom form komponenty obdobně jako shadcn, otázka, jak je to teď zásadní
d) Rozpracoval jsem si radiogroup, a pillgroup; pošlu asap