@vitavision/ui 0.8.0 → 0.10.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 CHANGED
@@ -123,12 +123,25 @@ keep independent preferences.
123
123
  ### Components
124
124
 
125
125
  **Primitives** — `Badge`, `CountRun`, `StatusDot` · `Button`, `ButtonLink` (a navigation styled as a button — never nest a `Button` in a link), `buttonClasses` · `Dialog`, `ConfirmDialog`,
126
- `DialogClose` · `Disclosure` · `Callout`, `Empty`, `ErrorBox`, `ProgressBar`, `Skeleton`,
127
- `SkeletonRows` · `Field` · `Input`, `NumberInput` (with an optional `unit` written in the
128
- field), `Textarea` · `VectorInput`, `PoseInput` · `PageHeader`, `Panel`,
129
- `ReadoutStrip`, `Section` · `SegmentedControl` · `Select` · `Slider` · `Table` ·
130
- `Checkbox`, `Switch` · `ToggleChip` · `InfoHint`, `Tooltip`, `TooltipProvider` ·
131
- `ThemeToggle` · plus `cn`, `focusRing`, `focusRingInset`.
126
+ `DialogClose` · `Disclosure` · `DropdownMenu`, `MenuItem`, `MenuCheckboxItem`, `MenuLabel`,
127
+ `MenuSeparator` (a non-modal command and toggle menu) · `Callout`, `Empty`, `ErrorBox`,
128
+ `ProgressBar`, `Skeleton`, `SkeletonRows` · `Field` · `Input`, `NumberInput` (with an optional
129
+ `unit` written in the field; number-valued with `onValueChange`), `Textarea` · `VectorInput`,
130
+ `PoseInput` · `Kbd` · `Listbox` (single choice among options that render their own content) ·
131
+ `PageHeader`, `Panel`, `ReadoutStrip`, `Section` · `Popover`, `PopoverClose` ·
132
+ `SegmentedControl` · `Select` · `Slider` · `Table` ·
133
+ `Checkbox`, `Switch` · `ToggleChip` · `Toaster`, `toast()` (notifications from anywhere; mount one `<Toaster />` near the root) ·
134
+ `InfoHint`, `Tooltip`, `TooltipProvider` ·
135
+ `ThemeToggle` · plus `cn`, `focusRing`, `focusRingInset`, and `formatNumber` / `parseNumber`
136
+ (the text ↔ number helpers behind the number fields).
137
+
138
+ A number-valued `NumberInput` keeps what is being typed and reports only finite numbers inside
139
+ `[min, max]`, so clearing a field to retype it never passes through 0. `onClear` is for a
140
+ quantity that may be unset:
141
+
142
+ ```tsx
143
+ <NumberInput unit="px" precision={1} min={4} value={roi.width} onValueChange={setWidth} />
144
+ ```
132
145
 
133
146
  `PoseInput` edits an SE(3) pose in the wire form `{ rotation: [qx, qy, qz, qw], translation:
134
147
  [tx, ty, tz] }` as a translation and roll/pitch/yaw in degrees, but does no rotation
package/dist/index.d.ts CHANGED
@@ -335,6 +335,77 @@ export declare function Disclosure({ summary, count, defaultOpen, className, chi
335
335
  children: ReactNode;
336
336
  }): import("react").JSX.Element;
337
337
  //#endregion
338
+ //#region src/components/ui/DropdownMenu.d.ts
339
+ /** Props of `DropdownMenu`. */
340
+ interface DropdownMenuProps {
341
+ /** The element that opens it: one focusable element that accepts a ref (Radix `asChild`). */
342
+ trigger: ReactNode;
343
+ /** `MenuItem`, `MenuCheckboxItem`, `MenuLabel` and `MenuSeparator` elements. */
344
+ children: ReactNode;
345
+ /** Controlled open state. */
346
+ open?: boolean | undefined;
347
+ /** Called when it opens or closes. */
348
+ onOpenChange?: ((open: boolean) => void) | undefined;
349
+ /** Its alignment along the trigger. Defaults to `start`. */
350
+ align?: "start" | "center" | "end" | undefined;
351
+ /** The trigger's side it opens on. Defaults to `bottom`. */
352
+ side?: "top" | "right" | "bottom" | "left" | undefined;
353
+ /** Merged with the menu's own classes through `cn`. */
354
+ className?: string | undefined;
355
+ }
356
+ /**
357
+ * A menu (`role="menu"`) that opens from `trigger`, with arrow-key navigation, typeahead,
358
+ * Escape or a press outside to close, and focus returned to the trigger. It is non-modal: the
359
+ * page behind stays readable and operable. Toggling a `MenuCheckboxItem` keeps the menu open,
360
+ * so several layers can be switched in one visit.
361
+ */
362
+ export declare function DropdownMenu({ trigger, children, open, onOpenChange, align, side, className }: DropdownMenuProps): import("react").JSX.Element;
363
+ /** Props of `MenuItem`. */
364
+ interface MenuItemProps {
365
+ /** The item's text. */
366
+ children: ReactNode;
367
+ /** Runs the command. */
368
+ onSelect?: (() => void) | undefined;
369
+ /** An icon before the text, in the indicator column. */
370
+ icon?: ReactNode;
371
+ /** `defect` for a destructive command. */
372
+ tone?: "defect" | undefined;
373
+ /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */
374
+ shortcut?: string | undefined;
375
+ /** Blocks the item. */
376
+ disabled?: boolean | undefined;
377
+ }
378
+ /**
379
+ * A command. `onSelect` runs when it is clicked or chosen with Enter, and the menu then
380
+ * closes. `tone="defect"` marks a destructive command.
381
+ */
382
+ export declare function MenuItem({ children, onSelect, shortcut, disabled, icon, tone }: MenuItemProps): import("react").JSX.Element;
383
+ /** Props of `MenuCheckboxItem`. */
384
+ interface MenuCheckboxItemProps {
385
+ /** The item's text. */
386
+ children: ReactNode;
387
+ /** Whether it is on. */
388
+ checked: boolean;
389
+ /** Called with the new state. */
390
+ onCheckedChange: (checked: boolean) => void;
391
+ /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */
392
+ shortcut?: string | undefined;
393
+ /** Blocks the item. */
394
+ disabled?: boolean | undefined;
395
+ }
396
+ /**
397
+ * A toggle (`role="menuitemcheckbox"`): a layer on or off. A check marks it on. Choosing it
398
+ * leaves the menu open.
399
+ */
400
+ export declare function MenuCheckboxItem({ children, checked, onCheckedChange, shortcut, disabled }: MenuCheckboxItemProps): import("react").JSX.Element;
401
+ /** A heading over a group of items, in the eyebrow role. Not focusable. */
402
+ export declare function MenuLabel({ children }: {
403
+ /** The heading. */
404
+ children: ReactNode;
405
+ }): import("react").JSX.Element;
406
+ /** A divider between groups of items. */
407
+ export declare function MenuSeparator(): import("react").JSX.Element;
408
+ //#endregion
338
409
  //#region src/components/ui/Feedback.d.ts
339
410
  /** A callout's kind: a fact (`info`), a caveat (`warning`), a failure (`error`) or `success`. */
340
411
  type CalloutTone = "info" | "warning" | "error" | "success";
@@ -460,8 +531,40 @@ export declare function useControlHeight(): string;
460
531
  * is described by the field's description and error.
461
532
  */
462
533
  export declare function Input({ className, "aria-describedby": describedBy, ...rest }: ComponentProps<"input">): import("react").JSX.Element;
463
- /** Props of `NumberInput`: every `<input>` prop, `ref` included, with numeric bounds and a `unit`. */
464
- type NumberInputProps = Omit<ComponentProps<"input">, "min" | "max"> & {
534
+ /**
535
+ * Props of `NumberInput`: every `<input>` prop, `ref` included, with numeric bounds, a `unit`,
536
+ * and an optional number-valued API (`onValueChange`, `precision`).
537
+ */
538
+ type NumberInputProps = Omit<ComponentProps<"input">, "min" | "max" | "value"> & {
539
+ /**
540
+ * The value. As text, it is the native input's `value`. With `onValueChange` it is a
541
+ * number, and `null` shows an empty field.
542
+ */
543
+ value?: string | number | readonly string[] | null | undefined;
544
+ /**
545
+ * Makes the field number-valued.
546
+ *
547
+ * - **While focused** it keeps the text as typed. A keystroke is reported only when the
548
+ * text is a finite number inside `[min, max]` and differs from `value`. Empty, partial
549
+ * (`-`, `1e`) and out-of-range text is never reported, so clearing a field to retype it
550
+ * does not pass through 0, and typing `5` on the way to `50` does not report a value under
551
+ * `min`.
552
+ * - **Enter** shows the value at `precision`. **Escape** restores the value the field had
553
+ * on focus.
554
+ * - **On leaving**, the field shows `value` again.
555
+ */
556
+ onValueChange?: ((value: number) => void) | undefined;
557
+ /**
558
+ * Called when a number-valued field is committed empty (left, or Enter pressed, with no
559
+ * text), for a quantity that may be unset. Without it, emptying the field and leaving it
560
+ * restores `value`.
561
+ */
562
+ onClear?: (() => void) | undefined;
563
+ /**
564
+ * Decimals shown when a number-valued field is not being edited. Without it the number is
565
+ * shown as JavaScript prints it.
566
+ */
567
+ precision?: number | undefined;
465
568
  /** The smallest accepted value. */
466
569
  min?: number | undefined;
467
570
  /** The largest accepted value. */
@@ -485,14 +588,97 @@ type NumberInputProps = Omit<ComponentProps<"input">, "min" | "max"> & {
485
588
  * With a `unit`, the input sits in a `relative` full-width wrapper carrying `data-unit`, keeps
486
589
  * room on its right for the unit, and is described by it; size such a field through its
487
590
  * container. Without one — or with `""` — the markup is exactly the plain input.
591
+ *
592
+ * With `onValueChange` the field is number-valued: `value` is a number (`null` shows empty), the
593
+ * typed text survives while the field has focus, and only finite numbers inside `[min, max]`
594
+ * are reported (see `onValueChange`; `onClear` reports an emptied field). Without it the field
595
+ * is the native text-valued input.
488
596
  */
489
- export declare function NumberInput({ unit, ...props }: NumberInputProps): import("react").JSX.Element;
597
+ export declare function NumberInput({ onValueChange, onClear, precision, ...props }: NumberInputProps): import("react").JSX.Element;
490
598
  /**
491
599
  * Multi-line text — set in mono, for JSON and lists. Takes every `<textarea>` prop, `ref`
492
600
  * included.
493
601
  */
494
602
  export declare function Textarea({ className, "aria-describedby": describedBy, ...rest }: ComponentProps<"textarea">): import("react").JSX.Element;
495
603
  //#endregion
604
+ //#region src/components/ui/Kbd.d.ts
605
+ /**
606
+ * A keyboard key or chord (`F`, `⌘K`, `[`), set in mono on a raised key cap.
607
+ *
608
+ * Renders `<kbd>`, which screen readers announce as text. Write the key as the user's keyboard
609
+ * labels it; for a chord, one `Kbd` with the whole chord reads better than several. Takes
610
+ * every `<kbd>` prop, `ref` included.
611
+ */
612
+ export declare function Kbd({ className, ...rest }: ComponentProps<"kbd">): import("react").JSX.Element;
613
+ //#endregion
614
+ //#region src/components/ui/Listbox.d.ts
615
+ /** One option of a `Listbox`. */
616
+ interface ListboxOption {
617
+ /** Its value, unique in the list. */
618
+ value: string;
619
+ /** Its name: the default rendering, and what a screen reader announces. */
620
+ label: string;
621
+ /** A second line under the label, in the default rendering. */
622
+ description?: string | undefined;
623
+ /** Shown but not choosable. */
624
+ disabled?: boolean | undefined;
625
+ }
626
+ /** What `renderOption` is told about an option. */
627
+ interface ListboxOptionState {
628
+ /** It is the value. */
629
+ selected: boolean;
630
+ /** It has the keyboard cursor or the pointer. */
631
+ active: boolean;
632
+ }
633
+ /** Props of `Listbox`. */
634
+ interface ListboxProps {
635
+ /** The options, in order. */
636
+ options: readonly ListboxOption[];
637
+ /** The selected value, or `null` for none. */
638
+ value: string | null;
639
+ /** Called with the value of the option chosen by click, Enter or Space. */
640
+ onValueChange?: ((value: string) => void) | undefined;
641
+ /**
642
+ * Renders an option's content (the row keeps its own padding, states and accessibility).
643
+ * Defaults to the label, with the description under it.
644
+ */
645
+ renderOption?: ((option: ListboxOption, state: ListboxOptionState) => ReactNode) | undefined;
646
+ /** Names the list. */
647
+ "aria-label": string;
648
+ /** Focus the list when it mounts, e.g. when it opens in a `Popover`. */
649
+ autoFocus?: boolean | undefined;
650
+ /** Merged with the list's own classes through `cn`. */
651
+ className?: string | undefined;
652
+ }
653
+ /**
654
+ * A single-select list (`role="listbox"`) of options that each render their own content.
655
+ *
656
+ * The list is one tab stop. The arrow keys, Home, End and Page keys move the active option
657
+ * (`aria-activedescendant`), skipping disabled ones, and Enter or Space choose it. A click
658
+ * chooses the option under the pointer. Controlled: the caller keeps `value`. Each option
659
+ * carries `aria-selected`, `data-selected` and `data-active`; the list scrolls the active
660
+ * option into view.
661
+ */
662
+ export declare function Listbox({ options, value, onValueChange, renderOption, "aria-label": ariaLabel, autoFocus, className }: ListboxProps): import("react").JSX.Element;
663
+ //#endregion
664
+ //#region src/components/ui/numberText.d.ts
665
+ /**
666
+ * A number at a fixed number of decimals, without a negative zero (`-0.000` reads as a sign
667
+ * error in a coordinate readout).
668
+ *
669
+ * @param value - The number. Non-finite values print as `NaN`, `Infinity`, `-Infinity`.
670
+ * @param precision - Decimals, 0–20.
671
+ * @returns The text.
672
+ */
673
+ export declare function formatNumber(value: number, precision: number): string;
674
+ /**
675
+ * The number a field's text means, if it means one.
676
+ *
677
+ * @param text - What is in the field.
678
+ * @returns The number, or `null` for empty, partial (`-`, `1e`) or non-numeric text.
679
+ */
680
+ export declare function parseNumber(text: string): number | null;
681
+ //#endregion
496
682
  //#region src/components/ui/Panel.d.ts
497
683
  /**
498
684
  * A bordered surface with an optional header (a title and actions) above a padded body.
@@ -587,6 +773,49 @@ export declare function ReadoutStrip({ items, className }: {
587
773
  className?: string | undefined;
588
774
  }): import("react").JSX.Element | null;
589
775
  //#endregion
776
+ //#region src/components/ui/Popover.d.ts
777
+ /** Props of `Popover`. */
778
+ interface PopoverProps {
779
+ /**
780
+ * The element that opens it: one focusable element that accepts a ref (Radix `asChild`).
781
+ * It carries `aria-expanded` and `data-state` (`open` / `closed`).
782
+ */
783
+ trigger: ReactNode;
784
+ /** The content. */
785
+ children: ReactNode;
786
+ /** Controlled open state. */
787
+ open?: boolean | undefined;
788
+ /** Initial open state when uncontrolled. */
789
+ defaultOpen?: boolean | undefined;
790
+ /** Called when it opens or closes. */
791
+ onOpenChange?: ((open: boolean) => void) | undefined;
792
+ /** The trigger's side it opens on. Defaults to `bottom`. */
793
+ side?: "top" | "right" | "bottom" | "left" | undefined;
794
+ /** Its alignment along that side. Defaults to `start`. */
795
+ align?: "start" | "center" | "end" | undefined;
796
+ /** Names the content (`aria-label`), when nothing inside it does. */
797
+ "aria-label"?: string | undefined;
798
+ /** Merged with the content's own classes through `cn`. */
799
+ className?: string | undefined;
800
+ }
801
+ /**
802
+ * Floating content anchored to `trigger`, on the `overlay` surface with a shadow (floating
803
+ * layers are the only ones that cast one).
804
+ *
805
+ * Controlled (`open` / `onOpenChange`) or not (`defaultOpen`). Focus moves into the content
806
+ * when it opens and back to the trigger when it closes. Escape and a press outside close it.
807
+ * The content carries `data-state` and `data-side`.
808
+ */
809
+ export declare function Popover({ trigger, children, open, defaultOpen, onOpenChange, side, align, "aria-label": ariaLabel, className }: PopoverProps): import("react").JSX.Element;
810
+ /**
811
+ * Closes the surrounding `Popover` when its child is activated. Wraps one element (Radix
812
+ * `asChild`).
813
+ */
814
+ export declare function PopoverClose({ children }: {
815
+ /** The one element that closes the popover. */
816
+ children: ReactNode;
817
+ }): import("react").JSX.Element;
818
+ //#endregion
590
819
  //#region src/components/ui/PoseInput.d.ts
591
820
  /** Three numbers: a translation `[tx, ty, tz]`, or Euler angles. */
592
821
  type Vec3 = readonly [number, number, number];
@@ -880,6 +1109,97 @@ export declare function ToggleChip({ checked, onCheckedChange, children, swatch,
880
1109
  className?: string | undefined;
881
1110
  }): import("react").JSX.Element;
882
1111
  //#endregion
1112
+ //#region src/components/ui/toastStore.d.ts
1113
+ /** A toast's kind: a fact (`info`), a completed action (`success`), a caveat (`warn`), a failure (`error`). */
1114
+ type ToastTone = "info" | "success" | "warn" | "error";
1115
+ /** What `toast()` takes. */
1116
+ interface ToastOptions {
1117
+ /** One line: what happened. */
1118
+ title: string;
1119
+ /** More detail, and what to do about it. */
1120
+ description?: string | undefined;
1121
+ /** Defaults to `info`. */
1122
+ tone?: ToastTone | undefined;
1123
+ /**
1124
+ * How long it stays, in milliseconds, while not hovered or focused. Defaults to 5000
1125
+ * (8000 for `error`); `Infinity` keeps it until dismissed.
1126
+ */
1127
+ duration?: number | undefined;
1128
+ /** Replace the toast with this id instead of adding one — for progress that updates in place. */
1129
+ id?: string | undefined;
1130
+ }
1131
+ /** A toast in the store. */
1132
+ interface ToastRecord {
1133
+ /** Identifies the toast to `dismiss` and to `id` replacement. */
1134
+ id: string;
1135
+ /** One line: what happened. */
1136
+ title: string;
1137
+ /** More detail, if any. */
1138
+ description: string | undefined;
1139
+ /** Its kind. */
1140
+ tone: ToastTone;
1141
+ /** Milliseconds before it dismisses itself (`Infinity`: never). */
1142
+ duration: number;
1143
+ }
1144
+ /**
1145
+ * A toast store: what `toast()` writes to and `<Toaster />` reads. Every member may be called
1146
+ * detached.
1147
+ */
1148
+ interface ToastStore {
1149
+ /** Add a toast (or replace the one with `options.id`), returning its id. */
1150
+ readonly toast: (options: ToastOptions) => string;
1151
+ /** Remove one toast, or every toast when `id` is omitted. */
1152
+ readonly dismiss: (id?: string) => void;
1153
+ /** The toasts, oldest first. The same array until the store changes. */
1154
+ readonly getSnapshot: () => readonly ToastRecord[];
1155
+ /** Be told when the toasts change; returns the unsubscribe function. */
1156
+ readonly subscribe: (listener: () => void) => () => void;
1157
+ }
1158
+ /**
1159
+ * A new, empty toast store. The package keeps one (behind `toast()` and the default
1160
+ * `<Toaster />`); create another only to isolate a region or a test.
1161
+ *
1162
+ * @returns The store.
1163
+ */
1164
+ export declare function createToastStore(): ToastStore;
1165
+ /** The store behind `toast()` and a `<Toaster />` given no `store`. */
1166
+ export declare const defaultToastStore: ToastStore;
1167
+ /**
1168
+ * Raise a notification, from anywhere — shown by the mounted `<Toaster />`.
1169
+ *
1170
+ * `toast({ title: "Scenario loaded", tone: "success" })`. Pass `id` to update a toast in
1171
+ * place (a progress message that becomes a result). `toast.dismiss(id)` removes one early,
1172
+ * `toast.dismiss()` all of them.
1173
+ *
1174
+ * @param options - The message, its tone and its duration.
1175
+ * @returns The toast's id.
1176
+ */
1177
+ export declare const toast: ((options: ToastOptions) => string) & {
1178
+ /** Remove one toast, or every toast when `id` is omitted. */
1179
+ dismiss: (id?: string) => void;
1180
+ };
1181
+ //#endregion
1182
+ //#region src/components/ui/Toaster.d.ts
1183
+ /** Props of `Toaster`. */
1184
+ interface ToasterProps {
1185
+ /** The store to show. Defaults to the one behind `toast()`. */
1186
+ store?: ToastStore | undefined;
1187
+ /** How many toasts are shown at once; older ones wait. Defaults to 5. */
1188
+ limit?: number | undefined;
1189
+ /** Merged with the stack's own classes through `cn` (it is fixed to the bottom-right corner). */
1190
+ className?: string | undefined;
1191
+ }
1192
+ /**
1193
+ * The stack of notifications raised with `toast()`. Mount it once, near the root.
1194
+ *
1195
+ * Fixed to the bottom-right corner, newest at the bottom. Each toast dismisses itself after
1196
+ * its `duration`; the countdown pauses while the pointer is over the stack or focus is inside
1197
+ * it. A `section` landmark ("Notifications") holding a polite live region, so new toasts are
1198
+ * announced; an `error` toast is a `role="alert"`, announced at once. Each toast carries
1199
+ * `data-tone` and a "Dismiss" button.
1200
+ */
1201
+ export declare function Toaster({ store, limit, className }: ToasterProps): import("react").JSX.Element;
1202
+ //#endregion
883
1203
  //#region src/components/ui/Tooltip.d.ts
884
1204
  /**
885
1205
  * Shares one open delay (200 ms) across the tooltips below it. Mount it once near the root:
@@ -952,8 +1272,8 @@ interface VectorInputProps {
952
1272
  * A row of number fields editing one small vector, with the axis labels before the fields
953
1273
  * and the unit once after them.
954
1274
  *
955
- * Controlled. A `role="group"` named by `aria-label`, each field named by its label and
956
- * described by the unit. While a field has focus it shows what is typed; on blur or Enter it
1275
+ * Controlled. A `role="group"` named by `aria-label`. Each field is named by the group and its
1276
+ * label ("Translation x"), so two vectors on one screen stay apart, and is described by the unit. While a field has focus it shows what is typed; on blur or Enter it
957
1277
  * shows the value at `precision`; Escape restores the value it had on focus. With `readOnly`
958
1278
  * it renders a `ReadoutStrip` (`x 0.100 m · y …`) instead. The row carries `data-readonly`
959
1279
  * and `data-disabled`.
@@ -999,5 +1319,5 @@ export declare function Tabs<Id extends string>({ items, active, onSelect, label
999
1319
  className?: string | undefined;
1000
1320
  }): import("react").JSX.Element;
1001
1321
  //#endregion
1002
- export type { BackLink, ButtonSize, ButtonVariant, CalloutTone, Column, Density, MeasureTone, NumberInputProps, PoseInputProps, PoseValue, Quaternion, ReadoutItem, RotationView, SelectOption, TabItem, ThemeChoice, Tone, Vec3, VectorInputProps };
1322
+ export type { BackLink, ButtonSize, ButtonVariant, CalloutTone, Column, Density, DropdownMenuProps, ListboxOption, ListboxOptionState, ListboxProps, MeasureTone, MenuCheckboxItemProps, MenuItemProps, NumberInputProps, PopoverProps, PoseInputProps, PoseValue, Quaternion, ReadoutItem, RotationView, SelectOption, TabItem, ThemeChoice, ToastOptions, ToastRecord, ToastStore, ToastTone, ToasterProps, Tone, Vec3, VectorInputProps };
1003
1323
  //# sourceMappingURL=index.d.ts.map