ionbase-ui 0.51.0 → 0.69.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 (174) hide show
  1. package/dist/components/Accordion.d.ts +54 -0
  2. package/dist/components/Accordion.d.ts.map +1 -0
  3. package/dist/components/Accordion.js +68 -0
  4. package/dist/components/Accordion.js.map +1 -0
  5. package/dist/components/AgentActivity.d.ts +2 -1
  6. package/dist/components/AgentActivity.d.ts.map +1 -1
  7. package/dist/components/AgentActivity.js +4 -17
  8. package/dist/components/AgentActivity.js.map +1 -1
  9. package/dist/components/Breadcrumb.d.ts +45 -0
  10. package/dist/components/Breadcrumb.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.js +31 -0
  12. package/dist/components/Breadcrumb.js.map +1 -0
  13. package/dist/components/Calendar.d.ts +20 -0
  14. package/dist/components/Calendar.d.ts.map +1 -0
  15. package/dist/components/Calendar.js +151 -0
  16. package/dist/components/Calendar.js.map +1 -0
  17. package/dist/components/CalendarPopover.d.ts +26 -0
  18. package/dist/components/CalendarPopover.d.ts.map +1 -0
  19. package/dist/components/CalendarPopover.js +45 -0
  20. package/dist/components/CalendarPopover.js.map +1 -0
  21. package/dist/components/Checkbox.d.ts +2 -1
  22. package/dist/components/Checkbox.d.ts.map +1 -1
  23. package/dist/components/Checkbox.js +4 -2
  24. package/dist/components/Checkbox.js.map +1 -1
  25. package/dist/components/Combobox.d.ts +86 -0
  26. package/dist/components/Combobox.d.ts.map +1 -0
  27. package/dist/components/Combobox.js +261 -0
  28. package/dist/components/Combobox.js.map +1 -0
  29. package/dist/components/DateField.d.ts +15 -0
  30. package/dist/components/DateField.d.ts.map +1 -0
  31. package/dist/components/DateField.js +74 -0
  32. package/dist/components/DateField.js.map +1 -0
  33. package/dist/components/DatePicker.d.ts +81 -0
  34. package/dist/components/DatePicker.d.ts.map +1 -0
  35. package/dist/components/DatePicker.js +102 -0
  36. package/dist/components/DatePicker.js.map +1 -0
  37. package/dist/components/DateRangePicker.d.ts +117 -0
  38. package/dist/components/DateRangePicker.d.ts.map +1 -0
  39. package/dist/components/DateRangePicker.js +152 -0
  40. package/dist/components/DateRangePicker.js.map +1 -0
  41. package/dist/components/Drawer.d.ts +52 -0
  42. package/dist/components/Drawer.d.ts.map +1 -0
  43. package/dist/components/Drawer.js +66 -0
  44. package/dist/components/Drawer.js.map +1 -0
  45. package/dist/components/FileUpload.d.ts +79 -0
  46. package/dist/components/FileUpload.d.ts.map +1 -0
  47. package/dist/components/FileUpload.js +218 -0
  48. package/dist/components/FileUpload.js.map +1 -0
  49. package/dist/components/Header.d.ts +10 -0
  50. package/dist/components/Header.d.ts.map +1 -1
  51. package/dist/components/Header.js +18 -3
  52. package/dist/components/Header.js.map +1 -1
  53. package/dist/components/NumberInput.d.ts +63 -0
  54. package/dist/components/NumberInput.d.ts.map +1 -0
  55. package/dist/components/NumberInput.js +102 -0
  56. package/dist/components/NumberInput.js.map +1 -0
  57. package/dist/components/ProgressBar.d.ts +46 -0
  58. package/dist/components/ProgressBar.d.ts.map +1 -0
  59. package/dist/components/ProgressBar.js +52 -0
  60. package/dist/components/ProgressBar.js.map +1 -0
  61. package/dist/components/PromptInput.d.ts +89 -0
  62. package/dist/components/PromptInput.d.ts.map +1 -0
  63. package/dist/components/PromptInput.js +130 -0
  64. package/dist/components/PromptInput.js.map +1 -0
  65. package/dist/components/SegmentedControl.d.ts +63 -0
  66. package/dist/components/SegmentedControl.d.ts.map +1 -0
  67. package/dist/components/SegmentedControl.js +79 -0
  68. package/dist/components/SegmentedControl.js.map +1 -0
  69. package/dist/components/Select.d.ts +9 -2
  70. package/dist/components/Select.d.ts.map +1 -1
  71. package/dist/components/Select.js.map +1 -1
  72. package/dist/components/Sidebar.d.ts +109 -0
  73. package/dist/components/Sidebar.d.ts.map +1 -0
  74. package/dist/components/Sidebar.js +106 -0
  75. package/dist/components/Sidebar.js.map +1 -0
  76. package/dist/components/Skeleton.d.ts +39 -0
  77. package/dist/components/Skeleton.d.ts.map +1 -0
  78. package/dist/components/Skeleton.js +50 -0
  79. package/dist/components/Skeleton.js.map +1 -0
  80. package/dist/components/Spinner.d.ts +45 -0
  81. package/dist/components/Spinner.d.ts.map +1 -0
  82. package/dist/components/Spinner.js +44 -0
  83. package/dist/components/Spinner.js.map +1 -0
  84. package/dist/components/Stepper.d.ts +73 -0
  85. package/dist/components/Stepper.d.ts.map +1 -0
  86. package/dist/components/Stepper.js +82 -0
  87. package/dist/components/Stepper.js.map +1 -0
  88. package/dist/components/Toggle.d.ts +2 -1
  89. package/dist/components/Toggle.d.ts.map +1 -1
  90. package/dist/components/Toggle.js +4 -2
  91. package/dist/components/Toggle.js.map +1 -1
  92. package/dist/components/ToolCall.d.ts +66 -0
  93. package/dist/components/ToolCall.d.ts.map +1 -0
  94. package/dist/components/ToolCall.js +78 -0
  95. package/dist/components/ToolCall.js.map +1 -0
  96. package/dist/components/agent-status.d.ts +14 -0
  97. package/dist/components/agent-status.d.ts.map +1 -0
  98. package/dist/components/agent-status.js +16 -0
  99. package/dist/components/agent-status.js.map +1 -0
  100. package/dist/components/index.d.ts +33 -0
  101. package/dist/components/index.d.ts.map +1 -1
  102. package/dist/components/index.js +16 -0
  103. package/dist/components/index.js.map +1 -1
  104. package/dist/components/iso-date.d.ts +53 -0
  105. package/dist/components/iso-date.d.ts.map +1 -0
  106. package/dist/components/iso-date.js +102 -0
  107. package/dist/components/iso-date.js.map +1 -0
  108. package/dist/components/resolve-selection.d.ts +36 -0
  109. package/dist/components/resolve-selection.d.ts.map +1 -0
  110. package/dist/components/resolve-selection.js +14 -0
  111. package/dist/components/resolve-selection.js.map +1 -0
  112. package/dist/figma-descriptions.json +255 -145
  113. package/dist/figma-map.json +3205 -1860
  114. package/dist/meta/Accordion.json +123 -0
  115. package/dist/meta/AccordionItem.json +90 -0
  116. package/dist/meta/AgentActivityStep.json +2 -2
  117. package/dist/meta/Breadcrumb.json +87 -0
  118. package/dist/meta/BreadcrumbItem.json +78 -0
  119. package/dist/meta/Checkbox.json +18 -1
  120. package/dist/meta/Combobox.json +265 -0
  121. package/dist/meta/DatePicker.json +270 -0
  122. package/dist/meta/DateRangePicker.json +284 -0
  123. package/dist/meta/Drawer.json +234 -0
  124. package/dist/meta/EmptyState.json +1 -1
  125. package/dist/meta/FileUpload.json +246 -0
  126. package/dist/meta/Header.json +32 -2
  127. package/dist/meta/NumberInput.json +407 -0
  128. package/dist/meta/PhoneInput.json +39 -39
  129. package/dist/meta/Popover.json +3 -3
  130. package/dist/meta/ProgressBar.json +166 -0
  131. package/dist/meta/PromptInput.json +212 -0
  132. package/dist/meta/SegmentedControl.json +217 -0
  133. package/dist/meta/SegmentedControlItem.json +122 -0
  134. package/dist/meta/Select.json +4 -3
  135. package/dist/meta/Sidebar.json +150 -0
  136. package/dist/meta/SidebarItem.json +184 -0
  137. package/dist/meta/SidebarSection.json +131 -0
  138. package/dist/meta/Skeleton.json +120 -0
  139. package/dist/meta/Spinner.json +123 -0
  140. package/dist/meta/Stepper.json +135 -0
  141. package/dist/meta/StepperStep.json +148 -0
  142. package/dist/meta/Tabs.json +5 -0
  143. package/dist/meta/Toggle.json +18 -1
  144. package/dist/meta/ToolCall.json +192 -0
  145. package/dist/meta/Tooltip.json +3 -3
  146. package/dist/meta/components.json +6759 -2705
  147. package/dist/meta/contrast.json +11075 -3098
  148. package/dist/meta/index.json +266 -8
  149. package/dist/meta/patterns/AgentRun.json +27 -0
  150. package/dist/meta/patterns/Form.json +9 -0
  151. package/dist/meta/patterns/PageShell.json +43 -3
  152. package/dist/meta/patterns/Wizard.json +20 -1
  153. package/dist/meta/patterns/index.json +10 -1
  154. package/dist/styles/accordion.css +102 -0
  155. package/dist/styles/breadcrumb.css +72 -0
  156. package/dist/styles/combobox.css +154 -0
  157. package/dist/styles/date-picker.css +420 -0
  158. package/dist/styles/date-range-picker.css +280 -0
  159. package/dist/styles/drawer.css +181 -0
  160. package/dist/styles/empty-state.css +16 -4
  161. package/dist/styles/file-upload.css +236 -0
  162. package/dist/styles/header.css +26 -0
  163. package/dist/styles/index.css +23 -0
  164. package/dist/styles/number-input.css +83 -0
  165. package/dist/styles/progress-bar.css +107 -0
  166. package/dist/styles/prompt-input.css +127 -0
  167. package/dist/styles/segmented-control.css +182 -0
  168. package/dist/styles/sidebar.css +307 -0
  169. package/dist/styles/skeleton.css +76 -0
  170. package/dist/styles/spinner.css +70 -0
  171. package/dist/styles/stepper.css +250 -0
  172. package/dist/styles/tool-call.css +202 -0
  173. package/llms.txt +2 -2
  174. package/package.json +2 -1
@@ -0,0 +1,102 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef } from 'react';
4
+ import { useDatePicker, useButton } from 'react-aria';
5
+ import { useDatePickerState } from 'react-stately';
6
+ import { DateField } from './DateField.js';
7
+ import { SingleCalendar } from './Calendar.js';
8
+ import { CalendarPopover } from './CalendarPopover.js';
9
+ import { toCalendarDate, toIso, wrapUnavailable } from './iso-date.js';
10
+ const CalendarIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M8 2v4m8-4v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
11
+ /**
12
+ * DatePicker — a segmented date field with a calendar attached.
13
+ *
14
+ * WHY NOT `<input type="date">`
15
+ *
16
+ * The native control is genuinely good on mobile and genuinely unusable as a
17
+ * design system component on desktop: its calendar cannot be styled at all, it
18
+ * cannot express an unavailable date that is not part of a continuous min/max
19
+ * range, and Safari on macOS renders no calendar button whatsoever. A system
20
+ * whose date field looks like a different control in every browser is not a
21
+ * design system, so this rebuilds it — and pays for that by having to rebuild
22
+ * the keyboard handling and the screen-reader semantics the native control gave
23
+ * away for free. React Aria's `useDatePicker` is what builds them back.
24
+ *
25
+ * THE VALUE IS A STRING, THE DISPLAY IS NOT
26
+ *
27
+ * `value` is always `YYYY-MM-DD` in the Gregorian calendar. What the user SEES
28
+ * is their own locale's field order and their own calendar system, resolved
29
+ * from `Intl` — so a German reader edits DD.MM.YYYY and the value still comes
30
+ * back as `2026-04-12`. See `iso-date.ts` for why the public type is not
31
+ * react-aria's `CalendarDate`.
32
+ *
33
+ * THE FIELD IS EDITABLE WITHOUT THE CALENDAR, AND THAT IS THE POINT
34
+ *
35
+ * Typing a date is faster than clicking one for anybody who knows the date they
36
+ * want, and paging a calendar back to a birth year is miserable. The segments
37
+ * take digits and arrow keys directly; the calendar is for the cases where the
38
+ * date is being chosen rather than recalled.
39
+ */
40
+ export function DatePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size = 'md', value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, name, calendarLabel = 'Open calendar', className, wrapperClassName, id, }) {
41
+ /*
42
+ * The ISO boundary, crossed once on the way in and once on the way out.
43
+ *
44
+ * `value === undefined` has to survive the conversion as `undefined`, not
45
+ * become `null`. react-aria reads the two differently: `undefined` means
46
+ * uncontrolled and `null` means controlled-and-empty, so collapsing them
47
+ * would make an uncontrolled picker refuse to hold the date it was given.
48
+ */
49
+ const ariaProps = {
50
+ label: typeof label === 'string' ? label : undefined,
51
+ value: value === undefined ? undefined : toCalendarDate(value, 'value'),
52
+ defaultValue: toCalendarDate(defaultValue, 'defaultValue') ?? undefined,
53
+ minValue: toCalendarDate(minValue, 'minValue') ?? undefined,
54
+ maxValue: toCalendarDate(maxValue, 'maxValue') ?? undefined,
55
+ isDateUnavailable: wrapUnavailable(isDateUnavailable),
56
+ isDisabled,
57
+ isReadOnly,
58
+ isRequired,
59
+ isInvalid,
60
+ /*
61
+ * `granularity: 'day'` is what keeps this a DATE picker. Left unset,
62
+ * react-aria infers granularity from the value's type, and a value of
63
+ * `undefined` infers 'minute' — so an empty picker renders hour and minute
64
+ * segments that disappear the moment a date-only value arrives.
65
+ */
66
+ granularity: 'day',
67
+ onChange: onChange
68
+ ? (date) => onChange(date ? toIso(date) : null)
69
+ : undefined,
70
+ };
71
+ const state = useDatePickerState(ariaProps);
72
+ const groupRef = useRef(null);
73
+ const buttonRef = useRef(null);
74
+ const { groupProps, labelProps, fieldProps, buttonProps: triggerProps, dialogProps, calendarProps, descriptionProps, errorMessageProps, } = useDatePicker({ ...ariaProps, id }, state, groupRef);
75
+ const { buttonProps } = useButton(triggerProps, buttonRef);
76
+ const helper = isInvalid && errorMessage ? errorMessage : description;
77
+ return (_jsxs("div", { className: [
78
+ 'ion-field',
79
+ isInvalid ? 'ion-field--error' : '',
80
+ wrapperClassName || '',
81
+ ]
82
+ .filter(Boolean)
83
+ .join(' '), children: [label && (
84
+ // A <span>, not a <label>. The group is a set of spinbuttons rather
85
+ // than one form control, so `htmlFor` has nothing to point at —
86
+ // `useDatePicker` wires the name through `aria-labelledby` instead.
87
+ _jsx("span", { ...labelProps, className: "ion-field__label", children: label })), _jsxs("div", { ...groupProps, ref: groupRef, className: [
88
+ 'ion-input',
89
+ 'ion-date-picker',
90
+ size !== 'md' ? `ion-input--${size}` : '',
91
+ isInvalid ? 'ion-input--invalid' : '',
92
+ isDisabled ? 'ion-input--disabled' : '',
93
+ isReadOnly ? 'ion-input--readonly' : '',
94
+ className || '',
95
+ ]
96
+ .filter(Boolean)
97
+ .join(' '), "data-open": state.isOpen || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, children: [_jsx(DateField, { ...fieldProps }), name && (_jsx("input", { type: "hidden", name: name, value: state.value ? toIso(state.value) : '' })), _jsx("button", { ...buttonProps, ref: buttonRef, type: "button", "aria-labelledby": undefined, "aria-label": calendarLabel, className: "ion-date-picker__button", children: _jsx(CalendarIcon, {}) })] }), state.isOpen && !isDisabled && !isReadOnly && (_jsx(CalendarPopover, { state: state, triggerRef: groupRef, dialogProps: dialogProps, children: _jsx(SingleCalendar, { ...calendarProps }) })), helper && (_jsx("span", { ...(isInvalid && errorMessage
98
+ ? errorMessageProps
99
+ : descriptionProps), className: "ion-field__helper", children: helper }))] }));
100
+ }
101
+ DatePicker.displayName = 'DatePicker';
102
+ //# sourceMappingURL=DatePicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatePicker.js","sourceRoot":"","sources":["../../src/components/DatePicker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ import React from 'react';
2
+ import type { IsoDate } from './iso-date.js';
3
+ export type DateRangePickerSize = 'sm' | 'md' | 'lg';
4
+ /** A closed interval of calendar dates. `end` is included in the range. */
5
+ export interface DateRange {
6
+ /** `YYYY-MM-DD`. */
7
+ start: IsoDate;
8
+ /** `YYYY-MM-DD`, inclusive. */
9
+ end: IsoDate;
10
+ }
11
+ /**
12
+ * A shortcut in the rail beside the calendar — "Last 7 days", "Year to date".
13
+ */
14
+ export interface DateRangePreset {
15
+ /** What the row reads. Also its accessible name. */
16
+ label: string;
17
+ /**
18
+ * The range it selects.
19
+ *
20
+ * A FUNCTION for anything relative to today, which is most of them. A literal
21
+ * object is evaluated once, when the caller's module is first imported, so
22
+ * "Last 7 days" built as a literal is seven days before whenever the tab was
23
+ * opened — correct on load and increasingly wrong for a session left open
24
+ * overnight. The function is re-run every time the calendar opens.
25
+ */
26
+ value: DateRange | (() => DateRange);
27
+ }
28
+ export interface DateRangePickerProps {
29
+ /** Field label. Required for a usable control — see `a11y.requires`. */
30
+ label?: React.ReactNode;
31
+ /** Helper text below the field. */
32
+ description?: React.ReactNode;
33
+ /** Replaces the helper text when `isInvalid` is set. */
34
+ errorMessage?: React.ReactNode;
35
+ isInvalid?: boolean;
36
+ isDisabled?: boolean;
37
+ isReadOnly?: boolean;
38
+ isRequired?: boolean;
39
+ /** Matches Input's `Size` variant: Small, Medium, Large. */
40
+ size?: DateRangePickerSize;
41
+ /**
42
+ * The selected range, both ends as `YYYY-MM-DD`. `null` means no range.
43
+ * A half-open range is not representable on purpose — see `antiPatterns`.
44
+ */
45
+ value?: DateRange | null;
46
+ /** The initial range, for an uncontrolled picker. */
47
+ defaultValue?: DateRange;
48
+ /** Fires with both ends, or `null` when the range is cleared. */
49
+ onChange?: (value: DateRange | null) => void;
50
+ /** Earliest selectable date, `YYYY-MM-DD`. */
51
+ minValue?: IsoDate;
52
+ /** Latest selectable date, `YYYY-MM-DD`. */
53
+ maxValue?: IsoDate;
54
+ /**
55
+ * Individual dates the calendar refuses. Receives `YYYY-MM-DD`.
56
+ *
57
+ * A range may not be dragged ACROSS one of these — react-aria stops the
58
+ * selection at the first unavailable day, which is what makes "book a room
59
+ * for these nights" correct rather than merely styled.
60
+ */
61
+ isDateUnavailable?: (date: IsoDate) => boolean;
62
+ /** Shortcut rows beside the calendar. Omitted, no rail is rendered. */
63
+ presets?: readonly DateRangePreset[];
64
+ /**
65
+ * How many months the calendar shows at once. Two by default: a range that
66
+ * crosses a month boundary is the common case, and a one-month calendar
67
+ * makes the user page back and forth to see both ends of their own selection.
68
+ */
69
+ visibleMonths?: 1 | 2 | 3;
70
+ /** Show a Clear button under the calendar. Defaults to `true`. */
71
+ isClearable?: boolean;
72
+ /** Posts `<name>-start` and `<name>-end` for an uncontrolled form. */
73
+ name?: string;
74
+ /** Accessible label for the button that opens the calendar. */
75
+ calendarLabel?: string;
76
+ clearLabel?: string;
77
+ presetsLabel?: string;
78
+ className?: string;
79
+ wrapperClassName?: string;
80
+ id?: string;
81
+ }
82
+ /**
83
+ * DateRangePicker — two segmented date fields with one calendar between them.
84
+ *
85
+ * NOT TWO `DatePicker`s SIDE BY SIDE, and the difference is the whole reason
86
+ * this exists. Two independent pickers cannot stop the end date being before
87
+ * the start, cannot show the days between them as a band, and give the user two
88
+ * calendars to reconcile by memory. `useDateRangePicker` owns both ends: the
89
+ * second click completes the range in whichever order the two dates fall, the
90
+ * band follows the pointer while it is being dragged out, and a range cannot be
91
+ * made to run backwards.
92
+ *
93
+ * THE VALUE COMMITS LIVE. THERE IS NO APPLY BUTTON.
94
+ *
95
+ * `onChange` fires the moment the second end lands, and the calendar closes.
96
+ * The reference designs this was drawn from put Clear and Apply under the grid,
97
+ * which is a draft model: the popover holds a pending range and only Apply
98
+ * promotes it. That was considered and rejected. Escape and an outside click
99
+ * would then silently discard a selection the user watched themselves make —
100
+ * and there is no way to tell those two gestures apart from "I am done". A
101
+ * button labelled Apply beside a value that already applied is worse still.
102
+ *
103
+ * Clear stays, because clearing is a real action with no other affordance.
104
+ *
105
+ * BOTH ENDS OR NEITHER
106
+ *
107
+ * `value` has no half-open form. A range with a start and no end is a state the
108
+ * calendar passes THROUGH between the two clicks; it is never a value the
109
+ * caller is handed, because "everything after March" and "March to today" are
110
+ * different questions and a `null` end cannot say which one was meant. A caller
111
+ * who wants an open end passes a sentinel of their own choosing as `maxValue`.
112
+ */
113
+ export declare function DateRangePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size, value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, presets, visibleMonths, isClearable, name, calendarLabel, clearLabel, presetsLabel, className, wrapperClassName, id, }: DateRangePickerProps): React.JSX.Element;
114
+ export declare namespace DateRangePicker {
115
+ var displayName: string;
116
+ }
117
+ //# sourceMappingURL=DateRangePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateRangePicker.d.ts","sourceRoot":"","sources":["../../src/components/DateRangePicker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAO/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,2EAA2E;AAC3E,MAAM,WAAW,SAAS;IACxB,oBAAoB;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC,CAAC;CACtC;AAcD,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,mCAAmC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAC/C,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,IAAW,EACX,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,OAAO,EACP,aAAiB,EACjB,WAAkB,EAClB,IAAI,EACJ,aAA+B,EAC/B,UAAoB,EACpB,YAAqC,EACrC,SAAS,EACT,gBAAgB,EAChB,EAAE,GACH,EAAE,oBAAoB,qBAuPtB;yBAhRe,eAAe"}
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useRef } from 'react';
4
+ import { useDateRangePicker, useButton } from 'react-aria';
5
+ import { useDateRangePickerState } from 'react-stately';
6
+ import { DateField } from './DateField.js';
7
+ import { RangeCalendar } from './Calendar.js';
8
+ import { CalendarPopover } from './CalendarPopover.js';
9
+ import { toCalendarDate, toIso, wrapUnavailable } from './iso-date.js';
10
+ const CalendarIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M8 2v4m8-4v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
11
+ /** Resolves a preset's `value`, which may be a literal or a function. */
12
+ const resolvePreset = (p) => typeof p.value === 'function' ? p.value() : p.value;
13
+ /**
14
+ * DateRangePicker — two segmented date fields with one calendar between them.
15
+ *
16
+ * NOT TWO `DatePicker`s SIDE BY SIDE, and the difference is the whole reason
17
+ * this exists. Two independent pickers cannot stop the end date being before
18
+ * the start, cannot show the days between them as a band, and give the user two
19
+ * calendars to reconcile by memory. `useDateRangePicker` owns both ends: the
20
+ * second click completes the range in whichever order the two dates fall, the
21
+ * band follows the pointer while it is being dragged out, and a range cannot be
22
+ * made to run backwards.
23
+ *
24
+ * THE VALUE COMMITS LIVE. THERE IS NO APPLY BUTTON.
25
+ *
26
+ * `onChange` fires the moment the second end lands, and the calendar closes.
27
+ * The reference designs this was drawn from put Clear and Apply under the grid,
28
+ * which is a draft model: the popover holds a pending range and only Apply
29
+ * promotes it. That was considered and rejected. Escape and an outside click
30
+ * would then silently discard a selection the user watched themselves make —
31
+ * and there is no way to tell those two gestures apart from "I am done". A
32
+ * button labelled Apply beside a value that already applied is worse still.
33
+ *
34
+ * Clear stays, because clearing is a real action with no other affordance.
35
+ *
36
+ * BOTH ENDS OR NEITHER
37
+ *
38
+ * `value` has no half-open form. A range with a start and no end is a state the
39
+ * calendar passes THROUGH between the two clicks; it is never a value the
40
+ * caller is handed, because "everything after March" and "March to today" are
41
+ * different questions and a `null` end cannot say which one was meant. A caller
42
+ * who wants an open end passes a sentinel of their own choosing as `maxValue`.
43
+ */
44
+ export function DateRangePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size = 'md', value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, presets, visibleMonths = 2, isClearable = true, name, calendarLabel = 'Open calendar', clearLabel = 'Clear', presetsLabel = 'Date range shortcuts', className, wrapperClassName, id, }) {
45
+ /*
46
+ * The ISO boundary. `undefined` has to stay `undefined` — react-aria reads it
47
+ * as "uncontrolled" where `null` means "controlled and empty", so collapsing
48
+ * the two makes an uncontrolled picker refuse to hold its own value.
49
+ */
50
+ const toRange = (r, prop) => r == null
51
+ ? null
52
+ : {
53
+ start: toCalendarDate(r.start, `${prop}.start`),
54
+ end: toCalendarDate(r.end, `${prop}.end`),
55
+ };
56
+ const ariaProps = {
57
+ label: typeof label === 'string' ? label : undefined,
58
+ value: value === undefined ? undefined : toRange(value, 'value'),
59
+ defaultValue: toRange(defaultValue, 'defaultValue') ?? undefined,
60
+ minValue: toCalendarDate(minValue, 'minValue') ?? undefined,
61
+ maxValue: toCalendarDate(maxValue, 'maxValue') ?? undefined,
62
+ isDateUnavailable: wrapUnavailable(isDateUnavailable),
63
+ isDisabled,
64
+ isReadOnly,
65
+ isRequired,
66
+ isInvalid,
67
+ /*
68
+ * Same reason as DatePicker: left unset, react-aria infers granularity from
69
+ * the value, and an empty picker infers 'minute' and grows time segments
70
+ * that vanish the moment a date arrives.
71
+ */
72
+ granularity: 'day',
73
+ onChange: onChange
74
+ ? (r) => onChange(r ? { start: toIso(r.start), end: toIso(r.end) } : null)
75
+ : undefined,
76
+ };
77
+ const state = useDateRangePickerState(ariaProps);
78
+ const groupRef = useRef(null);
79
+ const buttonRef = useRef(null);
80
+ const { groupProps, labelProps, startFieldProps, endFieldProps, buttonProps: triggerProps, dialogProps, calendarProps, descriptionProps, errorMessageProps, } = useDateRangePicker({ ...ariaProps, id }, state, groupRef);
81
+ const { buttonProps } = useButton(triggerProps, buttonRef);
82
+ /*
83
+ * Resolved once per open, not once per render.
84
+ *
85
+ * `state.isOpen` is the dependency on purpose. A relative preset is a
86
+ * function of today, so re-resolving it on every render would be correct and
87
+ * wasteful; resolving it once when the module loads would be cheap and wrong
88
+ * by morning. Re-resolving each time the calendar opens is the only cadence
89
+ * at which "Last 7 days" is both stable while the user reads it and accurate
90
+ * when they come back to it.
91
+ */
92
+ const resolvedPresets = useMemo(() => (presets ?? []).map((p) => ({ label: p.label, ...resolvePreset(p) })),
93
+ // `state.isOpen` is in here as a CADENCE, not as a value this reads —
94
+ // see above. An exhaustive-deps rule would call it extraneous and be
95
+ // wrong: dropping it is what makes a relative preset go stale.
96
+ [presets, state.isOpen]);
97
+ const selectedIso = state.value?.start &&
98
+ state.value?.end && {
99
+ start: toIso(state.value.start),
100
+ end: toIso(state.value.end),
101
+ };
102
+ const helper = isInvalid && errorMessage ? errorMessage : description;
103
+ const rail = resolvedPresets.length ? (_jsx("div", { className: "ion-range-presets", role: "group", "aria-label": presetsLabel, children: resolvedPresets.map((p) => {
104
+ const isSelected = selectedIso?.start === p.start && selectedIso?.end === p.end;
105
+ return (_jsx("button", { type: "button", className: "ion-range-presets__item", "aria-pressed": isSelected, "data-selected": isSelected || undefined, onClick: () => {
106
+ state.setValue({
107
+ start: toCalendarDate(p.start, `preset "${p.label}"`),
108
+ end: toCalendarDate(p.end, `preset "${p.label}"`),
109
+ });
110
+ /*
111
+ * Closed explicitly. A complete range from the GRID closes the
112
+ * calendar on its own, and a preset produces the same complete
113
+ * range — leaving it open after a shortcut was pressed would make
114
+ * the two paths behave differently for no reason the user could
115
+ * see.
116
+ */
117
+ state.setOpen(false);
118
+ }, children: p.label }, p.label));
119
+ }) })) : null;
120
+ const footer = isClearable && state.value ? (_jsx("div", { className: "ion-range-footer", children: _jsx("button", { type: "button", className: "ion-range-footer__clear", onClick: () => {
121
+ state.setValue(null);
122
+ /*
123
+ * Deliberately left OPEN. Clearing is nearly always the first half
124
+ * of choosing something else; closing the calendar would make the
125
+ * user reopen it to do the thing they cleared it for.
126
+ */
127
+ }, children: clearLabel }) })) : null;
128
+ return (_jsxs("div", { className: [
129
+ 'ion-field',
130
+ isInvalid ? 'ion-field--error' : '',
131
+ wrapperClassName || '',
132
+ ]
133
+ .filter(Boolean)
134
+ .join(' '), children: [label && (
135
+ // A <span>, not a <label> — the group holds two fields of spinbuttons
136
+ // rather than one form control, so `htmlFor` has nothing to point at.
137
+ _jsx("span", { ...labelProps, className: "ion-field__label", children: label })), _jsxs("div", { ...groupProps, ref: groupRef, className: [
138
+ 'ion-input',
139
+ 'ion-date-range-picker',
140
+ size !== 'md' ? `ion-input--${size}` : '',
141
+ isInvalid ? 'ion-input--invalid' : '',
142
+ isDisabled ? 'ion-input--disabled' : '',
143
+ isReadOnly ? 'ion-input--readonly' : '',
144
+ className || '',
145
+ ]
146
+ .filter(Boolean)
147
+ .join(' '), "data-open": state.isOpen || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, children: [_jsx(DateField, { ...startFieldProps }), _jsx("span", { className: "ion-date-range-picker__dash", "aria-hidden": "true", children: "\u2013" }), _jsx(DateField, { ...endFieldProps }), name && (_jsxs(_Fragment, { children: [_jsx("input", { type: "hidden", name: `${name}-start`, value: state.value?.start ? toIso(state.value.start) : '' }), _jsx("input", { type: "hidden", name: `${name}-end`, value: state.value?.end ? toIso(state.value.end) : '' })] })), _jsx("button", { ...buttonProps, ref: buttonRef, type: "button", "aria-labelledby": undefined, "aria-label": calendarLabel, className: "ion-date-range-picker__button", children: _jsx(CalendarIcon, {}) })] }), state.isOpen && !isDisabled && !isReadOnly && (_jsx(CalendarPopover, { state: state, triggerRef: groupRef, dialogProps: dialogProps, className: "ion-calendar-popover--range", children: _jsx(RangeCalendar, { ...calendarProps, visibleMonths: visibleMonths, aside: rail, footer: footer }) })), helper && (_jsx("span", { ...(isInvalid && errorMessage
148
+ ? errorMessageProps
149
+ : descriptionProps), className: "ion-field__helper", children: helper }))] }));
150
+ }
151
+ DateRangePicker.displayName = 'DateRangePicker';
152
+ //# sourceMappingURL=DateRangePicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateRangePicker.js","sourceRoot":"","sources":["../../src/components/DateRangePicker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import type { AriaModalOverlayProps } from 'react-aria';
3
+ export type DrawerPlacement = 'start' | 'end' | 'top' | 'bottom';
4
+ export type DrawerSize = 'sm' | 'md' | 'lg';
5
+ export interface DrawerProps extends AriaModalOverlayProps {
6
+ /** Whether the drawer is shown. */
7
+ isOpen?: boolean;
8
+ /** Called with the next open state — `false` when the drawer asks to close. */
9
+ onOpenChange?: (isOpen: boolean) => void;
10
+ /**
11
+ * Which edge it enters from. `start` and `end` follow writing direction, so
12
+ * a right-hand drawer in English is a left-hand one in Arabic without a
13
+ * second variant.
14
+ */
15
+ placement?: DrawerPlacement;
16
+ size?: DrawerSize;
17
+ /** Required: the dialog's accessible name, wired via `aria-labelledby`. */
18
+ title: React.ReactNode;
19
+ /** Supporting copy under the title. */
20
+ description?: React.ReactNode;
21
+ /** Action row, pinned to the bottom edge. */
22
+ footer?: React.ReactNode;
23
+ showClose?: boolean;
24
+ closeLabel?: string;
25
+ children?: React.ReactNode;
26
+ className?: string;
27
+ }
28
+ /**
29
+ * Drawer — a modal panel anchored to an edge.
30
+ *
31
+ * WHY IT IS A MODAL AND NOT A SIDEBAR
32
+ *
33
+ * It takes focus, traps it, closes on Escape and on an outside click, and marks
34
+ * the rest of the page inert — all of `Modal`'s behaviour, differing only in
35
+ * where the panel sits and how it enters. A persistent side panel that does not
36
+ * do those things is layout, not a drawer, and belongs in the page rather than
37
+ * in an overlay.
38
+ *
39
+ * WHEN TO REACH FOR MODAL INSTEAD
40
+ *
41
+ * A drawer suits work that is long or list-shaped — filters, a record's detail,
42
+ * a sequence of settings — because an edge panel can be tall without becoming a
43
+ * square that fights the viewport. A decision, especially a destructive one,
44
+ * belongs in a Modal: it is centred, it is smaller, and it does not invite
45
+ * scrolling past the thing being confirmed.
46
+ *
47
+ * RENDERS NOTHING WHEN CLOSED, and portals when open, for the reasons recorded
48
+ * on Modal: a closed overlay left in the tree is still keyboard-focusable, and
49
+ * an inline one inherits whatever `overflow` and stacking context surrounds it.
50
+ */
51
+ export declare const Drawer: React.ForwardRefExoticComponent<DrawerProps & React.RefAttributes<HTMLDivElement>>;
52
+ //# sourceMappingURL=Drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAkD,MAAM,OAAO,CAAC;AASvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WAAY,SAAQ,qBAAqB;IACxD,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2EAA2E;IAC3E,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,uCAAuC;IACvC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkGD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,MAAM,oFAUjB,CAAC"}
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
+ import { Overlay, useModalOverlay, useDialog, useButton, mergeProps, } from 'react-aria';
5
+ import { useOverlayTriggerState } from 'react-stately';
6
+ const CloseGlyph = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
7
+ /**
8
+ * The panel. Mounted only while open, which is what makes the hooks work.
9
+ *
10
+ * `useDialog` resolves the title id and moves focus in effects keyed on the
11
+ * panel ref — effects that run when the component CALLING them mounts. Called
12
+ * beside `useOverlayTriggerState` in `Drawer` below, that moment would be when
13
+ * the trigger mounts, with this panel not yet in the DOM: no `aria-labelledby`,
14
+ * and focus never entering the drawer, so Escape never reaches the key handler.
15
+ * Silent in every case. AGENTS.md records the same split for Modal and Popover.
16
+ */
17
+ function DrawerDialog(props, forwardedRef) {
18
+ const { state, placement = 'end', size = 'md', title, description, footer, showClose = true, closeLabel = 'Close drawer', children, className, } = props;
19
+ const panelRef = useRef(null);
20
+ useImperativeHandle(forwardedRef, () => panelRef.current);
21
+ const { modalProps, underlayProps } = useModalOverlay(props, state, panelRef);
22
+ const { dialogProps, titleProps } = useDialog({ role: 'dialog' }, panelRef);
23
+ const closeRef = useRef(null);
24
+ const { buttonProps } = useButton({ onPress: () => state.close(), 'aria-label': closeLabel }, closeRef);
25
+ const panelClassNames = [
26
+ 'ion-drawer',
27
+ `ion-drawer--${placement}`,
28
+ `ion-drawer--${size}`,
29
+ className || '',
30
+ ]
31
+ .filter(Boolean)
32
+ .join(' ');
33
+ return (_jsx("div", { ...underlayProps, className: "ion-drawer__scrim", children: _jsxs("div", { ...mergeProps(modalProps, dialogProps), ref: panelRef, className: panelClassNames, children: [_jsxs("div", { className: "ion-drawer__header", children: [_jsxs("div", { className: "ion-drawer__heading", children: [_jsx("h2", { ...titleProps, className: "ion-drawer__title", children: title }), description && (_jsx("p", { className: "ion-drawer__description", children: description }))] }), showClose && (_jsx("button", { ...buttonProps, ref: closeRef, type: "button", className: "ion-drawer__close", children: _jsx(CloseGlyph, {}) }))] }), children && _jsx("div", { className: "ion-drawer__body", children: children }), footer && _jsx("div", { className: "ion-drawer__footer", children: footer })] }) }));
34
+ }
35
+ const DrawerDialogWithRef = forwardRef(DrawerDialog);
36
+ /**
37
+ * Drawer — a modal panel anchored to an edge.
38
+ *
39
+ * WHY IT IS A MODAL AND NOT A SIDEBAR
40
+ *
41
+ * It takes focus, traps it, closes on Escape and on an outside click, and marks
42
+ * the rest of the page inert — all of `Modal`'s behaviour, differing only in
43
+ * where the panel sits and how it enters. A persistent side panel that does not
44
+ * do those things is layout, not a drawer, and belongs in the page rather than
45
+ * in an overlay.
46
+ *
47
+ * WHEN TO REACH FOR MODAL INSTEAD
48
+ *
49
+ * A drawer suits work that is long or list-shaped — filters, a record's detail,
50
+ * a sequence of settings — because an edge panel can be tall without becoming a
51
+ * square that fights the viewport. A decision, especially a destructive one,
52
+ * belongs in a Modal: it is centred, it is smaller, and it does not invite
53
+ * scrolling past the thing being confirmed.
54
+ *
55
+ * RENDERS NOTHING WHEN CLOSED, and portals when open, for the reasons recorded
56
+ * on Modal: a closed overlay left in the tree is still keyboard-focusable, and
57
+ * an inline one inherits whatever `overflow` and stacking context surrounds it.
58
+ */
59
+ export const Drawer = forwardRef((props, ref) => {
60
+ const state = useOverlayTriggerState(props);
61
+ if (!state.isOpen)
62
+ return null;
63
+ return (_jsx(Overlay, { children: _jsx(DrawerDialogWithRef, { ...props, state: state, ref: ref }) }));
64
+ });
65
+ Drawer.displayName = 'Drawer';
66
+ //# sourceMappingURL=Drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.js","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ export type FileUploadSize = 'sm' | 'md';
3
+ /** A file the control refused, and the reason, so the caller can say so. */
4
+ export interface RejectedFile {
5
+ file: File;
6
+ reason: 'type' | 'size' | 'count';
7
+ message: string;
8
+ }
9
+ export interface FileUploadProps {
10
+ /** Field label. Also the accessible name of the file input. */
11
+ label?: React.ReactNode;
12
+ /** Helper text below the control. */
13
+ description?: React.ReactNode;
14
+ /** Replaces the helper text when `isInvalid` is set. */
15
+ errorMessage?: React.ReactNode;
16
+ isInvalid?: boolean;
17
+ isDisabled?: boolean;
18
+ /** Matches the Figma `Size` variant when drawn. Height of the drop target. */
19
+ size?: FileUploadSize;
20
+ /** Native `accept` — a comma-separated list of extensions or MIME types. */
21
+ accept?: string;
22
+ /** Allow more than one file. */
23
+ multiple?: boolean;
24
+ /** Reject anything larger, in bytes. */
25
+ maxSize?: number;
26
+ /** Reject once this many files are held. Only meaningful with `multiple`. */
27
+ maxFiles?: number;
28
+ /**
29
+ * The files held. Pass it to control the list; omit and the component keeps
30
+ * its own. A controlled list is the usual case, because the caller is the
31
+ * only thing that knows which uploads have since succeeded or failed.
32
+ */
33
+ files?: readonly File[];
34
+ /** Fires with the full list after every add or remove, never with a delta. */
35
+ onChange?: (files: File[]) => void;
36
+ /** Fires with anything refused by `accept`, `maxSize` or `maxFiles`. */
37
+ onReject?: (rejected: RejectedFile[]) => void;
38
+ /** Call-to-action inside the drop target. */
39
+ prompt?: React.ReactNode;
40
+ /** Second line inside the drop target — the constraints, in words. */
41
+ hint?: React.ReactNode;
42
+ /** Accessible label for each file's remove button. Receives the file name. */
43
+ removeLabel?: (name: string) => string;
44
+ className?: string;
45
+ wrapperClassName?: string;
46
+ id?: string;
47
+ name?: string;
48
+ }
49
+ /**
50
+ * FileUpload — a drop target wrapped around a real `<input type="file">`.
51
+ *
52
+ * Drawn in Figma as `File Upload` (1367:2333) — two sizes by five states. The
53
+ * measurements came from `Input` and `EmptyState` rather than being invented:
54
+ * the border, radius and disabled treatment are Input's; the centred
55
+ * icon-over-text stack is EmptyState's. Figma draws the drop zone alone, the
56
+ * same split `Input` makes against `Form Field`.
57
+ *
58
+ * THE INPUT IS THE CONTROL. THE DROP ZONE IS DECORATION.
59
+ *
60
+ * The usual build of this component is a `<div>` with drag handlers and a
61
+ * click that calls `input.click()`. That version cannot be reached by keyboard,
62
+ * has no accessible name, does not participate in a form, and does not work in
63
+ * any environment without a pointer — which includes switch access, voice
64
+ * control and most screen-reader browse modes.
65
+ *
66
+ * Here the file input is a real, focusable, labelled control that is visually
67
+ * hidden but NOT `display: none` — it keeps its place in the tab order and its
68
+ * label. Drag-and-drop is layered on top as an enhancement, and every path it
69
+ * offers is also reachable without it. That ordering is the whole component.
70
+ *
71
+ * VALIDATION IS ADVISORY, NOT SECURITY. `accept` and `maxSize` are checked here
72
+ * so the user finds out immediately instead of after an upload. A server that
73
+ * trusts either one is trusting a value the client chose.
74
+ */
75
+ export declare function FileUpload({ label, description, errorMessage, isInvalid, isDisabled: isDisabledProp, size, accept, multiple, maxSize, maxFiles, files: controlledFiles, onChange, onReject, prompt, hint, removeLabel, className, wrapperClassName, id: providedId, name: inputName, }: FileUploadProps): React.JSX.Element;
76
+ export declare namespace FileUpload {
77
+ var displayName: string;
78
+ }
79
+ //# sourceMappingURL=FileUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUpload.d.ts","sourceRoot":"","sources":["../../src/components/FileUpload.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,qCAAqC;IACrC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC9C,6CAA6C;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,sEAAsE;IACtE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EAAE,cAAc,EAC1B,IAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,KAAK,EAAE,eAAe,EACtB,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,WAAwC,EACxC,SAAS,EACT,gBAAgB,EAChB,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,SAAS,GAChB,EAAE,eAAe,qBAuPjB;yBA5Qe,UAAU"}