@vitavision/ui 0.7.0 → 0.9.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
@@ -124,11 +124,27 @@ keep independent preferences.
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
126
  `DialogClose` · `Disclosure` · `Callout`, `Empty`, `ErrorBox`, `ProgressBar`, `Skeleton`,
127
- `SkeletonRows` · `Field` · `Input`, `NumberInput`, `Textarea` · `PageHeader`, `Panel`,
127
+ `SkeletonRows` · `Field` · `Input`, `NumberInput` (with an optional `unit` written in the
128
+ field), `Textarea` · `VectorInput`, `PoseInput` · `PageHeader`, `Panel`,
128
129
  `ReadoutStrip`, `Section` · `SegmentedControl` · `Select` · `Slider` · `Table` ·
129
- `Checkbox`, `Switch` · `ToggleChip` · `InfoHint`, `Tooltip`, `TooltipProvider` ·
130
+ `Checkbox`, `Switch` · `ToggleChip` · `Toaster`, `toast()` (notifications from anywhere; mount one `<Toaster />` near the root) ·
131
+ `InfoHint`, `Tooltip`, `TooltipProvider` ·
130
132
  `ThemeToggle` · plus `cn`, `focusRing`, `focusRingInset`.
131
133
 
134
+ `PoseInput` edits an SE(3) pose in the wire form `{ rotation: [qx, qy, qz, qw], translation:
135
+ [tx, ty, tz] }` as a translation and roll/pitch/yaw in degrees, but does no rotation
136
+ mathematics: the quaternion ↔ angles conversion — and so what "roll, pitch, yaw" means — is
137
+ passed in as a `RotationView` by the package that owns the frame conventions:
138
+
139
+ ```tsx
140
+ <PoseInput
141
+ value={camera.world_se3_self}
142
+ onValueChange={setPose}
143
+ rotationView={{ toEuler: quatToRpy, fromEuler: rpyToQuat }}
144
+ translationUnit="mm"
145
+ />
146
+ ```
147
+
132
148
  ### Density
133
149
 
134
150
  Every primitive with two spacings reads `useDensity()`. The default, `comfortable`, is what
package/dist/index.d.ts CHANGED
@@ -460,18 +460,33 @@ export declare function useControlHeight(): string;
460
460
  * is described by the field's description and error.
461
461
  */
462
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"> & {
463
+ /** Props of `NumberInput`: every `<input>` prop, `ref` included, with numeric bounds and a `unit`. */
464
+ type NumberInputProps = Omit<ComponentProps<"input">, "min" | "max"> & {
470
465
  /** The smallest accepted value. */
471
466
  min?: number | undefined;
472
467
  /** The largest accepted value. */
473
468
  max?: number | undefined;
474
- }): import("react").JSX.Element;
469
+ /**
470
+ * The quantity's unit (`mm`, `m`, `°`, `px`), written inside the field after the number, in
471
+ * mono and muted. It is not part of the value. It is announced as the field's description
472
+ * (`aria-describedby`): after the caller's own `aria-describedby`, before a surrounding
473
+ * `Field`'s description and error.
474
+ */
475
+ unit?: string | undefined;
476
+ };
477
+ /**
478
+ * A number, with the schema's own bounds attached and, optionally, its unit written in the
479
+ * field.
480
+ *
481
+ * `font-mono` because these are read as quantities and compared down a column. Takes every
482
+ * `<input>` prop, `ref` included (`type` is always `number`); `className`, `style` and `ref`
483
+ * always go to the `<input>`.
484
+ *
485
+ * With a `unit`, the input sits in a `relative` full-width wrapper carrying `data-unit`, keeps
486
+ * room on its right for the unit, and is described by it; size such a field through its
487
+ * container. Without one — or with `""` — the markup is exactly the plain input.
488
+ */
489
+ export declare function NumberInput({ unit, ...props }: NumberInputProps): import("react").JSX.Element;
475
490
  /**
476
491
  * Multi-line text — set in mono, for JSON and lists. Takes every `<textarea>` prop, `ref`
477
492
  * included.
@@ -572,6 +587,71 @@ export declare function ReadoutStrip({ items, className }: {
572
587
  className?: string | undefined;
573
588
  }): import("react").JSX.Element | null;
574
589
  //#endregion
590
+ //#region src/components/ui/PoseInput.d.ts
591
+ /** Three numbers: a translation `[tx, ty, tz]`, or Euler angles. */
592
+ type Vec3 = readonly [number, number, number];
593
+ /** A unit quaternion in the wire order `[qx, qy, qz, qw]` (scalar last). */
594
+ type Quaternion = readonly [number, number, number, number];
595
+ /**
596
+ * An SE(3) pose in the wire form (`a_se3_b` as nalgebra's `Isometry3` serialises it):
597
+ * translation in metres, rotation as a unit quaternion `[qx, qy, qz, qw]`.
598
+ */
599
+ interface PoseValue {
600
+ /** The rotation, `[qx, qy, qz, qw]`. */
601
+ rotation: Quaternion;
602
+ /** The translation `[tx, ty, tz]`, in metres. */
603
+ translation: Vec3;
604
+ }
605
+ /**
606
+ * How a rotation is shown as three angles: the conversion both ways, and the angles' names.
607
+ * Supplied by the package that owns the frame conventions; `PoseInput` only calls it.
608
+ */
609
+ interface RotationView {
610
+ /** The quaternion as three angles, in radians, in the order of `labels`. */
611
+ toEuler: (rotation: Quaternion) => Vec3;
612
+ /** Three angles in radians, in the order of `labels`, as a unit quaternion `[qx, qy, qz, qw]`. */
613
+ fromEuler: (angles: Vec3) => Quaternion;
614
+ /** The angles' names, shown before each field. Defaults to roll, pitch, yaw. */
615
+ labels?: readonly [string, string, string] | undefined;
616
+ }
617
+ /** Props of `PoseInput`. */
618
+ interface PoseInputProps {
619
+ /** The pose, in the wire form. */
620
+ value: PoseValue;
621
+ /** Called with a new pose (new arrays) whenever a component changes. */
622
+ onValueChange?: ((value: PoseValue) => void) | undefined;
623
+ /** The quaternion ↔ angles conversion and the angles' names. Required: see `RotationView`. */
624
+ rotationView: RotationView;
625
+ /** Show and edit the translation in metres (the default) or millimetres. The value is always metres. */
626
+ translationUnit?: "m" | "mm" | undefined;
627
+ /** Decimals of the translation. Defaults to 4 for m, 1 for mm. */
628
+ translationPrecision?: number | undefined;
629
+ /** Decimals of the angles, in degrees. Defaults to 2. */
630
+ rotationPrecision?: number | undefined;
631
+ /** Show a compact mono readout instead of fields. */
632
+ readOnly?: boolean | undefined;
633
+ /** Blocks every field. */
634
+ disabled?: boolean | undefined;
635
+ /** Names the pose (`role="group"`). Defaults to "Pose". */
636
+ "aria-label"?: string | undefined;
637
+ /** Merged with the component's own classes through `cn`. */
638
+ className?: string | undefined;
639
+ }
640
+ /**
641
+ * An SE(3) pose shown and edited as a translation (m or mm) and three angles in degrees,
642
+ * reading and writing the wire form `{ rotation: [qx, qy, qz, qw], translation: [tx, ty, tz] }`.
643
+ *
644
+ * The angles come from `rotationView.toEuler` and go back through `rotationView.fromEuler`.
645
+ * The angles as typed are kept while the pose they produced is the one shown, so a yaw of
646
+ * 190° is not redisplayed as −170° and a gimbal-locked pitch does not scramble the other two
647
+ * angles mid-edit.
648
+ *
649
+ * A `role="group"` named by `aria-label`, holding two `VectorInput` groups ("Translation",
650
+ * "Rotation"). With `readOnly` it renders a `ReadoutStrip` instead
651
+ * (`t 0.1000 0.2000 0.3000 m · rpy 0.00 90.00 0.00 °`). The root carries `data-readonly`.
652
+ */
653
+ export declare function PoseInput({ value, onValueChange, rotationView, translationUnit, translationPrecision, rotationPrecision, readOnly, disabled, "aria-label": ariaLabel, className }: PoseInputProps): import("react").JSX.Element;
654
+ //#endregion
575
655
  //#region src/components/ui/SegmentedControl.d.ts
576
656
  /**
577
657
  * A choice among two or three, shown as a strip of segments (native radios in one group, so
@@ -800,6 +880,97 @@ export declare function ToggleChip({ checked, onCheckedChange, children, swatch,
800
880
  className?: string | undefined;
801
881
  }): import("react").JSX.Element;
802
882
  //#endregion
883
+ //#region src/components/ui/toastStore.d.ts
884
+ /** A toast's kind: a fact (`info`), a completed action (`success`), a caveat (`warn`), a failure (`error`). */
885
+ type ToastTone = "info" | "success" | "warn" | "error";
886
+ /** What `toast()` takes. */
887
+ interface ToastOptions {
888
+ /** One line: what happened. */
889
+ title: string;
890
+ /** More detail, and what to do about it. */
891
+ description?: string | undefined;
892
+ /** Defaults to `info`. */
893
+ tone?: ToastTone | undefined;
894
+ /**
895
+ * How long it stays, in milliseconds, while not hovered or focused. Defaults to 5000
896
+ * (8000 for `error`); `Infinity` keeps it until dismissed.
897
+ */
898
+ duration?: number | undefined;
899
+ /** Replace the toast with this id instead of adding one — for progress that updates in place. */
900
+ id?: string | undefined;
901
+ }
902
+ /** A toast in the store. */
903
+ interface ToastRecord {
904
+ /** Identifies the toast to `dismiss` and to `id` replacement. */
905
+ id: string;
906
+ /** One line: what happened. */
907
+ title: string;
908
+ /** More detail, if any. */
909
+ description: string | undefined;
910
+ /** Its kind. */
911
+ tone: ToastTone;
912
+ /** Milliseconds before it dismisses itself (`Infinity`: never). */
913
+ duration: number;
914
+ }
915
+ /**
916
+ * A toast store: what `toast()` writes to and `<Toaster />` reads. Every member may be called
917
+ * detached.
918
+ */
919
+ interface ToastStore {
920
+ /** Add a toast (or replace the one with `options.id`), returning its id. */
921
+ readonly toast: (options: ToastOptions) => string;
922
+ /** Remove one toast, or every toast when `id` is omitted. */
923
+ readonly dismiss: (id?: string) => void;
924
+ /** The toasts, oldest first. The same array until the store changes. */
925
+ readonly getSnapshot: () => readonly ToastRecord[];
926
+ /** Be told when the toasts change; returns the unsubscribe function. */
927
+ readonly subscribe: (listener: () => void) => () => void;
928
+ }
929
+ /**
930
+ * A new, empty toast store. The package keeps one (behind `toast()` and the default
931
+ * `<Toaster />`); create another only to isolate a region or a test.
932
+ *
933
+ * @returns The store.
934
+ */
935
+ export declare function createToastStore(): ToastStore;
936
+ /** The store behind `toast()` and a `<Toaster />` given no `store`. */
937
+ export declare const defaultToastStore: ToastStore;
938
+ /**
939
+ * Raise a notification, from anywhere — shown by the mounted `<Toaster />`.
940
+ *
941
+ * `toast({ title: "Scenario loaded", tone: "success" })`. Pass `id` to update a toast in
942
+ * place (a progress message that becomes a result). `toast.dismiss(id)` removes one early,
943
+ * `toast.dismiss()` all of them.
944
+ *
945
+ * @param options - The message, its tone and its duration.
946
+ * @returns The toast's id.
947
+ */
948
+ export declare const toast: ((options: ToastOptions) => string) & {
949
+ /** Remove one toast, or every toast when `id` is omitted. */
950
+ dismiss: (id?: string) => void;
951
+ };
952
+ //#endregion
953
+ //#region src/components/ui/Toaster.d.ts
954
+ /** Props of `Toaster`. */
955
+ interface ToasterProps {
956
+ /** The store to show. Defaults to the one behind `toast()`. */
957
+ store?: ToastStore | undefined;
958
+ /** How many toasts are shown at once; older ones wait. Defaults to 5. */
959
+ limit?: number | undefined;
960
+ /** Merged with the stack's own classes through `cn` (it is fixed to the bottom-right corner). */
961
+ className?: string | undefined;
962
+ }
963
+ /**
964
+ * The stack of notifications raised with `toast()`. Mount it once, near the root.
965
+ *
966
+ * Fixed to the bottom-right corner, newest at the bottom. Each toast dismisses itself after
967
+ * its `duration`; the countdown pauses while the pointer is over the stack or focus is inside
968
+ * it. A `section` landmark ("Notifications") holding a polite live region, so new toasts are
969
+ * announced; an `error` toast is a `role="alert"`, announced at once. Each toast carries
970
+ * `data-tone` and a "Dismiss" button.
971
+ */
972
+ export declare function Toaster({ store, limit, className }: ToasterProps): import("react").JSX.Element;
973
+ //#endregion
803
974
  //#region src/components/ui/Tooltip.d.ts
804
975
  /**
805
976
  * Shares one open delay (200 ms) across the tooltips below it. Mount it once near the root:
@@ -840,6 +1011,46 @@ export declare function InfoHint({ children, label, icon: Icon }: {
840
1011
  icon?: LucideIcon | undefined;
841
1012
  }): import("react").JSX.Element;
842
1013
  //#endregion
1014
+ //#region src/components/ui/VectorInput.d.ts
1015
+ /** Props of `VectorInput`. */
1016
+ interface VectorInputProps {
1017
+ /** The components. Its length is the number of fields. */
1018
+ value: readonly number[];
1019
+ /** Called with the whole vector (a new array) whenever one component changes to a valid number. */
1020
+ onValueChange?: ((value: number[]) => void) | undefined;
1021
+ /** Each component's label, shown before its field and naming it. Defaults to x, y, z, w. */
1022
+ labels?: readonly string[] | undefined;
1023
+ /** The unit of every component, shown once after the row (`m`, `mm`, `°`). */
1024
+ unit?: string | undefined;
1025
+ /** The arrow-key step. Defaults to `any` (the browser then steps by 1). */
1026
+ step?: number | undefined;
1027
+ /** Decimals shown when a field is not being edited. Defaults to 3. */
1028
+ precision?: number | undefined;
1029
+ /** The smallest accepted component. */
1030
+ min?: number | undefined;
1031
+ /** The largest accepted component. */
1032
+ max?: number | undefined;
1033
+ /** Show the values as a compact mono readout instead of fields. */
1034
+ readOnly?: boolean | undefined;
1035
+ /** Blocks every field. */
1036
+ disabled?: boolean | undefined;
1037
+ /** Names the group (`role="group"`). */
1038
+ "aria-label": string;
1039
+ /** Merged with the row's own classes through `cn`. */
1040
+ className?: string | undefined;
1041
+ }
1042
+ /**
1043
+ * A row of number fields editing one small vector, with the axis labels before the fields
1044
+ * and the unit once after them.
1045
+ *
1046
+ * Controlled. A `role="group"` named by `aria-label`, each field named by its label and
1047
+ * described by the unit. While a field has focus it shows what is typed; on blur or Enter it
1048
+ * shows the value at `precision`; Escape restores the value it had on focus. With `readOnly`
1049
+ * it renders a `ReadoutStrip` (`x 0.100 m · y …`) instead. The row carries `data-readonly`
1050
+ * and `data-disabled`.
1051
+ */
1052
+ export declare function VectorInput({ value, onValueChange, labels, unit, step, precision, min, max, readOnly, disabled, "aria-label": ariaLabel, className }: VectorInputProps): import("react").JSX.Element;
1053
+ //#endregion
843
1054
  //#region src/components/Tabs.d.ts
844
1055
  /** One tab of a `Tabs` strip. */
845
1056
  interface TabItem<Id extends string> {
@@ -879,5 +1090,5 @@ export declare function Tabs<Id extends string>({ items, active, onSelect, label
879
1090
  className?: string | undefined;
880
1091
  }): import("react").JSX.Element;
881
1092
  //#endregion
882
- export type { BackLink, ButtonSize, ButtonVariant, CalloutTone, Column, Density, MeasureTone, ReadoutItem, SelectOption, TabItem, ThemeChoice, Tone };
1093
+ export type { BackLink, ButtonSize, ButtonVariant, CalloutTone, Column, Density, MeasureTone, NumberInputProps, PoseInputProps, PoseValue, Quaternion, ReadoutItem, RotationView, SelectOption, TabItem, ThemeChoice, ToastOptions, ToastRecord, ToastStore, ToastTone, ToasterProps, Tone, Vec3, VectorInputProps };
883
1094
  //# sourceMappingURL=index.d.ts.map