@campfire-interactive/ui 0.1.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.
- package/README.md +82 -0
- package/dist/index.d.ts +1224 -0
- package/dist/index.js +1894 -0
- package/package.json +56 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1224 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
3
|
+
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
4
|
+
import * as RadixSlider from '@radix-ui/react-slider';
|
|
5
|
+
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
6
|
+
import * as RadixPopover from '@radix-ui/react-popover';
|
|
7
|
+
import * as RadixMenu from '@radix-ui/react-dropdown-menu';
|
|
8
|
+
import * as RadixSelect from '@radix-ui/react-select';
|
|
9
|
+
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
10
|
+
import * as RadixTabs from '@radix-ui/react-tabs';
|
|
11
|
+
import * as RadixAccordion from '@radix-ui/react-accordion';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Joins class names, dropping anything falsy.
|
|
15
|
+
*
|
|
16
|
+
* Seven frontends carry a copy of this, all of them wrapping `clsx` +
|
|
17
|
+
* `tailwind-merge`. This package emits plain CSS classes rather than Tailwind
|
|
18
|
+
* utilities, so there is nothing to de-conflict and the dependency pair is not
|
|
19
|
+
* needed here — but the export exists because the apps import `cn` from
|
|
20
|
+
* somewhere on nearly every component, and one import is easier to migrate to
|
|
21
|
+
* than a local file per app.
|
|
22
|
+
*/
|
|
23
|
+
type ClassValue = string | number | null | undefined | false | ClassValue[];
|
|
24
|
+
declare function cn(...values: ClassValue[]): string;
|
|
25
|
+
|
|
26
|
+
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
|
|
27
|
+
type ButtonSize = 'sm' | 'md' | 'lg' | 'icon';
|
|
28
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
29
|
+
variant?: ButtonVariant;
|
|
30
|
+
size?: ButtonSize;
|
|
31
|
+
/**
|
|
32
|
+
* Renders a spinner in place of any leading icon and blocks activation.
|
|
33
|
+
* Kept distinct from `disabled` so the caller can tell "working" apart from
|
|
34
|
+
* "not allowed" — a submit button that goes plain-disabled mid-request reads
|
|
35
|
+
* as a broken form.
|
|
36
|
+
*/
|
|
37
|
+
loading?: boolean;
|
|
38
|
+
/** Rendered before the label. Ignored while `loading`. */
|
|
39
|
+
leadingIcon?: React.ReactNode;
|
|
40
|
+
/** Rendered after the label. */
|
|
41
|
+
trailingIcon?: React.ReactNode;
|
|
42
|
+
/** Stretches to the container's width — the usual shape inside a dialog footer on mobile. */
|
|
43
|
+
fullWidth?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The suite's button.
|
|
47
|
+
*
|
|
48
|
+
* `variant="secondary"` is the default rather than `primary`, because the
|
|
49
|
+
* common case on a dense screen is a neutral action and a screen with six
|
|
50
|
+
* primary buttons has no primary action at all.
|
|
51
|
+
*
|
|
52
|
+
* `type` defaults to `"button"`. HTML defaults it to `"submit"`, which makes
|
|
53
|
+
* any button inside a form submit it by accident — a bug that has shipped in
|
|
54
|
+
* this codebase before.
|
|
55
|
+
*/
|
|
56
|
+
declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
57
|
+
interface IconButtonProps extends Omit<ButtonProps, 'leadingIcon' | 'trailingIcon' | 'children' | 'size'> {
|
|
58
|
+
/** The icon to render. */
|
|
59
|
+
icon: React.ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* Required. A square button with no text is unusable with a screen reader
|
|
62
|
+
* and unguessable on hover without one, so the label is not optional — it
|
|
63
|
+
* becomes both the accessible name and the native tooltip.
|
|
64
|
+
*/
|
|
65
|
+
label: string;
|
|
66
|
+
size?: 'sm' | 'md' | 'lg';
|
|
67
|
+
}
|
|
68
|
+
/** A square, label-less button. The label is required and never optional. */
|
|
69
|
+
declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
70
|
+
interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
71
|
+
/** `end` is the default — action rows sit right in every screen in the suite. */
|
|
72
|
+
align?: 'start' | 'end' | 'between';
|
|
73
|
+
}
|
|
74
|
+
/** A row of buttons with the suite's gap. Saves the `flex items-center gap-2` every call site repeats. */
|
|
75
|
+
declare function ButtonGroup({ align, className, ...rest }: ButtonGroupProps): react.JSX.Element;
|
|
76
|
+
|
|
77
|
+
type InputSize = 'sm' | 'md' | 'lg';
|
|
78
|
+
/**
|
|
79
|
+
* `size` and `prefix` are dropped from the native attribute set on purpose.
|
|
80
|
+
* Native `size` means "width in characters" and native `prefix` is an RDFa
|
|
81
|
+
* string — both are dead weight on a styled input, and every vendored copy in
|
|
82
|
+
* the fleet shadowed `size` with a style variant anyway.
|
|
83
|
+
*/
|
|
84
|
+
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
|
|
85
|
+
size?: InputSize;
|
|
86
|
+
/**
|
|
87
|
+
* Marks the field as failing validation. Drives the border and, more
|
|
88
|
+
* importantly, `aria-invalid` — color alone does not tell a screen-reader
|
|
89
|
+
* user their input was rejected.
|
|
90
|
+
*/
|
|
91
|
+
invalid?: boolean;
|
|
92
|
+
/** Rendered inside the field on the leading edge — a search icon, a currency symbol. */
|
|
93
|
+
prefix?: React.ReactNode;
|
|
94
|
+
/** Rendered inside the field on the trailing edge — a unit, a clear button. */
|
|
95
|
+
suffix?: React.ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A text input.
|
|
99
|
+
*
|
|
100
|
+
* `prefix` / `suffix` render *inside* the field's border rather than beside it,
|
|
101
|
+
* because a currency symbol in its own box next to the input is the shape every
|
|
102
|
+
* app hand-built and none of them aligned.
|
|
103
|
+
*
|
|
104
|
+
* The style prop is `size`, matching Button and matching what all seven
|
|
105
|
+
* Tailwind apps already write. The native `size` attribute — a width in
|
|
106
|
+
* characters — is dropped from the type above, so the name is free, and taking
|
|
107
|
+
* it means a migrating app edits no call site.
|
|
108
|
+
*/
|
|
109
|
+
declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
|
|
110
|
+
interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
111
|
+
invalid?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/** A multi-line input. Resizes vertically only — horizontal resize breaks every layout it sits in. */
|
|
114
|
+
declare const Textarea: react.ForwardRefExoticComponent<TextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
|
|
115
|
+
|
|
116
|
+
interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
|
117
|
+
/** Appends the required marker. Purely visual — set `required` on the control too. */
|
|
118
|
+
required?: boolean;
|
|
119
|
+
}
|
|
120
|
+
/** A form label. Standalone, for the cases that don't want the full `Field` wrapper. */
|
|
121
|
+
declare function Label({ required, className, children, ...rest }: LabelProps): react.JSX.Element;
|
|
122
|
+
interface FieldContextValue {
|
|
123
|
+
controlId: string;
|
|
124
|
+
describedBy: string | undefined;
|
|
125
|
+
invalid: boolean;
|
|
126
|
+
required: boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Wires a field's label, control, hint and error together.
|
|
130
|
+
*
|
|
131
|
+
* Call it as `useFieldControl()` inside a custom control, or let `Field` clone
|
|
132
|
+
* a single child. Returns the ids and flags the control has to carry for the
|
|
133
|
+
* label and error text to be announced — the part every hand-rolled form row
|
|
134
|
+
* in the fleet leaves out.
|
|
135
|
+
*/
|
|
136
|
+
declare function useFieldControl(): FieldContextValue | null;
|
|
137
|
+
interface FieldProps {
|
|
138
|
+
label: React.ReactNode;
|
|
139
|
+
/** Guidance shown under the control. Hidden while an `error` is showing. */
|
|
140
|
+
hint?: React.ReactNode;
|
|
141
|
+
/**
|
|
142
|
+
* The validation message. Its presence is what marks the field invalid —
|
|
143
|
+
* there is no separate `invalid` flag to keep in sync, which is how a field
|
|
144
|
+
* ends up red with no message or messaged with no red.
|
|
145
|
+
*/
|
|
146
|
+
error?: React.ReactNode;
|
|
147
|
+
required?: boolean;
|
|
148
|
+
/** Renders label and control side by side instead of stacked. */
|
|
149
|
+
orientation?: 'vertical' | 'horizontal';
|
|
150
|
+
className?: string;
|
|
151
|
+
/** The control. Given `id`, `aria-describedby` and `aria-invalid` automatically. */
|
|
152
|
+
children: React.ReactNode;
|
|
153
|
+
/** Override the generated control id — needed when the control is rendered by a third party. */
|
|
154
|
+
id?: string;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A label + control + hint/error row.
|
|
158
|
+
*
|
|
159
|
+
* The five form-heavy apps each rebuilt this, and each one lost a different
|
|
160
|
+
* piece of it: the label/control `htmlFor` link, `aria-describedby` on the
|
|
161
|
+
* error, or the error replacing rather than stacking under the hint. Passing
|
|
162
|
+
* the message as `error` is the only switch — the styling and the ARIA follow
|
|
163
|
+
* from it.
|
|
164
|
+
*/
|
|
165
|
+
declare function Field({ label, hint, error, required, orientation, className, children, id, }: FieldProps): react.JSX.Element;
|
|
166
|
+
interface FieldsetProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
167
|
+
legend: React.ReactNode;
|
|
168
|
+
/** Number of columns to lay the fields out in. */
|
|
169
|
+
columns?: 1 | 2 | 3;
|
|
170
|
+
}
|
|
171
|
+
/** A titled group of fields. `legend` is a real `<legend>`, so the group is announced as one. */
|
|
172
|
+
declare function Fieldset({ legend, columns, className, children, ...rest }: FieldsetProps): react.JSX.Element;
|
|
173
|
+
|
|
174
|
+
interface CheckboxProps extends Omit<React.ComponentPropsWithoutRef<typeof RadixCheckbox.Root>, 'children'> {
|
|
175
|
+
/** Rendered beside the box and wired as the accessible label. */
|
|
176
|
+
label?: React.ReactNode;
|
|
177
|
+
/** Secondary line under the label — the shape a settings list repeats. */
|
|
178
|
+
description?: React.ReactNode;
|
|
179
|
+
invalid?: boolean;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* A checkbox, optionally with its own label.
|
|
183
|
+
*
|
|
184
|
+
* Radix handles the tri-state and the hidden native input; the label is built
|
|
185
|
+
* in because the alternative — a bare box the caller pairs with a `<Label>` —
|
|
186
|
+
* is what produced the unlabeled checkboxes in three of the vendored copies.
|
|
187
|
+
* Pass `checked="indeterminate"` for the partial state of a select-all header.
|
|
188
|
+
*/
|
|
189
|
+
declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLButtonElement>>;
|
|
190
|
+
|
|
191
|
+
interface SwitchProps extends React.ComponentPropsWithoutRef<typeof RadixSwitch.Root> {
|
|
192
|
+
label?: React.ReactNode;
|
|
193
|
+
description?: React.ReactNode;
|
|
194
|
+
/** Puts the label before the control — the shape a settings row uses. */
|
|
195
|
+
labelPosition?: 'start' | 'end';
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* An on/off switch.
|
|
199
|
+
*
|
|
200
|
+
* A switch takes effect immediately; a checkbox is a value you then save. That
|
|
201
|
+
* distinction is the whole reason both exist, and mixing them is why some
|
|
202
|
+
* settings screens in the fleet appear to save on toggle and others don't.
|
|
203
|
+
*/
|
|
204
|
+
declare const Switch: react.ForwardRefExoticComponent<SwitchProps & react.RefAttributes<HTMLButtonElement>>;
|
|
205
|
+
|
|
206
|
+
interface SliderProps extends React.ComponentPropsWithoutRef<typeof RadixSlider.Root> {
|
|
207
|
+
/**
|
|
208
|
+
* Shows the current value beside the track. A slider with no readout is
|
|
209
|
+
* unusable for anything the user has to report a number from, which is most
|
|
210
|
+
* of what this suite adjusts.
|
|
211
|
+
*/
|
|
212
|
+
showValue?: boolean;
|
|
213
|
+
/** Formats the readout — percentages, currency, "12 wk". */
|
|
214
|
+
formatValue?: (value: number) => string;
|
|
215
|
+
}
|
|
216
|
+
/** A range slider. One thumb per entry in `value` / `defaultValue`. */
|
|
217
|
+
declare const Slider: react.ForwardRefExoticComponent<SliderProps & react.RefAttributes<HTMLSpanElement>>;
|
|
218
|
+
|
|
219
|
+
type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
220
|
+
/**
|
|
221
|
+
* The root. `Dialog` is the ROOT, not a whole dialog in one component.
|
|
222
|
+
*
|
|
223
|
+
* That is the opposite of what an earlier version of this package did, and the
|
|
224
|
+
* change came out of migrating a real app: all seven Tailwind frontends already
|
|
225
|
+
* use `<Dialog open=... onOpenChange=...>` as the root across hundreds of call
|
|
226
|
+
* sites. Exporting a differently-shaped component under the same name meant a
|
|
227
|
+
* migration could swap the import and render something else entirely — a silent
|
|
228
|
+
* failure, not a compile error. The one-component convenience version is
|
|
229
|
+
* `Modal` below.
|
|
230
|
+
*/
|
|
231
|
+
declare const Dialog: react.FC<RadixDialog.DialogProps>;
|
|
232
|
+
/** @deprecated Use `Dialog`. Kept so existing imports keep working. */
|
|
233
|
+
declare const DialogRoot: react.FC<RadixDialog.DialogProps>;
|
|
234
|
+
declare const DialogTrigger: react.ForwardRefExoticComponent<RadixDialog.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
235
|
+
declare const DialogClose: react.ForwardRefExoticComponent<RadixDialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>;
|
|
236
|
+
declare const DialogPortal: react.FC<RadixDialog.DialogPortalProps>;
|
|
237
|
+
interface DialogContentProps extends React.ComponentPropsWithoutRef<typeof RadixDialog.Content> {
|
|
238
|
+
size?: DialogSize;
|
|
239
|
+
/** Slides in from an edge instead of centring. This is the Sheet/Drawer shape. */
|
|
240
|
+
side?: 'center' | 'right' | 'left';
|
|
241
|
+
/** Hides the corner close button. The dialog is still dismissible by Esc unless that is also blocked. */
|
|
242
|
+
hideClose?: boolean;
|
|
243
|
+
closeLabel?: string;
|
|
244
|
+
}
|
|
245
|
+
declare const DialogContent: react.ForwardRefExoticComponent<DialogContentProps & react.RefAttributes<HTMLDivElement>>;
|
|
246
|
+
declare function DialogHeader({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
247
|
+
declare const DialogTitle: react.ForwardRefExoticComponent<Omit<RadixDialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>, "ref"> & react.RefAttributes<HTMLHeadingElement>>;
|
|
248
|
+
declare const DialogDescription: react.ForwardRefExoticComponent<Omit<RadixDialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>, "ref"> & react.RefAttributes<HTMLParagraphElement>>;
|
|
249
|
+
/** The scrolling region. Only this scrolls, so the header and footer stay put. */
|
|
250
|
+
declare function DialogBody({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
251
|
+
declare function DialogFooter({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
252
|
+
interface ModalProps {
|
|
253
|
+
open: boolean;
|
|
254
|
+
onOpenChange: (open: boolean) => void;
|
|
255
|
+
title: React.ReactNode;
|
|
256
|
+
/** Sub-heading under the title. Also becomes the dialog's accessible description. */
|
|
257
|
+
description?: React.ReactNode;
|
|
258
|
+
size?: DialogSize;
|
|
259
|
+
side?: 'center' | 'right' | 'left';
|
|
260
|
+
/** Footer content — usually a `<ButtonGroup>`. Omit for a dialog with no actions. */
|
|
261
|
+
footer?: React.ReactNode;
|
|
262
|
+
hideClose?: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Blocks Esc and outside-click dismissal. For a dialog mid-save, where
|
|
265
|
+
* losing the form is worse than the extra click. Use sparingly.
|
|
266
|
+
*/
|
|
267
|
+
dismissible?: boolean;
|
|
268
|
+
className?: string;
|
|
269
|
+
children: React.ReactNode;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A modal dialog, header through footer, in one component.
|
|
273
|
+
*
|
|
274
|
+
* The fleet has 36 `*Dialog.tsx` and 24 `*Modal.tsx` files, and the reason so
|
|
275
|
+
* many are hand-built rather than composed is that the eight-part primitive
|
|
276
|
+
* API makes the common case — title, body, two buttons — cost eight imports.
|
|
277
|
+
* This is that case. Drop to the primitives above when the shape genuinely
|
|
278
|
+
* differs.
|
|
279
|
+
*
|
|
280
|
+
* The body is the only scrolling region, so a long form keeps its title and
|
|
281
|
+
* its Save button on screen. That single detail is missing from most of the
|
|
282
|
+
* hand-built copies.
|
|
283
|
+
*/
|
|
284
|
+
declare function Modal({ open, onOpenChange, title, description, size, side, footer, hideClose, dismissible, className, children, }: ModalProps): react.JSX.Element;
|
|
285
|
+
/**
|
|
286
|
+
* A `Modal` that slides in from the side.
|
|
287
|
+
*
|
|
288
|
+
* `Sheet` itself is the composable ROOT below, matching the fleet's existing
|
|
289
|
+
* `<Sheet><SheetTrigger/><SheetContent/></Sheet>` usage. This is the
|
|
290
|
+
* one-component form.
|
|
291
|
+
*/
|
|
292
|
+
declare function SideModal({ side, size, ...rest }: ModalProps): react.JSX.Element;
|
|
293
|
+
declare const Sheet: react.FC<RadixDialog.DialogProps>;
|
|
294
|
+
declare const SheetTrigger: react.ForwardRefExoticComponent<RadixDialog.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
295
|
+
declare const SheetClose: react.ForwardRefExoticComponent<RadixDialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>;
|
|
296
|
+
declare const SheetPortal: react.FC<RadixDialog.DialogPortalProps>;
|
|
297
|
+
interface SheetContentProps extends Omit<DialogContentProps, 'side'> {
|
|
298
|
+
side?: 'right' | 'left';
|
|
299
|
+
}
|
|
300
|
+
/** The panel. Defaults to the right edge, which is what every caller in the fleet wants. */
|
|
301
|
+
declare const SheetContent: react.ForwardRefExoticComponent<SheetContentProps & react.RefAttributes<HTMLDivElement>>;
|
|
302
|
+
declare const SheetHeader: typeof DialogHeader;
|
|
303
|
+
/** Base-sized, where DialogTitle is text-lg — matching the vendored pair. */
|
|
304
|
+
declare const SheetTitle: react.ForwardRefExoticComponent<Omit<RadixDialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>, "ref"> & react.RefAttributes<HTMLHeadingElement>>;
|
|
305
|
+
declare const SheetDescription: react.ForwardRefExoticComponent<Omit<RadixDialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>, "ref"> & react.RefAttributes<HTMLParagraphElement>>;
|
|
306
|
+
declare const SheetBody: typeof DialogBody;
|
|
307
|
+
declare const SheetFooter: typeof DialogFooter;
|
|
308
|
+
interface ConfirmDialogProps {
|
|
309
|
+
open: boolean;
|
|
310
|
+
onOpenChange: (open: boolean) => void;
|
|
311
|
+
title: React.ReactNode;
|
|
312
|
+
/** What is about to happen, and what cannot be undone. */
|
|
313
|
+
description?: React.ReactNode;
|
|
314
|
+
/** Defaults to "Confirm". Name the action instead — "Delete 3 quotes", "Archive". */
|
|
315
|
+
confirmLabel?: string;
|
|
316
|
+
cancelLabel?: string;
|
|
317
|
+
/** Red confirm button. Default true, since this component exists for destructive actions. */
|
|
318
|
+
destructive?: boolean;
|
|
319
|
+
/** Disables both buttons and spins the confirm while the action runs. */
|
|
320
|
+
loading?: boolean;
|
|
321
|
+
onConfirm: () => void;
|
|
322
|
+
children?: React.ReactNode;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The confirm-before-you-destroy dialog.
|
|
326
|
+
*
|
|
327
|
+
* Ten apps have some version of this and several still use `window.confirm`,
|
|
328
|
+
* which cannot be styled, cannot show what is about to be deleted, and blocks
|
|
329
|
+
* the whole tab.
|
|
330
|
+
*
|
|
331
|
+
* Not dismissible by outside click — a stray click behind a destructive
|
|
332
|
+
* confirm should not count as an answer either way. Esc still cancels, and
|
|
333
|
+
* focus starts on Cancel, so a held Enter key cannot confirm a deletion.
|
|
334
|
+
*/
|
|
335
|
+
declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive, loading, onConfirm, children, }: ConfirmDialogProps): react.JSX.Element;
|
|
336
|
+
|
|
337
|
+
declare const PopoverRoot: react.FC<RadixPopover.PopoverProps>;
|
|
338
|
+
declare const PopoverTrigger: react.ForwardRefExoticComponent<RadixPopover.PopoverTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
339
|
+
declare const PopoverAnchor: react.ForwardRefExoticComponent<RadixPopover.PopoverAnchorProps & react.RefAttributes<HTMLDivElement>>;
|
|
340
|
+
declare const PopoverClose: react.ForwardRefExoticComponent<RadixPopover.PopoverCloseProps & react.RefAttributes<HTMLButtonElement>>;
|
|
341
|
+
interface PopoverContentProps extends React.ComponentPropsWithoutRef<typeof RadixPopover.Content> {
|
|
342
|
+
/** Draws the little arrow pointing at the trigger. Off by default — it needs a solid edge to sit on. */
|
|
343
|
+
showArrow?: boolean;
|
|
344
|
+
/** Removes the default padding, for a popover whose content is a list or a table. */
|
|
345
|
+
flush?: boolean;
|
|
346
|
+
}
|
|
347
|
+
declare const PopoverContent: react.ForwardRefExoticComponent<PopoverContentProps & react.RefAttributes<HTMLDivElement>>;
|
|
348
|
+
interface PopoverProps extends Omit<PopoverContentProps, 'children'> {
|
|
349
|
+
trigger: React.ReactNode;
|
|
350
|
+
open?: boolean;
|
|
351
|
+
onOpenChange?: (open: boolean) => void;
|
|
352
|
+
defaultOpen?: boolean;
|
|
353
|
+
/** When false, the popover renders inline and does not trap focus or block the page. */
|
|
354
|
+
modal?: boolean;
|
|
355
|
+
children: React.ReactNode;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* A floating panel anchored to a trigger.
|
|
359
|
+
*
|
|
360
|
+
* `trigger` is rendered with `asChild`, so pass a real element — a Button, an
|
|
361
|
+
* IconButton — and it keeps its own styling while gaining the popover's
|
|
362
|
+
* aria-expanded and keyboard handling.
|
|
363
|
+
*/
|
|
364
|
+
declare function Popover({ trigger, open, onOpenChange, defaultOpen, modal, children, ...content }: PopoverProps): react.JSX.Element;
|
|
365
|
+
|
|
366
|
+
declare const MenuRoot: react.FC<RadixMenu.DropdownMenuProps>;
|
|
367
|
+
declare const MenuTrigger: react.ForwardRefExoticComponent<RadixMenu.DropdownMenuTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
368
|
+
declare const MenuGroup: react.ForwardRefExoticComponent<RadixMenu.DropdownMenuGroupProps & react.RefAttributes<HTMLDivElement>>;
|
|
369
|
+
declare const MenuSub: react.FC<RadixMenu.DropdownMenuSubProps>;
|
|
370
|
+
declare const MenuRadioGroup: react.ForwardRefExoticComponent<RadixMenu.DropdownMenuRadioGroupProps & react.RefAttributes<HTMLDivElement>>;
|
|
371
|
+
declare const MenuContent: react.ForwardRefExoticComponent<Omit<RadixMenu.DropdownMenuContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
372
|
+
interface MenuItemProps extends React.ComponentPropsWithoutRef<typeof RadixMenu.Item> {
|
|
373
|
+
icon?: React.ReactNode;
|
|
374
|
+
/** Right-aligned shortcut hint, e.g. "⌘K". Display only — bind the key yourself. */
|
|
375
|
+
shortcut?: string;
|
|
376
|
+
/** `danger` colors the item red. Destructive items should also confirm. */
|
|
377
|
+
variant?: 'default' | 'danger';
|
|
378
|
+
}
|
|
379
|
+
declare const MenuItem: react.ForwardRefExoticComponent<MenuItemProps & react.RefAttributes<HTMLDivElement>>;
|
|
380
|
+
declare const MenuCheckboxItem: react.ForwardRefExoticComponent<Omit<RadixMenu.DropdownMenuCheckboxItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
381
|
+
declare const MenuRadioItem: react.ForwardRefExoticComponent<Omit<RadixMenu.DropdownMenuRadioItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
382
|
+
declare function MenuLabel({ className, ...rest }: React.ComponentPropsWithoutRef<typeof RadixMenu.Label>): react.JSX.Element;
|
|
383
|
+
declare function MenuSeparator({ className, ...rest }: React.ComponentPropsWithoutRef<typeof RadixMenu.Separator>): react.JSX.Element;
|
|
384
|
+
declare const MenuSubTrigger: react.ForwardRefExoticComponent<Omit<RadixMenu.DropdownMenuSubTriggerProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
385
|
+
declare const MenuSubContent: react.ForwardRefExoticComponent<Omit<RadixMenu.DropdownMenuSubContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
386
|
+
interface MenuAction {
|
|
387
|
+
/** Stable key. Also used as the value when the item is part of a radio group. */
|
|
388
|
+
id: string;
|
|
389
|
+
label: React.ReactNode;
|
|
390
|
+
icon?: React.ReactNode;
|
|
391
|
+
shortcut?: string;
|
|
392
|
+
disabled?: boolean;
|
|
393
|
+
variant?: 'default' | 'danger';
|
|
394
|
+
onSelect?: () => void;
|
|
395
|
+
/** Draws a separator above this item. */
|
|
396
|
+
separatorBefore?: boolean;
|
|
397
|
+
}
|
|
398
|
+
interface MenuProps {
|
|
399
|
+
/** Rendered with `asChild` — pass a Button or IconButton and it keeps its own styling. */
|
|
400
|
+
trigger: React.ReactNode;
|
|
401
|
+
items: MenuAction[];
|
|
402
|
+
align?: 'start' | 'center' | 'end';
|
|
403
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
404
|
+
className?: string;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* The row-actions menu.
|
|
408
|
+
*
|
|
409
|
+
* Four apps vendored Radix's dropdown-menu and every one of them wrote the same
|
|
410
|
+
* item markup by hand at each call site. `items` is the shape a table's
|
|
411
|
+
* per-row menu actually wants: a flat list with icons, separators and
|
|
412
|
+
* destructive styling. Drop to the primitives above for submenus, checkbox
|
|
413
|
+
* items or a menu whose contents are a live query.
|
|
414
|
+
*/
|
|
415
|
+
declare function Menu({ trigger, items, align, side, className }: MenuProps): react.JSX.Element;
|
|
416
|
+
|
|
417
|
+
declare const SelectRoot: react.FC<RadixSelect.SelectProps>;
|
|
418
|
+
declare const SelectValue: react.ForwardRefExoticComponent<RadixSelect.SelectValueProps & react.RefAttributes<HTMLSpanElement>>;
|
|
419
|
+
declare const SelectGroup: react.ForwardRefExoticComponent<RadixSelect.SelectGroupProps & react.RefAttributes<HTMLDivElement>>;
|
|
420
|
+
interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof RadixSelect.Trigger> {
|
|
421
|
+
size?: 'sm' | 'md' | 'lg';
|
|
422
|
+
invalid?: boolean;
|
|
423
|
+
}
|
|
424
|
+
declare const SelectTrigger: react.ForwardRefExoticComponent<SelectTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
425
|
+
declare const SelectContent: react.ForwardRefExoticComponent<Omit<RadixSelect.SelectContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
426
|
+
declare const SelectItem: react.ForwardRefExoticComponent<Omit<RadixSelect.SelectItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
427
|
+
declare function SelectLabel({ className, ...rest }: React.ComponentPropsWithoutRef<typeof RadixSelect.Label>): react.JSX.Element;
|
|
428
|
+
declare function SelectSeparator({ className, ...rest }: React.ComponentPropsWithoutRef<typeof RadixSelect.Separator>): react.JSX.Element;
|
|
429
|
+
interface SelectOption<T extends string = string> {
|
|
430
|
+
value: T;
|
|
431
|
+
label: React.ReactNode;
|
|
432
|
+
disabled?: boolean;
|
|
433
|
+
/** Groups options under a heading. Options sharing a group render together, in first-seen order. */
|
|
434
|
+
group?: string;
|
|
435
|
+
}
|
|
436
|
+
interface SelectProps<T extends string = string> {
|
|
437
|
+
options: SelectOption<T>[];
|
|
438
|
+
value?: T;
|
|
439
|
+
onValueChange?: (value: T) => void;
|
|
440
|
+
defaultValue?: T;
|
|
441
|
+
/** Shown when nothing is selected. */
|
|
442
|
+
placeholder?: string;
|
|
443
|
+
size?: 'sm' | 'md' | 'lg';
|
|
444
|
+
invalid?: boolean;
|
|
445
|
+
disabled?: boolean;
|
|
446
|
+
required?: boolean;
|
|
447
|
+
name?: string;
|
|
448
|
+
id?: string;
|
|
449
|
+
/**
|
|
450
|
+
* Labelling and focus, forwarded to the trigger.
|
|
451
|
+
*
|
|
452
|
+
* The trigger already spreads unknown props, so these worked at runtime and
|
|
453
|
+
* only the type rejected them — which pm found at 33 call sites that label
|
|
454
|
+
* their select with `aria-label` because the visible label is a table column
|
|
455
|
+
* header, plus 8 inline-edit cells that need `autoFocus` to be usable from
|
|
456
|
+
* the keyboard. Declared explicitly rather than as an index signature, so a
|
|
457
|
+
* typo is still an error.
|
|
458
|
+
*/
|
|
459
|
+
'aria-label'?: string;
|
|
460
|
+
'aria-labelledby'?: string;
|
|
461
|
+
'aria-describedby'?: string;
|
|
462
|
+
title?: string;
|
|
463
|
+
autoFocus?: boolean;
|
|
464
|
+
/**
|
|
465
|
+
* Fires when the listbox opens or closes.
|
|
466
|
+
*
|
|
467
|
+
* Needed by any caller that commits on blur: opening the listbox moves focus
|
|
468
|
+
* into a portal, so the trigger blurs and a commit-on-blur handler runs
|
|
469
|
+
* before the user has picked anything. A caller can suppress its own blur
|
|
470
|
+
* while this reports open. pm's click-to-edit cells are the case.
|
|
471
|
+
*/
|
|
472
|
+
onOpenChange?: (open: boolean) => void;
|
|
473
|
+
onBlur?: React.FocusEventHandler<HTMLButtonElement>;
|
|
474
|
+
className?: string;
|
|
475
|
+
/** Fills its container. Default true — a select narrower than its own label reads as broken. */
|
|
476
|
+
fullWidth?: boolean;
|
|
477
|
+
/** Text for the empty list. */
|
|
478
|
+
emptyLabel?: string;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* A single-select dropdown.
|
|
482
|
+
*
|
|
483
|
+
* Six apps vendored Radix's select and each rebuilt the trigger, the viewport,
|
|
484
|
+
* the scroll buttons and the check indicator. This takes an options array,
|
|
485
|
+
* which is what every call site in the fleet actually has — usually straight
|
|
486
|
+
* out of a master-data query.
|
|
487
|
+
*
|
|
488
|
+
* Options carry an optional `group`, so a grouped list needs no extra nesting
|
|
489
|
+
* at the call site. Drop to the primitives above when an option needs content
|
|
490
|
+
* richer than a label.
|
|
491
|
+
*
|
|
492
|
+
* For a list long enough to need filtering, reach for `Combobox` instead: a
|
|
493
|
+
* select with four hundred options is a search field wearing a dropdown.
|
|
494
|
+
*/
|
|
495
|
+
declare function Select<T extends string = string>({ options, value, onValueChange, defaultValue, placeholder, size, invalid, disabled, required, name, id, className, fullWidth, emptyLabel, onOpenChange, onBlur, ...aria }: SelectProps<T>): react.JSX.Element;
|
|
496
|
+
|
|
497
|
+
interface ComboboxOption<T extends string = string> {
|
|
498
|
+
value: T;
|
|
499
|
+
label: string;
|
|
500
|
+
/** Second line under the label — a part number under a description, a plant code under a name. */
|
|
501
|
+
hint?: string;
|
|
502
|
+
disabled?: boolean;
|
|
503
|
+
group?: string;
|
|
504
|
+
}
|
|
505
|
+
interface ComboboxProps<T extends string = string> {
|
|
506
|
+
options: ComboboxOption<T>[];
|
|
507
|
+
value: T | null;
|
|
508
|
+
onValueChange: (value: T | null) => void;
|
|
509
|
+
placeholder?: string;
|
|
510
|
+
/** Placeholder for the search field inside the list. */
|
|
511
|
+
searchPlaceholder?: string;
|
|
512
|
+
/**
|
|
513
|
+
* Take over filtering. When provided, `options` is used verbatim and the
|
|
514
|
+
* caller is expected to be querying — the shape every master-data picker in
|
|
515
|
+
* the fleet needs, since none of them can hold the full list client-side.
|
|
516
|
+
*/
|
|
517
|
+
onSearchChange?: (search: string) => void;
|
|
518
|
+
/** Spins in the list. Use with `onSearchChange` while a query is in flight. */
|
|
519
|
+
loading?: boolean;
|
|
520
|
+
emptyLabel?: string;
|
|
521
|
+
loadingLabel?: string;
|
|
522
|
+
/** Adds a "Clear" affordance to the trigger once something is selected. */
|
|
523
|
+
clearable?: boolean;
|
|
524
|
+
size?: 'sm' | 'md' | 'lg';
|
|
525
|
+
invalid?: boolean;
|
|
526
|
+
disabled?: boolean;
|
|
527
|
+
id?: string;
|
|
528
|
+
name?: string;
|
|
529
|
+
'aria-describedby'?: string;
|
|
530
|
+
className?: string;
|
|
531
|
+
/**
|
|
532
|
+
* Applied to the wrapper, not the inner button. Sizing a combobox is the
|
|
533
|
+
* common reason to reach for `style` here, and on the button it would be
|
|
534
|
+
* overridden by the wrapper's own width.
|
|
535
|
+
*/
|
|
536
|
+
style?: React.CSSProperties;
|
|
537
|
+
fullWidth?: boolean;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* A searchable single-select.
|
|
541
|
+
*
|
|
542
|
+
* The fleet has roughly twenty of these — PartPicker, ProgramPicker,
|
|
543
|
+
* PlantPicker, CustomerPicker, OemPicker, four in cpq alone — and each one
|
|
544
|
+
* re-solves the same four problems: filter as you type, keep the arrow keys
|
|
545
|
+
* working, announce the highlighted row, and not blow up on a list too long to
|
|
546
|
+
* hold in the browser. This is that component with no domain knowledge in it;
|
|
547
|
+
* the domain pickers become thin wrappers that supply `options` and
|
|
548
|
+
* `onSearchChange`.
|
|
549
|
+
*
|
|
550
|
+
* Filtering is client-side by default and matches the label *and* the hint, so
|
|
551
|
+
* typing a part number finds a row whose label is a description. Pass
|
|
552
|
+
* `onSearchChange` to filter on the server instead.
|
|
553
|
+
*
|
|
554
|
+
* Keyboard: ArrowUp/ArrowDown move, Home/End jump, Enter selects, Escape
|
|
555
|
+
* closes. The list is `aria-activedescendant`-driven, so focus stays in the
|
|
556
|
+
* search field and a screen reader announces each row as it is highlighted.
|
|
557
|
+
*/
|
|
558
|
+
declare function Combobox<T extends string = string>({ options, value, onValueChange, placeholder, searchPlaceholder, onSearchChange, loading, emptyLabel, loadingLabel, clearable, size, invalid, disabled, id, name, className, style, fullWidth, ...aria }: ComboboxProps<T>): react.JSX.Element;
|
|
559
|
+
|
|
560
|
+
declare const TooltipProvider: react.FC<RadixTooltip.TooltipProviderProps>;
|
|
561
|
+
declare const TooltipRoot: react.FC<RadixTooltip.TooltipProps>;
|
|
562
|
+
declare const TooltipTrigger: react.ForwardRefExoticComponent<RadixTooltip.TooltipTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
563
|
+
interface TooltipContentProps extends React.ComponentPropsWithoutRef<typeof RadixTooltip.Content> {
|
|
564
|
+
}
|
|
565
|
+
declare const TooltipContent: react.ForwardRefExoticComponent<TooltipContentProps & react.RefAttributes<HTMLDivElement>>;
|
|
566
|
+
interface TooltipProps {
|
|
567
|
+
/** The tooltip text. Keep it short — this is a hint, not documentation. */
|
|
568
|
+
content: React.ReactNode;
|
|
569
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
570
|
+
align?: 'start' | 'center' | 'end';
|
|
571
|
+
/** Milliseconds before it appears. */
|
|
572
|
+
delay?: number;
|
|
573
|
+
/** Renders nothing but the child when false — for conditionally-hinted controls. */
|
|
574
|
+
disabled?: boolean;
|
|
575
|
+
children: React.ReactNode;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* A hover/focus hint.
|
|
579
|
+
*
|
|
580
|
+
* Twelve apps show tooltips and most of them use a bare `title=` attribute,
|
|
581
|
+
* which never appears on keyboard focus and never appears on touch. This one
|
|
582
|
+
* does both, via Radix.
|
|
583
|
+
*
|
|
584
|
+
* It carries its own `Provider`, so a single `<Tooltip>` works with no app-level
|
|
585
|
+
* setup. Wrapping the app in `TooltipProvider` is still worth doing — it shares
|
|
586
|
+
* the "one tooltip has already been shown, skip the delay" timer across the
|
|
587
|
+
* screen, which is what makes a toolbar feel responsive rather than sticky.
|
|
588
|
+
*
|
|
589
|
+
* A tooltip is never the only place information lives: it is unreachable on
|
|
590
|
+
* touch devices past the first tap, so anything required to complete a task
|
|
591
|
+
* belongs on the page.
|
|
592
|
+
*/
|
|
593
|
+
declare function Tooltip({ content, side, align, delay, disabled, children }: TooltipProps): react.JSX.Element;
|
|
594
|
+
|
|
595
|
+
type ToastTone = 'neutral' | 'success' | 'warning' | 'error' | 'info';
|
|
596
|
+
interface ToastOptions {
|
|
597
|
+
title: React.ReactNode;
|
|
598
|
+
/** Second line. Put the detail here — what failed, or what to do next. */
|
|
599
|
+
description?: React.ReactNode;
|
|
600
|
+
tone?: ToastTone;
|
|
601
|
+
/**
|
|
602
|
+
* Auto-dismiss delay in ms. `null` keeps it up until dismissed — the right
|
|
603
|
+
* choice for an error the user has to act on, and the reason this is not a
|
|
604
|
+
* fixed duration.
|
|
605
|
+
*/
|
|
606
|
+
duration?: number | null;
|
|
607
|
+
/** A single follow-up action, e.g. Undo or Retry. */
|
|
608
|
+
action?: {
|
|
609
|
+
label: string;
|
|
610
|
+
onClick: () => void;
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
interface ToastApi {
|
|
614
|
+
/** Shows a toast and returns its id, for dismissing it early. */
|
|
615
|
+
toast: (options: ToastOptions) => number;
|
|
616
|
+
success: (title: React.ReactNode, description?: React.ReactNode) => number;
|
|
617
|
+
error: (title: React.ReactNode, description?: React.ReactNode) => number;
|
|
618
|
+
warning: (title: React.ReactNode, description?: React.ReactNode) => number;
|
|
619
|
+
info: (title: React.ReactNode, description?: React.ReactNode) => number;
|
|
620
|
+
dismiss: (id: number) => void;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Reads the toast API.
|
|
624
|
+
*
|
|
625
|
+
* Throws when there is no `ToastProvider` above it, rather than silently doing
|
|
626
|
+
* nothing — a toast that never appears is the kind of failure that ships,
|
|
627
|
+
* because the happy path looks identical.
|
|
628
|
+
*/
|
|
629
|
+
declare function useToast(): ToastApi;
|
|
630
|
+
interface ToastProviderProps {
|
|
631
|
+
children: React.ReactNode;
|
|
632
|
+
/** Default auto-dismiss for toasts that do not set their own. */
|
|
633
|
+
defaultDuration?: number;
|
|
634
|
+
/** Beyond this many, the oldest is dropped. A stack taller than the viewport is unusable. */
|
|
635
|
+
max?: number;
|
|
636
|
+
position?: 'top-right' | 'top-center' | 'bottom-right' | 'bottom-center';
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Hosts the toast stack. Mount once, near the root.
|
|
640
|
+
*
|
|
641
|
+
* Five apps depend on `sonner` for this. It is a couple of hundred lines with
|
|
642
|
+
* no dependency, and keeping it here means the toast follows the design tokens
|
|
643
|
+
* and dark mode like everything else in the package rather than carrying its
|
|
644
|
+
* own theme.
|
|
645
|
+
*
|
|
646
|
+
* Errors default to staying up until dismissed. An error that vanishes after
|
|
647
|
+
* four seconds is an error the user never read.
|
|
648
|
+
*/
|
|
649
|
+
declare function ToastProvider({ children, defaultDuration, max, position, }: ToastProviderProps): react.JSX.Element;
|
|
650
|
+
|
|
651
|
+
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
652
|
+
/**
|
|
653
|
+
* `plain` drops the border and shadow. Border, fill and shadow each say
|
|
654
|
+
* "separate object", so a card inside a card inside a panel flattens the
|
|
655
|
+
* hierarchy instead of building one — use `plain` for the inner one.
|
|
656
|
+
*/
|
|
657
|
+
variant?: 'default' | 'plain' | 'outline';
|
|
658
|
+
/** Removes the body padding, for a card whose content is a full-bleed table. */
|
|
659
|
+
flush?: boolean;
|
|
660
|
+
/** Adds hover feedback and a pointer. Set this only when the whole card is clickable. */
|
|
661
|
+
interactive?: boolean;
|
|
662
|
+
}
|
|
663
|
+
declare const Card: react.ForwardRefExoticComponent<CardProps & react.RefAttributes<HTMLDivElement>>;
|
|
664
|
+
interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
665
|
+
/** Right-aligned controls — a menu, a filter, a link out. */
|
|
666
|
+
actions?: React.ReactNode;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* The `__heading` wrapper exists ONLY to group a title with its description so
|
|
670
|
+
* `actions` can sit opposite the pair. Without `actions` there is nothing to sit
|
|
671
|
+
* opposite, and wrapping anyway is actively harmful: the header is a flex row
|
|
672
|
+
* with `space-between`, so an inner box collapses every child into one
|
|
673
|
+
* content-width column and silently kills any `ml-auto` or `justify-between` the
|
|
674
|
+
* caller wrote. pm hit exactly that — a "Record a period" button that should
|
|
675
|
+
* have been hard right sat next to the title inside a 144px grid.
|
|
676
|
+
*
|
|
677
|
+
* Every vendored shadcn `CardHeader` in the fleet renders children directly, so
|
|
678
|
+
* passing a title and a button as siblings is the shape callers already have.
|
|
679
|
+
* Honouring it costs nothing and the wrapper is still there when `actions` asks
|
|
680
|
+
* for it.
|
|
681
|
+
*/
|
|
682
|
+
declare function CardHeader({ actions, className, children, ...rest }: CardHeaderProps): react.JSX.Element;
|
|
683
|
+
interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
684
|
+
/**
|
|
685
|
+
* The heading level. Defaults to `h3` because a card is almost never the
|
|
686
|
+
* page's top-level heading — the five PageHeaders in the fleet variously
|
|
687
|
+
* used h1 and h2, and cards under them used whatever was left.
|
|
688
|
+
*/
|
|
689
|
+
as?: 'h2' | 'h3' | 'h4';
|
|
690
|
+
}
|
|
691
|
+
declare function CardTitle({ as: Tag, className, ...rest }: CardTitleProps): react.JSX.Element;
|
|
692
|
+
declare function CardDescription({ className, ...rest }: React.HTMLAttributes<HTMLParagraphElement>): react.JSX.Element;
|
|
693
|
+
declare function CardBody({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
694
|
+
declare function CardFooter({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
695
|
+
|
|
696
|
+
type BadgeTone = 'neutral' | 'success' | 'warning' | 'error' | 'info' | 'accent';
|
|
697
|
+
interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
698
|
+
tone?: BadgeTone;
|
|
699
|
+
/** `solid` is for the rare badge that must win against a busy row. Default is the soft fill. */
|
|
700
|
+
variant?: 'soft' | 'solid' | 'outline';
|
|
701
|
+
size?: 'sm' | 'md';
|
|
702
|
+
/**
|
|
703
|
+
* Draws a dot before the label. Worth it wherever tone carries meaning: the
|
|
704
|
+
* dot's presence plus the word survives greyscale printing and any color
|
|
705
|
+
* vision deficiency, which the fill alone does not.
|
|
706
|
+
*/
|
|
707
|
+
dot?: boolean;
|
|
708
|
+
/** Rendered before the label, in place of the dot. */
|
|
709
|
+
icon?: React.ReactNode;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* A small status or category label.
|
|
713
|
+
*
|
|
714
|
+
* Six apps vendored a `badge` and four more wrapped it as a `StatusBadge`,
|
|
715
|
+
* `StatusPill` or `StatusChip` with four unrelated prop shapes. The soft fill
|
|
716
|
+
* uses the token pair (`-soft` background, `-text` foreground) rather than the
|
|
717
|
+
* `bg-[#10B981]/10 text-[#10B981]` pattern found across the fleet, which puts
|
|
718
|
+
* a mid-tone on a 10%-alpha wash of itself and lands near 2:1 contrast.
|
|
719
|
+
*
|
|
720
|
+
* This is the primitive. Mapping a domain enum to a tone and a label is a
|
|
721
|
+
* `StatusBadge` in ui-patterns, so the enum stays out of the design system.
|
|
722
|
+
*/
|
|
723
|
+
declare const Badge: react.ForwardRefExoticComponent<BadgeProps & react.RefAttributes<HTMLSpanElement>>;
|
|
724
|
+
interface CountBadgeProps extends Omit<BadgeProps, 'children' | 'dot' | 'icon'> {
|
|
725
|
+
count: number;
|
|
726
|
+
/** Renders as "99+" past this. */
|
|
727
|
+
max?: number;
|
|
728
|
+
/** Hides the badge at zero instead of showing "0". Default true. */
|
|
729
|
+
hideZero?: boolean;
|
|
730
|
+
}
|
|
731
|
+
/** A numeric count — unread items, filters applied, rows selected. */
|
|
732
|
+
declare const CountBadge: react.ForwardRefExoticComponent<CountBadgeProps & react.RefAttributes<HTMLSpanElement>>;
|
|
733
|
+
|
|
734
|
+
interface TableProps extends React.TableHTMLAttributes<HTMLTableElement> {
|
|
735
|
+
/** Tightens row height for dense data screens. */
|
|
736
|
+
density?: 'comfortable' | 'compact';
|
|
737
|
+
/** Keeps the header visible while the body scrolls. Requires a scrolling ancestor with a height. */
|
|
738
|
+
stickyHeader?: boolean;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* A data table.
|
|
742
|
+
*
|
|
743
|
+
* Wrap it in `TableScroller`, always. A wide table that widens the page body
|
|
744
|
+
* instead of scrolling in its own box is the single most common layout defect
|
|
745
|
+
* in the fleet, and it is invisible until someone opens the screen on a laptop.
|
|
746
|
+
*/
|
|
747
|
+
declare const Table: react.ForwardRefExoticComponent<TableProps & react.RefAttributes<HTMLTableElement>>;
|
|
748
|
+
/** The horizontal scroll container. Keeps a wide table inside its own box. */
|
|
749
|
+
declare function TableScroller({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
750
|
+
/**
|
|
751
|
+
* The `<thead>`.
|
|
752
|
+
*
|
|
753
|
+
* Named `TableHeader` to match the convention the seven Tailwind apps already
|
|
754
|
+
* use across ~1,300 call sites — and, more importantly, so that `TableHead`
|
|
755
|
+
* can mean what it means everywhere else: a single `<th>`. Having those two
|
|
756
|
+
* names swapped relative to every existing call site is a silent bug, not a
|
|
757
|
+
* naming preference: the wrong one produces a `<thead>` where a `<th>` belongs
|
|
758
|
+
* and the table still renders, just wrongly.
|
|
759
|
+
*/
|
|
760
|
+
declare function TableHeader({ className, ...rest }: React.HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
|
|
761
|
+
declare function TableBody({ className, ...rest }: React.HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
|
|
762
|
+
declare function TableFooter({ className, ...rest }: React.HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
|
|
763
|
+
/** @deprecated Use `TableFooter`. Kept so the rename is not a breaking change. */
|
|
764
|
+
declare const TableFoot: typeof TableFooter;
|
|
765
|
+
interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
766
|
+
selected?: boolean;
|
|
767
|
+
/** Adds hover feedback. Set it when the row itself navigates or opens something. */
|
|
768
|
+
interactive?: boolean;
|
|
769
|
+
}
|
|
770
|
+
declare function TableRow({ selected, interactive, className, ...rest }: TableRowProps): react.JSX.Element;
|
|
771
|
+
type SortDirection = 'asc' | 'desc';
|
|
772
|
+
interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
773
|
+
/** Right-aligns and tabular-aligns the column. Use for every numeric column. */
|
|
774
|
+
numeric?: boolean;
|
|
775
|
+
/**
|
|
776
|
+
* Makes the header a sort control. Pass the current direction, or `null` when
|
|
777
|
+
* this column is not the active sort.
|
|
778
|
+
*/
|
|
779
|
+
sortable?: boolean;
|
|
780
|
+
sortDirection?: SortDirection | null;
|
|
781
|
+
onSort?: () => void;
|
|
782
|
+
/** Fixes the column width — useful for an actions or checkbox column. */
|
|
783
|
+
width?: string | number;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* A header cell, optionally sortable.
|
|
787
|
+
*
|
|
788
|
+
* Eight apps have column sorting and each one built its own indicator; several
|
|
789
|
+
* render the arrow with no `aria-sort`, so the sort state is invisible to a
|
|
790
|
+
* screen reader. This sets `aria-sort` from the same prop that draws the arrow,
|
|
791
|
+
* so they cannot disagree.
|
|
792
|
+
*/
|
|
793
|
+
declare function TableHead({ numeric, sortable, sortDirection, onSort, width, className, children, style, ...rest }: TableHeadProps): react.JSX.Element;
|
|
794
|
+
interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
795
|
+
numeric?: boolean;
|
|
796
|
+
/** Truncates with an ellipsis instead of wrapping. Pair it with a `title`. */
|
|
797
|
+
truncate?: boolean;
|
|
798
|
+
}
|
|
799
|
+
declare function TableCell({ numeric, truncate, className, ...rest }: TableCellProps): react.JSX.Element;
|
|
800
|
+
interface TableEmptyProps {
|
|
801
|
+
/** Must match the table's column count, or the message will not span the width. */
|
|
802
|
+
colSpan: number;
|
|
803
|
+
children: React.ReactNode;
|
|
804
|
+
}
|
|
805
|
+
/** A full-width row for the empty case. */
|
|
806
|
+
declare function TableEmpty({ colSpan, children }: TableEmptyProps): react.JSX.Element;
|
|
807
|
+
declare function TableCaption({ className, ...rest }: React.HTMLAttributes<HTMLTableCaptionElement>): react.JSX.Element;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* The root. Same reasoning as `Dialog`: every existing call site in the fleet
|
|
811
|
+
* uses `<Tabs value=... onValueChange=...>` as the root, so the bare name has
|
|
812
|
+
* to mean that. The one-component convenience form is `TabSet` below.
|
|
813
|
+
*/
|
|
814
|
+
declare const Tabs: react.ForwardRefExoticComponent<RadixTabs.TabsProps & react.RefAttributes<HTMLDivElement>>;
|
|
815
|
+
/** @deprecated Use `Tabs`. Kept so existing imports keep working. */
|
|
816
|
+
declare const TabsRoot: react.ForwardRefExoticComponent<RadixTabs.TabsProps & react.RefAttributes<HTMLDivElement>>;
|
|
817
|
+
interface TabsListProps extends React.ComponentPropsWithoutRef<typeof RadixTabs.List> {
|
|
818
|
+
/**
|
|
819
|
+
* `pill` is a segmented control — a rounded tray with the active tab raised.
|
|
820
|
+
* `underline` is the page-level look: a row of tabs under a heading with the
|
|
821
|
+
* active one underscored.
|
|
822
|
+
*
|
|
823
|
+
* Defaults to `pill` because that is what the vendored shadcn TabsList
|
|
824
|
+
* rendered, and eleven apps use `<TabsList>` bare. Defaulting to `underline`
|
|
825
|
+
* would restyle every tab row in the fleet on adoption — a design decision
|
|
826
|
+
* dressed up as a migration. Opt into `underline` per call site.
|
|
827
|
+
*/
|
|
828
|
+
variant?: 'pill' | 'underline';
|
|
829
|
+
}
|
|
830
|
+
declare const TabsList: react.ForwardRefExoticComponent<TabsListProps & react.RefAttributes<HTMLDivElement>>;
|
|
831
|
+
interface TabsTriggerProps extends React.ComponentPropsWithoutRef<typeof RadixTabs.Trigger> {
|
|
832
|
+
icon?: React.ReactNode;
|
|
833
|
+
/** A count beside the label — rows in the tab, unread items. Hidden at zero. */
|
|
834
|
+
count?: number;
|
|
835
|
+
}
|
|
836
|
+
declare const TabsTrigger: react.ForwardRefExoticComponent<TabsTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
837
|
+
declare const TabsContent: react.ForwardRefExoticComponent<Omit<RadixTabs.TabsContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
838
|
+
interface TabDefinition {
|
|
839
|
+
value: string;
|
|
840
|
+
label: React.ReactNode;
|
|
841
|
+
icon?: React.ReactNode;
|
|
842
|
+
count?: number;
|
|
843
|
+
disabled?: boolean;
|
|
844
|
+
content: React.ReactNode;
|
|
845
|
+
}
|
|
846
|
+
interface TabSetProps {
|
|
847
|
+
tabs: TabDefinition[];
|
|
848
|
+
value?: string;
|
|
849
|
+
onValueChange?: (value: string) => void;
|
|
850
|
+
defaultValue?: string;
|
|
851
|
+
variant?: 'pill' | 'underline';
|
|
852
|
+
className?: string;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* A tab set.
|
|
856
|
+
*
|
|
857
|
+
* Eleven apps have page-level tabs and every one wires the list and the panels
|
|
858
|
+
* by hand, which is how a tab ends up with a trigger and no panel. Passing the
|
|
859
|
+
* pair together makes that impossible.
|
|
860
|
+
*
|
|
861
|
+
* `defaultValue` falls back to the first tab: Radix renders no panel at all
|
|
862
|
+
* when neither is set, which reads as a blank screen.
|
|
863
|
+
*/
|
|
864
|
+
declare function TabSet({ tabs, value, onValueChange, defaultValue, variant, className }: TabSetProps): react.JSX.Element;
|
|
865
|
+
|
|
866
|
+
interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
867
|
+
orientation?: 'horizontal' | 'vertical';
|
|
868
|
+
/** Optional centered label — "or", "Advanced", a date divider in a feed. */
|
|
869
|
+
label?: React.ReactNode;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* A rule.
|
|
873
|
+
*
|
|
874
|
+
* Radix's separator package is one `<div role="separator">`, so this is a plain
|
|
875
|
+
* element — five apps installed a dependency for it. `aria-orientation` is only
|
|
876
|
+
* meaningful on a real separator role, and a labeled one is a group heading
|
|
877
|
+
* rather than a separator, so it drops the role.
|
|
878
|
+
*/
|
|
879
|
+
declare function Separator({ orientation, label, className, ...rest }: SeparatorProps): react.JSX.Element;
|
|
880
|
+
interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
881
|
+
/** 0–100. Pass `null` for an indeterminate bar. */
|
|
882
|
+
value: number | null;
|
|
883
|
+
/** Overrides the accent fill. Use a semantic tone when the bar means "how bad is it". */
|
|
884
|
+
tone?: 'accent' | 'success' | 'warning' | 'error';
|
|
885
|
+
size?: 'sm' | 'md';
|
|
886
|
+
/** Accessible name. Required — "62%" alone does not say 62% of what. */
|
|
887
|
+
label: string;
|
|
888
|
+
/** Renders the percentage beside the bar. */
|
|
889
|
+
showValue?: boolean;
|
|
890
|
+
}
|
|
891
|
+
/** A determinate or indeterminate progress bar. */
|
|
892
|
+
declare function Progress({ value, tone, size, label, showValue, className, ...rest }: ProgressProps): react.JSX.Element;
|
|
893
|
+
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
894
|
+
/** `text` sizes itself to a line of body copy; `circle` for an avatar slot. */
|
|
895
|
+
shape?: 'text' | 'block' | 'circle';
|
|
896
|
+
width?: string | number;
|
|
897
|
+
height?: string | number;
|
|
898
|
+
/** Number of text lines. The last one is shortened, the way real text ends mid-line. */
|
|
899
|
+
lines?: number;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* A loading placeholder.
|
|
903
|
+
*
|
|
904
|
+
* Marked `aria-hidden` with the live region left to the caller: a screen reader
|
|
905
|
+
* announcing eight shimmering rectangles is worse than silence. Put one
|
|
906
|
+
* "Loading parts" status message on the region instead.
|
|
907
|
+
*/
|
|
908
|
+
declare function Skeleton({ shape, width, height, lines, className, style, ...rest }: SkeletonProps): react.JSX.Element;
|
|
909
|
+
interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
910
|
+
/** Full name. Drives the initials and the accessible label. */
|
|
911
|
+
name: string;
|
|
912
|
+
src?: string | null;
|
|
913
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* A user avatar with an initials fallback.
|
|
917
|
+
*
|
|
918
|
+
* Four apps vendored Radix's avatar for what is an image with a fallback. The
|
|
919
|
+
* initials are derived from the name rather than passed separately, so they
|
|
920
|
+
* cannot go stale against it.
|
|
921
|
+
*/
|
|
922
|
+
declare const Avatar: react.ForwardRefExoticComponent<AvatarProps & react.RefAttributes<HTMLSpanElement>>;
|
|
923
|
+
interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
924
|
+
/** Beyond this many, the rest collapse into a "+n" chip. */
|
|
925
|
+
max?: number;
|
|
926
|
+
children: React.ReactNode;
|
|
927
|
+
}
|
|
928
|
+
/** Overlapping avatars, for an assignee list or an approval chain. */
|
|
929
|
+
declare function AvatarGroup({ max, className, children, ...rest }: AvatarGroupProps): react.JSX.Element;
|
|
930
|
+
interface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
931
|
+
/** Caps the height so the content scrolls rather than the page. */
|
|
932
|
+
maxHeight?: string | number;
|
|
933
|
+
orientation?: 'vertical' | 'horizontal' | 'both';
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* A scrolling region with a styled scrollbar.
|
|
937
|
+
*
|
|
938
|
+
* Native overflow plus `scrollbar-width`/`scrollbar-color`, which every target
|
|
939
|
+
* browser now supports — six apps pulled in Radix's scroll-area, which
|
|
940
|
+
* replaces the scrollbar with divs and takes the keyboard's Page Up/Down and
|
|
941
|
+
* the platform's overscroll behavior with it.
|
|
942
|
+
*/
|
|
943
|
+
declare function ScrollArea({ maxHeight, orientation, className, style, ...rest }: ScrollAreaProps): react.JSX.Element;
|
|
944
|
+
|
|
945
|
+
declare const AccordionRoot: react.ForwardRefExoticComponent<(RadixAccordion.AccordionSingleProps | RadixAccordion.AccordionMultipleProps) & react.RefAttributes<HTMLDivElement>>;
|
|
946
|
+
declare const AccordionItem: react.ForwardRefExoticComponent<Omit<RadixAccordion.AccordionItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
947
|
+
interface AccordionTriggerProps extends React.ComponentPropsWithoutRef<typeof RadixAccordion.Trigger> {
|
|
948
|
+
/** Right-aligned summary shown while collapsed — a count, a total, a status. */
|
|
949
|
+
meta?: React.ReactNode;
|
|
950
|
+
}
|
|
951
|
+
declare const AccordionTrigger: react.ForwardRefExoticComponent<AccordionTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
952
|
+
declare const AccordionContent: react.ForwardRefExoticComponent<Omit<RadixAccordion.AccordionContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
953
|
+
interface AccordionSection {
|
|
954
|
+
value: string;
|
|
955
|
+
label: React.ReactNode;
|
|
956
|
+
meta?: React.ReactNode;
|
|
957
|
+
disabled?: boolean;
|
|
958
|
+
content: React.ReactNode;
|
|
959
|
+
}
|
|
960
|
+
interface AccordionProps {
|
|
961
|
+
sections: AccordionSection[];
|
|
962
|
+
/** `multiple` lets several stay open. Default, since collapsing one to read another loses your place. */
|
|
963
|
+
type?: 'single' | 'multiple';
|
|
964
|
+
defaultValue?: string | string[];
|
|
965
|
+
value?: string | string[];
|
|
966
|
+
onValueChange?: (value: string | string[]) => void;
|
|
967
|
+
className?: string;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* A collapsible section list.
|
|
971
|
+
*
|
|
972
|
+
* Defaults to `multiple`: with `single`, opening one section closes the one you
|
|
973
|
+
* were comparing it against, which is exactly what these are used for on the
|
|
974
|
+
* cost and feasibility screens.
|
|
975
|
+
*/
|
|
976
|
+
declare function Accordion({ sections, type, defaultValue, value, onValueChange, className, }: AccordionProps): react.JSX.Element;
|
|
977
|
+
interface ToggleOption<T extends string = string> {
|
|
978
|
+
value: T;
|
|
979
|
+
label: React.ReactNode;
|
|
980
|
+
icon?: React.ReactNode;
|
|
981
|
+
disabled?: boolean;
|
|
982
|
+
/** Accessible name for an icon-only option. Required when there is no label. */
|
|
983
|
+
ariaLabel?: string;
|
|
984
|
+
}
|
|
985
|
+
interface ToggleGroupProps<T extends string = string> {
|
|
986
|
+
options: ToggleOption<T>[];
|
|
987
|
+
value?: T;
|
|
988
|
+
onValueChange?: (value: T) => void;
|
|
989
|
+
size?: 'sm' | 'md';
|
|
990
|
+
/**
|
|
991
|
+
* Allows deselecting the active option, leaving nothing selected. Off by
|
|
992
|
+
* default — a view switcher with no view selected shows nothing.
|
|
993
|
+
*/
|
|
994
|
+
allowEmpty?: boolean;
|
|
995
|
+
className?: string;
|
|
996
|
+
'aria-label': string;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* A segmented single-choice control — a view switcher, a unit toggle, a
|
|
1000
|
+
* density picker.
|
|
1001
|
+
*
|
|
1002
|
+
* Clicking the active option does nothing unless `allowEmpty` is set. Radix
|
|
1003
|
+
* clears the value by default, which is how a chart switcher ends up rendering
|
|
1004
|
+
* an empty panel.
|
|
1005
|
+
*/
|
|
1006
|
+
declare function ToggleGroup<T extends string = string>({ options, value, onValueChange, size, allowEmpty, className, ...aria }: ToggleGroupProps<T>): react.JSX.Element;
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* The states every data-backed screen has to render.
|
|
1010
|
+
*
|
|
1011
|
+
* These are the ones that get bolted on last and inconsistently — one page
|
|
1012
|
+
* spins, another goes blank, a third prints a raw error object — and by the
|
|
1013
|
+
* time anyone notices, nobody wants to unify them. The audit found some form
|
|
1014
|
+
* of these in twelve frontends, four of them byte-identical.
|
|
1015
|
+
*
|
|
1016
|
+
* Deliberately NOT i18n-aware. This package has no translation runtime and
|
|
1017
|
+
* should not acquire one: an app that has i18n passes already-translated
|
|
1018
|
+
* strings, and an app that does not gets sensible English. Every user-visible
|
|
1019
|
+
* string here is therefore a prop with a default.
|
|
1020
|
+
*/
|
|
1021
|
+
interface LoadingStateProps {
|
|
1022
|
+
/** Defaults to "Loading…". Pass a translated string in an i18n app. */
|
|
1023
|
+
label?: string;
|
|
1024
|
+
/** Vertical breathing room. `inline` for a state inside a card or a table cell. */
|
|
1025
|
+
size?: 'inline' | 'block';
|
|
1026
|
+
className?: string;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* `role="status"` with `aria-live="polite"`, so a screen reader announces the
|
|
1030
|
+
* wait instead of going silent while the page sits empty.
|
|
1031
|
+
*/
|
|
1032
|
+
declare function LoadingState({ label, size, className }: LoadingStateProps): react.JSX.Element;
|
|
1033
|
+
interface ErrorStateProps {
|
|
1034
|
+
/** An Error, or anything thrown. Only its message is rendered. */
|
|
1035
|
+
error?: unknown;
|
|
1036
|
+
title?: string;
|
|
1037
|
+
/** Renders a retry control when given. */
|
|
1038
|
+
onRetry?: () => void;
|
|
1039
|
+
retryLabel?: string;
|
|
1040
|
+
size?: 'inline' | 'block';
|
|
1041
|
+
className?: string;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* `role="alert"`, because a failure that only appears visually is a failure a
|
|
1045
|
+
* screen-reader user finds by waiting.
|
|
1046
|
+
*
|
|
1047
|
+
* Renders the message, never the object: a rendered `[object Object]` tells
|
|
1048
|
+
* the user nothing and tells support less.
|
|
1049
|
+
*/
|
|
1050
|
+
declare function ErrorState({ error, title, onRetry, retryLabel, size, className, }: ErrorStateProps): react.JSX.Element;
|
|
1051
|
+
interface EmptyStateProps {
|
|
1052
|
+
title: string;
|
|
1053
|
+
description?: string;
|
|
1054
|
+
/** Usually a Button that creates the first one. */
|
|
1055
|
+
action?: React.ReactNode;
|
|
1056
|
+
icon?: React.ReactNode;
|
|
1057
|
+
size?: 'inline' | 'block';
|
|
1058
|
+
className?: string;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Nothing here yet — and say why, not just that.
|
|
1062
|
+
*
|
|
1063
|
+
* An empty state with no `description` is a blank page with a heading on it;
|
|
1064
|
+
* the description is where "no quotes match these filters" becomes "clear the
|
|
1065
|
+
* OEM filter to see the other 40".
|
|
1066
|
+
*/
|
|
1067
|
+
declare function EmptyState({ title, description, action, icon, size, className, }: EmptyStateProps): react.JSX.Element;
|
|
1068
|
+
interface ComingSoonProps {
|
|
1069
|
+
title: string;
|
|
1070
|
+
/** What it is waiting on — a phase, a release, a decision. */
|
|
1071
|
+
detail?: string;
|
|
1072
|
+
className?: string;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* A screen that exists in the design but not yet in the product.
|
|
1076
|
+
*
|
|
1077
|
+
* Distinct from `EmptyState`, which means "you have none of these yet". This
|
|
1078
|
+
* means "this is not built", and saying so is the house rule: a half-page of
|
|
1079
|
+
* invented figures is worse than an honest gap.
|
|
1080
|
+
*/
|
|
1081
|
+
declare function ComingSoon({ title, detail, className }: ComingSoonProps): react.JSX.Element;
|
|
1082
|
+
|
|
1083
|
+
interface SortState<K extends string> {
|
|
1084
|
+
key: K;
|
|
1085
|
+
direction: SortDirection;
|
|
1086
|
+
}
|
|
1087
|
+
interface UseSortStateResult<K extends string> {
|
|
1088
|
+
sort: SortState<K>;
|
|
1089
|
+
/** Toggles direction when the same column is clicked, otherwise selects it. */
|
|
1090
|
+
toggle: (key: K) => void;
|
|
1091
|
+
setSort: (sort: SortState<K>) => void;
|
|
1092
|
+
/**
|
|
1093
|
+
* Spread onto a `TableHead` — it supplies `sortable`, `sortDirection` and
|
|
1094
|
+
* `onSort` in one, so a header cell is `<TableHead {...sortProps('name')}>`
|
|
1095
|
+
* and the `aria-sort` follows automatically.
|
|
1096
|
+
*/
|
|
1097
|
+
sortProps: (key: K) => {
|
|
1098
|
+
sortable: true;
|
|
1099
|
+
sortDirection: SortDirection | null;
|
|
1100
|
+
onSort: () => void;
|
|
1101
|
+
};
|
|
1102
|
+
/** Sorts a list by the active column. Pass a value accessor per key. */
|
|
1103
|
+
sortBy: <T>(rows: T[], accessor: (row: T, key: K) => string | number | Date | null | undefined) => T[];
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Column sort state, which eight apps hand-write and six pm files did.
|
|
1107
|
+
*
|
|
1108
|
+
* The state itself is three lines; what people get wrong is the rest. A fresh
|
|
1109
|
+
* column starts ASCENDING rather than inheriting the last column's direction,
|
|
1110
|
+
* because "sort by owner" meaning descending-because-the-previous-column-was
|
|
1111
|
+
* is a surprise every time. `sortProps` exists so the `aria-sort` cannot drift
|
|
1112
|
+
* from the arrow — the shared `TableHead` derives both from the same prop, and
|
|
1113
|
+
* the common bug is an app that draws an arrow and tells a screen reader
|
|
1114
|
+
* nothing.
|
|
1115
|
+
*
|
|
1116
|
+
* `sortBy` is a convenience for the in-memory case and is deliberately
|
|
1117
|
+
* boring: a stable comparator over strings, numbers and dates, nullish last
|
|
1118
|
+
* in both directions. Server-side sorting ignores it and reads `sort` instead.
|
|
1119
|
+
*/
|
|
1120
|
+
declare function useSortState<K extends string>(initialKey: K, initialDirection?: SortDirection): UseSortStateResult<K>;
|
|
1121
|
+
|
|
1122
|
+
interface SearchInputProps extends Omit<InputProps, 'value' | 'onChange' | 'prefix' | 'suffix' | 'type'> {
|
|
1123
|
+
value: string;
|
|
1124
|
+
/**
|
|
1125
|
+
* Fires after `delay`, not on every keystroke. The input itself stays
|
|
1126
|
+
* responsive — it is controlled locally and only the callback is debounced.
|
|
1127
|
+
*/
|
|
1128
|
+
onChange: (value: string) => void;
|
|
1129
|
+
/** Milliseconds. 0 disables debouncing. */
|
|
1130
|
+
delay?: number;
|
|
1131
|
+
placeholder?: string;
|
|
1132
|
+
/** Label for the clear button, and for screen readers. */
|
|
1133
|
+
clearLabel?: string;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* A search field: magnifier, debounce, and a clear button once there is
|
|
1137
|
+
* something to clear. Five apps wrote their own.
|
|
1138
|
+
*
|
|
1139
|
+
* THE DEBOUNCE IS ON THE CALLBACK, NOT THE VALUE. The obvious implementation
|
|
1140
|
+
* debounces the controlled `value`, which makes the field itself lag behind
|
|
1141
|
+
* typing — characters appear late, and the caret jumps if the parent
|
|
1142
|
+
* re-renders mid-word. This keeps the DOM value local and immediate, and only
|
|
1143
|
+
* delays telling the parent.
|
|
1144
|
+
*
|
|
1145
|
+
* The parent stays the source of truth: if `value` changes from outside — a
|
|
1146
|
+
* cleared filter set, a restored URL — the field follows.
|
|
1147
|
+
*/
|
|
1148
|
+
declare const SearchInput: react.ForwardRefExoticComponent<SearchInputProps & react.RefAttributes<HTMLInputElement>>;
|
|
1149
|
+
|
|
1150
|
+
interface PaginationProps {
|
|
1151
|
+
/** 1-based. A 0-based page index is the single most common bug here. */
|
|
1152
|
+
page: number;
|
|
1153
|
+
pageSize: number;
|
|
1154
|
+
totalItems: number;
|
|
1155
|
+
onPageChange: (page: number) => void;
|
|
1156
|
+
/** Renders "1–25 of 312". Off for a compact toolbar. */
|
|
1157
|
+
showRange?: boolean;
|
|
1158
|
+
labels?: {
|
|
1159
|
+
previous?: string;
|
|
1160
|
+
next?: string;
|
|
1161
|
+
range?: (from: number, to: number, total: number) => string;
|
|
1162
|
+
};
|
|
1163
|
+
className?: string;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Page controls for a table. Seven apps hand-rolled these.
|
|
1167
|
+
*
|
|
1168
|
+
* PAGE NUMBERS ARE 1-BASED, and the prop is named `page` rather than `index`
|
|
1169
|
+
* to say so — an off-by-one here shows up as a silently missing first row,
|
|
1170
|
+
* which is the kind of bug that survives review.
|
|
1171
|
+
*
|
|
1172
|
+
* The range readout is the part people skip and the part users need: "1–25 of
|
|
1173
|
+
* 312" answers "is it worth paging" in a way that bare arrows never do. It
|
|
1174
|
+
* counts from the real total, so the last page reads "301–312 of 312" rather
|
|
1175
|
+
* than "301–325".
|
|
1176
|
+
*
|
|
1177
|
+
* Renders nothing when everything fits on one page. A pager under a four-row
|
|
1178
|
+
* table is furniture.
|
|
1179
|
+
*/
|
|
1180
|
+
declare function Pagination({ page, pageSize, totalItems, onPageChange, showRange, labels, className, }: PaginationProps): react.JSX.Element | null;
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Date and money formatting, because eleven apps call `toLocaleDateString`
|
|
1184
|
+
* raw and two wrote their own `formatMoney`.
|
|
1185
|
+
*
|
|
1186
|
+
* The point is not saving four lines. It is that a date rendered as `9/9/2026`
|
|
1187
|
+
* in one app and `09 Sep 2026` in the next is the same kind of inconsistency
|
|
1188
|
+
* as two different buttons, and money is worse: a per-unit price rounded to
|
|
1189
|
+
* two decimals silently loses money on a part quoted at $0.1275.
|
|
1190
|
+
*/
|
|
1191
|
+
/**
|
|
1192
|
+
* ISO-ish and unambiguous: 2026-09-11.
|
|
1193
|
+
*
|
|
1194
|
+
* Deliberately NOT locale-formatted. This is an automotive supply chain shared
|
|
1195
|
+
* between US and European users, and `09/11/2026` means two different days
|
|
1196
|
+
* depending on who is reading. The one format nobody misreads wins over the
|
|
1197
|
+
* one that matches a browser setting.
|
|
1198
|
+
*/
|
|
1199
|
+
declare function formatDate(value: Date | string | number | null | undefined): string;
|
|
1200
|
+
/** `2026-09-11 14:30`. Same reasoning as `formatDate`, plus a 24-hour clock. */
|
|
1201
|
+
declare function formatDateTime(value: Date | string | number | null | undefined): string;
|
|
1202
|
+
interface MoneyOptions {
|
|
1203
|
+
currency?: string;
|
|
1204
|
+
/**
|
|
1205
|
+
* Decimal places. Defaults to the house convention: **4 for a per-unit
|
|
1206
|
+
* price, 2 for a total.** A part quoted at $0.1275 rounded to $0.13 is a
|
|
1207
|
+
* 2% error multiplied by an annual volume, which is real money; a total
|
|
1208
|
+
* shown to four places is noise.
|
|
1209
|
+
*/
|
|
1210
|
+
decimals?: number;
|
|
1211
|
+
/** Set for a per-piece figure to get 4 decimals without naming the number. */
|
|
1212
|
+
perUnit?: boolean;
|
|
1213
|
+
}
|
|
1214
|
+
declare function formatMoney(value: number | null | undefined, { currency, decimals, perUnit }?: MoneyOptions): string;
|
|
1215
|
+
/** Thousands-separated, no currency. `null` renders as an em dash, not `0`. */
|
|
1216
|
+
declare function formatNumber(value: number | null | undefined, decimals?: number): string;
|
|
1217
|
+
/**
|
|
1218
|
+
* `12.5%`. Takes a PERCENTAGE (12.5), not a fraction (0.125) — the two
|
|
1219
|
+
* conventions collide constantly, and a function that guesses is worse than
|
|
1220
|
+
* one that states which it wants.
|
|
1221
|
+
*/
|
|
1222
|
+
declare function formatPercent(value: number | null | undefined, decimals?: number): string;
|
|
1223
|
+
|
|
1224
|
+
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionRoot, type AccordionSection, AccordionTrigger, type AccordionTriggerProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, CardDescription, CardFooter, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ClassValue, Combobox, type ComboboxOption, type ComboboxProps, ComingSoon, type ComingSoonProps, ConfirmDialog, type ConfirmDialogProps, CountBadge, type CountBadgeProps, Dialog, DialogBody, DialogClose, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, DialogPortal, DialogRoot, type DialogSize, DialogTitle, DialogTrigger, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, type FieldProps, Fieldset, type FieldsetProps, IconButton, type IconButtonProps, Input, type InputProps, type InputSize, Label, type LabelProps, LoadingState, type LoadingStateProps, Menu, type MenuAction, MenuCheckboxItem, MenuContent, MenuGroup, MenuItem, type MenuItemProps, MenuLabel, type MenuProps, MenuRadioGroup, MenuRadioItem, MenuRoot, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Modal, type ModalProps, type MoneyOptions, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverRoot, PopoverTrigger, Progress, type ProgressProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOption, type SelectProps, SelectRoot, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, SideModal, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortDirection, type SortState, Switch, type SwitchProps, type TabDefinition, TabSet, type TabSetProps, Table, TableBody, TableCaption, TableCell, type TableCellProps, TableEmpty, TableFoot, TableFooter, TableHead, type TableHeadProps, TableHeader, type TableProps, TableRow, type TableRowProps, TableScroller, Tabs, TabsContent, TabsList, type TabsListProps, TabsRoot, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, ToggleGroup, type ToggleGroupProps, type ToggleOption, Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, TooltipRoot, TooltipTrigger, type UseSortStateResult, cn, formatDate, formatDateTime, formatMoney, formatNumber, formatPercent, useFieldControl, useSortState, useToast };
|