react-os-shell 4.15.0 → 4.20.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 (95) hide show
  1. package/README.md +46 -0
  2. package/dist/{Browser-XV34CJFP.js → Browser-2TRNQ4MK.js} +9 -6
  3. package/dist/Browser-2TRNQ4MK.js.map +1 -0
  4. package/dist/{Calculator-FFCYNYU3.js → Calculator-QCAJ6ITI.js} +8 -5
  5. package/dist/Calculator-QCAJ6ITI.js.map +1 -0
  6. package/dist/ConfirmDialog-WAM5OKAV.js +3 -0
  7. package/dist/{ConfirmDialog-CXHJCDUL.js.map → ConfirmDialog-WAM5OKAV.js.map} +1 -1
  8. package/dist/{CurrencyConverter-3NCK7WY6.js → CurrencyConverter-PCYC6RL5.js} +8 -5
  9. package/dist/CurrencyConverter-PCYC6RL5.js.map +1 -0
  10. package/dist/{Documents-S6EQOLUF.js → Documents-O5TEGSYH.js} +10 -7
  11. package/dist/Documents-O5TEGSYH.js.map +1 -0
  12. package/dist/{Files-TFZNYXAH.js → Files-CD3FTPLK.js} +16 -12
  13. package/dist/Files-CD3FTPLK.js.map +1 -0
  14. package/dist/{Notepad-EAA6CXZX.js → Notepad-5KZS7PUJ.js} +10 -7
  15. package/dist/Notepad-5KZS7PUJ.js.map +1 -0
  16. package/dist/{PomodoroTimer-BXI752XM.js → PomodoroTimer-YABFEPEV.js} +10 -7
  17. package/dist/PomodoroTimer-YABFEPEV.js.map +1 -0
  18. package/dist/{Preview-ZKYM7A5X.js → Preview-UUNXWQME.js} +11 -8
  19. package/dist/Preview-UUNXWQME.js.map +1 -0
  20. package/dist/{Sidebar-IMACKSHS.js → Sidebar-B3LY5T2U.js} +5 -4
  21. package/dist/Sidebar-B3LY5T2U.js.map +1 -0
  22. package/dist/{Spreadsheet-BXOPROMB.js → Spreadsheet-DB5GMW2X.js} +10 -7
  23. package/dist/Spreadsheet-DB5GMW2X.js.map +1 -0
  24. package/dist/{Stock-BEQCSYNT.js → Stock-IPLLRUOC.js} +8 -5
  25. package/dist/Stock-IPLLRUOC.js.map +1 -0
  26. package/dist/{Weather-QPV6G7UN.js → Weather-SI5QN7X2.js} +8 -5
  27. package/dist/Weather-SI5QN7X2.js.map +1 -0
  28. package/dist/{WorldClock-I3ZE57V4.js → WorldClock-WYDSMUWX.js} +8 -5
  29. package/dist/WorldClock-WYDSMUWX.js.map +1 -0
  30. package/dist/apps/index.js +22 -19
  31. package/dist/apps/index.js.map +1 -1
  32. package/dist/chunk-22BNPLM4.js +46 -0
  33. package/dist/chunk-22BNPLM4.js.map +1 -0
  34. package/dist/{chunk-IVLDAUYM.js → chunk-3CUSBZWG.js} +3 -56
  35. package/dist/chunk-3CUSBZWG.js.map +1 -0
  36. package/dist/{chunk-VENYVK3L.js → chunk-4AKVHY4B.js} +48 -12
  37. package/dist/chunk-4AKVHY4B.js.map +1 -0
  38. package/dist/chunk-C22EFSND.js +188 -0
  39. package/dist/chunk-C22EFSND.js.map +1 -0
  40. package/dist/{chunk-LFTHOYKW.js → chunk-CZH7CN3S.js} +5 -5
  41. package/dist/{chunk-LFTHOYKW.js.map → chunk-CZH7CN3S.js.map} +1 -1
  42. package/dist/chunk-EGM44WML.js +5008 -0
  43. package/dist/chunk-EGM44WML.js.map +1 -0
  44. package/dist/{chunk-H4IEH6FK.js → chunk-EWDYCO65.js} +3 -3
  45. package/dist/{chunk-H4IEH6FK.js.map → chunk-EWDYCO65.js.map} +1 -1
  46. package/dist/{chunk-DINNWYVB.js → chunk-HYAW6KIL.js} +4 -45
  47. package/dist/chunk-HYAW6KIL.js.map +1 -0
  48. package/dist/{chunk-H3HFM4JQ.js → chunk-IGPSRRL7.js} +262 -712
  49. package/dist/chunk-IGPSRRL7.js.map +1 -0
  50. package/dist/chunk-L2O6B5KT.js +394 -0
  51. package/dist/chunk-L2O6B5KT.js.map +1 -0
  52. package/dist/{chunk-YS4ZW2P7.js → chunk-OI253GOA.js} +4 -4
  53. package/dist/{chunk-YS4ZW2P7.js.map → chunk-OI253GOA.js.map} +1 -1
  54. package/dist/{chunk-ZPCGIR62.js → chunk-OQ7GRU7G.js} +3 -3
  55. package/dist/{chunk-ZPCGIR62.js.map → chunk-OQ7GRU7G.js.map} +1 -1
  56. package/dist/{chunk-7J3HTJJ3.js → chunk-Q3AOIJ7B.js} +3 -3
  57. package/dist/{chunk-7J3HTJJ3.js.map → chunk-Q3AOIJ7B.js.map} +1 -1
  58. package/dist/{chunk-N2UW3CAS.js → chunk-QJS6RBH4.js} +3 -3
  59. package/dist/{chunk-N2UW3CAS.js.map → chunk-QJS6RBH4.js.map} +1 -1
  60. package/dist/chunk-WNLPTLT6.js +56 -0
  61. package/dist/chunk-WNLPTLT6.js.map +1 -0
  62. package/dist/{chunk-QU5L5ZYM.js → chunk-X5PZYTAK.js} +3 -3
  63. package/dist/{chunk-QU5L5ZYM.js.map → chunk-X5PZYTAK.js.map} +1 -1
  64. package/dist/chunk-ZKQHO2Z7.js +7 -0
  65. package/dist/{chunk-F562QDVS.js.map → chunk-ZKQHO2Z7.js.map} +1 -1
  66. package/dist/index-C5c6FA0c.d.ts +1931 -0
  67. package/dist/index.d.ts +7 -1375
  68. package/dist/index.js +159 -4082
  69. package/dist/index.js.map +1 -1
  70. package/dist/shell.css +105 -0
  71. package/dist/styles.css +12 -501
  72. package/dist/ui/index.d.ts +3 -0
  73. package/dist/ui/index.js +11 -0
  74. package/dist/ui/index.js.map +1 -0
  75. package/dist/ui.css +425 -0
  76. package/package.json +7 -2
  77. package/dist/Browser-XV34CJFP.js.map +0 -1
  78. package/dist/Calculator-FFCYNYU3.js.map +0 -1
  79. package/dist/ConfirmDialog-CXHJCDUL.js +0 -4
  80. package/dist/CurrencyConverter-3NCK7WY6.js.map +0 -1
  81. package/dist/Documents-S6EQOLUF.js.map +0 -1
  82. package/dist/Files-TFZNYXAH.js.map +0 -1
  83. package/dist/Notepad-EAA6CXZX.js.map +0 -1
  84. package/dist/PomodoroTimer-BXI752XM.js.map +0 -1
  85. package/dist/Preview-ZKYM7A5X.js.map +0 -1
  86. package/dist/Sidebar-IMACKSHS.js.map +0 -1
  87. package/dist/Spreadsheet-BXOPROMB.js.map +0 -1
  88. package/dist/Stock-BEQCSYNT.js.map +0 -1
  89. package/dist/Weather-QPV6G7UN.js.map +0 -1
  90. package/dist/WorldClock-I3ZE57V4.js.map +0 -1
  91. package/dist/chunk-DINNWYVB.js.map +0 -1
  92. package/dist/chunk-F562QDVS.js +0 -7
  93. package/dist/chunk-H3HFM4JQ.js.map +0 -1
  94. package/dist/chunk-IVLDAUYM.js.map +0 -1
  95. package/dist/chunk-VENYVK3L.js.map +0 -1
@@ -0,0 +1,1931 @@
1
+ import * as react from 'react';
2
+ import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes, LabelHTMLAttributes, ElementType, CSSProperties, RefObject } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+
5
+ type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
6
+ /**
7
+ * `sm`/`md` are the desktop scale. The `touch-*` rungs are for a finger on
8
+ * glass — a till, a warehouse tablet — and are a SEPARATE ladder on purpose:
9
+ * a till's idea of "medium" is 56px, and naming it `md` would have made the
10
+ * same word mean two sizes depending on which app you were reading.
11
+ *
12
+ * Nothing picks a touch size automatically. An app that wants them asks for
13
+ * them, so a desktop portal cannot grow finger-sized buttons by accident.
14
+ */
15
+ type ButtonSize = 'sm' | 'md' | 'touch' | 'touch-lg' | 'touch-xl';
16
+ interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
17
+ variant?: ButtonVariant;
18
+ size?: ButtonSize;
19
+ /** Show a spinner and disable the button while an action is in flight. */
20
+ loading?: boolean;
21
+ leftIcon?: ReactNode;
22
+ rightIcon?: ReactNode;
23
+ /** Stretch to the full width of the container. */
24
+ block?: boolean;
25
+ className?: string;
26
+ /**
27
+ * Why this button is disabled, rendered as text BESIDE it — never as a
28
+ * `title` tooltip. A tooltip needs a hover, a touchscreen has no hover, and a
29
+ * cashier facing a dead button with no explanation calls a manager.
30
+ *
31
+ * Only rendered while the button is actually disabled (or loading); ignored
32
+ * otherwise, so it is safe to pass unconditionally. Note it changes the
33
+ * rendered shape: the button gains a wrapper element, which matters to a
34
+ * parent `flex gap-*` row or a `:first-child` selector.
35
+ */
36
+ disabledReason?: ReactNode;
37
+ }
38
+ declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
39
+
40
+ /**
41
+ * Shared form-control styling — the single source of truth for the kit's
42
+ * text-input look. Promoted out of SearchableSelect so every form control
43
+ * (Input, Textarea, Select, SearchableSelect) renders identically and picks
44
+ * up the same `[data-theme="dark"]` input remaps from styles.css.
45
+ *
46
+ * Keep this in the documented Tailwind vocabulary (see .design-sync/conventions.md)
47
+ * so the classes survive both the dark-mode allow-list and the design-sync
48
+ * compiled stylesheet.
49
+ */
50
+ /** `md` is the desktop control; `touch` is a finger on glass. See ButtonSize
51
+ * for why the touch scale is a separate ladder rather than a bigger `lg`. */
52
+ type InputSize = 'md' | 'touch';
53
+ declare const INPUT_SIZES: Record<InputSize, string>;
54
+ /** Base look for a text-bearing form control. Native <input>/<select>/<textarea>
55
+ * also receive dark styling for free via the global rule in styles.css. */
56
+ declare const INPUT_BASE: string;
57
+ /** Compose the input classes for a control. Omitting `size` yields exactly
58
+ * `INPUT_BASE`, so every existing caller is unaffected. */
59
+ declare function inputClasses(opts?: {
60
+ invalid?: boolean;
61
+ size?: InputSize;
62
+ className?: string;
63
+ }): string;
64
+
65
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'size'> {
66
+ /** Error state — red border + ring. */
67
+ invalid?: boolean;
68
+ /** Icon rendered inside the field's left edge (e.g. a search glyph). */
69
+ leftIcon?: ReactNode;
70
+ /** Content pinned to the field's right edge (e.g. a unit, a small action). */
71
+ rightAdornment?: ReactNode;
72
+ /**
73
+ * `touch` gives a 56px field for a finger. Defaults to the desktop size.
74
+ *
75
+ * This shadows the native `size` attribute (a character count), which is
76
+ * omitted above rather than left to collide — the width of a `w-full` field
77
+ * has never come from that attribute here, so nothing loses a capability it
78
+ * was using. Set `style`/`className` for width instead.
79
+ */
80
+ size?: InputSize;
81
+ className?: string;
82
+ }
83
+ declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
84
+
85
+ interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'className'> {
86
+ /** Error state — red border + ring. */
87
+ invalid?: boolean;
88
+ /** Grow the field to fit its content instead of scrolling. */
89
+ autoGrow?: boolean;
90
+ className?: string;
91
+ }
92
+ declare const Textarea: react.ForwardRefExoticComponent<TextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
93
+
94
+ interface SelectOption {
95
+ value: string;
96
+ label: string;
97
+ disabled?: boolean;
98
+ }
99
+ interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'onChange' | 'value' | 'className'> {
100
+ value: string;
101
+ onChange: (value: string) => void;
102
+ options: SelectOption[];
103
+ /** Shown as a disabled first option when no value is selected. */
104
+ placeholder?: string;
105
+ invalid?: boolean;
106
+ className?: string;
107
+ }
108
+ /**
109
+ * Raw native `<select>` styled to match the kit's inputs. This is the original
110
+ * Select — kept as the mobile rendering and exported for callers that need a
111
+ * real `HTMLSelectElement` (form posts, native attribute spread, focus).
112
+ */
113
+ declare const NativeSelect: react.ForwardRefExoticComponent<SelectProps & react.RefAttributes<HTMLSelectElement>>;
114
+ /** Smart Select: native `<select>` on touch, custom listbox on desktop. */
115
+ declare const Select: react.ForwardRefExoticComponent<SelectProps & react.RefAttributes<HTMLSelectElement>>;
116
+
117
+ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'checked' | 'className'> {
118
+ checked: boolean;
119
+ onChange: (checked: boolean) => void;
120
+ label?: ReactNode;
121
+ description?: ReactNode;
122
+ className?: string;
123
+ }
124
+ declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLInputElement>>;
125
+
126
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'checked' | 'className'> {
127
+ checked: boolean;
128
+ onChange: (checked: boolean) => void;
129
+ label?: ReactNode;
130
+ description?: ReactNode;
131
+ className?: string;
132
+ }
133
+ declare const Radio: react.ForwardRefExoticComponent<RadioProps & react.RefAttributes<HTMLInputElement>>;
134
+
135
+ interface FormFieldProps {
136
+ label?: ReactNode;
137
+ /** id of the control, for label-for wiring. */
138
+ htmlFor?: string;
139
+ /** id for the `<label>` element itself, so a non-labelable control (e.g. a
140
+ * `role="group"` wrapper) can name itself via `aria-labelledby`. */
141
+ labelId?: string;
142
+ /** Greyed helper text below the control. */
143
+ hint?: ReactNode;
144
+ /** Red error text below the control — overrides `hint` when present. */
145
+ error?: ReactNode;
146
+ /** Append a red asterisk to the label. */
147
+ required?: boolean;
148
+ className?: string;
149
+ children: ReactNode;
150
+ }
151
+ declare function FormField({ label, htmlFor, labelId, hint, error, required, className, children, }: FormFieldProps): react_jsx_runtime.JSX.Element;
152
+
153
+ interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
154
+ /** Append a red asterisk. */
155
+ required?: boolean;
156
+ }
157
+ declare function Label({ required, children, className, ...rest }: LabelProps): react_jsx_runtime.JSX.Element;
158
+
159
+ /**
160
+ * Internal helpers shared by the media-upload primitives (`MediaUploadField`
161
+ * single slot + `MediaUploadGrid` gallery) so the filename derivation, the
162
+ * image-vs-video test, and the upload glyph / spinner live in one place (DRY /
163
+ * SSoT). Not part of the public API except `mediaFileName`, which the field
164
+ * re-exports.
165
+ */
166
+ /**
167
+ * Derive a human filename from a media URL: drop the query string, strip a
168
+ * 32-hex upload prefix, and URL-decode.
169
+ *
170
+ * "/media/uploads/9f3…a1_My Clip.mp4?v=2" → "My Clip.mp4"
171
+ *
172
+ * `data:` URLs have no path, so they are named from their media type instead
173
+ * — see `dataUrlName`.
174
+ */
175
+ declare function mediaFileName(url: string): string;
176
+
177
+ interface MediaUploadFieldProps {
178
+ /** The current media URL. Empty string (or null/undefined) = the CTA state. */
179
+ value?: string | null;
180
+ /** Controlled channel — the newly-chosen URL, or `''` when cleared (Remove). */
181
+ onChange: (url: string) => void;
182
+ /**
183
+ * Open the consumer's library/upload picker. Fires on click and on drop; the
184
+ * dropped File (if any) is passed so the consumer can seed an immediate
185
+ * upload. The consumer resolves the selection back through `onChange`.
186
+ * Omit to fall back to a hidden native `<input type=file>` that emits an
187
+ * object-URL (demos, staged-then-submit forms).
188
+ */
189
+ onPick?: (droppedFile?: File) => void;
190
+ /** Label rendered above the control. */
191
+ label?: ReactNode;
192
+ /** Greyed helper line below the control. */
193
+ hint?: ReactNode;
194
+ /** Red error line — overrides `hint`. */
195
+ error?: ReactNode;
196
+ /** Append a red asterisk to the label. */
197
+ required?: boolean;
198
+ className?: string;
199
+ /**
200
+ * Native `accept` string. Drives the preview kind (image vs video), the
201
+ * fallback file dialog, and the default empty-state copy. Default `image/*`.
202
+ */
203
+ accept?: string;
204
+ /** `object-fit` for the preview. Default `cover`; use `contain` for logos/SVG. */
205
+ fit?: 'cover' | 'contain';
206
+ /** Preview / dropzone height in px (applied via inline style). Default 112. */
207
+ height?: number;
208
+ /** Dim prompt line, e.g. "Upload a background video". Default derived from `accept`. */
209
+ placeholder?: ReactNode;
210
+ /** The link-style CTA line. Default "Choose from library or upload". */
211
+ cta?: ReactNode;
212
+ /** Small kind hint under the CTA, e.g. "PNG · JPG · WEBP". */
213
+ acceptHint?: ReactNode;
214
+ /** Overlay the derived filename on the preview. Default true. */
215
+ showFilename?: boolean;
216
+ /** Show the Replace button when filled. Default true. */
217
+ allowReplace?: boolean;
218
+ /** Show the Remove button when filled. Default true. */
219
+ allowRemove?: boolean;
220
+ /** Relabel Replace, e.g. "Change". Default "Replace". */
221
+ replaceLabel?: ReactNode;
222
+ /** Relabel Remove, e.g. "Clear". Default "Remove". */
223
+ removeLabel?: ReactNode;
224
+ /** Busy: disable controls and show a spinner while the caller's upload runs. */
225
+ busy?: boolean;
226
+ /** Busy label. Default "Uploading…". */
227
+ busyLabel?: ReactNode;
228
+ /** Fully disable the control (no click, no drop, no actions). */
229
+ disabled?: boolean;
230
+ }
231
+
232
+ declare function MediaUploadField({ value, onChange, onPick, label, hint, error, required, className, accept, fit, height, placeholder, cta, acceptHint, showFilename, allowReplace, allowRemove, replaceLabel, removeLabel, busy, busyLabel, disabled, }: MediaUploadFieldProps): react_jsx_runtime.JSX.Element;
233
+
234
+ interface MediaUploadGridItem {
235
+ /** Stable key — used for React keys, remove, and reorder. */
236
+ id: string;
237
+ /** Preview URL. */
238
+ url: string;
239
+ /** `image` | `video`. Inferred from the URL when omitted. */
240
+ kind?: 'image' | 'video';
241
+ /** Optional label overlaid on the thumbnail (filename, dimensions, …). */
242
+ caption?: ReactNode;
243
+ /** Force the "cover" badge on this item (else `showCover` badges the first). */
244
+ cover?: boolean;
245
+ }
246
+ interface MediaUploadGridProps {
247
+ /** The gallery contents, in display order. */
248
+ items: MediaUploadGridItem[];
249
+ /**
250
+ * Open the consumer's library/upload flow. Fires on click of the Add tile /
251
+ * empty dropzone and on a file drop onto the zone (the dropped File is passed
252
+ * so the consumer can seed an immediate upload). Without it the gallery is
253
+ * read-only for additions (no Add tile, no drop).
254
+ */
255
+ onPick?: (droppedFile?: File) => void;
256
+ /** Remove one item. The per-thumb ✕ shows only when this is provided. */
257
+ onRemove?: (id: string) => void;
258
+ /**
259
+ * Reorder within the grid. Enables drag-to-reorder when provided; called with
260
+ * the source and target indices on drop.
261
+ */
262
+ onReorder?: (fromIndex: number, toIndex: number) => void;
263
+ label?: ReactNode;
264
+ hint?: ReactNode;
265
+ error?: ReactNode;
266
+ required?: boolean;
267
+ className?: string;
268
+ /** Native accept string — drives preview kind, the drop filter, and the copy. Default `image/*`. */
269
+ accept?: string;
270
+ /** `object-fit` for the thumbnails. Default `cover`. */
271
+ fit?: 'cover' | 'contain';
272
+ /** Thumbnail (and Add tile) square size in px. Default 96. */
273
+ thumbSize?: number;
274
+ /** Dim prompt line on the empty dropzone. Default derived from `accept`. */
275
+ placeholder?: ReactNode;
276
+ /** The link CTA on the empty dropzone. Default "Choose from library or upload". */
277
+ cta?: ReactNode;
278
+ /** Small kind hint under the CTA, e.g. "PNG · JPG · WEBP". */
279
+ acceptHint?: ReactNode;
280
+ /** Badge the first item (or the one with `cover`) as the gallery cover. Default false. */
281
+ showCover?: boolean;
282
+ /** Cover badge text. Default "Cover". */
283
+ coverLabel?: ReactNode;
284
+ /** Busy: dim the zone and show a spinner while the caller's upload runs. */
285
+ busy?: boolean;
286
+ /** Busy label. Default "Uploading…". */
287
+ busyLabel?: ReactNode;
288
+ /** Disable all interaction. */
289
+ disabled?: boolean;
290
+ /** View only — thumbnails without Add / remove / reorder affordances. */
291
+ readOnly?: boolean;
292
+ }
293
+ declare function MediaUploadGrid({ items, onPick, onRemove, onReorder, label, hint, error, required, className, accept, fit, thumbSize, placeholder, cta, acceptHint, showCover, coverLabel, busy, busyLabel, disabled, readOnly, }: MediaUploadGridProps): react_jsx_runtime.JSX.Element;
294
+
295
+ interface DateRangePickerProps {
296
+ /** Start of the range as `YYYY-MM-DD`, or '' when unset. */
297
+ from: string;
298
+ /** End of the range as `YYYY-MM-DD`, or '' when unset. */
299
+ to: string;
300
+ /** Called with the applied range. Both values are '' when cleared. */
301
+ onChange: (from: string, to: string) => void;
302
+ /**
303
+ * Render a `YYYY-MM-DD` for display. Defaults to `DD/MM/YYYY`; portals pass
304
+ * their own so the picker agrees with every other date on screen.
305
+ */
306
+ formatDisplay?: (iso: string) => string;
307
+ /**
308
+ * Offer the Clear affordances. Pass `false` where a range is REQUIRED — an
309
+ * accounting report seeded with a period has nothing sensible to show for an
310
+ * empty one, so it should not offer to empty it.
311
+ */
312
+ clearable?: boolean;
313
+ /** Trigger text when no range is set. */
314
+ placeholder?: string;
315
+ }
316
+ /**
317
+ * Serialise a Date to `YYYY-MM-DD`, reading its LOCAL calendar fields.
318
+ *
319
+ * This must not go through `toISOString()`. Every Date this component builds is
320
+ * constructed at local midnight — calendar cells, the preset boundaries, and
321
+ * `new Date()` — and local midnight always falls in the *previous* UTC day for
322
+ * any positive UTC offset. Serialising through UTC would shift the whole picker
323
+ * back one day for every user east of Greenwich.
324
+ */
325
+ declare function toISODate(d: Date): string;
326
+ declare function DateRangePicker({ from, to, onChange, formatDisplay, clearable, placeholder, }: DateRangePickerProps): react_jsx_runtime.JSX.Element;
327
+
328
+ interface NumericKeypadProps {
329
+ value: string;
330
+ onChange: (next: string) => void;
331
+ /** Renders a full-width Enter key below the pad when provided. */
332
+ onEnter?: () => void;
333
+ /** Label for that key — name the action ("Tender", "Add"), not "Enter". */
334
+ enterLabel?: ReactNode;
335
+ enterDisabled?: boolean;
336
+ className?: string;
337
+ }
338
+ declare function NumericKeypad({ value, onChange, onEnter, enterLabel, enterDisabled, className, }: NumericKeypadProps): react_jsx_runtime.JSX.Element;
339
+
340
+ /**
341
+ * The rules for typing a decimal amount on an on-screen keypad — pure string
342
+ * functions, no React and no DOM.
343
+ *
344
+ * Split out for the same reason as `selectNav.ts`: these are the rules people
345
+ * actually get wrong, and they are far easier to pin exhaustively as functions
346
+ * than by rendering a keypad and firing clicks at it.
347
+ *
348
+ * A STRING, not a number, all the way through. `'1.'` and `'1.0'` are states a
349
+ * user passes through while typing and neither survives a round trip through a
350
+ * float — `'1.'` would become `1` and erase the decimal point the moment it was
351
+ * pressed. The caller converts once, at the end, when it has a whole amount.
352
+ */
353
+ /** Digits after the point. Two, because these are money amounts. */
354
+ declare const MAX_FRACTION_DIGITS = 2;
355
+ /**
356
+ * Apply one keypress. `key` is a digit `'0'`-`'9'` or `'.'`.
357
+ * Returns the value unchanged when the press is not allowed, so a caller can
358
+ * always assign the result — an ignored key is a no-op, never an error.
359
+ */
360
+ declare function appendKey(value: string, key: string): string;
361
+ /** Remove the last character. Empty stays empty rather than underflowing. */
362
+ declare function backspace(value: string): string;
363
+
364
+ type TileSize = 'md' | 'lg';
365
+ interface TileButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'title'> {
366
+ /** Primary line — the thing being chosen. */
367
+ title: ReactNode;
368
+ /** Secondary line: a price, a count, a reason it is unavailable. */
369
+ subtitle?: ReactNode;
370
+ size?: TileSize;
371
+ /** Draw as chosen. Uses a ring rather than a fill so the label stays legible. */
372
+ selected?: boolean;
373
+ className?: string;
374
+ }
375
+ declare const TileButton: react.ForwardRefExoticComponent<TileButtonProps & react.RefAttributes<HTMLButtonElement>>;
376
+
377
+ type SegmentedSize = 'sm' | 'md' | 'touch';
378
+ interface SegmentedOption<T extends string = string> {
379
+ value: T;
380
+ label: ReactNode;
381
+ icon?: ReactNode;
382
+ disabled?: boolean;
383
+ }
384
+ interface SegmentedProps<T extends string = string> {
385
+ value: T;
386
+ onChange: (value: T) => void;
387
+ options: SegmentedOption<T>[];
388
+ /** Present as a real radio group under this field name. See the docstring. */
389
+ name?: string;
390
+ size?: SegmentedSize;
391
+ /** Stretch to fill the container, each option an equal share. */
392
+ block?: boolean;
393
+ /** Accessible name for the group. */
394
+ label?: string;
395
+ className?: string;
396
+ }
397
+ declare function Segmented<T extends string = string>({ value, onChange, options, name, size, block, label, className, }: SegmentedProps<T>): react_jsx_runtime.JSX.Element;
398
+
399
+ type SwitchSize = 'sm' | 'md' | 'touch';
400
+ interface SwitchProps {
401
+ checked: boolean;
402
+ onChange: (checked: boolean) => void;
403
+ disabled?: boolean;
404
+ size?: SwitchSize;
405
+ /** Text beside the control. Clicking it toggles, as a label should. */
406
+ label?: ReactNode;
407
+ /** A line under the label for what the setting actually does. */
408
+ hint?: ReactNode;
409
+ id?: string;
410
+ className?: string;
411
+ }
412
+ declare function Switch({ checked, onChange, disabled, size, label, hint, id, className, }: SwitchProps): react_jsx_runtime.JSX.Element;
413
+
414
+ interface InputNumberProps {
415
+ value: number | null;
416
+ onChange: (value: number | null) => void;
417
+ min?: number;
418
+ max?: number;
419
+ /** Decimal places enforced on blur. */
420
+ precision?: number;
421
+ step?: number;
422
+ disabled?: boolean;
423
+ invalid?: boolean;
424
+ placeholder?: string;
425
+ size?: InputSize;
426
+ /** Inside the field's edges — a currency mark, a unit. */
427
+ prefix?: ReactNode;
428
+ suffix?: ReactNode;
429
+ id?: string;
430
+ name?: string;
431
+ className?: string;
432
+ onBlur?: () => void;
433
+ }
434
+ declare const InputNumber: react.ForwardRefExoticComponent<InputNumberProps & react.RefAttributes<HTMLInputElement>>;
435
+
436
+ interface FilePickerProps {
437
+ files: File[];
438
+ onChange: (files: File[]) => void;
439
+ /** `accept` for the native picker, e.g. "image/*,.pdf". */
440
+ accept?: string;
441
+ multiple?: boolean;
442
+ /** Rejected above this, with the reason shown. */
443
+ maxSizeBytes?: number;
444
+ /** Rejected beyond this many files total. */
445
+ maxFiles?: number;
446
+ disabled?: boolean;
447
+ label?: ReactNode;
448
+ hint?: ReactNode;
449
+ error?: ReactNode;
450
+ className?: string;
451
+ }
452
+ declare function FilePicker({ files, onChange, accept, multiple, maxSizeBytes, maxFiles, disabled, label, hint, error, className, }: FilePickerProps): react_jsx_runtime.JSX.Element;
453
+
454
+ interface SearchableOption {
455
+ value: string;
456
+ label: string;
457
+ /** Optional secondary text rendered greyed-out on the right of the
458
+ * option (e.g. an order date next to a proforma number). */
459
+ sublabel?: string;
460
+ }
461
+ interface SearchableSelectProps {
462
+ value: string;
463
+ onChange: (value: string) => void;
464
+ options: SearchableOption[];
465
+ /** Placeholder shown when no value is selected and the field is empty. */
466
+ placeholder?: string;
467
+ /** Fallback placeholder text when no `placeholder` is provided and
468
+ * nothing is selected. Clearing the selection is done via the hover
469
+ * "×" on the input itself. */
470
+ emptyOptionLabel?: string;
471
+ /** Extra Tailwind classes appended to the input (error rings, custom
472
+ * widths…). The base form-input styling is built in. */
473
+ className?: string;
474
+ disabled?: boolean;
475
+ /** Optional id for label-for wiring. */
476
+ id?: string;
477
+ /** When true, the user can submit a value that isn't in `options` —
478
+ * pressing Enter or clicking outside with non-empty search text
479
+ * fires `onChange` with the typed string. The trigger then displays
480
+ * that free-text value as-is. Default false: only listed options
481
+ * can be picked, typing only filters. */
482
+ allowFreeText?: boolean;
483
+ /** Optional notifier fired whenever the user-typed search text changes
484
+ * (including resets to ''). Lets a parent debounce the value and feed
485
+ * a server-side query, so this component can still front a list that
486
+ * is too large to load up-front. */
487
+ onSearchChange?: (text: string) => void;
488
+ /** Optional content rendered inside the trigger's right edge — to the
489
+ * left of the clear button — when the dropdown is closed. Use for
490
+ * compact status pills that should read alongside the selected label.
491
+ * Hidden while the user is typing so the search text stays legible. */
492
+ rightAdornment?: ReactNode;
493
+ }
494
+ declare function SearchableSelect({ value, onChange, options, placeholder, emptyOptionLabel, className, disabled, id, allowFreeText, onSearchChange, rightAdornment, }: SearchableSelectProps): react_jsx_runtime.JSX.Element;
495
+
496
+ /** How much room the card gives its contents. `md` is the desktop default;
497
+ * `lg` suits a touch layout, where the same density reads as cramped. */
498
+ type CardPadding = 'none' | 'sm' | 'md' | 'lg';
499
+ interface CardProps {
500
+ children: ReactNode;
501
+ /** Title row above the body, divided by a hairline. */
502
+ header?: ReactNode;
503
+ /** Row below the body, divided by a hairline. */
504
+ footer?: ReactNode;
505
+ /** Apply default padding to the body. Default true; set false to fill edge-to-edge. */
506
+ padded?: boolean;
507
+ /**
508
+ * Padding scale. Takes precedence over `padded`, which remains supported and
509
+ * is now the two-value shorthand for it (`true` → `md`, `false` → `none`).
510
+ */
511
+ padding?: CardPadding;
512
+ className?: string;
513
+ }
514
+ declare function Card({ children, header, footer, padded, padding, className }: CardProps): react_jsx_runtime.JSX.Element;
515
+ interface StatCardProps {
516
+ label: ReactNode;
517
+ value: ReactNode;
518
+ icon?: ReactNode;
519
+ /** Trend pill, e.g. `{ value: '12%', direction: 'up' }`. */
520
+ delta?: {
521
+ value: ReactNode;
522
+ direction: 'up' | 'down' | 'flat';
523
+ };
524
+ className?: string;
525
+ }
526
+ declare function StatCard({ label, value, icon, delta, className }: StatCardProps): react_jsx_runtime.JSX.Element;
527
+
528
+ type TextTone = 'default' | 'secondary' | 'tertiary' | 'disabled' | 'danger' | 'success' | 'warning' | 'link' | 'inherit';
529
+ type TextSize = 'xs' | 'sm' | 'md' | 'lg';
530
+ type TextWeight = 'normal' | 'medium' | 'semibold';
531
+ interface TextProps {
532
+ children?: ReactNode;
533
+ tone?: TextTone;
534
+ size?: TextSize;
535
+ weight?: TextWeight;
536
+ /** Single-line ellipsis. Sets `title` so the full string stays readable. */
537
+ truncate?: boolean;
538
+ /** Render as something other than a span — `label`, `div`, `p`. */
539
+ as?: ElementType;
540
+ className?: string;
541
+ htmlFor?: string;
542
+ title?: string;
543
+ }
544
+ declare function Text({ children, tone, size, weight, truncate, as: Tag, className, ...rest }: TextProps): react_jsx_runtime.JSX.Element;
545
+ type TitleLevel = 1 | 2 | 3 | 4 | 5;
546
+ interface TitleProps {
547
+ children?: ReactNode;
548
+ /** Heading level — drives BOTH the tag and the size, so the document
549
+ * outline and the visual hierarchy cannot drift apart. */
550
+ level?: TitleLevel;
551
+ tone?: TextTone;
552
+ className?: string;
553
+ }
554
+ declare function Title({ children, level, tone, className }: TitleProps): react_jsx_runtime.JSX.Element;
555
+ interface ParagraphProps {
556
+ children?: ReactNode;
557
+ tone?: TextTone;
558
+ size?: TextSize;
559
+ className?: string;
560
+ }
561
+ /** Body copy. Carries its own bottom margin so stacked paragraphs breathe;
562
+ * the last one's margin is removed rather than left to collapse. */
563
+ declare function Paragraph({ children, tone, size, className }: ParagraphProps): react_jsx_runtime.JSX.Element;
564
+
565
+ type Gap = 0 | 1 | 2 | 3 | 4 | 6 | 8;
566
+ type Align = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
567
+ type Justify = 'start' | 'center' | 'end' | 'between' | 'around';
568
+ interface StackProps {
569
+ children?: ReactNode;
570
+ gap?: Gap;
571
+ align?: Align;
572
+ justify?: Justify;
573
+ className?: string;
574
+ }
575
+ /** Vertical flow. */
576
+ declare function Stack({ children, gap, align, justify, className }: StackProps): react_jsx_runtime.JSX.Element;
577
+ interface InlineProps extends StackProps {
578
+ /** Wrap onto a second line rather than overflowing. Default true — an
579
+ * unwrapped row is how a toolbar quietly breaks a narrow screen. */
580
+ wrap?: boolean;
581
+ }
582
+ /** Horizontal flow. Defaults to vertically centred, which is what a row of
583
+ * mixed-height controls almost always wants. */
584
+ declare function Inline({ children, gap, align, justify, wrap, className }: InlineProps): react_jsx_runtime.JSX.Element;
585
+ type GridCols = 1 | 2 | 3 | 4 | 6 | 12;
586
+ interface GridProps {
587
+ children?: ReactNode;
588
+ /** Columns at the narrowest width, and optionally from `sm`/`lg` up. */
589
+ cols?: GridCols;
590
+ smCols?: GridCols;
591
+ lgCols?: GridCols;
592
+ gap?: Gap;
593
+ className?: string;
594
+ }
595
+ declare function Grid({ children, cols, smCols, lgCols, gap, className }: GridProps): react_jsx_runtime.JSX.Element;
596
+
597
+ type DividerSpacing = 'sm' | 'md' | 'lg';
598
+ interface DividerProps {
599
+ orientation?: 'horizontal' | 'vertical';
600
+ /** Label set into the rule. Horizontal only. */
601
+ children?: ReactNode;
602
+ align?: 'left' | 'center' | 'right';
603
+ spacing?: DividerSpacing;
604
+ className?: string;
605
+ }
606
+ declare function Divider({ orientation, children, align, spacing, className, }: DividerProps): react_jsx_runtime.JSX.Element;
607
+
608
+ type SkeletonVariant = 'text' | 'rect' | 'circle';
609
+ interface SkeletonProps {
610
+ variant?: SkeletonVariant;
611
+ /** CSS length. Defaults to full width for text/rect, and to `height` for a circle. */
612
+ width?: number | string;
613
+ height?: number | string;
614
+ /** For `text`: how many lines. The last is short, the way real prose ends. */
615
+ lines?: number;
616
+ className?: string;
617
+ }
618
+ declare function Skeleton({ variant, width, height, lines, className }: SkeletonProps): react_jsx_runtime.JSX.Element;
619
+
620
+ type DescriptionColumns = 1 | 2 | 3;
621
+ interface DescriptionItem {
622
+ label: ReactNode;
623
+ value: ReactNode;
624
+ /** Occupy the full row — for an address or a note among short fields. */
625
+ span?: boolean;
626
+ key?: string;
627
+ }
628
+ interface DescriptionListProps {
629
+ items: DescriptionItem[];
630
+ /** A count, or per-breakpoint counts. `{ base: 1, sm: 2, lg: 3 }`. */
631
+ columns?: DescriptionColumns | {
632
+ base?: DescriptionColumns;
633
+ sm?: DescriptionColumns;
634
+ lg?: DescriptionColumns;
635
+ };
636
+ /** Hairline-separated rows in a bordered panel. */
637
+ bordered?: boolean;
638
+ size?: 'sm' | 'md';
639
+ title?: ReactNode;
640
+ className?: string;
641
+ }
642
+ declare function DescriptionList({ items, columns, bordered, size, title, className, }: DescriptionListProps): react_jsx_runtime.JSX.Element;
643
+
644
+ type ResultStatus = 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500';
645
+ interface ResultProps {
646
+ status?: ResultStatus;
647
+ title?: ReactNode;
648
+ subTitle?: ReactNode;
649
+ /** Actions — usually one primary route out. Always give the user one. */
650
+ extra?: ReactNode;
651
+ /** Replace the status glyph entirely. */
652
+ icon?: ReactNode;
653
+ children?: ReactNode;
654
+ className?: string;
655
+ }
656
+ declare function Result({ status, title, subTitle, extra, icon, children, className, }: ResultProps): react_jsx_runtime.JSX.Element;
657
+
658
+ type CountBadgeTone = 'danger' | 'neutral' | 'accent';
659
+ interface CountBadgeProps {
660
+ count?: number;
661
+ /** Cap the display: 100 with `max={99}` shows "99+". */
662
+ max?: number;
663
+ /** A plain dot with no number — "something changed" without a quantity. */
664
+ dot?: boolean;
665
+ tone?: CountBadgeTone;
666
+ /** Render at zero. Off by default. */
667
+ showZero?: boolean;
668
+ /** The thing being badged. Without one the badge renders inline on its own. */
669
+ children?: ReactNode;
670
+ className?: string;
671
+ }
672
+ declare function CountBadge({ count, max, dot, tone, showZero, children, className, }: CountBadgeProps): react_jsx_runtime.JSX.Element | null;
673
+
674
+ type StatisticSize = 'sm' | 'md' | 'lg';
675
+ type StatisticTone = 'default' | 'success' | 'danger' | 'muted';
676
+ interface StatisticProps {
677
+ title?: ReactNode;
678
+ value: ReactNode;
679
+ /** Fixed decimal places. Applied only to a numeric `value`. */
680
+ precision?: number;
681
+ /** Sits before/after the figure at a smaller size — a currency mark, a unit. */
682
+ prefix?: ReactNode;
683
+ suffix?: ReactNode;
684
+ tone?: StatisticTone;
685
+ size?: StatisticSize;
686
+ className?: string;
687
+ }
688
+ declare function Statistic({ title, value, precision, prefix, suffix, tone, size, className, }: StatisticProps): react_jsx_runtime.JSX.Element;
689
+
690
+ type AvatarSize = 'xs' | 'sm' | 'md' | 'lg';
691
+ type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';
692
+ interface AvatarProps {
693
+ src?: string;
694
+ /** Used for the initials fallback and the image alt text. */
695
+ name?: string;
696
+ size?: AvatarSize;
697
+ status?: AvatarStatus;
698
+ className?: string;
699
+ }
700
+ declare function Avatar({ src, name, size, status, className }: AvatarProps): react_jsx_runtime.JSX.Element;
701
+ interface AvatarGroupProps {
702
+ children: ReactNode;
703
+ /** Show at most this many avatars; the rest collapse into a +N chip. */
704
+ max?: number;
705
+ /** Size of the children — used for the overlap + overflow-chip geometry.
706
+ * Give the child Avatars this same size. */
707
+ size?: AvatarSize;
708
+ className?: string;
709
+ }
710
+ declare function AvatarGroup({ children, max, size, className }: AvatarGroupProps): react_jsx_runtime.JSX.Element;
711
+
712
+ type BannerTone = 'info' | 'success' | 'warning' | 'danger';
713
+ /**
714
+ * How loudly the banner speaks.
715
+ *
716
+ * `subtle` (the default, and what every existing caller renders) is a tinted
717
+ * panel that sits politely in a form. `solid` is a saturated full-contrast bar
718
+ * for a condition the user must not miss and cannot work around — a till that
719
+ * has lost the server and cannot take payment. The subtle version of that
720
+ * message is a bug: a cashier who misses it takes money for a sale that will
721
+ * not complete.
722
+ */
723
+ type BannerEmphasis = 'subtle' | 'solid';
724
+ interface BannerProps {
725
+ tone?: BannerTone;
726
+ emphasis?: BannerEmphasis;
727
+ title?: ReactNode;
728
+ children?: ReactNode;
729
+ /** Override the default tone icon. */
730
+ icon?: ReactNode;
731
+ /** When provided, renders a dismiss × that calls this. */
732
+ onDismiss?: () => void;
733
+ /**
734
+ * Pin to the top of the scroll container while its content scrolls past.
735
+ *
736
+ * `position: sticky`, so the banner still occupies layout — it pushes the
737
+ * page down rather than covering the first row of it, which is what a
738
+ * blocking notice needs. Defeated by any ancestor with `overflow: hidden`.
739
+ */
740
+ sticky?: boolean;
741
+ className?: string;
742
+ }
743
+ declare function Banner({ tone, emphasis, title, children, icon, onDismiss, sticky, className, }: BannerProps): react_jsx_runtime.JSX.Element;
744
+
745
+ interface TabItem {
746
+ id: string;
747
+ label: ReactNode;
748
+ icon?: ReactNode;
749
+ disabled?: boolean;
750
+ }
751
+ interface TabsProps {
752
+ items: TabItem[];
753
+ value: string;
754
+ onChange: (id: string) => void;
755
+ variant?: 'underline' | 'pill';
756
+ className?: string;
757
+ }
758
+ declare function Tabs({ items, value, onChange, variant, className }: TabsProps): react_jsx_runtime.JSX.Element;
759
+
760
+ interface AccordionItem {
761
+ id: string;
762
+ title: ReactNode;
763
+ content: ReactNode;
764
+ disabled?: boolean;
765
+ }
766
+ interface AccordionProps {
767
+ items: AccordionItem[];
768
+ defaultOpenIds?: string[];
769
+ /** Controlled open set — when provided, the component stops tracking its own. */
770
+ openIds?: string[];
771
+ onOpenChange?: (ids: string[]) => void;
772
+ allowMultiple?: boolean;
773
+ className?: string;
774
+ }
775
+ declare function Accordion({ items, defaultOpenIds, openIds, onOpenChange, allowMultiple, className, }: AccordionProps): react_jsx_runtime.JSX.Element;
776
+
777
+ interface TooltipProps {
778
+ content: ReactNode;
779
+ side?: 'top' | 'bottom' | 'left' | 'right';
780
+ /** Hover delay before showing, in ms. */
781
+ delay?: number;
782
+ children: ReactNode;
783
+ }
784
+ declare function Tooltip({ content, side, delay, children }: TooltipProps): react_jsx_runtime.JSX.Element;
785
+
786
+ interface ColoredBadgeProps {
787
+ /** Tailwind classes for the badge color, e.g. `bg-green-100 text-green-800`.
788
+ * Keep a per-app palette map rather than hardcoding colors at call sites. */
789
+ colorClass: string;
790
+ children: ReactNode;
791
+ /** `xs` = 10px (dense list cells), `sm` = 12px / px-2 (default), `md` =
792
+ * 12px / px-2.5 (matches StatusBadge). */
793
+ size?: 'xs' | 'sm' | 'md';
794
+ /** Capitalize each word — useful for raw status strings like `in_progress`
795
+ * (rendered as `In Progress`). Default false. */
796
+ capitalize?: boolean;
797
+ }
798
+ /**
799
+ * ColoredBadge — a small rounded-full pill whose colors are supplied as a
800
+ * Tailwind class string. Generic counterpart to StatusBadge (which maps status
801
+ * strings to semantic groups); use this when the caller already knows the color.
802
+ */
803
+ declare function ColoredBadge({ colorClass, children, size, capitalize }: ColoredBadgeProps): react_jsx_runtime.JSX.Element;
804
+
805
+ type SemanticGroup = 'success' | 'active' | 'queued' | 'info' | 'pending' | 'warning' | 'danger' | 'draft' | 'neutral';
806
+ declare function StatusBadgeProvider({ groups, children, }: {
807
+ groups: Record<string, SemanticGroup>;
808
+ children: ReactNode;
809
+ }): react_jsx_runtime.JSX.Element;
810
+ interface StatusBadgeProps {
811
+ status: string;
812
+ }
813
+ declare function StatusBadge({ status }: StatusBadgeProps): react_jsx_runtime.JSX.Element;
814
+
815
+ interface EmptyStateProps {
816
+ /** Bold heading, e.g. "No invoices yet". */
817
+ title?: string;
818
+ /** Muted primary line. Defaults to "Nothing here yet." when neither
819
+ * `title` nor `message` is given. */
820
+ message?: string;
821
+ /** Secondary muted line. (`hint` is an accepted alias.) */
822
+ description?: string;
823
+ /** @deprecated alias for `description`. */
824
+ hint?: string;
825
+ /** Frame around the content: `dashed` (default), `card` (bordered surface),
826
+ * or `none`. */
827
+ variant?: 'dashed' | 'card' | 'none';
828
+ /** @deprecated set `variant="none"` instead. */
829
+ frameless?: boolean;
830
+ /** Show the placeholder icon. Defaults to true (false for `variant="card"`). */
831
+ icon?: boolean;
832
+ /** Action(s) rendered under the text. */
833
+ children?: ReactNode;
834
+ }
835
+ /**
836
+ * EmptyState — placeholder for empty lists/panes. Supersedes the per-portal
837
+ * copies: accepts both the `title`/`description` and `message`/`hint`/`frameless`
838
+ * prop shapes, with a single unified look.
839
+ */
840
+ declare function EmptyState({ title, message, description, hint, variant, frameless, icon, children, }: EmptyStateProps): react_jsx_runtime.JSX.Element;
841
+
842
+ interface PageHeaderProps {
843
+ title: string;
844
+ /** Muted line under the title. (`subtitle` is an accepted alias.) */
845
+ description?: string;
846
+ /** @deprecated alias for `description`. */
847
+ subtitle?: string;
848
+ /** Right-aligned actions. (`children` is also accepted.) */
849
+ actions?: ReactNode;
850
+ children?: ReactNode;
851
+ }
852
+ /**
853
+ * PageHeader — a page/section title with an optional muted description and a
854
+ * right-aligned actions slot. Accepts both the `description`/`actions` and the
855
+ * `subtitle`/`children` prop shapes the portals previously used locally.
856
+ */
857
+ declare function PageHeader({ title, description, subtitle, actions, children }: PageHeaderProps): react_jsx_runtime.JSX.Element;
858
+
859
+ interface LoadingSpinnerProps {
860
+ /** Ring diameter: `sm` = 20px, `md` = 32px (default), `lg` = 48px. */
861
+ size?: 'sm' | 'md' | 'lg';
862
+ /** Wrapper padding utility (default `py-12`). Pass `''` to remove. */
863
+ padding?: string;
864
+ /**
865
+ * Caption under the ring, e.g. "Signing in…". Naming what is happening beats
866
+ * a bare ring on a full-screen wait, where there is nothing else on the page
867
+ * to infer it from. Adds `role="status"` so it is announced.
868
+ */
869
+ label?: ReactNode;
870
+ /** Extra classes on the centering wrapper. */
871
+ className?: string;
872
+ }
873
+ /**
874
+ * LoadingSpinner — a centered animated ring for pending/loading regions.
875
+ * (Distinct from the shell's internal "Loading…" text used inside data grids.)
876
+ */
877
+ declare function LoadingSpinner({ size, padding, label, className }: LoadingSpinnerProps): react_jsx_runtime.JSX.Element;
878
+
879
+ /**
880
+ * Severity — the health subset of the shell's status vocabulary.
881
+ *
882
+ * The shell already speaks `success | warning | danger` everywhere status has a
883
+ * colour (`StatusBadge`'s `SemanticGroup`, `Banner`'s `BannerTone`,
884
+ * `ConfirmDialog`'s `variant`). Health surfaces reuse those three words rather
885
+ * than introducing an ok/warn/crit dialect that would mean the same thing in a
886
+ * different language — a consumer mapping one to the other is a bug waiting to
887
+ * happen. The type is deliberately narrower than `SemanticGroup`: an alarm
888
+ * marker has exactly three things to say.
889
+ *
890
+ * "No severity" is expressed as the ABSENCE of a tone (`undefined`/`null`), not
891
+ * as a fourth member. Two different facts collapse into it — no reading at all,
892
+ * and a reading with nothing to judge it against — and both must render as
893
+ * "unknown" rather than borrowing the calm green of a measured healthy value.
894
+ * See `MetricBar`, which keeps them apart.
895
+ *
896
+ * A severity usually arrives from a backend rollup, so it crosses a JSON
897
+ * boundary where the compiler cannot follow it. `resolveSeverity` is what the
898
+ * components call: a token the shell does not recognise renders a LOUD unknown
899
+ * marker and logs, it never renders nothing. An alarm surface that silently
900
+ * stops alarming is the one failure these components exist to prevent, so it is
901
+ * also the one failure they must not have themselves.
902
+ */
903
+ /** Health tone. Same three words the rest of the shell uses for status colour. */
904
+ type SeverityTone = 'success' | 'warning' | 'danger';
905
+ /** True when `value` is one of the three tones. Exported so a consumer can
906
+ * validate a rollup at the fetch boundary — where a bad token can still be
907
+ * reported against its payload — rather than discovering it as a wrong pixel. */
908
+ declare function isSeverityTone(value: unknown): value is SeverityTone;
909
+ /**
910
+ * Severity of `value` against its bounds, or `null` when nothing can be said.
911
+ *
912
+ * Both bounds are INCLUSIVE (`>=`): a disk at exactly its `crit` number is
913
+ * `danger`. Either bound may be omitted — a metric with only a `crit` still
914
+ * judges — but with neither the answer is `null`, never `success`: no threshold
915
+ * is hardcoded here, not even as a fallback, so with nothing to measure against
916
+ * there is no verdict to report.
917
+ *
918
+ * A non-finite bound counts as omitted, not as a bound that nothing exceeds.
919
+ * `NaN >= NaN` is false, so an unguarded `warn={NaN}` would have quietly
920
+ * returned `success` — a verdict invented out of a missing threshold.
921
+ */
922
+ declare function severityOf(value: number | null | undefined, warn?: number | null, crit?: number | null): SeverityTone | null;
923
+
924
+ interface MetricBarProps {
925
+ /** Row label — `CPU`, `Memory`, `Disk`. Rendered in a fixed-width column so
926
+ * a stack of rows aligns. */
927
+ label?: ReactNode;
928
+ /** The measured value. `null` / `undefined` means NO READING — rendered as
929
+ * unknown, never as 0. */
930
+ value: number | null | undefined;
931
+ /** Value mapped to a full bar. Default 100. Must be a positive finite number:
932
+ * it is the divisor, so `0`, a negative or `NaN` is not a scale. Given one,
933
+ * the row prints the value but draws no proportional bar and no ticks —
934
+ * there is nothing truthful to draw them against. */
935
+ max?: number;
936
+ /** Warning bound, in the same unit as `value`. Inclusive (`>=`). */
937
+ warn?: number | null;
938
+ /** Critical bound, in the same unit as `value`. Inclusive (`>=`). */
939
+ crit?: number | null;
940
+ /** Forces the tone when the caller judges severity itself (e.g. a rollup
941
+ * served by the backend). Overrides `warn`/`crit` for colour; the ticks
942
+ * still render wherever `warn`/`crit` say. */
943
+ severity?: SeverityTone;
944
+ /** Trailing secondary text — `10.9 / 16 GiB`, `4 vCPU`. Hidden when there is
945
+ * no reading, where the row has something more important to say. */
946
+ detail?: ReactNode;
947
+ /** Formats the printed value. Default: one decimal plus `%` on the default
948
+ * 0–100 scale, the bare number on any other `max` (a custom scale is not a
949
+ * percentage, and printing one would be a lie). */
950
+ formatValue?: (value: number) => string;
951
+ /** Wording for "no reading". Default `no data`. */
952
+ emptyLabel?: string;
953
+ /** `sm` (default) — the compact row that stacks three to a card.
954
+ * `md` — bigger value type for a standalone stat. Neither draws a frame;
955
+ * wrap in a `Card` (or your own) if you want one. */
956
+ size?: 'sm' | 'md';
957
+ /** Accessible name for the meter. Defaults to `label` when it's a string. */
958
+ ariaLabel?: string;
959
+ className?: string;
960
+ }
961
+ declare function MetricBar({ label, value, max, warn, crit, severity, detail, formatValue, emptyLabel, size, ariaLabel, className, }: MetricBarProps): react_jsx_runtime.JSX.Element;
962
+
963
+ /** Visual category for a milestone. Drives shape + colour so the user can
964
+ * tell different milestone types apart at a glance. */
965
+ type MilestoneKind = 'default' | 'dfm' | 'shipment' | 'testing' | 'completion';
966
+ /** A single point on the timeline. Generic, product-agnostic: the consuming
967
+ * app maps its domain records to this shape in a thin wrapper. */
968
+ interface Milestone {
969
+ /** Stable key — used for React keys and the active-dot lookup. */
970
+ key: string;
971
+ /** Short label rendered above the dot when active / hovered. */
972
+ label: string;
973
+ /** ISO date string (`YYYY-MM-DD`) for the milestone. Null / undefined =
974
+ * "not reached yet"; renders as a faded outline dot at the *expected*
975
+ * position (between its neighbours) instead of a real coordinate. */
976
+ date: string | null | undefined;
977
+ /** Optional second line for the hover tooltip. */
978
+ detail?: string;
979
+ /** Optional click handler so the caller can open a related entity. */
980
+ onClick?: () => void;
981
+ /** Optional visual category — defaults to `'default'` (blue circle). */
982
+ kind?: MilestoneKind;
983
+ /** Optional phase grouping — milestones sharing the same `phase` value
984
+ * render with a bracket below the bar showing they happened in parallel
985
+ * (e.g. two concurrent QA steps). Lookup the human-readable name from
986
+ * `MilestoneTimelineProps.phaseLabels`. */
987
+ phase?: string;
988
+ }
989
+ interface MilestoneTimelineProps {
990
+ /** Title rendered above the bar — e.g. "Mould Development Timeline". */
991
+ title: string;
992
+ /** Ordered milestones from earliest expected to latest expected. The order
993
+ * controls the fallback position for milestones with no date yet. */
994
+ milestones: Milestone[];
995
+ /** Optional sub-title to the right of the title (e.g. lead-time summary).
996
+ * When omitted, an auto lead-time summary is computed from the dates. */
997
+ summary?: string;
998
+ /** Optional explicit right edge — when provided, the bar always ends here
999
+ * rather than padding to today. Used when the axis should stop at a known
1000
+ * final milestone and not run on past it. */
1001
+ endDate?: string | null;
1002
+ /** Maps `Milestone.phase` keys to the human-readable phase label that
1003
+ * appears under the bracket — e.g. `{ qa: 'QA & Sample' }`. Phases
1004
+ * without an entry fall back to the phase key itself. */
1005
+ phaseLabels?: Record<string, string>;
1006
+ }
1007
+ /**
1008
+ * Static date-axis bar showing a sequence of milestones along a single line.
1009
+ *
1010
+ * - No scrubber / playback / interpolation — milestones don't move.
1011
+ * - "Not reached yet" milestones (no date) render as outline dots wedged at
1012
+ * their expected position so the user can see what's still pending.
1013
+ * - First / last milestones render as inline edge labels; middle ones stagger
1014
+ * above/below the bar so adjacent labels never collide.
1015
+ * - Milestones sharing a `phase` (2+ members) get a bracket marking parallel
1016
+ * work. The filled portion of the bar never runs past today.
1017
+ *
1018
+ * Product-agnostic: it takes generic `Milestone` data as props. Map your
1019
+ * domain records to the `Milestone` shape in a thin wrapper at the call site.
1020
+ */
1021
+ declare function MilestoneTimeline({ title, milestones, summary, endDate, phaseLabels }: MilestoneTimelineProps): react_jsx_runtime.JSX.Element;
1022
+
1023
+ /** Default item shape — overridable via the accessor props for any other shape. */
1024
+ interface ContainerFillItem {
1025
+ quantity?: number | null;
1026
+ actual_qty?: number | null;
1027
+ _isNew?: boolean;
1028
+ }
1029
+ interface ContainerFillChartProps<T = ContainerFillItem> {
1030
+ /** Line items to chart. Each contributes `qty * getVolume(item)` to the total. */
1031
+ items: T[];
1032
+ /**
1033
+ * Per-unit volume (m³) for an item. The lifted app concern — return 0 when
1034
+ * unknown. Total volume per item is `quantity * getVolume(item)`.
1035
+ */
1036
+ getVolume: (item: T) => number;
1037
+ /**
1038
+ * Single-bar quantity source when no actuals are present:
1039
+ * - 'instruction' (default): use the instruction quantity.
1040
+ * - 'actual': use the actual quantity (falling back to instruction).
1041
+ *
1042
+ * When actuals exist on at least one item the chart auto-switches to dual-bar
1043
+ * mode regardless of `qtyField` — instruction (blue) and loaded (green) are
1044
+ * layered into the same bar so both are visible at once.
1045
+ */
1046
+ qtyField?: 'instruction' | 'actual';
1047
+ /** Show the "new items" indicator next to the row count. */
1048
+ showNewIndicator?: boolean;
1049
+ /** Instruction quantity accessor. Defaults to `item.quantity`. */
1050
+ getInstructionQty?: (item: T) => number | null | undefined;
1051
+ /** Actual/loaded quantity accessor. Defaults to `item.actual_qty`. */
1052
+ getActualQty?: (item: T) => number | null | undefined;
1053
+ /**
1054
+ * Whether an item should count as charted at all. Defaults to "has a non-empty
1055
+ * key" — i.e. items whose volume source identifies a real part. By default
1056
+ * every item is charted; supply this to drop placeholder/empty rows.
1057
+ */
1058
+ isFilled?: (item: T) => boolean;
1059
+ /** Whether an item is newly added (drives `showNewIndicator`). Defaults to `item._isNew`. */
1060
+ isNew?: (item: T) => boolean;
1061
+ className?: string;
1062
+ style?: CSSProperties;
1063
+ }
1064
+ /**
1065
+ * Container-fill chart for shipping loading lists. Presentational only — pass
1066
+ * `getVolume` to inject per-unit volumes; the chart owns the container math and
1067
+ * the instruction-vs-loaded dual-bar rendering.
1068
+ */
1069
+ declare function ContainerFillChart<T = ContainerFillItem>({ items, getVolume, qtyField, showNewIndicator, getInstructionQty, getActualQty, isFilled, isNew, className, style, }: ContainerFillChartProps<T>): react_jsx_runtime.JSX.Element | null;
1070
+
1071
+ interface MarkdownProps {
1072
+ children: string;
1073
+ className?: string;
1074
+ }
1075
+ declare function Markdown({ children, className }: MarkdownProps): react_jsx_runtime.JSX.Element;
1076
+
1077
+ /**
1078
+ * One help article. Generic shape — the consuming portal maps its own
1079
+ * help-doc records onto this before passing them in.
1080
+ */
1081
+ interface HelpCenterDoc {
1082
+ id: string;
1083
+ slug: string;
1084
+ title: string;
1085
+ body: string;
1086
+ /** Grouping key (e.g. `'getting_started'`). */
1087
+ category: string;
1088
+ /** Group header text (e.g. "Getting Started"). */
1089
+ category_label: string;
1090
+ /** When `false`, a "Draft" badge is shown. Omit for always-published docs. */
1091
+ is_published?: boolean;
1092
+ }
1093
+ interface HelpCenterProps {
1094
+ docs: HelpCenterDoc[];
1095
+ loading?: boolean;
1096
+ /**
1097
+ * Category keys in display order. Categories not listed fall to the end in
1098
+ * first-seen order. Without it, groups follow first-seen order.
1099
+ */
1100
+ categoryOrder?: string[];
1101
+ /** Show the New/Edit affordances (consumer gates this on write permission). */
1102
+ canEdit?: boolean;
1103
+ onNew?: () => void;
1104
+ onEdit?: (doc: HelpCenterDoc) => void;
1105
+ /** Custom body renderer; defaults to preformatted text. */
1106
+ renderBody?: (doc: HelpCenterDoc) => ReactNode;
1107
+ emptyMessage?: string;
1108
+ }
1109
+ /**
1110
+ * In-app help / documentation viewer. Two panes: a searchable, collapsible
1111
+ * category tree on the left, the selected article's body on the right.
1112
+ *
1113
+ * Presentational only — it holds no API or permission knowledge. The consumer
1114
+ * fetches its own help docs, maps them to `HelpCenterDoc`, and (for editors)
1115
+ * wires `canEdit` + `onNew`/`onEdit` to its own create/edit UI.
1116
+ *
1117
+ * Search is client-side: a case-insensitive substring match over title, body
1118
+ * and category label. While searching, matching groups auto-expand.
1119
+ */
1120
+ declare function HelpCenter({ docs, loading, categoryOrder, canEdit, onNew, onEdit, renderBody, emptyMessage, }: HelpCenterProps): react_jsx_runtime.JSX.Element;
1121
+
1122
+ /**
1123
+ * Two-pane layout with a drag-to-resize sidebar (left or right).
1124
+ *
1125
+ * Renders flush (`h-full w-full`) — pair it with a `flushBody` window so the
1126
+ * sidebar runs from just under the title bar to the very bottom with no
1127
+ * surrounding padding. The user can drag the sidebar's inner edge to resize
1128
+ * it; pass `storageKey` to persist that width across reopens, or double-click
1129
+ * the handle to reset to `defaultWidth`. Set `side="right"` to put the sidebar
1130
+ * on the right (e.g. a detail-panel layout).
1131
+ *
1132
+ * @example
1133
+ * <SidebarLayout sidebar={<MyNav />} storageKey="todo.sidebarWidth">
1134
+ * <MyContent />
1135
+ * </SidebarLayout>
1136
+ */
1137
+ interface SidebarLayoutProps {
1138
+ /** Content of the left sidebar pane. When `sidebarTop`/`sidebarBottom` are
1139
+ * set this is the scrolling middle region (the filter nav); otherwise it is
1140
+ * the whole pane. */
1141
+ sidebar: ReactNode;
1142
+ /** Optional node pinned at the TOP of the sidebar, above `sidebar`, in a
1143
+ * padded non-scrolling region (e.g. a primary "New X" `SidebarActionButton`).
1144
+ * The list-window convention: primary create action lives here. */
1145
+ sidebarTop?: ReactNode;
1146
+ /** Optional node pinned at the BOTTOM of the sidebar, below `sidebar`, in a
1147
+ * padded, top-bordered non-scrolling region (e.g. an "Export CSV" button).
1148
+ * The middle `sidebar` grows to fill, so this stays flush to the bottom. */
1149
+ sidebarBottom?: ReactNode;
1150
+ /** Content of the main pane (opposite the sidebar). */
1151
+ children: ReactNode;
1152
+ /** Which side the sidebar sits on. Default `'left'`. When `'right'`, the
1153
+ * sidebar renders on the right with the resize handle on its left edge. */
1154
+ side?: 'left' | 'right';
1155
+ /** localStorage key to persist the sidebar width across reopens. When set,
1156
+ * the last dragged width is restored on mount. Omit for session-only width. */
1157
+ storageKey?: string;
1158
+ /** Initial sidebar width in px (used when nothing is persisted). Default 256. */
1159
+ defaultWidth?: number;
1160
+ /** Minimum sidebar width in px. Default 180. */
1161
+ minWidth?: number;
1162
+ /** Maximum sidebar width in px. Default 480. */
1163
+ maxWidth?: number;
1164
+ /** Extra classes for the outer flex row. */
1165
+ className?: string;
1166
+ /** Classes for the sidebar pane (background, border, …). The pane is a flex
1167
+ * column that scrolls its own overflow. Defaults to a light-grey,
1168
+ * right-bordered sidebar. */
1169
+ sidebarClassName?: string;
1170
+ /** Classes for the main content pane. Defaults to a white background. */
1171
+ contentClassName?: string;
1172
+ }
1173
+ declare function SidebarLayout({ sidebar, sidebarTop, sidebarBottom, children, side, storageKey, defaultWidth, minWidth, maxWidth, className, sidebarClassName, contentClassName, }: SidebarLayoutProps): react_jsx_runtime.JSX.Element;
1174
+
1175
+ /**
1176
+ * Full-width action button for the `SidebarLayout` `sidebarTop` / `sidebarBottom`
1177
+ * slots — the standard list-window pattern of a primary "New X" button pinned
1178
+ * at the top and a secondary action (e.g. "Export CSV") pinned at the bottom.
1179
+ *
1180
+ * `variant="primary"` is the solid-blue create button; `"secondary"` is the
1181
+ * white outline button. Pass `hotkey` (e.g. `ALT_SHIFT_N`) to render the small
1182
+ * keyboard-hint chip the shell's `useNewHotkey` binds.
1183
+ *
1184
+ * @example
1185
+ * <SidebarLayout
1186
+ * sidebarTop={<SidebarActionButton hotkey={ALT_SHIFT_N} onClick={onNew}>New Invoice</SidebarActionButton>}
1187
+ * sidebar={<StatusNav />}
1188
+ * >…</SidebarLayout>
1189
+ */
1190
+ interface SidebarActionButtonProps {
1191
+ children: ReactNode;
1192
+ onClick?: () => void;
1193
+ /** Solid-blue create button (`'primary'`, default) or white outline (`'secondary'`). */
1194
+ variant?: 'primary' | 'secondary';
1195
+ /** Keyboard-hint label rendered as a trailing chip, e.g. `ALT_SHIFT_N`. */
1196
+ hotkey?: string;
1197
+ disabled?: boolean;
1198
+ type?: 'button' | 'submit';
1199
+ title?: string;
1200
+ /** Extra classes appended after the variant styles (escape hatch). */
1201
+ className?: string;
1202
+ }
1203
+ declare function SidebarActionButton({ children, onClick, variant, hotkey, disabled, type, title, className, }: SidebarActionButtonProps): react_jsx_runtime.JSX.Element;
1204
+
1205
+ /**
1206
+ * Presentational building blocks for SidebarLayout filter sidebars (status
1207
+ * buckets, categories, …) — the same item + group-label markup every list
1208
+ * window renders. Data wiring (count fetching) stays in the consuming app.
1209
+ */
1210
+ declare function SidebarGroupLabel({ children }: {
1211
+ children: ReactNode;
1212
+ }): react_jsx_runtime.JSX.Element;
1213
+ declare function SidebarNavItem({ label, count, active, onClick, severity }: {
1214
+ label: string;
1215
+ count?: number;
1216
+ active: boolean;
1217
+ onClick: () => void;
1218
+ /**
1219
+ * Optional health marker — a small dot before the label, in the shell's
1220
+ * status vocabulary (`success | warning | danger`). For sidebars that double
1221
+ * as an alarm surface: a problem several levels down inside a section stays
1222
+ * visible on the always-on nav item that leads to it, without the operator
1223
+ * having to open anything.
1224
+ *
1225
+ * Roll the tone up in the consuming app (worst-of its children) — the item
1226
+ * renders a severity, it never computes one. Omitting it is not a claim of
1227
+ * health, it is no claim at all, and renders exactly as an item did before
1228
+ * this prop existed.
1229
+ *
1230
+ * A tone that arrives from a backend rollup has not been near the compiler,
1231
+ * so an unrecognised token (`'crit'`, `'critical'`, a stale enum) renders a
1232
+ * visible "unknown severity" marker and logs — never nothing. This item is
1233
+ * often the ONLY place a deep alarm surfaces; a marker that quietly vanishes
1234
+ * on a bad token would turn the safety feature into the outage.
1235
+ */
1236
+ severity?: SeverityTone;
1237
+ }): react_jsx_runtime.JSX.Element;
1238
+
1239
+ /**
1240
+ * Generic top navigation bar. A horizontal row of tab-style links with an
1241
+ * optional brand on the left and free-form actions on the right. Self-contained
1242
+ * and styled with the shell's Tailwind utilities, so it themes for free.
1243
+ *
1244
+ * Controlled: pass `activeKey` and handle `onSelect`. The active item gets an
1245
+ * accent underline. Items can carry an `icon`, a `badge` (e.g. a count) and be
1246
+ * `disabled`.
1247
+ */
1248
+ interface TopNavItem {
1249
+ /** Stable key — what `onSelect` receives and `activeKey` matches. */
1250
+ key: string;
1251
+ label: ReactNode;
1252
+ /** Optional leading icon (typically a 4×4 svg). */
1253
+ icon?: ReactNode;
1254
+ /** Optional trailing badge — a count, dot, "New", etc. */
1255
+ badge?: ReactNode;
1256
+ disabled?: boolean;
1257
+ }
1258
+ interface TopNavProps {
1259
+ items: TopNavItem[];
1260
+ /** Key of the active item — it gets the accent underline. */
1261
+ activeKey?: string;
1262
+ onSelect?: (key: string) => void;
1263
+ /** Left slot — logo / product title. */
1264
+ brand?: ReactNode;
1265
+ /** Right slot — actions (buttons, search, avatar…). Pinned to the far right. */
1266
+ actions?: ReactNode;
1267
+ className?: string;
1268
+ }
1269
+ declare function TopNav({ items, activeKey, onSelect, brand, actions, className }: TopNavProps): react_jsx_runtime.JSX.Element;
1270
+
1271
+ /**
1272
+ * Generic breadcrumb trail. Self-contained and styled with the same Tailwind
1273
+ * utilities the shell already ships, so consumers get it for free.
1274
+ *
1275
+ * Pass an ordered list of `items` from root → current. Every crumb except the
1276
+ * last renders as a button when it has an `onClick`; the last crumb is treated
1277
+ * as the current location — rendered inert with `aria-current="page"`. When the
1278
+ * trail is long, set `maxItems` to collapse the middle into an ellipsis
1279
+ * (`first … last-n` ), keeping the first and the tail visible.
1280
+ */
1281
+ interface BreadcrumbItem {
1282
+ /** Visible label. */
1283
+ label: ReactNode;
1284
+ /** Optional leading icon (typically a 3.5×3.5 svg). */
1285
+ icon?: ReactNode;
1286
+ /** Navigate to this crumb. Omitted on the current (last) crumb. */
1287
+ onClick?: () => void;
1288
+ }
1289
+ interface BreadcrumbsProps {
1290
+ items: BreadcrumbItem[];
1291
+ /** Node rendered between crumbs. Defaults to a chevron. */
1292
+ separator?: ReactNode;
1293
+ /** Collapse the middle to an ellipsis when there are more than this many
1294
+ * crumbs. `0` (default) never collapses. */
1295
+ maxItems?: number;
1296
+ className?: string;
1297
+ }
1298
+ declare function Breadcrumbs({ items, separator, maxItems, className }: BreadcrumbsProps): react_jsx_runtime.JSX.Element | null;
1299
+
1300
+ /**
1301
+ * Unified popup menu component — used for all context menus, dropdowns, and flyouts.
1302
+ * Reads --menu-density CSS variable: 'tight', 'normal' (default), or 'large'.
1303
+ */
1304
+ interface PopupMenuProps {
1305
+ children: ReactNode;
1306
+ style?: CSSProperties;
1307
+ className?: string;
1308
+ onClose?: () => void;
1309
+ minWidth?: number;
1310
+ /** Render into document.body instead of in place. Required when the menu
1311
+ * is opened from INSIDE a window: the window panel is a transformed /
1312
+ * backdrop-filtered `overflow-hidden` container, which both re-anchors
1313
+ * `position: fixed` descendants to itself and clips them — a menu
1314
+ * positioned at viewport coordinates ends up offset or invisible.
1315
+ * Portaling restores true viewport positioning. */
1316
+ portal?: boolean;
1317
+ }
1318
+ /** Container for a popup menu — auto-clamps to stay within viewport */
1319
+ declare function PopupMenu({ children, style, className, onClose, minWidth, portal }: PopupMenuProps): react_jsx_runtime.JSX.Element;
1320
+ /** A clickable menu item */
1321
+ declare function PopupMenuItem({ onClick, children, className, danger, disabled }: {
1322
+ onClick?: () => void;
1323
+ children: ReactNode;
1324
+ className?: string;
1325
+ danger?: boolean;
1326
+ disabled?: boolean;
1327
+ }): react_jsx_runtime.JSX.Element;
1328
+ /** A divider line between menu items */
1329
+ declare function PopupMenuDivider(): react_jsx_runtime.JSX.Element;
1330
+ /** A section header label */
1331
+ declare function PopupMenuLabel({ children }: {
1332
+ children: ReactNode;
1333
+ }): react_jsx_runtime.JSX.Element;
1334
+
1335
+ /**
1336
+ * Two presentations:
1337
+ *
1338
+ * 1. toast.success / error / info — brief operation feedback, top-center,
1339
+ * auto-dismiss (success/error ~3s, info ~4.5s). The everyday "what just
1340
+ * happened" feedback — most messages want this.
1341
+ * 2. toast.notify — system notification, top-right card, stays 10s, dismissible.
1342
+ * For an alert worth lingering on; reach for it deliberately, not by default.
1343
+ *
1344
+ * (Historically `toast.info` rendered the top-right notification card; it now
1345
+ * renders a brief toast — that persistent card moved to `toast.notify`.)
1346
+ */
1347
+ type ToastPlacement = 'top' | 'bottom';
1348
+ interface ToastOptions {
1349
+ /** Milliseconds before auto-dismiss. Ignored when `sticky`. */
1350
+ duration?: number;
1351
+ /**
1352
+ * Stay until dismissed. For a message the user must actually act on — the
1353
+ * kind that must not scroll past while they are looking at a customer.
1354
+ * Implies tap-to-dismiss is the only way out, which it already is.
1355
+ */
1356
+ sticky?: boolean;
1357
+ /**
1358
+ * `bottom` centres toasts along the bottom edge. On a till the top of the
1359
+ * screen is the cart total and the bottom is nearest the hand.
1360
+ */
1361
+ placement?: ToastPlacement;
1362
+ /**
1363
+ * Drop a message identical to one already showing, and RESTART its timer
1364
+ * rather than stacking a second copy. A failing poll otherwise writes the
1365
+ * same sentence a dozen times. The restart matters: the user asked twice, so
1366
+ * the message should persist, not expire on the first one's schedule.
1367
+ */
1368
+ dedupe?: boolean;
1369
+ }
1370
+ declare const toast: {
1371
+ success: (message: string, opts?: ToastOptions) => void;
1372
+ error: (message: string, opts?: ToastOptions) => void;
1373
+ info: (message: string, opts?: ToastOptions) => void;
1374
+ notify: (message: string, opts?: {
1375
+ duration?: number;
1376
+ }) => void;
1377
+ /**
1378
+ * Set defaults for every subsequent toast. Call once at app startup; later
1379
+ * calls replace the whole object rather than merging, so a consumer sets its
1380
+ * policy in one place. Per-call options still take precedence.
1381
+ */
1382
+ configure: (next: ToastOptions) => void;
1383
+ };
1384
+
1385
+ type DialogSize = 'sm' | 'md' | 'lg';
1386
+ interface DialogProps {
1387
+ open: boolean;
1388
+ /** Called for Escape, a backdrop click, and the close control. */
1389
+ onClose: () => void;
1390
+ title?: ReactNode;
1391
+ children?: ReactNode;
1392
+ /** Action row, right-aligned below the body. */
1393
+ footer?: ReactNode;
1394
+ /**
1395
+ * Escape and backdrop clicks do nothing — the only way out is an action in
1396
+ * the footer. For a state the user must actually resolve rather than dismiss,
1397
+ * such as a sale whose outcome is unknown. Use it sparingly: a dialog that
1398
+ * cannot be dismissed is a dead end if its actions ever fail to render.
1399
+ */
1400
+ blocking?: boolean;
1401
+ size?: DialogSize;
1402
+ /**
1403
+ * Element to focus on open. Defaults to the first focusable thing in the
1404
+ * dialog — which for a destructive confirm should be Cancel, not the button
1405
+ * that does the irreversible thing.
1406
+ */
1407
+ initialFocus?: RefObject<HTMLElement | null>;
1408
+ className?: string;
1409
+ }
1410
+ declare function Dialog({ open, onClose, title, children, footer, blocking, size, initialFocus, className, }: DialogProps): react_jsx_runtime.JSX.Element | null;
1411
+
1412
+ type DrawerSide = 'right' | 'left' | 'bottom';
1413
+ type DrawerSize = 'sm' | 'md' | 'lg';
1414
+ interface DrawerProps {
1415
+ open: boolean;
1416
+ onClose: () => void;
1417
+ side?: DrawerSide;
1418
+ size?: DrawerSize;
1419
+ title?: ReactNode;
1420
+ /** Action row pinned to the bottom edge, outside the scrolling body. */
1421
+ footer?: ReactNode;
1422
+ children?: ReactNode;
1423
+ /** Escape and the scrim do nothing. See Dialog's note before reaching for it. */
1424
+ blocking?: boolean;
1425
+ initialFocus?: RefObject<HTMLElement | null>;
1426
+ className?: string;
1427
+ }
1428
+ declare function Drawer({ open, onClose, side, size, title, footer, children, blocking, initialFocus, className, }: DrawerProps): react_jsx_runtime.JSX.Element | null;
1429
+
1430
+ /**
1431
+ * Focus containment and scroll locking for a modal surface — the two things
1432
+ * Headless UI was doing for `ConfirmDialog` before it was rewritten to import
1433
+ * nothing.
1434
+ *
1435
+ * Both are small, and both are the kind of small that is wrong in a way nobody
1436
+ * notices until someone navigates by keyboard: focus tabbing out of a dialog
1437
+ * onto the page behind it reads as the dialog having closed, and a page that
1438
+ * scrolls under a fixed overlay loses the user's place when it reopens.
1439
+ */
1440
+
1441
+ /**
1442
+ * While `active`, keep Tab inside `ref`, move focus in on open, and restore it
1443
+ * to wherever it came from on close.
1444
+ *
1445
+ * Restoring matters more than trapping: a dialog opened from a button in a
1446
+ * toolbar should hand focus back to that button, or a keyboard user is dumped
1447
+ * at the top of the document and has to find their place again.
1448
+ */
1449
+ declare function useFocusTrap(ref: RefObject<HTMLElement | null>, active: boolean, initialFocus?: RefObject<HTMLElement | null>): void;
1450
+ declare function useScrollLock(active: boolean): void;
1451
+
1452
+ interface ConfirmOptions {
1453
+ title?: string;
1454
+ message: string;
1455
+ confirmLabel?: string;
1456
+ cancelLabel?: string;
1457
+ variant?: 'danger' | 'warning' | 'info';
1458
+ }
1459
+ type ConfirmFn = (options: ConfirmOptions | string) => Promise<boolean>;
1460
+ declare const useConfirm: () => ConfirmFn;
1461
+ declare const confirm: (opts: ConfirmOptions | string) => Promise<boolean>;
1462
+ interface DestructiveConfirmOptions {
1463
+ title?: string;
1464
+ message: string;
1465
+ /**
1466
+ * Require typing this word (case-sensitive) before the action is enabled —
1467
+ * for something genuinely irreversible at scale, like dropping a tenant.
1468
+ *
1469
+ * OPTIONAL since 4.18.0. Omit it for a plain two-button destructive confirm.
1470
+ * Type-to-confirm assumes a keyboard, and a touch device that has none (a
1471
+ * till, a warehouse scanner) cannot satisfy it at all — the dialog becomes an
1472
+ * unanswerable question. Ask for a word when the cost of a mis-tap is high
1473
+ * enough to justify making the user work; not by default.
1474
+ */
1475
+ confirmWord?: string;
1476
+ /** Label for the action button when there is no `confirmWord`. */
1477
+ confirmLabel?: string;
1478
+ cancelLabel?: string;
1479
+ variant?: 'danger' | 'warning';
1480
+ }
1481
+ declare const confirmDestructive: (opts: DestructiveConfirmOptions) => Promise<boolean>;
1482
+ interface PromptOptions {
1483
+ title?: string;
1484
+ message?: string;
1485
+ defaultValue?: string;
1486
+ placeholder?: string;
1487
+ confirmLabel?: string;
1488
+ cancelLabel?: string;
1489
+ allowEmpty?: boolean;
1490
+ }
1491
+ declare const prompt: (opts: PromptOptions | string) => Promise<string | null>;
1492
+ declare function ConfirmProvider({ children }: {
1493
+ children: React.ReactNode;
1494
+ }): react_jsx_runtime.JSX.Element;
1495
+
1496
+ interface GridColumn {
1497
+ key: string;
1498
+ title: string;
1499
+ width?: number;
1500
+ readOnly?: boolean;
1501
+ align?: 'left' | 'right' | 'center';
1502
+ }
1503
+ type CellStyle = {
1504
+ bold?: boolean;
1505
+ italic?: boolean;
1506
+ underline?: boolean;
1507
+ fontSize?: 'sm' | 'base' | 'lg' | 'xl';
1508
+ };
1509
+ interface EditableGridProps {
1510
+ columns: GridColumn[];
1511
+ data: string[][];
1512
+ onChange: (data: string[][]) => void;
1513
+ onColumnsChange?: (columns: GridColumn[]) => void;
1514
+ /** Fixed row count — disables add/delete rows */
1515
+ fixedRows?: boolean;
1516
+ minRows?: number;
1517
+ maxHeight?: string;
1518
+ /** Per-cell text styling, keyed by `${row}:${col}`. */
1519
+ cellStyles?: Record<string, CellStyle>;
1520
+ /** Notifies the parent when the focused/edited cell changes. */
1521
+ onFocusChange?: (pos: {
1522
+ row: number;
1523
+ col: number;
1524
+ } | null) => void;
1525
+ /** Notifies the parent when the selection rectangle changes. */
1526
+ onSelectionChange?: (sel: {
1527
+ anchor: {
1528
+ row: number;
1529
+ col: number;
1530
+ };
1531
+ end: {
1532
+ row: number;
1533
+ col: number;
1534
+ };
1535
+ } | null) => void;
1536
+ }
1537
+ /**
1538
+ * Lightweight editable grid with spreadsheet-like features:
1539
+ * - Click + drag to select a range of cells
1540
+ * - Ctrl+C / Cmd+C to copy selection as tab-delimited text
1541
+ * - Multi-cell paste from spreadsheets (Ctrl+V)
1542
+ * - Tab/Enter/Arrow keyboard navigation
1543
+ */
1544
+ declare function EditableGrid({ columns, data, onChange, onColumnsChange, fixedRows, minRows, maxHeight, cellStyles, onFocusChange, onSelectionChange }: EditableGridProps): react_jsx_runtime.JSX.Element;
1545
+
1546
+ /**
1547
+ * Generic, drag-and-drop Kanban board. Self-contained (native HTML5 DnD, no
1548
+ * library) and styled with the same Tailwind utilities + `grid-scroll` class the
1549
+ * shell already ships, so consumers get it for free.
1550
+ *
1551
+ * Group items into columns with `columnOf`, render each card with `renderCard`.
1552
+ * Cards can be dragged **between** columns (to change which column they belong
1553
+ * to) and **within** a column (to reorder / prioritise). On drop, `onMove(id,
1554
+ * toColumn, toIndex)` fires — `toIndex` is the target position within `toColumn`
1555
+ * measured against that column's cards *excluding* the dragged one, so the
1556
+ * consumer can persist an order (e.g. midpoint between neighbours) that every
1557
+ * user then sees. Sort each column by that order via `sortInColumn`.
1558
+ *
1559
+ * Affordance: a blue **drop-line** shows exactly where the card will land
1560
+ * (between two cards, in either the source or a different column), and the
1561
+ * target column highlights. The dragged card dims. The insertion point tracks
1562
+ * `dragenter` (once per card crossed) so the line stays stable rather than
1563
+ * flickering. Pass `onAddItem` to give each column a "+ Add item" button at its
1564
+ * foot, revealed on column hover / keyboard focus.
1565
+ *
1566
+ * With no items the board is replaced by `emptyState` — right when the items
1567
+ * are the whole story, wrong when the columns are (a pipeline the user just
1568
+ * configured has a shape worth showing, and a board with no drop target can't
1569
+ * take its first card). Pass `showColumnsWhenEmpty` for that case and the
1570
+ * normal layout draws with every column on its `columnEmptyText` placeholder.
1571
+ */
1572
+ interface KanbanColumn {
1573
+ /** Stable column key — what `columnOf` returns and `onMove` receives. */
1574
+ value: string;
1575
+ label: string;
1576
+ /** Tailwind text + bg classes for the column header. */
1577
+ accent?: string;
1578
+ /** Tailwind bg class for the header dot. */
1579
+ dot?: string;
1580
+ }
1581
+ interface KanbanProps<T> {
1582
+ items: T[];
1583
+ columns: KanbanColumn[];
1584
+ columnOf: (item: T) => string;
1585
+ getId: (item: T) => string;
1586
+ /**
1587
+ * Fired on drop. `toColumn` is the destination column's `value`; `toIndex` is
1588
+ * the target position within that column measured against its cards
1589
+ * **excluding** the dragged card (0 = top). A same-column drop that wouldn't
1590
+ * change the order is not reported.
1591
+ */
1592
+ onMove: (id: string, toColumn: string, toIndex: number) => void;
1593
+ /** Inner card content — the card chrome (border, padding, hover) is provided. */
1594
+ renderCard: (item: T) => ReactNode;
1595
+ onCardClick?: (item: T) => void;
1596
+ /** Comparator for ordering within a column — sort by the persisted order field. */
1597
+ sortInColumn?: (a: T, b: T) => number;
1598
+ isLoading?: boolean;
1599
+ loadingText?: string;
1600
+ /** Shown INSTEAD of the board when there are no items at all. */
1601
+ emptyState?: ReactNode;
1602
+ /**
1603
+ * Draw the board — headers, empty columns, drop targets — even with no items,
1604
+ * instead of replacing it with `emptyState`. Opt in when the columns are
1605
+ * themselves the information (a configurable pipeline the user just defined)
1606
+ * or when an empty board still needs somewhere to drop a card into. Default
1607
+ * `false` keeps the `emptyState` behaviour every existing caller relies on.
1608
+ */
1609
+ showColumnsWhenEmpty?: boolean;
1610
+ /** Placeholder text inside an empty column. */
1611
+ columnEmptyText?: string;
1612
+ /**
1613
+ * When provided, each column gets a "+ Add item" button at its foot, revealed
1614
+ * on column hover or keyboard focus, that fires with the column's `value`.
1615
+ */
1616
+ onAddItem?: (toColumn: string) => void;
1617
+ /** Label for the per-column add button. */
1618
+ addItemText?: string;
1619
+ }
1620
+ declare function Kanban<T>({ items, columns, columnOf, getId, onMove, renderCard, onCardClick, sortInColumn, isLoading, loadingText, emptyState, showColumnsWhenEmpty, columnEmptyText, onAddItem, addItemText, }: KanbanProps<T>): react_jsx_runtime.JSX.Element;
1621
+
1622
+ /**
1623
+ * Pagination — a numbered page control for classic paginated tables/lists, with
1624
+ * prev/next and optional first/last edges and ellipsis gaps. Controlled: the
1625
+ * consumer owns `page` and updates it in `onPageChange`. Complements ListFooter
1626
+ * (which shows counts/selection for infinite-scroll lists).
1627
+ */
1628
+ interface PaginationProps {
1629
+ /** Current page, 1-based. */
1630
+ page: number;
1631
+ pageCount: number;
1632
+ onPageChange: (page: number) => void;
1633
+ /** How many page numbers to show on each side of the current page. */
1634
+ siblingCount?: number;
1635
+ /** Show first/last («/») buttons. */
1636
+ showEdges?: boolean;
1637
+ className?: string;
1638
+ }
1639
+ declare function Pagination({ page, pageCount, onPageChange, siblingCount, showEdges, className, }: PaginationProps): react_jsx_runtime.JSX.Element | null;
1640
+
1641
+ interface PaginatedResponse<T> {
1642
+ count: number;
1643
+ next: string | null;
1644
+ previous: string | null;
1645
+ results: T[];
1646
+ }
1647
+ interface ColumnDef {
1648
+ key: string;
1649
+ label: string;
1650
+ defaultWidth?: number;
1651
+ minWidth?: number;
1652
+ defaultHidden?: boolean;
1653
+ /** Optional rich header node (e.g. icon-only column). Falls back to label. */
1654
+ headerNode?: React.ReactNode;
1655
+ /** Optional override for the field used to sort against. */
1656
+ sortField?: string;
1657
+ }
1658
+ interface SortState {
1659
+ field: string;
1660
+ direction: 'asc' | 'desc';
1661
+ }
1662
+
1663
+ interface DataTableColumn<T> {
1664
+ key: string;
1665
+ title: ReactNode;
1666
+ /** Read this field when there is no `render`. */
1667
+ dataIndex?: keyof T & string;
1668
+ render?: (row: T, index: number) => ReactNode;
1669
+ align?: 'left' | 'right' | 'center';
1670
+ /** Px. Also feeds the table's minimum width — see `minWidth`. */
1671
+ width?: number;
1672
+ /** Single-line with an ellipsis. */
1673
+ ellipsis?: boolean;
1674
+ /**
1675
+ * Pin to the left edge while the table scrolls horizontally. Only useful
1676
+ * with `minWidth` set, and only worth it for the columns that identify the
1677
+ * row — a part number, a name.
1678
+ */
1679
+ fixed?: 'left';
1680
+ /**
1681
+ * Offer a sort control. The FIELD sent to the server is `sortField ?? key`,
1682
+ * which is what lets a column named `no` sort by `part_number` without the
1683
+ * caller mapping names at the call site.
1684
+ */
1685
+ sortable?: boolean;
1686
+ sortField?: string;
1687
+ headerClassName?: string;
1688
+ }
1689
+ interface DataTableProps<T> {
1690
+ columns: DataTableColumn<T>[];
1691
+ data: T[];
1692
+ /** Stable identity per row. A string names a field; a function computes it. */
1693
+ rowKey: (keyof T & string) | ((row: T, index: number) => string);
1694
+ /** Current sort, or null. Controlled — this component never sorts `data`. */
1695
+ sort?: SortState | null;
1696
+ onSortChange?: (sort: SortState | null) => void;
1697
+ /** Server pagination. Omit entirely for an unpaginated or infinite list. */
1698
+ pagination?: {
1699
+ page: number;
1700
+ pageCount: number;
1701
+ onPageChange: (page: number) => void;
1702
+ };
1703
+ loading?: boolean;
1704
+ bordered?: boolean;
1705
+ size?: 'sm' | 'md';
1706
+ /**
1707
+ * Minimum table width in px. Below it the table scrolls horizontally instead
1708
+ * of crushing columns. Usually the sum of the columns' widths.
1709
+ */
1710
+ minWidth?: number;
1711
+ rowClassName?: (row: T, index: number) => string;
1712
+ onRow?: (row: T, index: number) => {
1713
+ onClick?: () => void;
1714
+ className?: string;
1715
+ };
1716
+ emptyText?: ReactNode;
1717
+ /** Rendered under the rows — an infinite-scroll sentinel, a totals strip. */
1718
+ footer?: ReactNode;
1719
+ className?: string;
1720
+ }
1721
+ declare function DataTable<T>({ columns, data, rowKey, sort, onSortChange, pagination, loading, bordered, size, minWidth, rowClassName, onRow, emptyText, footer, className, }: DataTableProps<T>): react_jsx_runtime.JSX.Element;
1722
+
1723
+ interface ListFooterProps {
1724
+ selectedCount: number;
1725
+ loadedCount: number;
1726
+ totalCount?: number;
1727
+ label: string;
1728
+ isFetchingMore?: boolean;
1729
+ extra?: ReactNode;
1730
+ }
1731
+ /**
1732
+ * Standardized footer for `EntityList`. Shows:
1733
+ * "[X selected — ] Y records"
1734
+ * "Y of Z records · Loading more..."
1735
+ * "All N records loaded"
1736
+ */
1737
+ declare function ListFooter({ selectedCount, loadedCount, totalCount, label, isFetchingMore, extra, }: ListFooterProps): react_jsx_runtime.JSX.Element;
1738
+
1739
+ interface ListLoadErrorProps {
1740
+ /** Bold heading. Defaults to "Couldn't load this list". */
1741
+ title?: string;
1742
+ /** Muted explanatory line. Defaults to a generic connection/retry message. */
1743
+ message?: string;
1744
+ /** When provided, renders a "Try again" button that calls it (wire the
1745
+ * data source's `refetch`). Omit for a static, non-retryable error. */
1746
+ onRetry?: () => void;
1747
+ /** Frame around the content, mirroring {@link EmptyState}: `dashed`
1748
+ * (default), `card` (bordered surface), or `none`. */
1749
+ variant?: 'dashed' | 'card' | 'none';
1750
+ }
1751
+ /**
1752
+ * ListLoadError — the error counterpart to `EmptyState` for data lists. Shown
1753
+ * when a list's fetch fails (5xx, auth expiry, network) so an outage reads as
1754
+ * an error with a retry affordance instead of a misleading "nothing here"
1755
+ * empty state. Visual language mirrors `EmptyState`, tinted for error.
1756
+ */
1757
+ declare function ListLoadError({ title, message, onRetry, variant, }: ListLoadErrorProps): react_jsx_runtime.JSX.Element;
1758
+
1759
+ /**
1760
+ * Shared chart types. The charts are dependency-free inline SVG/CSS — color
1761
+ * defaults to `currentColor` so a parent `text-*` class themes them, and
1762
+ * geometry comes from numeric props (not Tailwind classes), so they sidestep
1763
+ * the design-sync compiled-CSS / arbitrary-value constraints entirely.
1764
+ */
1765
+
1766
+ interface SparklineProps {
1767
+ data: number[];
1768
+ width?: number;
1769
+ height?: number;
1770
+ /** Line color. Defaults to `currentColor`. */
1771
+ stroke?: string;
1772
+ /** Area fill under the line. Omit for a bare line. */
1773
+ fill?: string;
1774
+ strokeWidth?: number;
1775
+ showDots?: boolean;
1776
+ className?: string;
1777
+ style?: CSSProperties;
1778
+ }
1779
+ interface BarChartProps {
1780
+ data: number[];
1781
+ /** Optional labels under each bar. */
1782
+ labels?: string[];
1783
+ height?: number;
1784
+ /** Bar color. Defaults to `currentColor`. */
1785
+ color?: string;
1786
+ /** Per-bar color overrides. */
1787
+ colors?: string[];
1788
+ /** Value mapped to a full-height bar. Defaults to the max of `data`. */
1789
+ max?: number;
1790
+ /** Gap between bars, in px. */
1791
+ gap?: number;
1792
+ className?: string;
1793
+ style?: CSSProperties;
1794
+ }
1795
+ interface DonutSegment {
1796
+ label: string;
1797
+ value: number;
1798
+ color?: string;
1799
+ }
1800
+ interface DonutChartProps {
1801
+ segments: DonutSegment[];
1802
+ size?: number;
1803
+ thickness?: number;
1804
+ /** Rendered in the hole, e.g. a total. */
1805
+ centerLabel?: ReactNode;
1806
+ className?: string;
1807
+ style?: CSSProperties;
1808
+ }
1809
+
1810
+ declare function Sparkline({ data, width, height, stroke, fill, strokeWidth, showDots, className, style, }: SparklineProps): react_jsx_runtime.JSX.Element | null;
1811
+
1812
+ declare function BarChart({ data, labels, height, color, colors, max, gap, className, style, }: BarChartProps): react_jsx_runtime.JSX.Element | null;
1813
+
1814
+ declare function DonutChart({ segments, size, thickness, centerLabel, className, style }: DonutChartProps): react_jsx_runtime.JSX.Element;
1815
+
1816
+ declare function DashboardTemplate(): react_jsx_runtime.JSX.Element;
1817
+
1818
+ declare function DataTablePage(): react_jsx_runtime.JSX.Element;
1819
+
1820
+ declare function FormLayoutPage(): react_jsx_runtime.JSX.Element;
1821
+
1822
+ declare function CheckoutTemplate(): react_jsx_runtime.JSX.Element;
1823
+
1824
+ declare function EmailTemplate(): react_jsx_runtime.JSX.Element;
1825
+
1826
+ declare function ChatTemplate(): react_jsx_runtime.JSX.Element;
1827
+
1828
+ declare function GalleryTemplate(): react_jsx_runtime.JSX.Element;
1829
+
1830
+ interface AuthScreenProps {
1831
+ mode?: 'login' | 'register' | 'forgot';
1832
+ }
1833
+ declare function AuthScreen({ mode }: AuthScreenProps): react_jsx_runtime.JSX.Element;
1834
+
1835
+ interface ErrorPageProps {
1836
+ code?: 403 | 404 | 500;
1837
+ }
1838
+ declare function ErrorPage({ code }: ErrorPageProps): react_jsx_runtime.JSX.Element;
1839
+
1840
+ type Theme = 'system' | 'light' | 'dark' | 'pink' | 'green' | 'grey' | 'blue';
1841
+ /** Resolve a stored theme to the attribute value <html> actually carries
1842
+ * ('system' follows the OS dark-mode media query). */
1843
+ declare function resolveTheme(theme: string | null | undefined): string;
1844
+ /**
1845
+ * Imperatively apply a theme/accent/custom-color prefs snapshot to <html>.
1846
+ *
1847
+ * useTheme() already reconciles these from prefs, but it does so via effects
1848
+ * that only fire once prefs actually changes — and a backend-backed prefs
1849
+ * adapter (the admin/supplier portals PATCH /auth/me/ and refetch) can take
1850
+ * seconds to settle, leaving the repaint stalled the whole time. The shell's
1851
+ * <Customization> calls this on every theme/accent/color click so the switch
1852
+ * lands on the same frame, then persists through save() in the background.
1853
+ */
1854
+ declare function applyThemePrefs(p: {
1855
+ theme?: string | null;
1856
+ accent_color?: string | null;
1857
+ custom_bg_color?: string | null;
1858
+ custom_title_color?: string | null;
1859
+ custom_window_color?: string | null;
1860
+ custom_button_color?: string | null;
1861
+ }): void;
1862
+ declare function useTheme(): {
1863
+ theme: Theme;
1864
+ resolved: "dark" | "blue" | "green" | "grey" | "pink" | "light";
1865
+ };
1866
+
1867
+ interface ShellPrefsAdapter {
1868
+ /** The current prefs object. The shell reads its known keys directly off
1869
+ * this; consumer-private keys are ignored. */
1870
+ prefs: Record<string, any>;
1871
+ /** Patch a subset of prefs. The shell calls this with shallow diffs. */
1872
+ save: (patch: Record<string, any>) => Promise<void> | void;
1873
+ }
1874
+ declare function ShellPrefsProvider({ value, children, }: {
1875
+ value: ShellPrefsAdapter;
1876
+ children: ReactNode;
1877
+ }): react_jsx_runtime.JSX.Element;
1878
+ /** Default localStorage-backed adapter — useful when the consumer doesn't
1879
+ * ship a backend. Pass the result into <ShellPrefsProvider value={…}>.
1880
+ *
1881
+ * `defaults` are merged behind whatever's already stored, so they only
1882
+ * apply for keys the user hasn't set yet. Useful for opting out of bundled
1883
+ * features (e.g. `{ show_desktop_version: false }`). */
1884
+ declare function useLocalStoragePrefs(storageKey?: string, defaults?: Record<string, any>): ShellPrefsAdapter;
1885
+ /** Returns the active prefs adapter. When no provider is mounted, returns a
1886
+ * no-op adapter that reads as empty and silently drops saves — components
1887
+ * still render, but persistence is a no-op. */
1888
+ declare function useShellPrefs(): ShellPrefsAdapter;
1889
+
1890
+ /** Frosted glass style — shared across all menus, popups, and glass UI elements.
1891
+ * Reads --menu-opacity CSS variable set by the theme system, and adapts the
1892
+ * base tint to dark mode so menus don't stay light-cream when text is light. */
1893
+ declare function glassStyle(opacity?: number): CSSProperties;
1894
+ /** Glass divider border color */
1895
+ declare const GLASS_DIVIDER = "border-white/20";
1896
+ /** Glass input/search bar background — declared in styles.css so it can adapt
1897
+ * to dark mode (a flat `bg-white/15` reads as a too-bright tile on the dark
1898
+ * glass gradient). */
1899
+ declare const GLASS_INPUT_BG = "glass-input-bg";
1900
+
1901
+ /**
1902
+ * Format a date string (YYYY-MM-DD or ISO timestamp) for display.
1903
+ * Returns '—' for null/empty values.
1904
+ */
1905
+ declare function formatDate(value: string | null | undefined): string;
1906
+
1907
+ declare function useClickOutside(ref: RefObject<HTMLElement | null>, onClose: () => void): void;
1908
+
1909
+ declare function useIsMobile(): boolean;
1910
+
1911
+ declare const isMac: boolean;
1912
+ /** Platform-aware modifier symbols */
1913
+ declare const MOD: string;
1914
+ declare const ALT: string;
1915
+ declare const SHIFT = "\u21E7";
1916
+ declare const ENTER = "\u23CE";
1917
+ /** Common shortcut labels */
1918
+ declare const CMD_ENTER: string;
1919
+ declare const CMD_S: string;
1920
+ declare const CMD_K: string;
1921
+ declare const CMD_DOT: string;
1922
+ declare const CMD_A: string;
1923
+ declare const ALT_SHIFT_D: string;
1924
+ declare const ALT_SHIFT_E: string;
1925
+ declare const ALT_SHIFT_N: string;
1926
+
1927
+ /** Package version, injected by tsup at build time. Stays as an empty
1928
+ * string when the source is consumed without a build (e.g. tests). */
1929
+ declare const VERSION: string;
1930
+
1931
+ export { type CountBadgeTone as $, ALT as A, Banner as B, type ColumnDef as C, type ButtonVariant as D, CMD_A as E, CMD_DOT as F, CMD_ENTER as G, CMD_K as H, CMD_S as I, Card as J, type CardPadding as K, type CardProps as L, type CellStyle as M, ChatTemplate as N, Checkbox as O, type PaginatedResponse as P, type CheckboxProps as Q, CheckoutTemplate as R, type SortState as S, ColoredBadge as T, type ColoredBadgeProps as U, ConfirmProvider as V, ContainerFillChart as W, type ContainerFillChartProps as X, type ContainerFillItem as Y, CountBadge as Z, type CountBadgeProps as _, ALT_SHIFT_D as a, ListFooter as a$, DashboardTemplate as a0, DataTable as a1, type DataTableColumn as a2, DataTablePage as a3, type DataTableProps as a4, DateRangePicker as a5, type DateRangePickerProps as a6, type DescriptionColumns as a7, type DescriptionItem as a8, DescriptionList as a9, FormLayoutPage as aA, GLASS_DIVIDER as aB, GLASS_INPUT_BG as aC, GalleryTemplate as aD, type Gap as aE, Grid as aF, type GridCols as aG, type GridColumn as aH, type GridProps as aI, HelpCenter as aJ, type HelpCenterDoc as aK, type HelpCenterProps as aL, INPUT_BASE as aM, INPUT_SIZES as aN, Inline as aO, type InlineProps as aP, Input as aQ, InputNumber as aR, type InputNumberProps as aS, type InputProps as aT, type InputSize as aU, type Justify as aV, Kanban as aW, type KanbanColumn as aX, type KanbanProps as aY, Label as aZ, type LabelProps as a_, type DescriptionListProps as aa, Dialog as ab, type DialogProps as ac, type DialogSize as ad, Divider as ae, type DividerProps as af, type DividerSpacing as ag, DonutChart as ah, type DonutChartProps as ai, type DonutSegment as aj, Drawer as ak, type DrawerProps as al, type DrawerSide as am, type DrawerSize as an, ENTER as ao, EditableGrid as ap, type EditableGridProps as aq, EmailTemplate as ar, EmptyState as as, type EmptyStateProps as at, ErrorPage as au, type ErrorPageProps as av, FilePicker as aw, type FilePickerProps as ax, FormField as ay, type FormFieldProps as az, ALT_SHIFT_E as b, Stack as b$, ListLoadError as b0, type ListLoadErrorProps as b1, LoadingSpinner as b2, type LoadingSpinnerProps as b3, MAX_FRACTION_DIGITS as b4, MOD as b5, Markdown as b6, type MarkdownProps as b7, MediaUploadField as b8, type MediaUploadFieldProps as b9, type ResultStatus as bA, SHIFT as bB, type SearchableOption as bC, SearchableSelect as bD, type SearchableSelectProps as bE, Segmented as bF, type SegmentedOption as bG, type SegmentedProps as bH, type SegmentedSize as bI, Select as bJ, type SelectOption as bK, type SelectProps as bL, type SemanticGroup as bM, type SeverityTone as bN, type ShellPrefsAdapter as bO, ShellPrefsProvider as bP, SidebarActionButton as bQ, type SidebarActionButtonProps as bR, SidebarGroupLabel as bS, SidebarLayout as bT, type SidebarLayoutProps as bU, SidebarNavItem as bV, Skeleton as bW, type SkeletonProps as bX, type SkeletonVariant as bY, Sparkline as bZ, type SparklineProps as b_, MediaUploadGrid as ba, type MediaUploadGridItem as bb, type MediaUploadGridProps as bc, MetricBar as bd, type MetricBarProps as be, type Milestone as bf, type MilestoneKind as bg, MilestoneTimeline as bh, type MilestoneTimelineProps as bi, NativeSelect as bj, NumericKeypad as bk, type NumericKeypadProps as bl, PageHeader as bm, type PageHeaderProps as bn, Pagination as bo, type PaginationProps as bp, Paragraph as bq, type ParagraphProps as br, PopupMenu as bs, PopupMenuDivider as bt, PopupMenuItem as bu, PopupMenuLabel as bv, Radio as bw, type RadioProps as bx, Result as by, type ResultProps as bz, ALT_SHIFT_N as c, type StackProps as c0, StatCard as c1, type StatCardProps as c2, Statistic as c3, type StatisticProps as c4, type StatisticSize as c5, type StatisticTone as c6, StatusBadge as c7, StatusBadgeProvider as c8, Switch as c9, VERSION as cA, appendKey as cB, applyThemePrefs as cC, backspace as cD, confirm as cE, confirmDestructive as cF, formatDate as cG, glassStyle as cH, inputClasses as cI, isMac as cJ, isSeverityTone as cK, mediaFileName as cL, prompt as cM, resolveTheme as cN, severityOf as cO, toISODate as cP, toast as cQ, useClickOutside as cR, useConfirm as cS, useFocusTrap as cT, useIsMobile as cU, useLocalStoragePrefs as cV, useScrollLock as cW, useShellPrefs as cX, useTheme as cY, type SwitchProps as ca, type SwitchSize as cb, type TabItem as cc, Tabs as cd, type TabsProps as ce, Text as cf, type TextProps as cg, type TextSize as ch, type TextTone as ci, type TextWeight as cj, Textarea as ck, type TextareaProps as cl, type Theme as cm, TileButton as cn, type TileButtonProps as co, type TileSize as cp, Title as cq, type TitleLevel as cr, type TitleProps as cs, type ToastOptions as ct, type ToastPlacement as cu, Tooltip as cv, type TooltipProps as cw, TopNav as cx, type TopNavItem as cy, type TopNavProps as cz, Accordion as d, type AccordionItem as e, type AccordionProps as f, type Align as g, AuthScreen as h, type AuthScreenProps as i, Avatar as j, AvatarGroup as k, type AvatarGroupProps as l, type AvatarProps as m, type AvatarSize as n, type AvatarStatus as o, type BannerEmphasis as p, type BannerProps as q, type BannerTone as r, BarChart as s, type BarChartProps as t, type BreadcrumbItem as u, Breadcrumbs as v, type BreadcrumbsProps as w, Button as x, type ButtonProps as y, type ButtonSize as z };