@ansh-ui/react 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-5CFQFMP5.js +178 -0
  3. package/dist/chunk-5CFQFMP5.js.map +1 -0
  4. package/dist/chunk-6VRF7X5C.cjs +164 -0
  5. package/dist/chunk-6VRF7X5C.cjs.map +1 -0
  6. package/dist/chunk-AVPEA5PB.cjs +718 -0
  7. package/dist/chunk-AVPEA5PB.cjs.map +1 -0
  8. package/dist/chunk-C32LKF23.cjs +198 -0
  9. package/dist/chunk-C32LKF23.cjs.map +1 -0
  10. package/dist/chunk-C5JG6TMM.cjs +1200 -0
  11. package/dist/chunk-C5JG6TMM.cjs.map +1 -0
  12. package/dist/chunk-DATBU46V.cjs +614 -0
  13. package/dist/chunk-DATBU46V.cjs.map +1 -0
  14. package/dist/chunk-EGG4CP7C.js +320 -0
  15. package/dist/chunk-EGG4CP7C.js.map +1 -0
  16. package/dist/chunk-H34OXXJU.js +55 -0
  17. package/dist/chunk-H34OXXJU.js.map +1 -0
  18. package/dist/chunk-HO2YSF5O.js +187 -0
  19. package/dist/chunk-HO2YSF5O.js.map +1 -0
  20. package/dist/chunk-HPGCILO4.js +760 -0
  21. package/dist/chunk-HPGCILO4.js.map +1 -0
  22. package/dist/chunk-IBXLC5IE.js +154 -0
  23. package/dist/chunk-IBXLC5IE.js.map +1 -0
  24. package/dist/chunk-LDCZQP3R.js +1185 -0
  25. package/dist/chunk-LDCZQP3R.js.map +1 -0
  26. package/dist/chunk-LXCIXR5T.js +27 -0
  27. package/dist/chunk-LXCIXR5T.js.map +1 -0
  28. package/dist/chunk-O2PX54KX.js +94 -0
  29. package/dist/chunk-O2PX54KX.js.map +1 -0
  30. package/dist/chunk-OHBWBXLO.cjs +101 -0
  31. package/dist/chunk-OHBWBXLO.cjs.map +1 -0
  32. package/dist/chunk-OJN5JBNP.cjs +776 -0
  33. package/dist/chunk-OJN5JBNP.cjs.map +1 -0
  34. package/dist/chunk-OLTS6G5M.js +606 -0
  35. package/dist/chunk-OLTS6G5M.js.map +1 -0
  36. package/dist/chunk-PO5DU3FU.cjs +29 -0
  37. package/dist/chunk-PO5DU3FU.cjs.map +1 -0
  38. package/dist/chunk-QO3E7TJS.js +2940 -0
  39. package/dist/chunk-QO3E7TJS.js.map +1 -0
  40. package/dist/chunk-RHTLNSVW.cjs +2976 -0
  41. package/dist/chunk-RHTLNSVW.cjs.map +1 -0
  42. package/dist/chunk-SFU5EPSN.cjs +61 -0
  43. package/dist/chunk-SFU5EPSN.cjs.map +1 -0
  44. package/dist/chunk-T4AK2KLM.js +699 -0
  45. package/dist/chunk-T4AK2KLM.js.map +1 -0
  46. package/dist/chunk-WGYNCSTP.cjs +333 -0
  47. package/dist/chunk-WGYNCSTP.cjs.map +1 -0
  48. package/dist/chunk-XEP6P7E6.js +279 -0
  49. package/dist/chunk-XEP6P7E6.js.map +1 -0
  50. package/dist/chunk-XMXECSQO.cjs +286 -0
  51. package/dist/chunk-XMXECSQO.cjs.map +1 -0
  52. package/dist/chunk-ZWAFLHC2.cjs +181 -0
  53. package/dist/chunk-ZWAFLHC2.cjs.map +1 -0
  54. package/dist/data.cjs +71 -0
  55. package/dist/data.cjs.map +1 -0
  56. package/dist/data.d.cts +316 -0
  57. package/dist/data.d.ts +316 -0
  58. package/dist/data.js +6 -0
  59. package/dist/data.js.map +1 -0
  60. package/dist/form.cjs +153 -0
  61. package/dist/form.cjs.map +1 -0
  62. package/dist/form.d.cts +660 -0
  63. package/dist/form.d.ts +660 -0
  64. package/dist/form.js +8 -0
  65. package/dist/form.js.map +1 -0
  66. package/dist/index.cjs +1641 -0
  67. package/dist/index.cjs.map +1 -0
  68. package/dist/index.d.cts +552 -0
  69. package/dist/index.d.ts +552 -0
  70. package/dist/index.js +956 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/layout.cjs +68 -0
  73. package/dist/layout.cjs.map +1 -0
  74. package/dist/layout.d.cts +169 -0
  75. package/dist/layout.d.ts +169 -0
  76. package/dist/layout.js +7 -0
  77. package/dist/layout.js.map +1 -0
  78. package/dist/motion-CQmCq6_k.d.ts +395 -0
  79. package/dist/motion-CSX4g2N8.d.cts +395 -0
  80. package/dist/motion.cjs +79 -0
  81. package/dist/motion.cjs.map +1 -0
  82. package/dist/motion.d.cts +4 -0
  83. package/dist/motion.d.ts +4 -0
  84. package/dist/motion.js +6 -0
  85. package/dist/motion.js.map +1 -0
  86. package/dist/navigation.cjs +48 -0
  87. package/dist/navigation.cjs.map +1 -0
  88. package/dist/navigation.d.cts +210 -0
  89. package/dist/navigation.d.ts +210 -0
  90. package/dist/navigation.js +7 -0
  91. package/dist/navigation.js.map +1 -0
  92. package/dist/overlay.cjs +69 -0
  93. package/dist/overlay.cjs.map +1 -0
  94. package/dist/overlay.d.cts +330 -0
  95. package/dist/overlay.d.ts +330 -0
  96. package/dist/overlay.js +8 -0
  97. package/dist/overlay.js.map +1 -0
  98. package/dist/position-DVScKcaa.d.cts +66 -0
  99. package/dist/position-DVScKcaa.d.ts +66 -0
  100. package/dist/primitives.cjs +162 -0
  101. package/dist/primitives.cjs.map +1 -0
  102. package/dist/primitives.d.cts +294 -0
  103. package/dist/primitives.d.ts +294 -0
  104. package/dist/primitives.js +9 -0
  105. package/dist/primitives.js.map +1 -0
  106. package/dist/sidebar-D-PafAXw.d.cts +71 -0
  107. package/dist/sidebar-D-PafAXw.d.ts +71 -0
  108. package/dist/styles.css +5575 -0
  109. package/dist/use-observers-B9qtimrk.d.cts +99 -0
  110. package/dist/use-observers-B9qtimrk.d.ts +99 -0
  111. package/package.json +92 -0
package/dist/form.d.ts ADDED
@@ -0,0 +1,660 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, InputHTMLAttributes, TextareaHTMLAttributes } from 'react';
3
+
4
+ /**
5
+ * The label / description / error scaffolding every form control shares.
6
+ *
7
+ * Wiring this by hand is where forms lose their accessibility. The control needs `id` matching the
8
+ * label's `htmlFor`, `aria-describedby` pointing at *both* the hint and the error, and
9
+ * `aria-invalid` when it's wrong. Six attributes across four elements, per field — so in practice
10
+ * some fields get it and some don't.
11
+ *
12
+ * `Field` generates the ids and distributes them through context, so a control only has to spread
13
+ * what `useFieldControl()` returns.
14
+ *
15
+ * ```tsx
16
+ * <Field label="Work email" description="We'll never share it." error={errors.email}>
17
+ * <Input type="email" value={email} onChange={…} />
18
+ * </Field>
19
+ * ```
20
+ */
21
+ interface FieldContextValue {
22
+ id: string;
23
+ describedBy: string | undefined;
24
+ invalid: boolean;
25
+ required: boolean;
26
+ disabled: boolean;
27
+ }
28
+ /**
29
+ * Props a control should spread onto its interactive element.
30
+ *
31
+ * Returns an empty object outside a `Field`, so every control works standalone — a control that
32
+ * throws without a wrapper is a control people stop using.
33
+ */
34
+ declare function useFieldControl(): {
35
+ id?: string;
36
+ 'aria-describedby'?: string;
37
+ 'aria-invalid'?: true;
38
+ 'aria-required'?: true;
39
+ disabled?: boolean;
40
+ };
41
+ /** The raw context, for controls that need the ids rather than the spreadable props. */
42
+ declare function useFieldContext(): FieldContextValue | null;
43
+ interface FieldProps {
44
+ label?: ReactNode;
45
+ /** Helper text below the label. Announced as part of the control's description. */
46
+ description?: ReactNode;
47
+ /** Error message. Its presence sets `aria-invalid` on the control. */
48
+ error?: ReactNode;
49
+ /** Marks the field required and appends an asterisk to the label. */
50
+ required?: boolean;
51
+ disabled?: boolean;
52
+ /** Label to the side rather than above — denser, for settings screens. */
53
+ orientation?: 'vertical' | 'horizontal';
54
+ className?: string;
55
+ children: ReactNode;
56
+ }
57
+ declare function Field({ label, description, error, required, disabled, orientation, className, children, }: FieldProps): react.JSX.Element;
58
+ interface FieldsetProps {
59
+ legend: ReactNode;
60
+ description?: ReactNode;
61
+ className?: string;
62
+ children: ReactNode;
63
+ }
64
+ /**
65
+ * A group of related fields.
66
+ *
67
+ * A real `<fieldset>`/`<legend>`, because that pairing is what makes a screen reader announce the
68
+ * group name before each control inside it. A styled `<div>` with a heading looks identical and
69
+ * conveys nothing.
70
+ */
71
+ declare function Fieldset({ legend, description, className, children }: FieldsetProps): react.JSX.Element;
72
+ /**
73
+ * Visually hidden, still announced.
74
+ *
75
+ * The clip-path technique rather than `display: none` or `visibility: hidden`, both of which
76
+ * remove the element from the accessibility tree entirely — which is the opposite of the intent.
77
+ */
78
+ declare function VisuallyHidden({ children, ...rest }: {
79
+ children: ReactNode;
80
+ } & React.HTMLAttributes<HTMLSpanElement>): react.JSX.Element;
81
+
82
+ type InputSize = 'sm' | 'md' | 'lg';
83
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
84
+ size?: InputSize;
85
+ /** Content inside the field, before the text — an icon or a currency symbol. */
86
+ prefix?: ReactNode;
87
+ /** Content inside the field, after the text — a unit, or a button. */
88
+ suffix?: ReactNode;
89
+ /** Show a clear button when non-empty. */
90
+ clearable?: boolean;
91
+ onClear?: () => void;
92
+ /** Force the error style outside a `Field`. */
93
+ invalid?: boolean;
94
+ className?: string;
95
+ /** Class for the outer wrapper, when there is a prefix or suffix. */
96
+ wrapperClassName?: string;
97
+ }
98
+ /**
99
+ * A text input.
100
+ *
101
+ * The affordance most inputs get wrong is the click target: with an icon inside the field, clicking
102
+ * the icon should focus the input. That works here because the wrapper forwards the click, rather
103
+ * than the icon being an absolutely positioned sibling that swallows it.
104
+ */
105
+ declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
106
+ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
107
+ /** Grow with the content instead of scrolling. */
108
+ autoResize?: boolean;
109
+ /** Upper bound for `autoResize`, in rows. Default `12`. */
110
+ maxRows?: number;
111
+ /** Show a live character count. Needs `maxLength` to show a limit. */
112
+ showCount?: boolean;
113
+ invalid?: boolean;
114
+ }
115
+ /** A multi-line text input, optionally growing with its content. */
116
+ declare const Textarea: react.ForwardRefExoticComponent<TextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
117
+ interface NumberInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'size' | 'type'> {
118
+ value?: number;
119
+ defaultValue?: number;
120
+ onChange?: (value: number) => void;
121
+ min?: number;
122
+ max?: number;
123
+ step?: number;
124
+ /** Decimal places to display. Default: inferred from `step`. */
125
+ precision?: number;
126
+ size?: InputSize;
127
+ /** Unit shown after the number — "kg", "%", "days". */
128
+ unit?: ReactNode;
129
+ /** Show the stepper buttons. Default `true`. */
130
+ steppers?: boolean;
131
+ }
132
+ /**
133
+ * A numeric input with steppers.
134
+ *
135
+ * Deliberately `type="text"` with `inputMode="decimal"` rather than `type="number"`. Native number
136
+ * inputs silently discard invalid intermediate text (typing "1e" clears the field), scroll-wheel
137
+ * over a focused field changes the value by accident, and the spinner cannot be styled
138
+ * consistently across browsers. All three are real bugs users hit on data-entry screens.
139
+ */
140
+ declare const NumberInput: react.ForwardRefExoticComponent<NumberInputProps & react.RefAttributes<HTMLInputElement>>;
141
+
142
+ /**
143
+ * Binary and single-choice controls.
144
+ *
145
+ * All three wrap a **real** `<input>` rather than painting a div. That decision carries a lot:
146
+ * form submission works, `:checked` styling works, the label association works, autofill works,
147
+ * password managers and browser translation work, and every assistive technology already knows what
148
+ * the control is. The visual is a sibling element the input drives via `:checked + .indicator`.
149
+ *
150
+ * The input is not `display: none` — that removes it from the accessibility tree and the tab order.
151
+ * It is transparent and stretched over the control, so it stays focusable and clickable.
152
+ */
153
+ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'size'> {
154
+ checked?: boolean;
155
+ defaultChecked?: boolean;
156
+ onChange?: (checked: boolean) => void;
157
+ /** Neither checked nor unchecked — a parent whose children are partly selected. */
158
+ indeterminate?: boolean;
159
+ label?: ReactNode;
160
+ description?: ReactNode;
161
+ size?: 'sm' | 'md';
162
+ invalid?: boolean;
163
+ }
164
+ declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLInputElement>>;
165
+ interface SwitchProps extends Omit<CheckboxProps, 'indeterminate'> {
166
+ /** Where the label sits. Default `'end'`; `'start'` suits settings rows. */
167
+ labelPosition?: 'start' | 'end';
168
+ }
169
+ /**
170
+ * An on/off toggle.
171
+ *
172
+ * `role="switch"` rather than a plain checkbox: it makes a screen reader announce "on"/"off"
173
+ * instead of "checked"/"not checked", which is the difference between a setting and a selection.
174
+ *
175
+ * A switch applies immediately; a checkbox applies on submit. If the change needs a Save button,
176
+ * the control should be a checkbox.
177
+ */
178
+ declare const Switch: react.ForwardRefExoticComponent<SwitchProps & react.RefAttributes<HTMLInputElement>>;
179
+ interface RadioGroupProps {
180
+ value?: string;
181
+ defaultValue?: string;
182
+ onChange?: (value: string) => void;
183
+ /** Shared `name`. Generated when omitted, which is fine outside a native form. */
184
+ name?: string;
185
+ label?: ReactNode;
186
+ orientation?: 'vertical' | 'horizontal';
187
+ disabled?: boolean;
188
+ className?: string;
189
+ children: ReactNode;
190
+ }
191
+ /**
192
+ * A group of mutually exclusive choices.
193
+ *
194
+ * Native radios already implement roving focus: arrow keys move between them and Tab enters and
195
+ * leaves the group as a unit. That behaviour is free as long as every input shares a `name`, which
196
+ * is what this context distributes — and it is why this doesn't reimplement key handling.
197
+ */
198
+ declare function RadioGroup({ value, defaultValue, onChange, name, label, orientation, disabled, className, children, }: RadioGroupProps): react.JSX.Element;
199
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'size'> {
200
+ value: string;
201
+ label?: ReactNode;
202
+ description?: ReactNode;
203
+ size?: 'sm' | 'md';
204
+ }
205
+ declare const Radio: react.ForwardRefExoticComponent<RadioProps & react.RefAttributes<HTMLInputElement>>;
206
+ interface ChoiceCardProps extends RadioProps {
207
+ icon?: ReactNode;
208
+ }
209
+ /**
210
+ * A radio rendered as a selectable card — plan pickers, onboarding branches.
211
+ *
212
+ * Still a radio underneath, so keyboard and screen-reader behaviour is identical to the plain one.
213
+ * Selection is marked by a border *and* a check, never colour alone: WCAG 1.4.1 requires that
214
+ * colour not be the sole carrier of information, and roughly 1 in 12 men cannot rely on it.
215
+ */
216
+ declare const ChoiceCard: react.ForwardRefExoticComponent<ChoiceCardProps & react.RefAttributes<HTMLInputElement>>;
217
+
218
+ /**
219
+ * Shared machinery for `Select` and `Combobox`.
220
+ *
221
+ * Both are listboxes: same option shape, same grouping, same active-descendant navigation, same
222
+ * "skip disabled items" rule. What differs is only how the query is entered — a hidden typeahead
223
+ * buffer versus a visible text input. Sharing this is what keeps the two consistent; the
224
+ * alternative is a `Select` whose Home key works and a `Combobox` whose doesn't.
225
+ */
226
+ interface SelectOption {
227
+ value: string;
228
+ label: string;
229
+ /** Secondary line under the label. */
230
+ description?: string;
231
+ icon?: ReactNode;
232
+ disabled?: boolean;
233
+ /** Options sharing a group render under one heading. */
234
+ group?: string;
235
+ /** Extra text matched by search but never displayed. */
236
+ keywords?: string;
237
+ }
238
+ /** Move `active` by `delta`, skipping disabled options and stopping at the ends. */
239
+ declare function stepActive(options: readonly SelectOption[], active: number, delta: number, loop?: boolean): number;
240
+ /** First enabled option at or after `from`. */
241
+ declare function firstEnabled(options: readonly SelectOption[], from?: number): number;
242
+ /**
243
+ * Type-ahead over a listbox with no text input.
244
+ *
245
+ * Typing "ca" jumps to "Canada". The buffer resets after a pause, so typing "c", waiting, then "a"
246
+ * moves to the first "c" and then the first "a" — which is what the native `<select>` does and what
247
+ * users have internalised over three decades.
248
+ */
249
+ declare function useTypeahead(options: readonly SelectOption[], onMatch: (index: number) => void, timeout?: number): (key: string) => boolean;
250
+ /**
251
+ * Subsequence match with a relevance score. `-1` means no match.
252
+ *
253
+ * Subsequence rather than substring because that is what users of a modern picker expect: typing
254
+ * "nds" should find "**N**ew **D**ata**s**ource".
255
+ */
256
+ declare function scoreOption(query: string, option: SelectOption): number;
257
+ /** Filter and rank options against a query, preserving order when the query is empty. */
258
+ declare function filterOptions(options: readonly SelectOption[], query: string): readonly SelectOption[];
259
+ interface OptionListProps {
260
+ options: readonly SelectOption[];
261
+ active: number;
262
+ selected: readonly string[];
263
+ listId: string;
264
+ multiple?: boolean;
265
+ emptyMessage?: ReactNode;
266
+ onSelect: (option: SelectOption) => void;
267
+ onActiveChange: (index: number) => void;
268
+ /** Highlight the matched characters. */
269
+ query?: string;
270
+ }
271
+ /** Render the option rows, with group headings emitted on change. */
272
+ declare function OptionList({ options, active, selected, listId, multiple, emptyMessage, onSelect, onActiveChange, query, }: OptionListProps): react.JSX.Element;
273
+
274
+ interface SelectProps {
275
+ options: readonly SelectOption[];
276
+ value?: string | null;
277
+ defaultValue?: string | null;
278
+ onChange?: (value: string | null) => void;
279
+ placeholder?: string;
280
+ disabled?: boolean;
281
+ invalid?: boolean;
282
+ size?: 'sm' | 'md' | 'lg';
283
+ /** Show a clear button once something is selected. */
284
+ clearable?: boolean;
285
+ /** Accessible name when there is no wrapping `Field`. */
286
+ label?: string;
287
+ className?: string;
288
+ /** Emit a hidden input so the value posts in a native form submit. */
289
+ name?: string;
290
+ /** Max height of the popup before it scrolls. Default `'18rem'`. */
291
+ maxHeight?: string;
292
+ emptyMessage?: ReactNode;
293
+ }
294
+ /**
295
+ * A single-choice select.
296
+ *
297
+ * Follows the ARIA 1.2 collapsed-listbox pattern: the trigger is a `button` with
298
+ * `aria-haspopup="listbox"`, and while open, focus moves to the listbox with a virtual cursor
299
+ * carried by `aria-activedescendant`.
300
+ *
301
+ * A custom select is only justified when the native one can't do the job — option descriptions,
302
+ * icons, grouping with custom rendering. When a plain list of strings will do, `<select>` is still
303
+ * better: it gets the platform's own picker on mobile, which no custom implementation matches.
304
+ * This exists because CRM and admin screens genuinely need the richer version.
305
+ */
306
+ declare function Select({ options, value, defaultValue, onChange, placeholder, disabled, invalid, size, clearable, label, className, name, maxHeight, emptyMessage, }: SelectProps): react.JSX.Element;
307
+
308
+ interface ComboboxProps {
309
+ options: readonly SelectOption[];
310
+ /** Selected values. Single-select uses a one-element array. */
311
+ value?: readonly string[];
312
+ defaultValue?: readonly string[];
313
+ onChange?: (value: readonly string[]) => void;
314
+ multiple?: boolean;
315
+ placeholder?: string;
316
+ disabled?: boolean;
317
+ invalid?: boolean;
318
+ size?: 'sm' | 'md' | 'lg';
319
+ label?: string;
320
+ className?: string;
321
+ maxHeight?: string;
322
+ emptyMessage?: ReactNode;
323
+ /** Offer the typed text as a new option. Returns the created option's value. */
324
+ onCreate?: (label: string) => string;
325
+ /** Text for the create row. Receives the current query. */
326
+ createLabel?: (query: string) => string;
327
+ /** Override filtering entirely — for server-side search. */
328
+ onSearch?: (query: string) => void;
329
+ /** Skip local filtering, e.g. when `onSearch` already returns filtered options. */
330
+ filter?: boolean;
331
+ /** Cap on how many chips render before collapsing to "+N". Default `Infinity`. */
332
+ maxVisibleChips?: number;
333
+ }
334
+ /**
335
+ * A searchable select, single or multiple.
336
+ *
337
+ * This is the ARIA 1.2 combobox pattern, and the distinguishing detail is that **focus never leaves
338
+ * the input**. Arrow keys move a virtual cursor announced through `aria-activedescendant`, so the
339
+ * user can keep typing to narrow the list while navigating it. Implementations that move DOM focus
340
+ * into the list break that: the next keystroke goes to the list, not the query.
341
+ *
342
+ * ```tsx
343
+ * <Combobox multiple options={owners} value={selected} onChange={setSelected} />
344
+ * ```
345
+ */
346
+ declare function Combobox({ options, value, defaultValue, onChange, multiple, placeholder, disabled, invalid, size, label, className, maxHeight, emptyMessage, onCreate, createLabel, onSearch, filter, maxVisibleChips, }: ComboboxProps): react.JSX.Element;
347
+
348
+ interface SliderMark {
349
+ value: number;
350
+ label?: ReactNode;
351
+ }
352
+ interface SliderProps {
353
+ value?: number;
354
+ defaultValue?: number;
355
+ onChange?: (value: number) => void;
356
+ /** Fired once on release — for the expensive work a live `onChange` shouldn't trigger. */
357
+ onCommit?: (value: number) => void;
358
+ min?: number;
359
+ max?: number;
360
+ step?: number;
361
+ disabled?: boolean;
362
+ /** Vertical sliders are for mixers and volume; default is horizontal. */
363
+ orientation?: 'horizontal' | 'vertical';
364
+ marks?: readonly SliderMark[];
365
+ /** Show the value in a bubble above the thumb while dragging. */
366
+ showValue?: boolean | 'always';
367
+ /** Format the bubble and the screen-reader announcement. */
368
+ formatValue?: (value: number) => string;
369
+ label?: string;
370
+ className?: string;
371
+ name?: string;
372
+ }
373
+ /**
374
+ * A single-value slider.
375
+ *
376
+ * The thumb is the focusable element and carries `role="slider"` with the full value triple, so a
377
+ * screen reader announces "40, slider, minimum 0, maximum 100". Dragging writes the position
378
+ * straight to a CSS custom property and only commits to React state on release — a re-render per
379
+ * pointer move is what makes hand-rolled sliders feel sticky.
380
+ */
381
+ declare function Slider({ value, defaultValue, onChange, onCommit, min, max, step, disabled, orientation, marks, showValue, formatValue, label, className, name, }: SliderProps): react.JSX.Element;
382
+ interface RangeSliderProps extends Omit<SliderProps, 'value' | 'defaultValue' | 'onChange' | 'onCommit' | 'name'> {
383
+ value?: readonly [number, number];
384
+ defaultValue?: readonly [number, number];
385
+ onChange?: (value: readonly [number, number]) => void;
386
+ onCommit?: (value: readonly [number, number]) => void;
387
+ /** Smallest allowed gap between the two thumbs. Default `0`. */
388
+ minDistance?: number;
389
+ }
390
+ /**
391
+ * A two-thumb range slider — price filters, date windows, numeric facets.
392
+ *
393
+ * The thumbs are allowed to pass each other, swapping roles when they do. Blocking the crossing is
394
+ * the more common implementation and it is worse: a user dragging the lower thumb to the right
395
+ * hits an invisible wall and has to work out that they need to grab the other one instead.
396
+ */
397
+ declare function RangeSlider({ value, defaultValue, onChange, onCommit, min, max, step, minDistance, disabled, marks, showValue, formatValue, label, className, }: RangeSliderProps): react.JSX.Element;
398
+
399
+ interface PinInputProps {
400
+ /** Number of cells. Default `6`. */
401
+ length?: number;
402
+ value?: string;
403
+ defaultValue?: string;
404
+ onChange?: (value: string) => void;
405
+ /** Fired when every cell is filled — wire the verification call here. */
406
+ onComplete?: (value: string) => void;
407
+ /** Restrict input. Default `'numeric'`. */
408
+ type?: 'numeric' | 'alphanumeric';
409
+ /** Render as password dots. */
410
+ mask?: boolean;
411
+ disabled?: boolean;
412
+ invalid?: boolean;
413
+ autoFocus?: boolean;
414
+ label?: string;
415
+ className?: string;
416
+ }
417
+ /**
418
+ * A one-time-code input.
419
+ *
420
+ * The details that make or break this component are all about paste and autofill:
421
+ *
422
+ * - `autoComplete="one-time-code"` on the first cell is what triggers iOS and Android to offer the
423
+ * code from the SMS. Without it users retype a six-digit code by hand.
424
+ * - Pasting into any cell distributes across all of them. Users paste from their SMS app
425
+ * constantly, and a per-cell input that only accepts the first character is infuriating.
426
+ * - Backspace on an empty cell moves back *and* clears, so holding it wipes the field.
427
+ *
428
+ * A single input styled to look segmented would be simpler, but the caret cannot be positioned per
429
+ * segment and mobile keyboards behave inconsistently with it.
430
+ */
431
+ declare function PinInput({ length, value, defaultValue, onChange, onComplete, type, mask, disabled, invalid, autoFocus, label, className, }: PinInputProps): react.JSX.Element;
432
+
433
+ interface TagsInputProps {
434
+ value?: readonly string[];
435
+ defaultValue?: readonly string[];
436
+ onChange?: (value: readonly string[]) => void;
437
+ placeholder?: string;
438
+ /** Maximum number of tags. The input hides once reached. */
439
+ max?: number;
440
+ /** Keys that commit the current text. Default Enter, Tab and comma. */
441
+ delimiters?: readonly string[];
442
+ /** Reject a value — return an error string, or `null` to accept. */
443
+ validate?: (value: string) => string | null;
444
+ /** Fold duplicates silently instead of flagging them. Default `true`. */
445
+ dedupe?: boolean;
446
+ disabled?: boolean;
447
+ invalid?: boolean;
448
+ label?: string;
449
+ className?: string;
450
+ }
451
+ /**
452
+ * Free-text tags — recipients, labels, keywords.
453
+ *
454
+ * Distinct from a multi-`Combobox`: there is no option list, any text is valid. The two are often
455
+ * conflated, and the giveaway is a "tags" component that forces you to pick from a list.
456
+ *
457
+ * Each tag is a real button so it can be reached with Tab and removed with Enter. Removing a tag
458
+ * moves focus to the next one rather than to the document, which is what stops keyboard users
459
+ * losing their place after deleting three in a row.
460
+ */
461
+ declare function TagsInput({ value, defaultValue, onChange, placeholder, max, delimiters, validate, dedupe, disabled, invalid, label, className, }: TagsInputProps): react.JSX.Element;
462
+
463
+ interface RatingProps {
464
+ value?: number;
465
+ defaultValue?: number;
466
+ onChange?: (value: number) => void;
467
+ /** Number of symbols. Default `5`. */
468
+ max?: number;
469
+ /** Allow halves. Default `false`. */
470
+ allowHalf?: boolean;
471
+ /** Display only — no interaction, no focus. */
472
+ readOnly?: boolean;
473
+ disabled?: boolean;
474
+ size?: 'sm' | 'md' | 'lg';
475
+ /** Custom symbol. Defaults to a star. */
476
+ icon?: ReactNode;
477
+ label?: string;
478
+ className?: string;
479
+ name?: string;
480
+ }
481
+ /**
482
+ * A star rating.
483
+ *
484
+ * Interactive ratings are a **radio group**, not a set of buttons: exactly one of N is chosen, which
485
+ * is what a radio group means, and it gets arrow-key navigation and correct announcements for free.
486
+ * Read-only ratings are `role="img"` with a text label instead — "4.2 out of 5" is the entire
487
+ * content, and exposing five separate radios that can't be changed is noise.
488
+ */
489
+ declare function Rating({ value, defaultValue, onChange, max, allowHalf, readOnly, disabled, size, icon, label, className, name, }: RatingProps): react.JSX.Element;
490
+
491
+ interface DropzoneFile {
492
+ file: File;
493
+ id: string;
494
+ /** Object URL for image previews. Revoked automatically on removal. */
495
+ preview?: string;
496
+ error?: string;
497
+ }
498
+ interface DropzoneProps {
499
+ onFiles?: (files: readonly DropzoneFile[]) => void;
500
+ /** `accept` attribute, e.g. `'image/*,.pdf'`. */
501
+ accept?: string;
502
+ multiple?: boolean;
503
+ /** Per-file size cap, in bytes. */
504
+ maxSize?: number;
505
+ /** Maximum number of files. */
506
+ maxFiles?: number;
507
+ disabled?: boolean;
508
+ /** Show thumbnails for images. Default `true`. */
509
+ previews?: boolean;
510
+ label?: ReactNode;
511
+ hint?: ReactNode;
512
+ className?: string;
513
+ children?: ReactNode;
514
+ }
515
+ /**
516
+ * A file dropzone with previews and validation.
517
+ *
518
+ * Two things distinguish a working dropzone from a broken one:
519
+ *
520
+ * 1. **Drag counting.** `dragleave` fires when the pointer crosses onto a *child* element, so the
521
+ * naive implementation flickers the highlight constantly while you move across it. A depth
522
+ * counter incremented on enter and decremented on leave fixes it.
523
+ * 2. **A real `<input type="file">` underneath.** Drag-and-drop is unusable with a keyboard, a
524
+ * screen reader, or on a phone. The input is the actual control; the drop surface is an
525
+ * enhancement layered on top.
526
+ *
527
+ * Object URLs are revoked when a file is removed. Skipping that leaks the entire file into memory
528
+ * for the page's lifetime, which is invisible in testing and obvious after a user uploads forty
529
+ * photos.
530
+ */
531
+ declare function Dropzone({ onFiles, accept, multiple, maxSize, maxFiles, disabled, previews, label, hint, className, children, }: DropzoneProps): react.JSX.Element;
532
+
533
+ interface ColorPickerProps {
534
+ /** Any CSS colour — hex, rgb, hsl, oklch, or a named colour. */
535
+ value?: string;
536
+ defaultValue?: string;
537
+ onChange?: (value: string) => void;
538
+ /** Output format. Default `'hex'`. */
539
+ format?: 'hex' | 'rgb' | 'hsl' | 'oklch';
540
+ /** Show the alpha slider. Default `false`. */
541
+ alpha?: boolean;
542
+ /** Swatches shown under the picker. */
543
+ presets?: readonly string[];
544
+ /** Report the contrast ratio against this colour, live. */
545
+ contrastAgainst?: string;
546
+ disabled?: boolean;
547
+ label?: string;
548
+ className?: string;
549
+ }
550
+ /**
551
+ * A colour picker built on the engine's colour system.
552
+ *
553
+ * The saturation/value plane, hue rail and alpha rail are the conventional layout. What's less
554
+ * conventional, and the reason this is worth shipping over a generic picker:
555
+ *
556
+ * - **It speaks every format the engine does.** Paste `oklch(62% 0.2 264)` into the text field and
557
+ * it parses. Most pickers accept hex and nothing else.
558
+ * - **It can report contrast live.** On a theme editor — which is what colour pickers in admin
559
+ * tools are usually for — picking a brand colour without seeing that it fails WCAG against your
560
+ * background is how inaccessible products get built one reasonable decision at a time.
561
+ *
562
+ * State is held as HSV, not as the output string. Round-tripping through hex on every pointer move
563
+ * loses information: dragging into the black corner would discard the hue, so releasing and
564
+ * dragging back out returns red instead of the colour you started from.
565
+ */
566
+ declare function ColorPicker({ value, defaultValue, onChange, format, alpha, presets, contrastAgainst, disabled, label, className, }: ColorPickerProps): react.JSX.Element;
567
+
568
+ /**
569
+ * A month calendar.
570
+ *
571
+ * Dates are plain `Date` objects normalised to local midnight. No date library: the whole surface
572
+ * this needs is "add days", "same day?" and "days in month", and pulling in a 70 KB dependency for
573
+ * that is how component libraries end up heavier than the app using them.
574
+ *
575
+ * Local midnight, specifically. Working in UTC makes a date shift by a day for anyone west of
576
+ * Greenwich — the bug where a birthday saved on the 14th displays as the 13th, which is the single
577
+ * most reported issue in every date picker ever shipped.
578
+ *
579
+ * Keyboard support is the WAI-ARIA date grid pattern: arrows by day, PageUp/PageDown by month,
580
+ * Shift+PageUp/PageDown by year, Home/End to the week's ends. A calendar without these is
581
+ * mouse-only, and entering a date of birth by clicking back 300 months is not a real option.
582
+ */
583
+ /** Strip the time, in local time. */
584
+ declare function startOfDay(date: Date): Date;
585
+ declare function addDays(date: Date, days: number): Date;
586
+ declare function addMonths(date: Date, months: number): Date;
587
+ declare function isSameDay(a: Date | null | undefined, b: Date | null | undefined): boolean;
588
+ type DateRange = {
589
+ start: Date | null;
590
+ end: Date | null;
591
+ };
592
+ interface CalendarProps {
593
+ /** `'single'` picks one date; `'range'` picks a start and an end. */
594
+ mode?: 'single' | 'range';
595
+ value?: Date | null | DateRange;
596
+ defaultValue?: Date | null | DateRange;
597
+ onChange?: (value: Date | null | DateRange) => void;
598
+ min?: Date;
599
+ max?: Date;
600
+ /** Disable specific dates — weekends, holidays, booked days. */
601
+ isDateDisabled?: (date: Date) => boolean;
602
+ /** 0 = Sunday, 1 = Monday. Default: inferred from the locale. */
603
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
604
+ locale?: string;
605
+ /** Months shown side by side. Default `1`; `2` suits range pickers. */
606
+ months?: number;
607
+ /** Show ISO week numbers down the left. */
608
+ showWeekNumbers?: boolean;
609
+ className?: string;
610
+ }
611
+ declare function Calendar({ mode, value, defaultValue, onChange, min, max, isDateDisabled, weekStartsOn, locale, months, showWeekNumbers, className, }: CalendarProps): react.JSX.Element;
612
+
613
+ interface DatePickerProps extends Omit<CalendarProps, 'value' | 'defaultValue' | 'onChange' | 'mode' | 'className'> {
614
+ value?: Date | null;
615
+ defaultValue?: Date | null;
616
+ onChange?: (value: Date | null) => void;
617
+ placeholder?: string;
618
+ disabled?: boolean;
619
+ invalid?: boolean;
620
+ /** Show a clear button once a date is chosen. */
621
+ clearable?: boolean;
622
+ /** Format the displayed date. Defaults to the locale's medium date. */
623
+ format?: (date: Date) => string;
624
+ label?: string;
625
+ className?: string;
626
+ name?: string;
627
+ }
628
+ /**
629
+ * A date field with a calendar popover.
630
+ *
631
+ * The text input is editable, not just a display. Typing "14/03/2026" is faster than clicking
632
+ * through a calendar for anyone who knows the date they want, and a picker that forces the calendar
633
+ * is a picker people complain about. The calendar is the fallback for "some Thursday next month",
634
+ * which is the case typing can't serve.
635
+ *
636
+ * Parsing is deliberately forgiving of separators and order within a locale, but never guesses
637
+ * between `03/04` as March 4th and April 3rd — it follows the locale's own order. Guessing is how
638
+ * date fields silently record the wrong day.
639
+ */
640
+ declare function DatePicker({ value, defaultValue, onChange, placeholder, disabled, invalid, clearable, format, label, className, name, locale, ...calendarProps }: DatePickerProps): react.JSX.Element;
641
+ interface DateRangePickerProps extends Omit<CalendarProps, 'value' | 'defaultValue' | 'onChange' | 'mode' | 'className'> {
642
+ value?: DateRange;
643
+ defaultValue?: DateRange;
644
+ onChange?: (value: DateRange) => void;
645
+ disabled?: boolean;
646
+ invalid?: boolean;
647
+ label?: string;
648
+ className?: string;
649
+ /** Months shown side by side. Default `2`. */
650
+ months?: number;
651
+ }
652
+ /**
653
+ * A start/end date range.
654
+ *
655
+ * Two months by default: choosing a range that crosses a month boundary with one month visible
656
+ * means paging back and forth to compare, which is where users lose track of what they picked.
657
+ */
658
+ declare function DateRangePicker({ value, defaultValue, onChange, disabled, invalid, label, className, months, locale, ...calendarProps }: DateRangePickerProps): react.JSX.Element;
659
+
660
+ export { Calendar, type CalendarProps, Checkbox, type CheckboxProps, ChoiceCard, type ChoiceCardProps, ColorPicker, type ColorPickerProps, Combobox, type ComboboxProps, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, Dropzone, type DropzoneFile, type DropzoneProps, Field, type FieldContextValue, type FieldProps, Fieldset, type FieldsetProps, Input, type InputProps, type InputSize, NumberInput, type NumberInputProps, OptionList, type OptionListProps, PinInput, type PinInputProps, Radio, RadioGroup, type RadioGroupProps, type RadioProps, RangeSlider, type RangeSliderProps, Rating, type RatingProps, Select, type SelectOption, type SelectProps, Slider, type SliderMark, type SliderProps, Switch, type SwitchProps, TagsInput, type TagsInputProps, Textarea, type TextareaProps, VisuallyHidden, addDays, addMonths, filterOptions, firstEnabled, isSameDay, scoreOption, startOfDay, stepActive, useFieldContext, useFieldControl, useTypeahead };
package/dist/form.js ADDED
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+ export { Calendar, Checkbox, ChoiceCard, ColorPicker, Combobox, DatePicker, DateRangePicker, Dropzone, Field, Fieldset, Input, NumberInput, OptionList, PinInput, Radio, RadioGroup, RangeSlider, Rating, Select, Slider, Switch, TagsInput, Textarea, VisuallyHidden, addDays, addMonths, filterOptions, firstEnabled, isSameDay, scoreOption, startOfDay, stepActive, useFieldContext, useFieldControl, useTypeahead } from './chunk-QO3E7TJS.js';
3
+ import './chunk-XEP6P7E6.js';
4
+ import './chunk-LXCIXR5T.js';
5
+ import './chunk-IBXLC5IE.js';
6
+ import './chunk-H34OXXJU.js';
7
+ //# sourceMappingURL=form.js.map
8
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"form.js"}