Migrate form components to Zod props

This commit is contained in:
Mats Bosson 2026-03-29 20:41:18 +07:00
parent c0019d57e7
commit 2e3d034a12
14 changed files with 165 additions and 53 deletions

View File

@ -0,0 +1,20 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const CheckboxRootPropsSchema = z.object({
checked: z.boolean().optional().describe("Controlled checked state of the checkbox"),
defaultChecked: z.boolean().optional().describe("Initial checked state (uncontrolled)"),
disabled: z.boolean().optional().describe("Whether the checkbox is disabled"),
required: z.boolean().optional().describe("Whether the checkbox is required for form submission"),
name: z.string().optional().describe("Native form field name — renders a hidden checkbox input"),
value: z.string().optional().describe("Value submitted with the form when checked. Defaults to 'on'"),
});
export interface CheckboxRootProps extends z.infer<typeof CheckboxRootPropsSchema> {
onCheckedChange?: (checked: boolean) => void;
children: JSX.Element;
}
export const CheckboxMeta: ComponentMeta = {
name: "Checkbox", description: "Toggle control for boolean input, supports indeterminate state",
parts: ["Root", "Input", "Control", "Indicator", "Label", "Description", "ErrorMessage"] as const,
requiredParts: ["Root"] as const,
} as const;

View File

@ -1,2 +1,4 @@
export { Checkbox } from "./checkbox"; import { Checkbox } from "./checkbox";
export { CheckboxRootPropsSchema, CheckboxMeta } from "./checkbox.props";
export type { CheckboxProps, CheckedState } from "./checkbox"; export type { CheckboxProps, CheckedState } from "./checkbox";
export { Checkbox };

View File

@ -1,23 +1,13 @@
// packages/core/src/components/number-field/index.ts import { useNumberFieldContext as useCtx } from "./number-field-context";
import { useNumberFieldContext } from "./number-field-context"; import { NumberFieldDecrementTrigger as Dec } from "./number-field-decrement-trigger";
import { NumberFieldDecrementTrigger } from "./number-field-decrement-trigger"; import { NumberFieldDescription as Desc } from "./number-field-description";
import { NumberFieldDescription } from "./number-field-description"; import { NumberFieldErrorMessage as Err } from "./number-field-error-message";
import { NumberFieldErrorMessage } from "./number-field-error-message"; import { NumberFieldIncrementTrigger as Inc } from "./number-field-increment-trigger";
import { NumberFieldIncrementTrigger } from "./number-field-increment-trigger"; import { NumberFieldInput as Input } from "./number-field-input";
import { NumberFieldInput } from "./number-field-input"; import { NumberFieldLabel as Label } from "./number-field-label";
import { NumberFieldLabel } from "./number-field-label"; import { NumberFieldRoot as Root } from "./number-field-root";
import { NumberFieldRoot } from "./number-field-root"; export { NumberFieldRootPropsSchema, NumberFieldMeta } from "./number-field.props";
export type { NumberFieldContextValue } from "./number-field-context";
export const NumberField = Object.assign(NumberFieldRoot, {
Label: NumberFieldLabel,
Input: NumberFieldInput,
IncrementTrigger: NumberFieldIncrementTrigger,
DecrementTrigger: NumberFieldDecrementTrigger,
Description: NumberFieldDescription,
ErrorMessage: NumberFieldErrorMessage,
useContext: useNumberFieldContext,
});
export type { NumberFieldRootProps } from "./number-field-root"; export type { NumberFieldRootProps } from "./number-field-root";
export type { NumberFieldLabelProps } from "./number-field-label"; export type { NumberFieldLabelProps } from "./number-field-label";
export type { NumberFieldInputProps } from "./number-field-input"; export type { NumberFieldInputProps } from "./number-field-input";
@ -25,4 +15,4 @@ export type { NumberFieldIncrementTriggerProps } from "./number-field-increment-
export type { NumberFieldDecrementTriggerProps } from "./number-field-decrement-trigger"; export type { NumberFieldDecrementTriggerProps } from "./number-field-decrement-trigger";
export type { NumberFieldDescriptionProps } from "./number-field-description"; export type { NumberFieldDescriptionProps } from "./number-field-description";
export type { NumberFieldErrorMessageProps } from "./number-field-error-message"; export type { NumberFieldErrorMessageProps } from "./number-field-error-message";
export type { NumberFieldContextValue } from "./number-field-context"; export const NumberField = Object.assign(Root, { Label, Input, IncrementTrigger: Inc, DecrementTrigger: Dec, Description: Desc, ErrorMessage: Err, useContext: useCtx });

View File

@ -0,0 +1,17 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const NumberFieldRootPropsSchema = z.object({
value: z.number().optional().describe("Controlled numeric value"),
defaultValue: z.number().optional().describe("Initial value (uncontrolled). Defaults to 0"),
min: z.number().optional().describe("Minimum allowed value"),
max: z.number().optional().describe("Maximum allowed value"),
step: z.number().optional().describe("Step amount for increment/decrement. Defaults to 1"),
disabled: z.boolean().optional().describe("Whether the number field is disabled"),
required: z.boolean().optional().describe("Whether the field is required for form submission"),
});
export interface NumberFieldRootProps extends z.infer<typeof NumberFieldRootPropsSchema> { onValueChange?: (value: number) => void; children: JSX.Element; }
export const NumberFieldMeta: ComponentMeta = {
name: "NumberField", description: "Numeric input with increment/decrement buttons and keyboard support",
parts: ["Root", "Input", "IncrementTrigger", "DecrementTrigger", "Label", "Description", "ErrorMessage"] as const, requiredParts: ["Root", "Input"] as const,
} as const;

View File

@ -1,12 +1,8 @@
import { useRadioGroupContext } from "./radio-group-context"; import { useRadioGroupContext } from "./radio-group-context";
import { RadioGroupItem } from "./radio-group-item"; import { RadioGroupItem } from "./radio-group-item";
import { RadioGroupRoot } from "./radio-group-root"; import { RadioGroupRoot } from "./radio-group-root";
export { RadioGroupRootPropsSchema, RadioGroupItemPropsSchema, RadioGroupMeta } from "./radio-group.props";
export const RadioGroup = Object.assign(RadioGroupRoot, {
Item: RadioGroupItem,
useContext: useRadioGroupContext,
});
export type { RadioGroupRootProps } from "./radio-group-root"; export type { RadioGroupRootProps } from "./radio-group-root";
export type { RadioGroupItemProps } from "./radio-group-item"; export type { RadioGroupItemProps } from "./radio-group-item";
export type { RadioGroupContextValue } from "./radio-group-context"; export type { RadioGroupContextValue } from "./radio-group-context";
export const RadioGroup = Object.assign(RadioGroupRoot, { Item: RadioGroupItem, useContext: useRadioGroupContext });

View File

@ -0,0 +1,27 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const RadioGroupRootPropsSchema = z.object({
value: z.string().optional().describe("Controlled selected value"),
defaultValue: z.string().optional().describe("Initial selected value (uncontrolled)"),
name: z.string().optional().describe("Native form field name for the radio group"),
disabled: z.boolean().optional().describe("Whether the entire radio group is disabled"),
required: z.boolean().optional().describe("Whether a selection is required for form submission"),
orientation: z.enum(["horizontal", "vertical"]).optional().describe("Layout orientation of the radio group items"),
});
export interface RadioGroupRootProps extends z.infer<typeof RadioGroupRootPropsSchema> {
onValueChange?: (value: string) => void;
children: JSX.Element;
}
export const RadioGroupItemPropsSchema = z.object({
value: z.string().describe("The value this radio item represents"),
disabled: z.boolean().optional().describe("Whether this individual radio item is disabled"),
});
export interface RadioGroupItemProps extends z.infer<typeof RadioGroupItemPropsSchema> {
children?: JSX.Element;
}
export const RadioGroupMeta: ComponentMeta = {
name: "RadioGroup", description: "Group of mutually exclusive options where only one can be selected",
parts: ["Root", "Item", "ItemInput", "ItemControl", "ItemIndicator", "ItemLabel", "Label", "Description", "ErrorMessage"] as const,
requiredParts: ["Root", "Item"] as const,
} as const;

View File

@ -3,16 +3,10 @@ import { SliderRange } from "./slider-range";
import { SliderRoot } from "./slider-root"; import { SliderRoot } from "./slider-root";
import { SliderThumb } from "./slider-thumb"; import { SliderThumb } from "./slider-thumb";
import { SliderTrack } from "./slider-track"; import { SliderTrack } from "./slider-track";
export { SliderRootPropsSchema, SliderMeta } from "./slider.props";
export const Slider = Object.assign(SliderRoot, {
Track: SliderTrack,
Range: SliderRange,
Thumb: SliderThumb,
useContext: useSliderContext,
});
export type { SliderRootProps } from "./slider-root"; export type { SliderRootProps } from "./slider-root";
export type { SliderTrackProps } from "./slider-track"; export type { SliderTrackProps } from "./slider-track";
export type { SliderRangeProps } from "./slider-range"; export type { SliderRangeProps } from "./slider-range";
export type { SliderThumbProps } from "./slider-thumb"; export type { SliderThumbProps } from "./slider-thumb";
export type { SliderContextValue } from "./slider-context"; export type { SliderContextValue } from "./slider-context";
export const Slider = Object.assign(SliderRoot, { Track: SliderTrack, Range: SliderRange, Thumb: SliderThumb, useContext: useSliderContext });

View File

@ -0,0 +1,17 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const SliderRootPropsSchema = z.object({
value: z.array(z.number()).optional().describe("Controlled value(s) as an array of numbers"),
defaultValue: z.array(z.number()).optional().describe("Initial value(s) (uncontrolled)"),
min: z.number().optional().describe("Minimum allowed value. Defaults to 0"),
max: z.number().optional().describe("Maximum allowed value. Defaults to 100"),
step: z.number().optional().describe("Step increment. Defaults to 1"),
disabled: z.boolean().optional().describe("Whether the slider is disabled"),
orientation: z.enum(["horizontal", "vertical"]).optional().describe("Layout orientation. Defaults to 'horizontal'"),
});
export interface SliderRootProps extends z.infer<typeof SliderRootPropsSchema> { onValueChange?: (value: number[]) => void; children: JSX.Element; }
export const SliderMeta: ComponentMeta = {
name: "Slider", description: "Range input for selecting numeric values by dragging a thumb along a track",
parts: ["Root", "Track", "Fill", "Thumb", "Label", "ValueLabel"] as const, requiredParts: ["Root", "Track", "Thumb"] as const,
} as const;

View File

@ -1,2 +1,4 @@
export { Switch } from "./switch"; import { Switch } from "./switch";
export { SwitchRootPropsSchema, SwitchMeta } from "./switch.props";
export type { SwitchProps } from "./switch"; export type { SwitchProps } from "./switch";
export { Switch };

View File

@ -0,0 +1,20 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const SwitchRootPropsSchema = z.object({
checked: z.boolean().optional().describe("Controlled checked state of the switch"),
defaultChecked: z.boolean().optional().describe("Initial checked state (uncontrolled)"),
disabled: z.boolean().optional().describe("Whether the switch is disabled"),
required: z.boolean().optional().describe("Whether the switch is required for form submission"),
name: z.string().optional().describe("Native form field name — renders a hidden checkbox input"),
value: z.string().optional().describe("Value submitted with the form when checked. Defaults to 'on'"),
});
export interface SwitchRootProps extends z.infer<typeof SwitchRootPropsSchema> {
onCheckedChange?: (checked: boolean) => void;
children: JSX.Element;
}
export const SwitchMeta: ComponentMeta = {
name: "Switch", description: "Toggle control for on/off states, visually distinct from checkbox",
parts: ["Root", "Input", "Control", "Thumb", "Label", "Description", "ErrorMessage"] as const,
requiredParts: ["Root"] as const,
} as const;

View File

@ -1,22 +1,14 @@
// packages/core/src/components/text-field/index.ts
import { useTextFieldContext } from "./text-field-context"; import { useTextFieldContext } from "./text-field-context";
import { TextFieldDescription } from "./text-field-description"; import { TextFieldDescription } from "./text-field-description";
import { TextFieldErrorMessage } from "./text-field-error-message"; import { TextFieldErrorMessage } from "./text-field-error-message";
import { TextFieldInput } from "./text-field-input"; import { TextFieldInput } from "./text-field-input";
import { TextFieldLabel } from "./text-field-label"; import { TextFieldLabel } from "./text-field-label";
import { TextFieldRoot } from "./text-field-root"; import { TextFieldRoot } from "./text-field-root";
export { TextFieldRootPropsSchema, TextFieldMeta } from "./text-field.props";
export const TextField = Object.assign(TextFieldRoot, {
Label: TextFieldLabel,
Input: TextFieldInput,
Description: TextFieldDescription,
ErrorMessage: TextFieldErrorMessage,
useContext: useTextFieldContext,
});
export type { TextFieldRootProps } from "./text-field-root"; export type { TextFieldRootProps } from "./text-field-root";
export type { TextFieldLabelProps } from "./text-field-label"; export type { TextFieldLabelProps } from "./text-field-label";
export type { TextFieldInputProps } from "./text-field-input"; export type { TextFieldInputProps } from "./text-field-input";
export type { TextFieldDescriptionProps } from "./text-field-description"; export type { TextFieldDescriptionProps } from "./text-field-description";
export type { TextFieldErrorMessageProps } from "./text-field-error-message"; export type { TextFieldErrorMessageProps } from "./text-field-error-message";
export type { TextFieldContextValue } from "./text-field-context"; export type { TextFieldContextValue } from "./text-field-context";
export const TextField = Object.assign(TextFieldRoot, { Label: TextFieldLabel, Input: TextFieldInput, Description: TextFieldDescription, ErrorMessage: TextFieldErrorMessage, useContext: useTextFieldContext });

View File

@ -0,0 +1,19 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const TextFieldRootPropsSchema = z.object({
value: z.string().optional().describe("Controlled value of the text field"),
defaultValue: z.string().optional().describe("Initial value (uncontrolled)"),
disabled: z.boolean().optional().describe("Whether the field is disabled"),
readOnly: z.boolean().optional().describe("Whether the field is read-only"),
required: z.boolean().optional().describe("Whether the field is required for form submission"),
});
export interface TextFieldRootProps extends z.infer<typeof TextFieldRootPropsSchema> {
onValueChange?: (value: string) => void;
children: JSX.Element;
}
export const TextFieldMeta: ComponentMeta = {
name: "TextField", description: "Text input field with label, description, and error message support",
parts: ["Root", "Label", "Input", "TextArea", "Description", "ErrorMessage"] as const,
requiredParts: ["Root", "Input"] as const,
} as const;

View File

@ -1,12 +1,8 @@
import { useToggleGroupContext } from "./toggle-group-context"; import { useToggleGroupContext } from "./toggle-group-context";
import { ToggleGroupItem } from "./toggle-group-item"; import { ToggleGroupItem } from "./toggle-group-item";
import { ToggleGroupRoot } from "./toggle-group-root"; import { ToggleGroupRoot } from "./toggle-group-root";
export { ToggleGroupRootPropsSchema, ToggleGroupItemPropsSchema, ToggleGroupMeta } from "./toggle-group.props";
export const ToggleGroup = Object.assign(ToggleGroupRoot, {
Item: ToggleGroupItem,
useContext: useToggleGroupContext,
});
export type { ToggleGroupRootProps } from "./toggle-group-root"; export type { ToggleGroupRootProps } from "./toggle-group-root";
export type { ToggleGroupItemProps } from "./toggle-group-item"; export type { ToggleGroupItemProps } from "./toggle-group-item";
export type { ToggleGroupContextValue } from "./toggle-group-context"; export type { ToggleGroupContextValue } from "./toggle-group-context";
export const ToggleGroup = Object.assign(ToggleGroupRoot, { Item: ToggleGroupItem, useContext: useToggleGroupContext });

View File

@ -0,0 +1,20 @@
import { z } from "zod/v4";
import type { JSX } from "solid-js";
import type { ComponentMeta } from "../../meta";
export const ToggleGroupRootPropsSchema = z.object({
value: z.union([z.string(), z.array(z.string())]).optional().describe("Controlled selected value(s). String for single mode, string[] for multiple"),
defaultValue: z.union([z.string(), z.array(z.string())]).optional().describe("Initial selected value(s) (uncontrolled)"),
disabled: z.boolean().optional().describe("Whether all toggle items are disabled"),
multiple: z.boolean().optional().describe("Whether multiple items can be selected simultaneously"),
orientation: z.enum(["horizontal", "vertical"]).optional().describe("Layout orientation of the toggle group"),
});
export interface ToggleGroupRootProps extends z.infer<typeof ToggleGroupRootPropsSchema> { onValueChange?: (value: string | string[] | undefined) => void; children: JSX.Element; }
export const ToggleGroupItemPropsSchema = z.object({
value: z.string().describe("The value this toggle item represents"),
disabled: z.boolean().optional().describe("Whether this individual toggle item is disabled"),
});
export interface ToggleGroupItemProps extends z.infer<typeof ToggleGroupItemPropsSchema> { children?: JSX.Element; }
export const ToggleGroupMeta: ComponentMeta = {
name: "ToggleGroup", description: "Group of toggle buttons where one or multiple can be selected",
parts: ["Root", "Item"] as const, requiredParts: ["Root", "Item"] as const,
} as const;