import type { ComponentProps, ComponentType, ReactElement, Ref, RefObject } from 'react'; type AnyComponent = ComponentType; /** Components `useFormControl` can bind. */ export declare const FORM_CONTROL_COMPONENTS: readonly string[]; /** The prop a Collet wrapper carries its value under. Resolution order matches * the wrappers: no component declares more than one of these. */ type ValueKey

= 'value' extends keyof P ? 'value' : 'selected' extends keyof P ? 'selected' : 'checked' extends keyof P ? 'checked' : 'tags' extends keyof P ? 'tags' : never; /** The type the component accepts for its value (what you pass in). */ type FieldValue = NonNullable[ValueKey>]>; /** The type the component emits (what your `onChange` receives). Narrower than * `FieldValue` for Checkbox, whose prop also accepts `'indeterminate'` but * whose `cx-change` detail is always a boolean. */ type EmittedValue = ValueKey> extends 'checked' ? boolean : FieldValue; /** The imperative handle the wrapper forwards (`TextInputRef`, `SelectRef`, …). */ type FieldRef = ComponentProps extends { ref?: infer R; } ? NonNullable extends Ref ? T : HTMLElement : HTMLElement; /** Props `render()` still needs — everything the hook does not bind. */ export type FieldRenderProps = Omit, ValueKey> | 'onInput' | 'onChange' | 'onBlur' | 'name' | 'ref' | 'key'>; /** Shaped to accept react-hook-form's `field` object verbatim. */ export interface FormControlOptions { /** Current value from form state. */ value: FieldValue; /** Called with the new value on every commit. */ onChange: (value: EmittedValue) => void; /** Called when the field is blurred (touched tracking). */ onBlur?: () => void; /** Field name. Forwarded to components that support it; ignored by the rest. */ name?: string; /** Overrides the component's own `disabled` when defined. */ disabled?: boolean; /** Extra ref to populate — react-hook-form passes one here for error focus. */ ref?: Ref> | ((instance: any) => void); } interface NotAColletFormField { /** This component has no `value`, `selected`, `checked` or `tags` prop, so it * cannot be bound to a form field. */ readonly __colletNotAFormField: never; } type Options = [ ValueKey> ] extends [never] ? NotAColletFormField : FormControlOptions; export interface ColletFormControl { /** * Renders the bound component. Supply the presentational props here — the * value prop, change callback, `onBlur`, `name` and `ref` come from the hook * and are deliberately not overridable. */ render(props: FieldRenderProps): ReactElement; /** The host element, for imperative access (`focus()`, `select()`, …). */ ref: RefObject | null>; } /** * Bind a Collet form component to a form library. * * @param component A Collet form wrapper — `TextInput`, `Select`, `Checkbox`, * `Switch`, `RadioGroup`, `Slider`, `DatePicker`, `Autocomplete`, `Listbox`, * `ToggleGroup`, `TagInput`, `SearchBar` or `ChatInput`. * @param options `{ value, onChange, onBlur?, name?, disabled?, ref? }` — * react-hook-form's `field` object satisfies this as-is. * @throws If `component` is not one of the supported form components. Failing * loudly is deliberate: the previous version of this hook failed silently. * * @example react-hook-form * ```tsx * import { useController, type Control } from 'react-hook-form'; * import { TextInput } from '@colletdev/react'; * import { useFormControl } from '@colletdev/react/form-control'; * * function EmailField({ control }: { control: Control }) { * const { field, fieldState } = useController({ name: 'email', control }); * const email = useFormControl(TextInput, field); * return email.render({ * label: 'Email', * kind: 'email', * required: true, * error: fieldState.error?.message, * }); * } * ``` * * @example Plain controlled state * ```tsx * const [role, setRole] = useState([]); * const roleField = useFormControl(Select, { value: role, onChange: setRole }); * return roleField.render({ label: 'Role', items: roleOptions }); * ``` */ export declare function useFormControl(component: C, options: Options): ColletFormControl; export {};