import { ClassValue } from "clsx"; import { Control, FieldError, FieldValues, Path, RegisterOptions } from "react-hook-form"; import { ReactNode } from "react"; //#region src/utils.d.ts /** * Utility function to merge CSS classes with Tailwind CSS conflict resolution. * * Combines `clsx` for conditional class handling with `tailwind-merge` * for proper Tailwind CSS class conflict resolution. * * @param inputs - Class values to merge (strings, arrays, objects, or conditionals) * @returns Merged and deduplicated class string * * @example * ```tsx * // Basic usage * cn("px-2 py-1", "px-4") // "py-1 px-4" * * // Conditional classes * cn("base", isActive && "active", { "disabled": isDisabled }) * * // Arrays * cn(["flex", "items-center"], "gap-2") * ``` */ declare function cn(...inputs: ClassValue[]): string; //#endregion //#region src/types.d.ts /** * Field type identifier. * Can be built-in types or custom string identifiers. */ type FieldType = "text" | "email" | "password" | "number" | "tel" | "phone" | "url" | "textarea" | "slug" | "select" | "combobox" | "multiselect" | "dependentSelect" | "checkbox" | "radio" | "switch" | "date" | "time" | "datetime" | "rich-text" | "color" | "rating" | "tags" | "json" | "file" | "hidden" | "group" | "array" | "custom" | (string & {}); /** * Layout type identifier. */ type LayoutType = "section" | "grid" | "default" | (string & {}); /** * Variant identifier for component styling. */ type Variant = "default" | "compact" | "inline" | (string & {}) | undefined; interface ConditionRule { watch: Path; operator: "===" | "!==" | "in" | "not-in" | "truthy" | "falsy"; value?: unknown; } /** * Condition configuration with explicit logic operator. * Allows combining rules with AND or OR logic. * * @example * ```ts * // Show field if country is "US" OR "CA" * condition: { * rules: [ * { watch: "country", operator: "===", value: "US" }, * { watch: "country", operator: "===", value: "CA" }, * ], * logic: "or", * } * ``` */ interface ConditionConfig { /** Array of condition rules to evaluate */ rules: ConditionRule[]; /** Logic operator: "and" (all must match) or "or" (any must match). Defaults to "and". */ logic?: "and" | "or"; } /** * Single option for select/radio/checkbox fields. */ interface FieldOption { /** Display label */ label: string; /** Option value */ value: TValue; /** Whether option is disabled */ disabled?: boolean; /** Optional description */ description?: string; /** Optional icon */ icon?: ReactNode; } /** * Option group for select fields. */ interface FieldOptionGroup { /** Group label */ label: string; /** Group options */ options: FieldOption[]; /** Whether group is disabled */ disabled?: boolean; } /** * Object form for length/range validation rules with a custom message. */ interface ValidationRuleObject { value: TValue; message: string; } /** * Object form for pattern validation with a custom message. */ interface PatternRuleObject { /** Regex string (will be compiled with `new RegExp(regex)`) */ regex: string; /** Custom error message */ message: string; } /** * AI-friendly metadata attached to any field. * Consumed by LLM coding assistants, documentation generators, and * schema-introspection tools — never by the form renderer itself. */ interface FieldMeta { /** Human-readable description of what this field collects */ description?: string; /** Representative example value (for documentation / AI context) */ example?: unknown; /** Logical category for grouping fields in documentation or tooling */ category?: string; /** Arbitrary tags for filtering, search, or feature-flagging */ tags?: string[]; /** Whether this field is considered PII */ pii?: boolean; /** Arbitrary extension point — anything extra you want to carry */ [key: string]: unknown; } /** * Base field configuration shared by all field types. * @template TFieldValues - Form field values type for type-safe field names */ interface BaseField { /** * Field name — a path into TFieldValues (e.g. `"email"`, `"address.street"`). * * Accepts any string so that: * - Namespaced sections can use relative names (`"street"` → prefixed to `"address.street"`) * - Group/array `itemFields` can use relative names (`"email"` → prefixed at render time) * * Use `field.for()` builder for call-site enforcement that names are valid paths. */ name: Path | (string & {}); /** Field type identifier */ type: FieldType; /** Field label */ label?: string; /** Placeholder text */ placeholder?: string; /** Helper text shown below the field */ helperText?: string; /** Whether field is disabled */ disabled?: boolean; /** Whether field is required */ required?: boolean; /** Whether field is read-only */ readOnly?: boolean; /** Field variant */ variant?: Variant; /** Whether field should span full width in grid */ fullWidth?: boolean; /** Custom CSS class name */ className?: string; /** * Conditional rendering function, declarative rules, or a condition config with logic. * Return true to show the field, false to hide. */ condition?: ((formValues: Partial) => boolean) | ConditionRule | ConditionRule[] | ConditionConfig; /** Default value */ defaultValue?: unknown; /** Options for select/radio/checkbox fields */ options?: (FieldOption | FieldOptionGroup)[]; /** * Minimum value (for number/date fields). * Pass an object `{ value, message }` to provide a custom error message. */ min?: number | string | ValidationRuleObject; /** * Maximum value (for number/date fields). * Pass an object `{ value, message }` to provide a custom error message. */ max?: number | string | ValidationRuleObject; /** Step value (for number fields) */ step?: number; /** * Pattern for validation. * Pass a regex string or `{ regex, message }` for a custom error message. * * @example * ```ts * // simple * pattern: "^[a-z]+$" * // with custom message * pattern: { regex: "^[a-z]+$", message: "Only lowercase letters allowed" } * ``` */ pattern?: string | PatternRuleObject; /** * Minimum length. * Pass an object `{ value, message }` to provide a custom error message. */ minLength?: number | ValidationRuleObject; /** * Maximum length. * Pass an object `{ value, message }` to provide a custom error message. */ maxLength?: number | ValidationRuleObject; /** Number of rows (for textarea) */ rows?: number; /** Multiple selection (for select/file) */ multiple?: boolean; /** Accepted file types (for file input) */ accept?: string; /** Auto-complete attribute */ autoComplete?: string; /** Auto-focus on mount */ autoFocus?: boolean; /** Debounce loadOptions requests in milliseconds, helps prevent API storms */ debounceMs?: number; /** * Dynamic options loaded based on current form values. * Useful for dependent selects (e.g., state depends on country). */ loadOptions?: (formValues: Partial) => Promise<(FieldOption | FieldOptionGroup)[]> | (FieldOption | FieldOptionGroup)[]; /** * Error callback for loadOptions failures. * Called when loadOptions rejects. Defaults to console.error. */ onLoadError?: (error: unknown) => void; /** * Sub-fields for `group` and `array` field types. * * These fields use **relative** names (`"street"`, `"email"`) — FormGenerator * prefixes them with the parent field name at render time (`"address.street"`). * They are intentionally untyped to TFieldValues for this reason. */ itemFields?: BaseField[]; /** * Custom render function to override the component registry for this specific field. * Completely bypasses the globally registered FieldComponent for this type. */ render?: (props: FieldComponentProps) => ReactNode; /** * Cross-field validation function. * Receives the field value and all form values for cross-field checks. * Return `true` for valid, a string error message for invalid, or a Promise of either * for async validation (e.g., checking server-side uniqueness). * * @example * ```ts * validate: (value, formValues) => * value > formValues.minPrice || "Must be greater than min price" * * // Async example * validate: async (value) => { * const taken = await checkUsernameAvailability(value as string); * return taken ? "Username already taken" : true; * } * ``` */ validate?: (value: unknown, formValues: Partial) => string | true | Promise; /** * Dependencies for optimizing conditionally rendered fields. * Allows specifying specific field names to watch, preventing full form re-renders. */ watchNames?: Path | Path[]; /** Additional field-specific props for custom components */ customProps?: Record; /** * AI / agent-friendly metadata for this field. * * Provides hints that help LLM coding assistants generate correct field * configs without needing to inspect the schema at runtime. * * @example * ```ts * field.text("companyName", "Company", { * meta: { * description: "Legal entity name of the organization", * example: "Acme Corp", * category: "identity", * tags: ["crm", "required-for-billing"], * } * }) * ``` */ meta?: FieldMeta; /** * Escape hatch for adapter-specific or custom props. * Allows passing arbitrary props directly on the field object so they * flow through the adapter spread (`{...field}`) without needing `customProps`. * Intentionally broad to support diverse UI component libraries. */ [key: string]: unknown; } /** * Props passed to field components. * * Components receive both: * 1. A `field` object with the complete field configuration * 2. All field properties spread at the top level * * @template TFieldValues - Form field values type * * @example * ```tsx * // Schema * { name: "email", type: "email", label: "Email", placeholder: "user@example.com" } * * // Your component receives: * { * field: { name: "email", type: "email", label: "Email", placeholder: "..." }, * control: {...}, * disabled: false, * variant: undefined, * // PLUS all field props at top level: * name: "email", * type: "email", * label: "Email", * placeholder: "user@example.com" * } * ``` */ interface FieldComponentProps extends BaseField { /** Field configuration object (contains all field props) */ field: BaseField; /** React Hook Form control for Controller integration */ control: Control; /** Whether field is globally disabled */ disabled?: boolean; /** Component variant */ variant?: Variant; /** Field validation error from react-hook-form */ error?: FieldError; /** Field state from react-hook-form */ fieldState?: { invalid: boolean; isDirty: boolean; isTouched: boolean; isValidating: boolean; /** True after the enclosing form's submit handler has been called at least once. */ isSubmitted: boolean; error?: FieldError; }; /** * Generated field ID for label-input association (e.g. `formkit-field-email`). * Use as `id` on the input element and `htmlFor` on the `