@vitavision/ui 0.6.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.
@@ -0,0 +1,883 @@
1
+ import { LucideIcon } from "lucide-react";
2
+ import { ComponentProps, KeyboardEvent, MouseEvent, ReactElement, ReactNode } from "react";
3
+ import { ClassValue } from "clsx";
4
+ import * as RadixDialog from "@radix-ui/react-dialog";
5
+ //#region src/theme.d.ts
6
+ /**
7
+ * A stored theme preference: a palette, or `system` to follow the OS
8
+ * (`prefers-color-scheme`) and keep following it.
9
+ */
10
+ type ThemeChoice = "light" | "dark" | "system";
11
+ /**
12
+ * The `localStorage` key a consumer's theme choice is kept under.
13
+ *
14
+ * Every function here takes it as an optional last argument and defaults to this constant,
15
+ * so a single app needs to pass nothing, while two apps sharing a browser profile -- or one
16
+ * app that wants a namespaced key -- can each choose their own without stepping on the
17
+ * other's stored preference.
18
+ */
19
+ export declare const DEFAULT_THEME_STORAGE_KEY = "vitavision-theme";
20
+ /**
21
+ * The stored preference.
22
+ *
23
+ * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.
24
+ * @returns The stored choice, or `system` when nothing valid is stored or storage cannot be
25
+ * read (private browsing, the server).
26
+ */
27
+ export declare function readThemeChoice(storageKey?: string): ThemeChoice;
28
+ /**
29
+ * What `choice` actually resolves to right now.
30
+ *
31
+ * @param choice - The preference.
32
+ * @returns `choice` itself for `light`/`dark`; for `system`, the OS palette (`light` where
33
+ * it cannot be queried).
34
+ */
35
+ export declare function resolveTheme(choice: ThemeChoice): "light" | "dark";
36
+ /**
37
+ * Store a preference and paint it (the `dark` class on `<html>`).
38
+ *
39
+ * @param choice - The new preference.
40
+ * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.
41
+ */
42
+ export declare function setThemeChoice(choice: ThemeChoice, storageKey?: string): void;
43
+ /**
44
+ * Paint the stored choice and keep following the OS while the choice is "system".
45
+ *
46
+ * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.
47
+ * @returns An unsubscribe, so a test can tear the listener down; a long-lived app never does.
48
+ */
49
+ export declare function initTheme(storageKey?: string): () => void;
50
+ //#endregion
51
+ //#region src/tone.d.ts
52
+ /**
53
+ * A paint for SVG overlays and charts: the accent (`signal`), a verdict colour, or `muted`.
54
+ *
55
+ * Named `MeasureTone` rather than `Tone` — `components/ui/Badge.tsx` already owns that name
56
+ * for the verdict-badge vocabulary (`neutral`/`normal`/`defect`/`unlabeled`/`warning`/
57
+ * `info`), and both are re-exported from the package root, where two types of the same name
58
+ * would collide.
59
+ */
60
+ type MeasureTone = "signal" | "normal" | "defect" | "warn" | "muted";
61
+ /**
62
+ * The CSS paint value for a tone — usable directly as a `stroke`, `fill` or `color`. It is a
63
+ * `var(--token)` reference, so it follows the theme without a re-render.
64
+ *
65
+ * @param tone - The tone; `undefined` falls back.
66
+ * @param fallback - The tone used when `tone` is `undefined`. Defaults to `signal`.
67
+ * @returns A `var(--…)` CSS value.
68
+ */
69
+ export declare function toneColor(tone: MeasureTone | undefined, fallback?: MeasureTone): string;
70
+ //#endregion
71
+ //#region src/components/ThemeToggle.d.ts
72
+ /**
73
+ * The palette switch: one button cycling system → light → dark, stored in `localStorage`
74
+ * under `storageKey` and painted as the `dark` class on `<html>`.
75
+ *
76
+ * Its `aria-label` (repeated in a tooltip) names the current state; it needs a
77
+ * `TooltipProvider` above it. The stored choice is read as an external store, so the server
78
+ * render and the hydrating first client render agree ("system") and the stored choice
79
+ * follows without a mismatch. The state is exposed as `data-theme-choice`.
80
+ */
81
+ export declare function ThemeToggle({ storageKey, className }: {
82
+ /** The `localStorage` key; per app, so two apps sharing a profile keep their own choice. */
83
+ storageKey?: string | undefined;
84
+ /** Merged with the button's own classes through `cn`. */
85
+ className?: string | undefined;
86
+ }): import("react").JSX.Element;
87
+ //#endregion
88
+ //#region src/components/ui/cn.d.ts
89
+ /**
90
+ * Compose class names: `clsx` for the conditionals, then `tailwind-merge` so a later class
91
+ * replaces an earlier one of the same conflict group (the caller's `px-6` beats the base
92
+ * `px-3`). Every primitive merges its caller's `className` through this.
93
+ *
94
+ * @param inputs - Class values: strings, arrays, and `{ class: condition }` records.
95
+ * @returns One class string with conflicts resolved.
96
+ */
97
+ export declare function cn(...inputs: ClassValue[]): string;
98
+ /**
99
+ * One focus treatment for every interactive element in the design system.
100
+ *
101
+ * `outline` rather than `ring` because an outline follows the element's own border-radius
102
+ * and needs no offset *colour* -- a ring offset has to know which surface it sits on, and
103
+ * these primitives are used on `ground`, `surface` and `raised` alike.
104
+ *
105
+ * `focus-visible` rather than `focus`, so a mouse click does not leave a ring behind.
106
+ */
107
+ export declare const focusRing = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal";
108
+ /**
109
+ * The focus treatment for controls whose own box is the hit target, where an offset ring
110
+ * would clip: the same outline as {@link focusRing}, drawn with no offset.
111
+ */
112
+ export declare const focusRingInset = "focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-signal";
113
+ //#endregion
114
+ //#region src/components/ui/Badge.d.ts
115
+ /**
116
+ * The badge and status-dot vocabulary: the verdicts (`normal`, `defect`, `warning`),
117
+ * `unlabeled` for a sample with no verdict yet, `info` for a statement of fact about a
118
+ * method, and `neutral` for everything else.
119
+ */
120
+ type Tone = "neutral" | "normal" | "defect" | "unlabeled" | "warning" | "info";
121
+ /**
122
+ * A short label in a tone: a verdict, a status, a count.
123
+ *
124
+ * The tone is exposed as `data-tone`, so a stylesheet or a test can read it without parsing
125
+ * class names.
126
+ */
127
+ export declare function Badge({ tone, className, children }: {
128
+ /** Which colour, by meaning. Defaults to `neutral`. */
129
+ tone?: Tone | undefined;
130
+ /** Merged with the badge's own classes through `cn`. */
131
+ className?: string | undefined;
132
+ /** The label. */
133
+ children: ReactNode;
134
+ }): import("react").JSX.Element;
135
+ /**
136
+ * A status as a dot and a word.
137
+ *
138
+ * The mapping from a domain status to a tone belongs to each caller -- only it knows
139
+ * whether "running" is good news -- but the *rendering* of the answer does not.
140
+ */
141
+ export declare function StatusDot({ tone, className, children }: {
142
+ /** The status, as a tone; also exposed as `data-tone`. */
143
+ tone: Tone;
144
+ /** Merged with the row's own classes through `cn`. */
145
+ className?: string | undefined;
146
+ /** The word beside the dot. The dot alone is decoration: say the status in words. */
147
+ children?: ReactNode;
148
+ }): import("react").JSX.Element;
149
+ /** Counts as a compact `12 normal · 9 defect` run of badges, skipping the zeroes. */
150
+ export declare function CountRun({ counts, className }: {
151
+ /** `[name, count, tone]` triples, in display order. Zero counts are dropped. */
152
+ counts: [string, number, Tone][];
153
+ /** Merged with the run's own classes through `cn`. */
154
+ className?: string | undefined;
155
+ }): import("react").JSX.Element;
156
+ //#endregion
157
+ //#region src/components/ui/Button.d.ts
158
+ /**
159
+ * A button's look, by what the action is: `primary` for the one action a screen is for,
160
+ * `secondary` for the rest, `ghost` for toolbar actions, `danger` for an action that
161
+ * destroys something.
162
+ */
163
+ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
164
+ /** A button's height and padding. `md` is the comfortable density's size, `sm` the compact one's. */
165
+ type ButtonSize = "sm" | "md";
166
+ /**
167
+ * The classes that make a control look like a button, for the one element that has to be
168
+ * something else. Prefer `Button` and `ButtonLink`; this exists so a third shape — a
169
+ * `<label>` wrapping a file input, say — does not have to copy the variant table.
170
+ *
171
+ * @param options - The variant (default `secondary`), the size (default `md`) and the
172
+ * caller's own classes, merged last.
173
+ * @returns The class string.
174
+ */
175
+ export declare function buttonClasses({ variant, size, className }?: {
176
+ variant?: ButtonVariant | undefined;
177
+ size?: ButtonSize | undefined;
178
+ className?: string | undefined;
179
+ }): string;
180
+ /**
181
+ * An action. `asChild` renders the button's look onto the single child element instead of a
182
+ * `<button>` — how a router's link becomes a button-shaped navigation without this package
183
+ * knowing about routers: `<Button asChild><Link to="/runs">Runs</Link></Button>`.
184
+ * For a plain URL, `ButtonLink` is the shorter spelling.
185
+ *
186
+ * Takes every `<button>` prop, `ref` included. State is exposed as `data-variant` and, while
187
+ * `loading`, `data-loading` (plus `aria-busy`).
188
+ */
189
+ export declare function Button({ variant, size, type, loading, icon, asChild, className, children, disabled, ...rest }: ComponentProps<"button"> & {
190
+ /** What kind of action this is. Defaults to `secondary`. */
191
+ variant?: ButtonVariant | undefined;
192
+ /** Defaults to the density in force — `md` comfortable, `sm` compact. */
193
+ size?: ButtonSize | undefined;
194
+ /** Swaps the icon for a spinner and blocks the click, without the label changing. */
195
+ loading?: boolean | undefined;
196
+ /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */
197
+ icon?: ReactNode;
198
+ /**
199
+ * Render onto the one child element (Radix `Slot`) rather than a `<button>`. The child
200
+ * keeps its own semantics — a link stays a link — so `type`, `disabled` and `loading` do
201
+ * not apply.
202
+ */
203
+ asChild?: boolean | undefined;
204
+ }): import("react").JSX.Element;
205
+ /**
206
+ * A navigation that looks like a button: one `<a>`, styled by the same variants and density.
207
+ *
208
+ * The alternative every consumer reached for was `<Link><Button/></Link>` — a button inside
209
+ * an anchor, which is invalid HTML, gives a keyboard two tab stops for one action, and makes
210
+ * a screen reader announce a button that navigates. There is no `disabled` or `loading`:
211
+ * a link that cannot be followed should not be rendered as one, and a navigation has no
212
+ * pending state of its own.
213
+ *
214
+ * It renders a plain `<a href>`, and takes every `<a>` prop, `ref` included. For a router's
215
+ * link, pass it as the only child with `asChild` —
216
+ * `<ButtonLink asChild><Link to="/runs">Runs</Link></ButtonLink>` — so client-side
217
+ * navigation keeps working and this package still never imports a router.
218
+ */
219
+ export declare function ButtonLink({ variant, size, icon, asChild, className, children, ...rest }: ComponentProps<"a"> & {
220
+ /** What kind of action this is. Defaults to `secondary`. */
221
+ variant?: ButtonVariant | undefined;
222
+ /** Defaults to the density in force — `md` comfortable, `sm` compact. */
223
+ size?: ButtonSize | undefined;
224
+ /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */
225
+ icon?: ReactNode;
226
+ /** Render onto the one child element — a router's `<Link>` — instead of an `<a>`. */
227
+ asChild?: boolean | undefined;
228
+ }): import("react").JSX.Element;
229
+ //#endregion
230
+ //#region src/components/ui/Density.d.ts
231
+ /**
232
+ * How much room the chrome takes: `comfortable` (the default, for a page you read through)
233
+ * or `compact` (for a permanent tool surface such as an inspector column).
234
+ */
235
+ type Density = "comfortable" | "compact";
236
+ /**
237
+ * The density in force here. Every primitive that has two spacings reads this.
238
+ *
239
+ * @returns The value of the nearest `DensityProvider`, or `comfortable` outside one.
240
+ */
241
+ export declare function useDensity(): Density;
242
+ /**
243
+ * Pick one of two values by the density in force — the primitives' own idiom.
244
+ *
245
+ * @param density - The density in force, from `useDensity()`.
246
+ * @param comfortable - The value for `comfortable`.
247
+ * @param compact - The value for `compact`.
248
+ * @returns `compact` when the density is compact, else `comfortable`.
249
+ */
250
+ export declare function byDensity<T>(density: Density, comfortable: T, compact: T): T;
251
+ /**
252
+ * Sets the density for a region: every primitive below it reads the value through
253
+ * `useDensity`. Nest providers to switch a sub-region back.
254
+ */
255
+ export declare function DensityProvider({ value, children }: {
256
+ /** The density for everything below. */
257
+ value: Density;
258
+ /** The region. */
259
+ children: ReactNode;
260
+ }): import("react").JSX.Element;
261
+ //#endregion
262
+ //#region src/components/ui/Dialog.d.ts
263
+ /**
264
+ * A modal: a title, a body the caller fills, and a footer it chooses.
265
+ *
266
+ * Controlled — the caller owns `open`. The body scrolls under a height cap so the footer
267
+ * stays on screen; while it overflows it is a keyboard tab stop (with the focus ring) and
268
+ * carries `data-overflowing`, so it can be scrolled without a pointer.
269
+ */
270
+ export declare function Dialog({ open, onOpenChange, title, description, footer, className, children }: {
271
+ /** Whether the dialog is shown. */
272
+ open: boolean;
273
+ /** Called with `false` on Escape, an overlay click or a `DialogClose`. */
274
+ onOpenChange: (open: boolean) => void;
275
+ /** Names the dialog (`aria-labelledby`). */
276
+ title: string;
277
+ /** Optional: what the reader needs before they can answer, not decoration. */
278
+ description?: ReactNode;
279
+ /** The action row, right-aligned below the body. */
280
+ footer?: ReactNode;
281
+ /** Merged with the dialog panel's own classes through `cn`. */
282
+ className?: string | undefined;
283
+ /** The body. */
284
+ children?: ReactNode;
285
+ }): import("react").JSX.Element;
286
+ /**
287
+ * The destructive-action confirmation: a `Dialog` whose footer is Cancel and one confirm
288
+ * button. Say in `description` what will actually happen.
289
+ */
290
+ export declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, onConfirm, loading, disabled, destructive, className }: {
291
+ /** Whether the dialog is shown. */
292
+ open: boolean;
293
+ /** Called with `false` on Cancel, Escape or an overlay click. */
294
+ onOpenChange: (open: boolean) => void;
295
+ /** The question, e.g. "Delete dataset?". */
296
+ title: string;
297
+ /** Say what will actually happen, naming the thing by the name the reader gave it. */
298
+ description: ReactNode;
299
+ /** The confirm button's label: the verb, e.g. "Delete". */
300
+ confirmLabel: string;
301
+ /** Called on confirm. The dialog stays open; close it (or set `loading`) yourself. */
302
+ onConfirm: () => void;
303
+ /** Shows the confirm button's spinner and blocks it while the action runs. */
304
+ loading?: boolean;
305
+ /** Blocks the confirm button. */
306
+ disabled?: boolean;
307
+ /** Renders the confirm button as `danger` rather than `primary`. */
308
+ destructive?: boolean;
309
+ /** Merged with the dialog panel's own classes through `cn`. */
310
+ className?: string | undefined;
311
+ }): import("react").JSX.Element;
312
+ /**
313
+ * The dialog's own dismiss, for a footer the caller builds: wrap a button in it with
314
+ * `asChild` — `<DialogClose asChild><Button>Done</Button></DialogClose>`.
315
+ */
316
+ export declare const DialogClose: import("react").ForwardRefExoticComponent<RadixDialog.DialogCloseProps & import("react").RefAttributes<HTMLButtonElement>>;
317
+ //#endregion
318
+ //#region src/components/ui/Disclosure.d.ts
319
+ /**
320
+ * Content folded away behind a summary line, on a native `<details>`, with a caret that
321
+ * rotates with the element's own `open` state (no JavaScript involved).
322
+ *
323
+ * Uncontrolled: `defaultOpen` sets the initial state and the element owns it after that.
324
+ */
325
+ export declare function Disclosure({ summary, count, defaultOpen, className, children }: {
326
+ /** The always-visible line that toggles the content. */
327
+ summary: ReactNode;
328
+ /** How many things are inside, where the number is the reason to open it. */
329
+ count?: number;
330
+ /** Whether it starts open. */
331
+ defaultOpen?: boolean;
332
+ /** Merged with the `<details>` element's own classes through `cn`. */
333
+ className?: string | undefined;
334
+ /** The folded content. */
335
+ children: ReactNode;
336
+ }): import("react").JSX.Element;
337
+ //#endregion
338
+ //#region src/components/ui/Feedback.d.ts
339
+ /** A callout's kind: a fact (`info`), a caveat (`warning`), a failure (`error`) or `success`. */
340
+ type CalloutTone = "info" | "warning" | "error" | "success";
341
+ /**
342
+ * A boxed message with a tone icon, an optional title and optional actions.
343
+ *
344
+ * An `error` callout is a `role="alert"` (announced at once); the others are
345
+ * `role="status"`. The tone is exposed as `data-tone`.
346
+ */
347
+ export declare function Callout({ tone, title, actions, className, children }: {
348
+ /** Defaults to `info`. */
349
+ tone?: CalloutTone | undefined;
350
+ /** A one-line summary above the message. */
351
+ title?: ReactNode;
352
+ /** Buttons for what to do about it, below the message. */
353
+ actions?: ReactNode;
354
+ /** Merged with the callout's own classes through `cn`. */
355
+ className?: string | undefined;
356
+ /** The message: what happened and what to do about it. */
357
+ children: ReactNode;
358
+ }): import("react").JSX.Element;
359
+ /** The common error shape: an `error` `Callout` with only a message. */
360
+ export declare function ErrorBox({ className, children }: {
361
+ /** Merged with the callout's own classes through `cn`. */
362
+ className?: string | undefined;
363
+ /** What went wrong and what to do about it. */
364
+ children: ReactNode;
365
+ }): import("react").JSX.Element;
366
+ /** An empty state: a sentence saying why there is nothing here, and the action that fixes it. */
367
+ export declare function Empty({ action, className, children }: {
368
+ /** The action — usually a primary `Button` — rather than a description of it. */
369
+ action?: ReactNode;
370
+ /** Merged with the empty state's own classes through `cn`. */
371
+ className?: string | undefined;
372
+ /** Why there is nothing to show. */
373
+ children: ReactNode;
374
+ }): import("react").JSX.Element;
375
+ /** One placeholder bar for content on its way (`aria-hidden`; size it with `className`). */
376
+ export declare function Skeleton({ className }: {
377
+ /** Sets the bar's size; merged with its own classes through `cn`. */
378
+ className?: string | undefined;
379
+ }): import("react").JSX.Element;
380
+ /** A stand-in for a panel's worth of content, so the layout does not jump when it lands. */
381
+ export declare function SkeletonRows({ rows, className }: {
382
+ /** How many bars. Defaults to 3; the last is shorter. */
383
+ rows?: number | undefined;
384
+ /** Merged with the column's own classes through `cn`. */
385
+ className?: string | undefined;
386
+ }): import("react").JSX.Element;
387
+ /**
388
+ * A known fraction done, as a bar and (with `label`) a percentage line.
389
+ *
390
+ * A `role="progressbar"` with `aria-valuenow` in percent. Its accessible name is
391
+ * `aria-label` if given, else `label`, else "Progress" — pass one that names the job when
392
+ * a screen shows more than one bar.
393
+ */
394
+ export declare function ProgressBar({ fraction, label, "aria-label": ariaLabel, className }: {
395
+ /** Done, from 0 to 1. Clamped. */
396
+ fraction: number;
397
+ /** What is progressing, printed after the percentage: "42 of 100 images". */
398
+ label?: string | undefined;
399
+ /** The bar's accessible name. Defaults to `label`, then to "Progress". */
400
+ "aria-label"?: string | undefined;
401
+ /** Merged with the wrapper's own classes through `cn`. */
402
+ className?: string | undefined;
403
+ }): import("react").JSX.Element;
404
+ //#endregion
405
+ //#region src/components/ui/Field.d.ts
406
+ /**
407
+ * A label, the control, and optionally a description, an error and a compact annotation.
408
+ *
409
+ * The label text and the annotation name the control (the `<label>` wraps them and the
410
+ * control); the description and the error are outside the label and wired into the
411
+ * control's `aria-describedby` instead (the error also sets `aria-invalid`): directly for
412
+ * this package's controls — `Input`, `NumberInput`, `Textarea`, `Select`, `Slider`,
413
+ * `SegmentedControl`, `Checkbox` — and, with `as="group"`, on the group itself. A control of your own inside a `label` field does not
414
+ * pick them up; give it `aria-describedby` yourself. State is exposed as `data-invalid` and
415
+ * `data-required`.
416
+ */
417
+ export declare function Field({ label, description, error, required, annotation, as, className, children }: {
418
+ /** The field's name, shown above the control. */
419
+ label: string;
420
+ /** What the value means or does. Always visible, and read with the control. */
421
+ description?: ReactNode;
422
+ /** What is wrong with the current value. Shown in the verdict red, announced as an alert. */
423
+ error?: ReactNode;
424
+ /** Marks the field required (a `*` after the label). */
425
+ required?: boolean | undefined;
426
+ /** A compact fact about the accepted values -- a range, a unit -- kept out of the prose. */
427
+ annotation?: ReactNode;
428
+ /**
429
+ * `label` (the default) wraps the control in a `<label>`; `group` renders a
430
+ * `role="group"` named by `label`, for a set of radios or a segmented control.
431
+ */
432
+ as?: "label" | "group";
433
+ /** Merged with the field's own classes through `cn`. */
434
+ className?: string | undefined;
435
+ /** The control. */
436
+ children: ReactNode;
437
+ }): import("react").JSX.Element;
438
+ //#endregion
439
+ //#region src/components/ui/Input.d.ts
440
+ /**
441
+ * The shared look of a text-like control — border, palette, placeholder, hover, disabled and
442
+ * focus — for a native control that is not one of `Input`, `NumberInput` or `Textarea`.
443
+ * Add the height with `useControlHeight()`.
444
+ */
445
+ export declare const controlClasses: string;
446
+ /**
447
+ * The former name of `controlClasses`, kept as an alias for existing consumers.
448
+ *
449
+ * @deprecated Use `Input`, `NumberInput`, `Textarea` or `Select`, or `controlClasses`.
450
+ */
451
+ export declare const inputClasses: string;
452
+ /**
453
+ * The control height in force — exported so an app's own native control can match.
454
+ *
455
+ * @returns A Tailwind height class: `h-8` comfortable, `h-7` compact.
456
+ */
457
+ export declare function useControlHeight(): string;
458
+ /**
459
+ * A single-line text input. Takes every `<input>` prop, `ref` included; inside a `Field` it
460
+ * is described by the field's description and error.
461
+ */
462
+ export declare function Input({ className, "aria-describedby": describedBy, ...rest }: ComponentProps<"input">): import("react").JSX.Element;
463
+ /**
464
+ * A number, with the schema's own bounds attached.
465
+ *
466
+ * `font-mono` because these are read as quantities and compared down a column. Takes every
467
+ * `<input>` prop, `ref` included (`type` is always `number`).
468
+ */
469
+ export declare function NumberInput({ className, "aria-describedby": describedBy, ...rest }: Omit<ComponentProps<"input">, "min" | "max"> & {
470
+ /** The smallest accepted value. */
471
+ min?: number | undefined;
472
+ /** The largest accepted value. */
473
+ max?: number | undefined;
474
+ }): import("react").JSX.Element;
475
+ /**
476
+ * Multi-line text — set in mono, for JSON and lists. Takes every `<textarea>` prop, `ref`
477
+ * included.
478
+ */
479
+ export declare function Textarea({ className, "aria-describedby": describedBy, ...rest }: ComponentProps<"textarea">): import("react").JSX.Element;
480
+ //#endregion
481
+ //#region src/components/ui/Panel.d.ts
482
+ /**
483
+ * A bordered surface with an optional header (a title and actions) above a padded body.
484
+ * The header and the padding follow the density in force.
485
+ */
486
+ export declare function Panel({ title, actions, className, bodyClassName, children }: {
487
+ /** The header's heading (an `<h2>`). */
488
+ title?: ReactNode;
489
+ /** Controls at the header's right end. */
490
+ actions?: ReactNode;
491
+ /** Merged with the panel's own classes through `cn`. */
492
+ className?: string | undefined;
493
+ /** Merged with the body's own classes (its padding) through `cn`. */
494
+ bodyClassName?: string | undefined;
495
+ /** The body. */
496
+ children: ReactNode;
497
+ }): import("react").JSX.Element;
498
+ /**
499
+ * A numbered step inside a form.
500
+ *
501
+ * The number is not decoration: it marks a genuine ordering, where each choice narrows the
502
+ * next. Do not reach for this where the parts are merely adjacent.
503
+ */
504
+ export declare function Section({ step, title, hint, actions, className, children }: {
505
+ /** The step's number, shown zero-padded before the title (decorative, `aria-hidden`). */
506
+ step?: number | undefined;
507
+ /** The step's heading (an `<h3>`). */
508
+ title: string;
509
+ /** A short line after the title. */
510
+ hint?: ReactNode;
511
+ /** Controls at the heading row's right end. */
512
+ actions?: ReactNode;
513
+ /** Merged with the section's own classes through `cn`. */
514
+ className?: string | undefined;
515
+ /** The step's content. */
516
+ children: ReactNode;
517
+ }): import("react").JSX.Element;
518
+ /**
519
+ * Where `PageHeader`'s back link goes: `{ href, label }` for a plain URL, or the app's own
520
+ * link element (a router's `<Link>`).
521
+ */
522
+ type BackLink = {
523
+ href: string;
524
+ label: string;
525
+ } |
526
+ /** A link element — typically a router's `<Link to="…">Label</Link>` — styled and prefixed with an arrow. */
527
+ ReactElement;
528
+ /**
529
+ * The top of a screen: an optional back link, the `<h1>` title with actions beside it, and
530
+ * a line of meta facts below. Links go through `back`, never a router import.
531
+ */
532
+ export declare function PageHeader({ title, meta, actions, back, className }: {
533
+ /** The screen's heading (the `<h1>`). */
534
+ title: ReactNode;
535
+ /** Small facts under the title — an id, a date, a status badge. */
536
+ meta?: ReactNode;
537
+ /** The screen's actions, beside the title. */
538
+ actions?: ReactNode;
539
+ /**
540
+ * The way up. `{ href, label }` renders a plain `<a>`; for client-side navigation pass the
541
+ * router's own link — `back={<Link to="/experiments">Experiments</Link>}` — which is
542
+ * rendered with the back-link style and arrow, so this package never imports a router.
543
+ */
544
+ back?: BackLink | undefined;
545
+ /** Merged with the header's own classes through `cn`. */
546
+ className?: string | undefined;
547
+ }): import("react").JSX.Element;
548
+ /** One fact on a `ReadoutStrip`: a value, optionally labelled and optionally a link. */
549
+ type ReadoutItem = {
550
+ /** A quiet prefix naming what the value is, where the value alone is ambiguous. */
551
+ label?: string | undefined;
552
+ /** The fact. An item whose value is `null` or `undefined` is skipped. */
553
+ value: ReactNode;
554
+ /** Makes the value a plain `<a href>`. */
555
+ href?: string | undefined;
556
+ /**
557
+ * Makes the value a link through the app's own element — a router's `<Link to="…" />`,
558
+ * given without children: the value becomes its content. Takes precedence over `href`.
559
+ */
560
+ link?: ReactElement | undefined;
561
+ };
562
+ /**
563
+ * The instrument's display line: the facts about what is on screen, in one fixed slot.
564
+ *
565
+ * Stated in the same place on every screen, in mono, the way a piece of measuring equipment
566
+ * has one readout rather than a label per panel. Rendered as a list because it is one.
567
+ */
568
+ export declare function ReadoutStrip({ items, className }: {
569
+ /** The facts, in order. Items with no value are skipped. */
570
+ items: ReadoutItem[];
571
+ /** Merged with the list's own classes through `cn`. */
572
+ className?: string | undefined;
573
+ }): import("react").JSX.Element | null;
574
+ //#endregion
575
+ //#region src/components/ui/SegmentedControl.d.ts
576
+ /**
577
+ * A choice among two or three, shown as a strip of segments (native radios in one group, so
578
+ * arrow keys move between them).
579
+ *
580
+ * **`""` means unset.** The highlighted segment is always the *effective* value — `value`,
581
+ * or `defaultValue` while `value` is `""` — and choosing the segment that matches
582
+ * `defaultValue` reports `""`, so the default stays defined in one place. Each segment
583
+ * carries `data-state="checked"` or `"unchecked"`; the group carries `data-disabled`.
584
+ */
585
+ export declare function SegmentedControl({ value, defaultValue, options, onValueChange, disabled, "aria-label": ariaLabel, className }: {
586
+ /** `""` when the field is untouched. */
587
+ value: string;
588
+ /** What applies when nothing is sent. Highlighted while `value` is `""`. */
589
+ defaultValue?: string | undefined;
590
+ /** The segments, in order. */
591
+ options: {
592
+ value: string;
593
+ label: string;
594
+ }[];
595
+ /** Called with the chosen value, or `""` when the default's segment is chosen. */
596
+ onValueChange: (value: string) => void;
597
+ /** Blocks every segment. */
598
+ disabled?: boolean | undefined;
599
+ /** Names the radio group. */
600
+ "aria-label"?: string | undefined;
601
+ /** Merged with the strip's own classes through `cn`. */
602
+ className?: string | undefined;
603
+ }): import("react").JSX.Element;
604
+ //#endregion
605
+ //#region src/components/ui/Select.d.ts
606
+ /** One entry of a `Select`'s list. */
607
+ type SelectOption = {
608
+ /** What `onValueChange` reports. Must not be `""`, which means unset. */
609
+ value: string;
610
+ /** What the list and the trigger show. */
611
+ label: string;
612
+ /** Shown quietly after the label -- a strategy, a count, a reason it is unavailable. */
613
+ note?: string;
614
+ /** Shown but not choosable; say why in `note`. */
615
+ disabled?: boolean;
616
+ };
617
+ /**
618
+ * A picker whose option list is styled with the app, with room for a quiet note per option.
619
+ *
620
+ * Controlled, and **`""` means unset**: the trigger then shows `placeholder`, and the
621
+ * `unsetLabel` entry (when given) reports `""`. The trigger carries Radix's `data-state`
622
+ * (`open`/`closed`), `data-placeholder` while unset and `data-disabled`; inside a `Field` it
623
+ * is described by the field's description and error.
624
+ */
625
+ export declare function Select({ value, onValueChange, options, placeholder, unsetLabel, disabled, "aria-label": ariaLabel, "aria-describedby": ownDescribedBy, className }: {
626
+ /** The chosen option's value, or `""` for unset. */
627
+ value: string;
628
+ /** Called with the chosen value, or `""` for the unset entry. */
629
+ onValueChange: (value: string) => void;
630
+ /** The list, in order. */
631
+ options: SelectOption[];
632
+ /** Shown on the trigger while the value is unset. Defaults to "Choose…". */
633
+ placeholder?: string | undefined;
634
+ /** When given, adds a leading entry that returns the field to unset. */
635
+ unsetLabel?: string | undefined;
636
+ /** Blocks the trigger. */
637
+ disabled?: boolean | undefined;
638
+ /** Names the trigger, where no `<label>` does. */
639
+ "aria-label"?: string | undefined;
640
+ /** Ids of elements describing the trigger, merged with a surrounding `Field`'s. */
641
+ "aria-describedby"?: string | undefined;
642
+ /** Merged with the trigger's own classes through `cn`. */
643
+ className?: string | undefined;
644
+ }): import("react").JSX.Element;
645
+ //#endregion
646
+ //#region src/components/ui/Slider.d.ts
647
+ /**
648
+ * A continuous value, with its number rendered beside the track.
649
+ *
650
+ * Controlled. The name (`aria-label`) and a surrounding `Field`'s description go on the
651
+ * thumb — the element with `role="slider"` that a keyboard and a screen reader operate.
652
+ * Radix exposes `data-disabled` and `data-orientation` on the parts.
653
+ */
654
+ export declare function Slider({ value, onValueChange, onValueCommit, min, max, step, readout, disabled, "aria-label": ariaLabel, "aria-describedby": ownDescribedBy, className }: {
655
+ /** The current value. */
656
+ value: number;
657
+ /** Called on every move. */
658
+ onValueChange: (value: number) => void;
659
+ /** Called once when a drag or a key press ends — for updates too expensive to run per move. */
660
+ onValueCommit?: ((value: number) => void) | undefined;
661
+ /** Defaults to 0. */
662
+ min?: number | undefined;
663
+ /** Defaults to 1. */
664
+ max?: number | undefined;
665
+ /** Defaults to 0.01. */
666
+ step?: number | undefined;
667
+ /** Rendered to the right of the track. Give it a fixed-width mono span for stability. */
668
+ readout?: ReactNode;
669
+ /** Blocks the thumb and dims the track. */
670
+ disabled?: boolean | undefined;
671
+ /** Names the slider (set on the thumb). */
672
+ "aria-label"?: string | undefined;
673
+ /** Ids of elements describing the slider, merged with a surrounding `Field`'s. */
674
+ "aria-describedby"?: string | undefined;
675
+ /** Merged with the row's own classes through `cn`. */
676
+ className?: string | undefined;
677
+ }): import("react").JSX.Element;
678
+ //#endregion
679
+ //#region src/components/ui/Table.d.ts
680
+ /** One column of a `Table`: its header and how to render a row's cell. */
681
+ type Column<Row> = {
682
+ /** Unique among the columns; the React key of its header and cells. */
683
+ key: string;
684
+ /** The `<th>` content. */
685
+ header: ReactNode;
686
+ /** Right-aligned, mono, tabular. Use for anything that is a quantity. */
687
+ numeric?: boolean;
688
+ /** A CSS width for the column, e.g. `"6rem"`. */
689
+ width?: string;
690
+ /** The cell's content for a row. */
691
+ cell: (row: Row) => ReactNode;
692
+ };
693
+ /**
694
+ * A table of rows, with numeric columns set in mono and right-aligned.
695
+ *
696
+ * With `onRowClick` the rows are keyboard-reachable (Tab, then Enter or Space) while keeping
697
+ * their table semantics. The active row carries `aria-current` and `data-state="active"`;
698
+ * padding follows the density in force. With no rows it renders the `empty` message instead.
699
+ */
700
+ export declare function Table<Row>({ columns, rows, rowKey, empty, caption, className, onRowClick, isRowActive, onRowHover }: {
701
+ /** The columns, in order. */
702
+ columns: Column<Row>[];
703
+ /** The rows, in order. */
704
+ rows: Row[];
705
+ /** A stable key per row. */
706
+ rowKey: (row: Row, index: number) => string | number;
707
+ /** Shown instead of the table when there are no rows. Defaults to "Nothing here.". */
708
+ empty?: ReactNode;
709
+ /** Names the table for assistive technology (a visually hidden `<caption>`). */
710
+ caption?: string | undefined;
711
+ /** Merged with the scroll wrapper's (or the empty message's) own classes through `cn`. */
712
+ className?: string | undefined;
713
+ /** Makes rows activatable. Keyboard-reachable, so a table used as a list of
714
+ * destinations is not a mouse-only control. The event is passed so a caller can read
715
+ * modifiers — a list kept in step with a canvas needs shift-range and meta-toggle to mean
716
+ * the same thing in both places. */
717
+ onRowClick?: (row: Row, index: number, event: MouseEvent<HTMLTableRowElement> | KeyboardEvent<HTMLTableRowElement>) => void;
718
+ /** Marks the row that is current — the selected match, the open frame. Rendered as
719
+ * `aria-current`, so it is announced rather than only tinted. */
720
+ isRowActive?: (row: Row, index: number) => boolean;
721
+ /** Pointer enter/leave, for tables kept in step with a canvas overlay.
722
+ * `null` on leave. */
723
+ onRowHover?: (row: Row | null, index: number | null) => void;
724
+ }): import("react").JSX.Element;
725
+ //#endregion
726
+ //#region src/components/ui/Toggle.d.ts
727
+ /**
728
+ * A setting: changes how the thing behaves from now on, and reads as one ("show the mask").
729
+ * Always labelled; the label and the description sit beside the switch.
730
+ *
731
+ * Controlled. Radix exposes `data-state` (`checked`/`unchecked`) and `data-disabled`.
732
+ */
733
+ export declare function Switch({ checked, onCheckedChange, label, description, disabled, className }: {
734
+ /** Whether it is on. */
735
+ checked: boolean;
736
+ /** Called with the new state. */
737
+ onCheckedChange: (checked: boolean) => void;
738
+ /** What the setting is. */
739
+ label: ReactNode;
740
+ /** What turning it on does. */
741
+ description?: ReactNode;
742
+ /** Blocks the switch. */
743
+ disabled?: boolean | undefined;
744
+ /** Merged with the row's own classes through `cn`. */
745
+ className?: string | undefined;
746
+ }): import("react").JSX.Element;
747
+ /**
748
+ * A statement about a state you are about to commit ("keep this channel"). With a `label`
749
+ * it renders its own row; without one it is a bare box (give it `aria-label`, e.g. in a
750
+ * table cell).
751
+ *
752
+ * Controlled. Radix exposes `data-state` (`checked`/`unchecked`/`indeterminate`) and
753
+ * `data-disabled`; inside a `Field` it is described by the field's description and error.
754
+ */
755
+ export declare function Checkbox({ checked, onCheckedChange, onClick, label, description, disabled, "aria-label": ariaLabel, className }: {
756
+ /** `"indeterminate"` for a partial selection over a set. */
757
+ checked: boolean | "indeterminate";
758
+ /** Called with the new state (never `"indeterminate"`: a click resolves it). */
759
+ onCheckedChange: (checked: boolean) => void;
760
+ /**
761
+ * For a checkbox whose meaning depends on the modifier keys — extending a range with
762
+ * shift, toggling one out of a selection with the platform key. `onCheckedChange` gets a
763
+ * boolean and cannot carry them. Keyboard activation synthesises a click with its
764
+ * modifiers intact, so this sees both input paths.
765
+ */
766
+ onClick?: ((event: MouseEvent<HTMLButtonElement>) => void) | undefined;
767
+ /** What checking it asserts. Omit for a bare box. */
768
+ label?: ReactNode;
769
+ /** More about the statement, under the label. */
770
+ description?: ReactNode;
771
+ /** Blocks the box. */
772
+ disabled?: boolean | undefined;
773
+ /** For a checkbox in a table cell, where the row is the label. */
774
+ "aria-label"?: string | undefined;
775
+ /** Merged with the row's (or, without a label, the box's) own classes through `cn`. */
776
+ className?: string | undefined;
777
+ }): import("react").JSX.Element;
778
+ //#endregion
779
+ //#region src/components/ui/ToggleChip.d.ts
780
+ /**
781
+ * A dense on/off chip for a layer drawn on screen, with an optional swatch in the layer's
782
+ * colour: a toolbar-sized `Switch` (`role="switch"`, `aria-checked`).
783
+ *
784
+ * Controlled. State is exposed as `data-state` (`checked`/`unchecked`).
785
+ */
786
+ export declare function ToggleChip({ checked, onCheckedChange, children, swatch, disabled, title, className }: {
787
+ /** Whether the layer is shown. */
788
+ checked: boolean;
789
+ /** Called with the new state. */
790
+ onCheckedChange: (checked: boolean) => void;
791
+ /** The layer's name. */
792
+ children: ReactNode;
793
+ /** A CSS colour: the colour this layer is drawn in, shown as a dot. */
794
+ swatch?: string;
795
+ /** Blocks the chip. */
796
+ disabled?: boolean | undefined;
797
+ /** Why the control is disabled, or what the layer is. Never the only explanation. */
798
+ title?: string | undefined;
799
+ /** Merged with the chip's own classes through `cn`. */
800
+ className?: string | undefined;
801
+ }): import("react").JSX.Element;
802
+ //#endregion
803
+ //#region src/components/ui/Tooltip.d.ts
804
+ /**
805
+ * Shares one open delay (200 ms) across the tooltips below it. Mount it once near the root:
806
+ * `Tooltip` and `InfoHint` need it above them.
807
+ */
808
+ export declare function TooltipProvider({ children }: {
809
+ /** The app, or the region that uses tooltips. */
810
+ children: ReactNode;
811
+ }): import("react").JSX.Element;
812
+ /**
813
+ * A short explanation on hover or focus of its child, for text that helps but is not needed
814
+ * to operate the control (that belongs in a field's description).
815
+ *
816
+ * The child must be one focusable element that accepts a ref (Radix `asChild`). Radix
817
+ * exposes `data-state` (`closed`/`delayed-open`/`instant-open`) on it.
818
+ */
819
+ export declare function Tooltip({ content, children }: {
820
+ /** The explanation. */
821
+ content: ReactNode;
822
+ /** The one element it explains. */
823
+ children: ReactNode;
824
+ }): import("react").JSX.Element;
825
+ /**
826
+ * The standard affordance: a quiet mark that yields the explanation on hover or focus.
827
+ *
828
+ * `icon` exists because the mark answers two different questions. The default `?` offers
829
+ * help with a control; `Info` offers *facts about the thing on screen* -- where a dataset
830
+ * came from, which method produced a measurement -- which the tone demotes here rather
831
+ * than spending a permanent line on. Reading them as the same affordance would be a small
832
+ * lie.
833
+ */
834
+ export declare function InfoHint({ children, label, icon: Icon }: {
835
+ /** The explanation shown on hover or focus. */
836
+ children: ReactNode;
837
+ /** The button's accessible name. Defaults to "More information". */
838
+ label?: string | undefined;
839
+ /** The mark: `HelpCircle` (the default) for help, `Info` for facts about the thing. */
840
+ icon?: LucideIcon | undefined;
841
+ }): import("react").JSX.Element;
842
+ //#endregion
843
+ //#region src/components/Tabs.d.ts
844
+ /** One tab of a `Tabs` strip. */
845
+ interface TabItem<Id extends string> {
846
+ /** Identifies the tab to `active` and `onSelect`. Unique within the strip. */
847
+ id: Id;
848
+ /** The tab's text, and its accessible name. */
849
+ label: string;
850
+ /** Shown after the label, for the chips that carry a row or override count. */
851
+ count?: number;
852
+ /** Shown but not selectable, and skipped by the arrow keys. */
853
+ disabled?: boolean;
854
+ /** Why this tab has nothing to show, for a reader wondering what they missed. */
855
+ title?: string;
856
+ }
857
+ /**
858
+ * A tab strip (`role="tablist"`): the caller renders the panel for the `active` tab.
859
+ *
860
+ * Keyboard: one tab stop for the whole strip (the active tab); ←/→ move to the previous or
861
+ * next enabled tab and select it, Home/End to the first and last. Each tab carries
862
+ * `data-state` (`active`/`inactive`). Pass `idPrefix` to wire the tabs to the panel: the tab
863
+ * for `id` gets the element id `<idPrefix>-tab-<id>` and the active tab gets
864
+ * `aria-controls="<idPrefix>-panel-<id>"`, so render the panel as a `role="tabpanel"` element
865
+ * with the id `<idPrefix>-panel-<active>` and `aria-labelledby="<idPrefix>-tab-<active>"`.
866
+ */
867
+ export declare function Tabs<Id extends string>({ items, active, onSelect, label, idPrefix, className }: {
868
+ /** The tabs, in order. */
869
+ items: TabItem<Id>[];
870
+ /** The selected tab's id. */
871
+ active: Id;
872
+ /** Called with a tab's id when it is clicked or reached with the arrow keys. */
873
+ onSelect: (id: Id) => void;
874
+ /** Names the tab list. */
875
+ label: string;
876
+ /** Prefix for the tab and panel element ids; enables `aria-controls` (see above). */
877
+ idPrefix?: string | undefined;
878
+ /** Merged with the strip's own classes through `cn`. */
879
+ className?: string | undefined;
880
+ }): import("react").JSX.Element;
881
+ //#endregion
882
+ export type { BackLink, ButtonSize, ButtonVariant, CalloutTone, Column, Density, MeasureTone, ReadoutItem, SelectOption, TabItem, ThemeChoice, Tone };
883
+ //# sourceMappingURL=index.d.ts.map