@4i4/theme-form
v0.1.8
Published
Form primitives for @4i4/theme-registry
Readme
@4i4/theme-form
Form-oriented components built on top of @4i4/theme-registry.
Installation
npm install @4i4/theme-form @4i4/theme-registryyarn add @4i4/theme-form @4i4/theme-registryUsage
import { FormElement } from "@4i4/theme-form";
<FormElement type="input" name="email" label="Email" />;
<FormElement
type="select"
name="country"
label="Country"
placeholder="Select country"
options={[
{ value: "de", label: "Germany" },
{ value: "us", label: "United States" },
{ value: "au", label: "Australia" },
]}
/>;FormElement looks up both field and wrapper templates within the registry using sensible defaults based on the provided type and name. When a template is missing it renders a simple diagnostic fallback component so gaps in the registry are easy to spot during development.
The optional scope prop lets you override the context key used for template lookup. By default it uses form-element, but you can specify a custom scope if your registry stores form templates under a different namespace.
Registering templates
A minimal text field template might look like this:
// TextField.tsx
import type { FormFieldProps } from "@4i4/theme-form";
type TextFieldProps = FormFieldProps;
export default function TextField({ name, ...props }: TextFieldProps) {
return <input type="text" name={name} {...props} />;
}
A select field template can consume the same base props while rendering choices:
// SelectField.tsx
import type { FormFieldProps } from "@4i4/theme-form";
type SelectFieldProps = FormFieldProps;
export default function SelectField({ options = [], ...props }: SelectFieldProps) {
return (
<select {...props}>
{options.filter(({ hidden }) => !hidden).map(({ value, label }) => (
<option key={String(value)} value={value}>
{label}
</option>
))}
</select>
);
}
Each option can also declare hidden: true to keep it out of the rendered list while preserving the value for other use cases.
A minimal wrapper can decorate the resolved field while accepting additional props:
// FieldWrapper.tsx
import type { ReactNode } from "react";
import type { FieldWrapperProps } from "@4i4/theme-form";
type WrapperProps = FieldWrapperProps<{
error?: ReactNode;
helper?: ReactNode;
}>;
export default function FieldWrapper({
Component,
label,
error,
helper,
type,
name,
id,
...props
}: WrapperProps) {
const labelTemplates = [
'label',
`label--${type}`,
`label--${name}`,
`label--${type}--${name}`,
];
return (
<div>
{label ? (
<Template template={labelTemplates} htmlFor={id}>
{label}
</Template>
) : null}
<Component name={name} id={id} {...props} />
{error}
{helper}
</div>
);
}