@tensor_1/react 2.47.0 → 2.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/README.md +540 -2
  2. package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
  3. package/dist/announce.d.ts +10 -0
  4. package/dist/announce.js +30 -0
  5. package/dist/chart-analytic.cjs +33 -25
  6. package/dist/chart-preset.cjs +23 -15
  7. package/dist/chart.cjs +23 -15
  8. package/dist/combobox-model.d.ts +10 -1
  9. package/dist/combobox-model.js +15 -0
  10. package/dist/command-palette-model.d.ts +44 -0
  11. package/dist/command-palette-model.js +36 -0
  12. package/dist/components/AccountMenu.d.ts +44 -0
  13. package/dist/components/AccountMenu.js +43 -0
  14. package/dist/components/AppShell.d.ts +45 -2
  15. package/dist/components/AppShell.js +116 -5
  16. package/dist/components/AuditLog.d.ts +128 -0
  17. package/dist/components/AuditLog.js +196 -0
  18. package/dist/components/BrandProfiles.js +4 -3
  19. package/dist/components/Breadcrumbs.d.ts +9 -2
  20. package/dist/components/Breadcrumbs.js +3 -2
  21. package/dist/components/Button.d.ts +12 -5
  22. package/dist/components/Button.js +7 -3
  23. package/dist/components/Card.d.ts +18 -1
  24. package/dist/components/Card.js +5 -1
  25. package/dist/components/ChangeHighlight.d.ts +30 -0
  26. package/dist/components/ChangeHighlight.js +21 -0
  27. package/dist/components/ChartCore.js +2 -1
  28. package/dist/components/Checkbox.d.ts +27 -1
  29. package/dist/components/Checkbox.js +31 -2
  30. package/dist/components/Chip.d.ts +7 -3
  31. package/dist/components/Chip.js +2 -2
  32. package/dist/components/CodeDisplay.d.ts +65 -0
  33. package/dist/components/CodeDisplay.js +124 -0
  34. package/dist/components/Combobox.d.ts +27 -6
  35. package/dist/components/Combobox.js +195 -58
  36. package/dist/components/CommandPalette.d.ts +36 -1
  37. package/dist/components/CommandPalette.js +254 -2
  38. package/dist/components/ContactCard.d.ts +66 -0
  39. package/dist/components/ContactCard.js +74 -0
  40. package/dist/components/Count.d.ts +24 -0
  41. package/dist/components/Count.js +32 -0
  42. package/dist/components/DashboardCanvas.js +14 -0
  43. package/dist/components/DataWorkspace.d.ts +167 -0
  44. package/dist/components/DataWorkspace.js +380 -0
  45. package/dist/components/DateFieldParts.d.ts +2 -1
  46. package/dist/components/DateFieldParts.js +7 -0
  47. package/dist/components/DatePicker.d.ts +9 -2
  48. package/dist/components/DatePicker.js +67 -28
  49. package/dist/components/DateRangePicker.d.ts +12 -2
  50. package/dist/components/DateRangePicker.js +13 -12
  51. package/dist/components/DetailPage.d.ts +7 -1
  52. package/dist/components/DetailPage.js +2 -1
  53. package/dist/components/Dialogs.d.ts +71 -10
  54. package/dist/components/Dialogs.js +130 -30
  55. package/dist/components/DiffViewer.d.ts +23 -3
  56. package/dist/components/DiffViewer.js +26 -12
  57. package/dist/components/DiscardPrompt.js +16 -0
  58. package/dist/components/DisplayPreferences.d.ts +83 -0
  59. package/dist/components/DisplayPreferences.js +185 -0
  60. package/dist/components/EditorialHome.d.ts +23 -3
  61. package/dist/components/EditorialHome.js +16 -11
  62. package/dist/components/EntityPicker.d.ts +20 -3
  63. package/dist/components/EntityPicker.js +32 -13
  64. package/dist/components/FilterEditor.d.ts +18 -4
  65. package/dist/components/FilterEditor.js +141 -19
  66. package/dist/components/FocusOnMount.d.ts +282 -0
  67. package/dist/components/FocusOnMount.js +12 -0
  68. package/dist/components/InfoTip.js +11 -12
  69. package/dist/components/InlineEdit.d.ts +87 -20
  70. package/dist/components/InlineEdit.js +197 -54
  71. package/dist/components/Input.d.ts +12 -2
  72. package/dist/components/Input.js +5 -3
  73. package/dist/components/Kanban.d.ts +14 -3
  74. package/dist/components/Kanban.js +9 -4
  75. package/dist/components/Kbd.d.ts +6 -1
  76. package/dist/components/Kbd.js +6 -6
  77. package/dist/components/Lightbox.d.ts +41 -5
  78. package/dist/components/Lightbox.js +95 -9
  79. package/dist/components/MatchReview.d.ts +40 -1
  80. package/dist/components/MatchReview.js +130 -4
  81. package/dist/components/MetricInput.js +3 -2
  82. package/dist/components/Modal.d.ts +2 -1
  83. package/dist/components/Modal.js +13 -4
  84. package/dist/components/MotionList.d.ts +303 -0
  85. package/dist/components/MotionList.js +175 -0
  86. package/dist/components/NavFavourites.d.ts +5 -0
  87. package/dist/components/NavFavourites.js +7 -7
  88. package/dist/components/OtpInput.d.ts +33 -0
  89. package/dist/components/OtpInput.js +123 -0
  90. package/dist/components/PageHeader.d.ts +7 -1
  91. package/dist/components/PageHeader.js +2 -2
  92. package/dist/components/Pagination.js +6 -5
  93. package/dist/components/PasswordInput.d.ts +34 -0
  94. package/dist/components/PasswordInput.js +101 -0
  95. package/dist/components/Peek.js +3 -3
  96. package/dist/components/Popover.js +11 -0
  97. package/dist/components/PrintLayout.d.ts +63 -0
  98. package/dist/components/PrintLayout.js +114 -0
  99. package/dist/components/QrCode.d.ts +47 -0
  100. package/dist/components/QrCode.js +55 -0
  101. package/dist/components/QrScanner.d.ts +95 -0
  102. package/dist/components/QrScanner.js +266 -0
  103. package/dist/components/RelationshipGraph.d.ts +32 -1
  104. package/dist/components/RelationshipGraph.js +22 -6
  105. package/dist/components/RelationshipGraphLayered.d.ts +1 -1
  106. package/dist/components/RelationshipGraphLayered.js +268 -118
  107. package/dist/components/RelationshipGraphList.js +33 -6
  108. package/dist/components/RuleBuilder.js +39 -7
  109. package/dist/components/SchedulingAgenda.d.ts +3 -1
  110. package/dist/components/SchedulingCalendar.d.ts +3 -1
  111. package/dist/components/SchedulingCalendar.js +43 -4
  112. package/dist/components/SchedulingResourceDay.d.ts +57 -0
  113. package/dist/components/SchedulingResourceDay.js +287 -0
  114. package/dist/components/SchedulingTimeGrid.d.ts +3 -1
  115. package/dist/components/SchedulingTimeline.d.ts +3 -1
  116. package/dist/components/SearchSuggest.d.ts +23 -3
  117. package/dist/components/SearchSuggest.js +56 -18
  118. package/dist/components/Segmented.d.ts +39 -4
  119. package/dist/components/Segmented.js +39 -5
  120. package/dist/components/Select.d.ts +3 -2
  121. package/dist/components/Select.js +7 -1
  122. package/dist/components/ShortcutHint.d.ts +11 -10
  123. package/dist/components/ShortcutHint.js +44 -38
  124. package/dist/components/Sidebar.d.ts +36 -4
  125. package/dist/components/Sidebar.js +37 -12
  126. package/dist/components/SortableList.d.ts +45 -0
  127. package/dist/components/SortableList.js +147 -0
  128. package/dist/components/Stat.d.ts +28 -3
  129. package/dist/components/Stat.js +13 -3
  130. package/dist/components/States.d.ts +33 -1
  131. package/dist/components/States.js +43 -1
  132. package/dist/components/StatusPill.d.ts +14 -3
  133. package/dist/components/StatusPill.js +11 -3
  134. package/dist/components/Steps.js +2 -1
  135. package/dist/components/Table.d.ts +9 -2
  136. package/dist/components/Table.js +4 -2
  137. package/dist/components/TableControls.js +18 -58
  138. package/dist/components/TableOverflowProbe.d.ts +12 -0
  139. package/dist/components/TableOverflowProbe.js +27 -0
  140. package/dist/components/Tabs.d.ts +2 -1
  141. package/dist/components/Tabs.js +10 -2
  142. package/dist/components/TagInput.d.ts +67 -0
  143. package/dist/components/TagInput.js +342 -0
  144. package/dist/components/Textarea.d.ts +5 -0
  145. package/dist/components/Textarea.js +2 -2
  146. package/dist/components/TimePicker.d.ts +80 -0
  147. package/dist/components/TimePicker.js +347 -0
  148. package/dist/components/Toast.d.ts +1 -1
  149. package/dist/components/Toast.js +109 -4
  150. package/dist/components/Tooltip.d.ts +8 -4
  151. package/dist/components/Tooltip.js +86 -28
  152. package/dist/components/Tree.d.ts +8 -0
  153. package/dist/components/Tree.js +11 -5
  154. package/dist/display-preferences-model.d.ts +53 -0
  155. package/dist/display-preferences-model.js +64 -0
  156. package/dist/emoji.cjs +143 -49
  157. package/dist/filter-model.d.ts +59 -3
  158. package/dist/filter-model.js +67 -2
  159. package/dist/hooks/useAnchoredPosition.js +14 -2
  160. package/dist/hooks/useChangeHighlight.d.ts +33 -0
  161. package/dist/hooks/useChangeHighlight.js +44 -0
  162. package/dist/hooks/useDeferredReorder.d.ts +32 -0
  163. package/dist/hooks/useDeferredReorder.js +47 -0
  164. package/dist/hooks/useDialog.d.ts +5 -0
  165. package/dist/hooks/useDialog.js +17 -3
  166. package/dist/hooks/useKeyboardGrab.d.ts +58 -0
  167. package/dist/hooks/useKeyboardGrab.js +118 -0
  168. package/dist/hooks/useOptimisticValue.d.ts +51 -0
  169. package/dist/hooks/useOptimisticValue.js +61 -0
  170. package/dist/hooks/useReducedMotion.d.ts +4 -1
  171. package/dist/hooks/useReducedMotion.js +7 -1
  172. package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
  173. package/dist/hooks/useResolvedFilterLabels.js +34 -0
  174. package/dist/hooks/useSlidingIndicator.d.ts +18 -0
  175. package/dist/hooks/useSlidingIndicator.js +80 -0
  176. package/dist/hooks/useVirtualRows.d.ts +21 -2
  177. package/dist/hooks/useVirtualRows.js +30 -3
  178. package/dist/icons/Icon.d.ts +1 -1
  179. package/dist/index.cjs +19687 -13423
  180. package/dist/index.d.cts +3419 -651
  181. package/dist/index.d.ts +48 -18
  182. package/dist/index.js +153 -9
  183. package/dist/labels-context.d.ts +93 -3
  184. package/dist/motion.d.ts +11 -0
  185. package/dist/motion.js +35 -0
  186. package/dist/overlay-stack.d.ts +6 -0
  187. package/dist/overlay-stack.js +13 -0
  188. package/dist/qr-model.d.ts +65 -0
  189. package/dist/qr-model.js +323 -0
  190. package/dist/relationship-graph-model.d.ts +92 -3
  191. package/dist/relationship-graph-model.js +134 -15
  192. package/dist/reorder.d.ts +30 -0
  193. package/dist/reorder.js +74 -0
  194. package/dist/rich-text.cjs +46 -4
  195. package/dist/scheduling-resource-model.d.ts +76 -0
  196. package/dist/scheduling-resource-model.js +74 -0
  197. package/dist/search-suggest-model.js +3 -1
  198. package/dist/shell-context.d.ts +11 -0
  199. package/dist/shell-context.js +7 -0
  200. package/dist/time-field-model.d.ts +46 -0
  201. package/dist/time-field-model.js +152 -0
  202. package/dist/toast-store.d.ts +22 -3
  203. package/dist/toast-store.js +26 -9
  204. package/package.json +2 -1
@@ -39,19 +39,23 @@ const Button = forwardRef(
39
39
  ...props,
40
40
  children: [
41
41
  loading !== void 0 || result !== void 0 ? /* @__PURE__ */ jsx(ButtonFeedback, { loading: !!loading, loadingDelay, result, resultLabels, children }) : children,
42
- shortcut && shortcut.length > 0 && /* @__PURE__ */ jsx(ShortcutHint, { keys: shortcut, label: icon ? props["aria-label"] : void 0, ownsAria: props["aria-keyshortcuts"] === void 0 })
42
+ shortcut && shortcut.length > 0 && /* @__PURE__ */ jsx(ShortcutHint, { keys: shortcut, label: icon ? props["aria-label"] : void 0, describedBy: props["aria-describedby"] })
43
43
  ]
44
44
  }
45
45
  );
46
46
  }
47
47
  );
48
48
  Button.displayName = "Button";
49
- function ButtonLink({ href, variant = "secondary", size = "md", icon = false, wrap = false, disabled = false, renderLink, className, children, onClick, ...props }) {
49
+ function ButtonLink({ href, variant = "secondary", size = "md", icon = false, wrap = false, disabled = false, renderLink, shortcut, className, children, onClick, ...props }) {
50
50
  const classes = buttonClasses(variant, size, icon, wrap, className);
51
51
  if (disabled) {
52
52
  return /* @__PURE__ */ jsx("a", { ...props, className: classes, role: "link", "aria-disabled": "true", children });
53
53
  }
54
- return /* @__PURE__ */ jsx(Fragment, { children: renderUixLink(renderLink, { ...props, href, onClick, className: classes, children }) });
54
+ const content = shortcut && shortcut.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
55
+ children,
56
+ /* @__PURE__ */ jsx(ShortcutHint, { keys: shortcut, label: icon ? props["aria-label"] : void 0, describedBy: props["aria-describedby"] })
57
+ ] }) : children;
58
+ return /* @__PURE__ */ jsx(Fragment, { children: renderUixLink(renderLink, { ...props, href, onClick, className: classes, children: content }) });
55
59
  }
56
60
  function ButtonGroup({ children, className }) {
57
61
  return /* @__PURE__ */ jsx("div", { className: cx("uix-btn-group", className), children });
@@ -33,5 +33,22 @@ interface CardLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 't
33
33
  }
34
34
  /** A whole-card anchor with visible hover and keyboard focus affordances. */
35
35
  declare function CardLink({ title, subtitle, headerAction, footer, children, className, ...props }: CardLinkProps): react.JSX.Element;
36
+ interface InsetProps extends HTMLAttributes<HTMLDivElement> {
37
+ /** `md` (default) pads with `--uix-space-4`; `sm` with `--uix-space-3`, for a dense card. */
38
+ padding?: 'sm' | 'md';
39
+ /**
40
+ * `sunken` (default) is `--uix-bg-subtle` on the card's surface; `surface` is for a well on a
41
+ * panel that is already subtle.
42
+ */
43
+ tone?: 'sunken' | 'surface';
44
+ children?: ReactNode;
45
+ }
46
+ /**
47
+ * A bordered, sunken well inside a card body (HAR-1818), over `.uix-inset`: for a preview or a
48
+ * specimen (a rendered header, an image crop, a code sample). Use it instead of a `Card` in a
49
+ * `Card`, which brings a second shadow, card padding and card semantics. It lays out nothing:
50
+ * the content is yours. Give it `role="group"` and an `aria-label` when the preview needs a name.
51
+ */
52
+ declare function Inset({ padding, tone, children, className, ...props }: InsetProps): react.JSX.Element;
36
53
 
37
- export { Card, CardLink, type CardLinkProps, type CardProps };
54
+ export { Card, CardLink, type CardLinkProps, type CardProps, Inset, type InsetProps };
@@ -34,7 +34,11 @@ function CardLink({ title, subtitle, headerAction, footer, children, className,
34
34
  footer != null && /* @__PURE__ */ jsx("div", { className: "uix-card__footer", children: footer })
35
35
  ] });
36
36
  }
37
+ function Inset({ padding = "md", tone = "sunken", children, className, ...props }) {
38
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-inset", padding === "sm" && "uix-inset--sm", tone === "surface" && "uix-inset--surface", className), ...props, children });
39
+ }
37
40
  export {
38
41
  Card,
39
- CardLink
42
+ CardLink,
43
+ Inset
40
44
  };
@@ -0,0 +1,30 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface ChangeHighlightProps extends HTMLAttributes<HTMLElement> {
5
+ /** The value shown inside. When it changes after mount the element flashes; never on the first render. */
6
+ value: unknown;
7
+ /** The element to render (default `span`): `td`, `tr`, `div`, `li`… A `tr` tints its cells. */
8
+ as?: keyof HTMLElementTagNameMap;
9
+ /**
10
+ * `tint` (default) fades a background tint, for a row, a cell or a field; `ring` fades an
11
+ * outline instead, for something with a colour of its own, such as a status pill.
12
+ */
13
+ variant?: 'tint' | 'ring';
14
+ /** Said politely when the value changes, e.g. "Status changed to Resolved by A. Novak". */
15
+ announce?: string;
16
+ /** No highlight, e.g. while the person is editing this value themselves. */
17
+ disabled?: boolean;
18
+ children?: ReactNode;
19
+ }
20
+ /**
21
+ * Shows that a value changed in place (HAR-2585, motion standard §2.5): a live update, the
22
+ * result of a transition, a rolled-back optimistic change. The element is tinted at once and
23
+ * the tint fades; under reduced motion or forced colours it is outlined for the same time
24
+ * instead. The flash is the signal and its place the location; pass `announce` for the
25
+ * description. For an element you render yourself, use `useChangeHighlight`; on plain markup,
26
+ * add `.uix-changed` (remove it, force a reflow and add it again to replay).
27
+ */
28
+ declare const ChangeHighlight: react.ForwardRefExoticComponent<ChangeHighlightProps & react.RefAttributes<HTMLElement | null>>;
29
+
30
+ export { ChangeHighlight, type ChangeHighlightProps };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { createElement, forwardRef, useCallback } from "react";
3
+ import { cx } from "../cx.js";
4
+ import { useChangeHighlight } from "../hooks/useChangeHighlight.js";
5
+ const ChangeHighlight = forwardRef(function ChangeHighlight2({ value, as = "span", variant = "tint", announce, disabled, className, children, ...props }, forwarded) {
6
+ const ref = useChangeHighlight(value, { announce, disabled });
7
+ const setRef = useCallback((node) => {
8
+ ref.current = node;
9
+ if (typeof forwarded === "function") forwarded(node);
10
+ else if (forwarded) forwarded.current = node;
11
+ }, [ref, forwarded]);
12
+ return createElement(as, {
13
+ ...props,
14
+ ref: setRef,
15
+ className: cx("uix-change-highlight", variant === "ring" && "uix-change-highlight--ring", className)
16
+ }, children);
17
+ });
18
+ ChangeHighlight.displayName = "ChangeHighlight";
19
+ export {
20
+ ChangeHighlight
21
+ };
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useId, useRef } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  import { mergeChartTheme, uixChartTheme, uixChartTokens } from "../chart-theme.js";
6
+ import { prefersReducedMotion } from "../hooks/useReducedMotion.js";
6
7
  function readToken(name) {
7
8
  if (typeof getComputedStyle === "undefined") return "";
8
9
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -15,7 +16,7 @@ function themed(option, theme, element) {
15
16
  return theme === "none" ? resolved : mergeChartTheme(uixChartTheme(element), resolved);
16
17
  }
17
18
  function motionSafe(option) {
18
- if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
19
+ if (prefersReducedMotion()) {
19
20
  return { ...option, animation: false };
20
21
  }
21
22
  return option;
@@ -6,5 +6,31 @@ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'typ
6
6
  indeterminate?: boolean;
7
7
  }
8
8
  declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLInputElement>>;
9
+ interface CheckboxGroupProps {
10
+ children: ReactNode;
11
+ /** The group's name, rendered as the `<legend>`: announced with each checkbox (HAR-1799). */
12
+ label: ReactNode;
13
+ /** Help under the boxes, tied to the group with `aria-describedby`. */
14
+ hint?: ReactNode;
15
+ /** Shown instead of the hint and announced (`role="alert"`). */
16
+ error?: ReactNode;
17
+ /** Draws the required marker after the legend. Which boxes must be ticked is your rule to check. */
18
+ required?: boolean;
19
+ /** Disables every checkbox in the group (a disabled `<fieldset>`). */
20
+ disabled?: boolean;
21
+ /**
22
+ * The most columns the boxes form. Default 1. A column is never narrower than 10rem: on a phone,
23
+ * or in a narrow card, the boxes fall back to fewer columns.
24
+ */
25
+ columns?: number;
26
+ id?: string;
27
+ className?: string;
28
+ }
29
+ /**
30
+ * Checkboxes under one name (HAR-1799; MOTUS UXR-05): a `<fieldset>` whose `<legend>` names the
31
+ * group, so a screen reader says "Tags, group" before each box, with the field's hint and error
32
+ * slot below. The checkbox counterpart of `<RadioGroup label>`. Put `Checkbox`es inside.
33
+ */
34
+ declare function CheckboxGroup({ children, label, hint, error, required, disabled, columns, id, className }: CheckboxGroupProps): react.JSX.Element;
9
35
 
10
- export { Checkbox, type CheckboxProps };
36
+ export { Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { forwardRef, useEffect, useRef } from "react";
3
+ import { forwardRef, useEffect, useId, useRef } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  const CheckIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("polyline", { points: "1.5,6 4.5,9 10.5,3" }) });
6
6
  const IndeterminateIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ jsx("rect", { x: "2", y: "5", width: "8", height: "2", rx: "1", fill: "currentColor" }) });
@@ -30,6 +30,35 @@ const Checkbox = forwardRef(
30
30
  }
31
31
  );
32
32
  Checkbox.displayName = "Checkbox";
33
+ function CheckboxGroup({ children, label, hint, error, required, disabled, columns = 1, id, className }) {
34
+ const uid = useId();
35
+ const msgId = `${id ?? uid}-msg`;
36
+ const message = error != null && error !== false ? error : hint;
37
+ const cols = Math.max(1, Math.floor(columns));
38
+ return /* @__PURE__ */ jsxs(
39
+ "fieldset",
40
+ {
41
+ id,
42
+ className: cx("uix-checkbox-group", className),
43
+ disabled,
44
+ "aria-describedby": message != null && message !== false ? msgId : void 0,
45
+ "data-invalid": error != null && error !== false || void 0,
46
+ children: [
47
+ /* @__PURE__ */ jsx("legend", { className: "uix-checkbox-group__legend", "data-required": required || void 0, children: label }),
48
+ /* @__PURE__ */ jsx(
49
+ "div",
50
+ {
51
+ className: "uix-checkbox-group__options",
52
+ style: cols > 1 ? { "--uix-checkbox-group-columns": cols } : void 0,
53
+ children
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx("div", { className: "uix-field__msg", id: msgId, children: error != null && error !== false ? /* @__PURE__ */ jsx("span", { className: "uix-field__error", role: "alert", children: error }) : hint != null && hint !== false ? /* @__PURE__ */ jsx("span", { className: "uix-field__hint", children: hint }) : null })
57
+ ]
58
+ }
59
+ );
60
+ }
33
61
  export {
34
- Checkbox
62
+ Checkbox,
63
+ CheckboxGroup
35
64
  };
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { HTMLAttributes, ReactNode } from 'react';
2
+ import { HTMLAttributes, ReactNode, MouseEvent } from 'react';
3
3
  import { UixRenderLink } from '../link.js';
4
4
 
5
5
  interface ChipProps extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
@@ -11,8 +11,12 @@ interface ChipProps extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
11
11
  /** Toggle chip: `aria-pressed`, filled while on. Pair with `onPressedChange`. */
12
12
  pressed?: boolean;
13
13
  onPressedChange?: (pressed: boolean) => void;
14
- /** A chip that acts in place (e.g. opens the filter it shows for editing). */
15
- onClick?: () => void;
14
+ /**
15
+ * A chip that acts in place (e.g. opens the filter it shows for editing). Gets the click event,
16
+ * so a chip can be the `trigger` of a `Menu` (which reads `defaultPrevented`); a handler that
17
+ * takes no argument still works. Not called while `disabled`.
18
+ */
19
+ onClick?: (event: MouseEvent<HTMLElement>) => void;
16
20
  /** A link chip. */
17
21
  href?: string;
18
22
  renderLink?: UixRenderLink;
@@ -30,10 +30,10 @@ const Chip = forwardRef(function Chip2({
30
30
  /* @__PURE__ */ jsx("span", { className: "uix-chip__label", children }),
31
31
  count != null && /* @__PURE__ */ jsx("span", { className: "uix-chip__count", children: count })
32
32
  ] });
33
- const handleClick = () => {
33
+ const handleClick = (event) => {
34
34
  if (disabled) return;
35
35
  if (toggle) onPressedChange?.(!pressed);
36
- onClick?.();
36
+ onClick?.(event);
37
37
  };
38
38
  const outer = cx(
39
39
  "uix-chip",
@@ -0,0 +1,65 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ /** The words of a `CodeDisplay` (HAR-1253). */
5
+ interface CodeDisplayLabels {
6
+ /** The live countdown; `{time}` is `m:ss` (or `h:mm:ss`). */
7
+ expiresIn: string;
8
+ /** Announced once when `warnBefore` seconds are left; `{time}` is that time as `m:ss`. */
9
+ expiresSoon: string;
10
+ /** Shown, and announced once, when the code has expired. Text, so the state never rests on colour. */
11
+ expired: string;
12
+ /** The copy button's accessible name. */
13
+ copy: string;
14
+ /** Announced after a copy. */
15
+ copied: string;
16
+ /** Announced when the copy failed. */
17
+ copyFailed: string;
18
+ }
19
+ declare const DEFAULT_CODE_DISPLAY_LABELS: CodeDisplayLabels;
20
+ interface CodeDisplayProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
21
+ /**
22
+ * The code, e.g. `"K7Q4PX9MW"` or `"K7Q-4PX-9MW"`. A code that already contains spaces or
23
+ * hyphens is shown in those groups; otherwise it is cut into groups of `groupSize`.
24
+ */
25
+ code: string;
26
+ /** The visible name above the code, e.g. "Pairing code"; it also names the group. */
27
+ label: ReactNode;
28
+ /** Characters per group, or the length of each group in turn (`[4, 4, 4]`). Default 3. */
29
+ groupSize?: number | readonly number[];
30
+ /** Drawn between groups, never read out. Default `-`. */
31
+ separator?: string;
32
+ /** `md` (default) for a panel; `lg` for a code read across a counter. */
33
+ size?: 'md' | 'lg';
34
+ /** When the code stops working (an ISO instant, epoch milliseconds or a Date): shows a countdown, then the expired state. */
35
+ expiresAt?: string | number | Date;
36
+ /**
37
+ * Seconds before expiry when the countdown turns to a warning and a screen reader hears it
38
+ * once (`labels.expiresSoon`). Default 60; 0 turns the warning off.
39
+ */
40
+ warnBefore?: number;
41
+ /** Called once when the countdown reaches zero while the component is on screen. */
42
+ onExpire?: () => void;
43
+ /** Adds a copy button while the code is live. */
44
+ copyable?: boolean;
45
+ /**
46
+ * What a screen reader says for one character. Default: the character itself, so "K7Q" is
47
+ * read "K, 7, Q". Use it for a spelling alphabet or to tell O from 0.
48
+ */
49
+ spell?: (character: string) => string;
50
+ /** The words. Default: `UixLabelsProvider` `codeDisplay`, then English. */
51
+ labels?: Partial<CodeDisplayLabels>;
52
+ }
53
+ /** The code in its display groups. */
54
+ declare function codeGroups(code: string, groupSize?: number | readonly number[]): string[];
55
+ /** `m:ss`, or `h:mm:ss` from an hour up, for a number of whole seconds. */
56
+ declare function formatCountdown(seconds: number): string;
57
+ /**
58
+ * A one-time code shown large (HAR-1253; SHOPx PRE-08 till pairing, the shopper's voucher):
59
+ * monospace characters in groups, spelled character by character to a screen reader, an
60
+ * optional countdown that ends in an expired state said in words, and an optional copy button.
61
+ * The consumer makes the code and enforces its expiry; this only shows it.
62
+ */
63
+ declare function CodeDisplay({ code, label, groupSize, separator, size, expiresAt, warnBefore, onExpire, copyable, spell, labels: labelsProp, className, ...props }: CodeDisplayProps): react.JSX.Element;
64
+
65
+ export { CodeDisplay, type CodeDisplayLabels, type CodeDisplayProps, DEFAULT_CODE_DISPLAY_LABELS, codeGroups, formatCountdown };
@@ -0,0 +1,124 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { fillLabel } from "../fill-label.js";
6
+ import { useUixLabels } from "../labels-context.js";
7
+ import { CopyButton } from "./CopyButton.js";
8
+ const DEFAULT_CODE_DISPLAY_LABELS = {
9
+ expiresIn: "Expires in {time}",
10
+ expiresSoon: "{time} left before the code expires",
11
+ expired: "Expired. Ask for a new code.",
12
+ copy: "Copy code",
13
+ copied: "Copied",
14
+ copyFailed: "Could not copy. Select the code instead."
15
+ };
16
+ function codeGroups(code, groupSize = 3) {
17
+ const trimmed = code.trim();
18
+ if (/[\s-]/.test(trimmed)) return trimmed.split(/[\s-]+/).filter(Boolean);
19
+ const sizes = typeof groupSize === "number" ? [groupSize] : groupSize;
20
+ const groups = [];
21
+ for (let i = 0, at = 0; at < trimmed.length; i += 1) {
22
+ const size = Math.max(1, Math.floor(sizes[Math.min(i, sizes.length - 1)] ?? trimmed.length));
23
+ groups.push(trimmed.slice(at, at + size));
24
+ at += size;
25
+ }
26
+ return groups;
27
+ }
28
+ function formatCountdown(seconds) {
29
+ const s = Math.max(0, Math.ceil(seconds));
30
+ const hours = Math.floor(s / 3600);
31
+ const minutes = Math.floor(s % 3600 / 60);
32
+ const rest = String(s % 60).padStart(2, "0");
33
+ return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${rest}` : `${minutes}:${rest}`;
34
+ }
35
+ const instant = (value) => value === void 0 ? void 0 : new Date(value).getTime();
36
+ function CodeDisplay({
37
+ code,
38
+ label,
39
+ groupSize = 3,
40
+ separator = "-",
41
+ size = "md",
42
+ expiresAt,
43
+ warnBefore = 60,
44
+ onExpire,
45
+ copyable = false,
46
+ spell,
47
+ labels: labelsProp,
48
+ className,
49
+ ...props
50
+ }) {
51
+ const uixLabels = useUixLabels();
52
+ const labels = { ...DEFAULT_CODE_DISPLAY_LABELS, ...uixLabels.codeDisplay, ...labelsProp };
53
+ const labelId = useId();
54
+ const deadline = instant(expiresAt);
55
+ const [now, setNow] = useState(() => Date.now());
56
+ const remaining = deadline === void 0 || Number.isNaN(deadline) ? void 0 : deadline - now;
57
+ const expired = remaining !== void 0 && remaining <= 0;
58
+ const warnAt = Math.max(0, warnBefore) * 1e3;
59
+ const phase = remaining === void 0 ? "live" : remaining <= 0 ? "expired" : remaining <= warnAt ? "soon" : "live";
60
+ const [announcement, setAnnouncement] = useState("");
61
+ const shownPhase = useRef(phase);
62
+ const onExpireRef = useRef(onExpire);
63
+ onExpireRef.current = onExpire;
64
+ const labelsRef = useRef(labels);
65
+ labelsRef.current = labels;
66
+ useEffect(() => {
67
+ if (deadline === void 0 || Number.isNaN(deadline)) return void 0;
68
+ let timer;
69
+ const tick = () => {
70
+ const current = Date.now();
71
+ setNow(current);
72
+ const left = deadline - current;
73
+ if (left > 0) timer = setTimeout(tick, left % 1e3 || 1e3);
74
+ };
75
+ tick();
76
+ return () => clearTimeout(timer);
77
+ }, [deadline]);
78
+ useEffect(() => {
79
+ const before = shownPhase.current;
80
+ shownPhase.current = phase;
81
+ if (phase === before) return;
82
+ if (phase === "live") setAnnouncement("");
83
+ else if (phase === "soon" && before === "live") setAnnouncement(fillLabel(labelsRef.current.expiresSoon, { time: formatCountdown(warnAt / 1e3) }));
84
+ else if (phase === "expired") {
85
+ setAnnouncement(labelsRef.current.expired);
86
+ onExpireRef.current?.();
87
+ }
88
+ }, [phase, warnAt]);
89
+ const groups = codeGroups(code, groupSize);
90
+ const say = spell ?? ((character) => character);
91
+ const spoken = groups.map((group) => Array.from(group).map(say).join(" ")).join(", ");
92
+ return /* @__PURE__ */ jsxs(
93
+ "div",
94
+ {
95
+ className: cx("uix-code-display", size === "lg" && "uix-code-display--lg", className),
96
+ role: "group",
97
+ "aria-labelledby": labelId,
98
+ "data-state": phase,
99
+ ...props,
100
+ children: [
101
+ /* @__PURE__ */ jsx("div", { className: "uix-code-display__label", id: labelId, children: label }),
102
+ /* @__PURE__ */ jsxs("div", { className: "uix-code-display__row", children: [
103
+ /* @__PURE__ */ jsx("span", { className: "uix-code-display__code", "aria-hidden": "true", translate: "no", children: groups.map((group, index) => (
104
+ // The separator ends its group, so a wrapped line never starts with one.
105
+ /* @__PURE__ */ jsxs("span", { className: "uix-code-display__group", children: [
106
+ group,
107
+ index < groups.length - 1 && /* @__PURE__ */ jsx("span", { className: "uix-code-display__sep", children: separator })
108
+ ] }, index)
109
+ )) }),
110
+ /* @__PURE__ */ jsx("span", { className: "uix-code-display__spoken uix-visually-hidden", children: spoken }),
111
+ copyable && !expired && /* @__PURE__ */ jsx(CopyButton, { value: groups.join(""), label: labels.copy, copiedLabel: labels.copied, failedLabel: labels.copyFailed })
112
+ ] }),
113
+ remaining !== void 0 && /* @__PURE__ */ jsx("div", { className: "uix-code-display__meta", suppressHydrationWarning: true, children: expired ? labels.expired : fillLabel(labels.expiresIn, { time: formatCountdown(remaining / 1e3) }) }),
114
+ /* @__PURE__ */ jsx("span", { role: "status", className: "uix-visually-hidden", children: announcement })
115
+ ]
116
+ }
117
+ );
118
+ }
119
+ export {
120
+ CodeDisplay,
121
+ DEFAULT_CODE_DISPLAY_LABELS,
122
+ codeGroups,
123
+ formatCountdown
124
+ };
@@ -1,19 +1,40 @@
1
1
  import * as react from 'react';
2
- import { HTMLAttributes } from 'react';
3
- import { ComboboxOption } from '../combobox-model.js';
2
+ import { HTMLAttributes, AriaAttributes } from 'react';
3
+ import { ComboboxOption, ComboboxGroup } from '../combobox-model.js';
4
+ import { Placement } from '../overlay-position.js';
4
5
 
5
6
  interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
6
- options: readonly ComboboxOption[];
7
+ /** The options, and groups of options (`{ label, options }`, HAR-1588). */
8
+ options: readonly (ComboboxOption | ComboboxGroup)[];
7
9
  value?: string;
8
10
  defaultValue?: string;
9
11
  onValueChange?: (value: string, option: ComboboxOption) => void;
12
+ /** The field's name. Not used as its name when `aria-labelledby` points at a visible label. */
10
13
  label: string;
11
14
  placeholder?: string;
15
+ /** Shown in the open list when nothing matches (outside the listbox, so it is never an option). */
12
16
  emptyLabel: string;
13
17
  name?: string;
14
18
  disabled?: boolean;
19
+ /** Ids of the visible label(s) that name the field; replaces `label` as its name. */
20
+ 'aria-labelledby'?: string;
21
+ /** Ids of hint or error text; lands on the input. */
22
+ 'aria-describedby'?: string;
23
+ 'aria-invalid'?: AriaAttributes['aria-invalid'];
24
+ /** Id of the input itself (for a `<label for>`); `id` stays on the wrapper. */
25
+ inputId?: string;
26
+ /** Preferred side of the list. Default `'bottom-start'`; it flips when there is no room. */
27
+ placement?: Placement;
15
28
  }
16
- /** Searchable single-select combobox with keyboard navigation and no framework dependencies. */
17
- declare function Combobox({ options, value, defaultValue, onValueChange, label, placeholder, emptyLabel, name, disabled, className, ...props }: ComboboxProps): react.JSX.Element;
29
+ /**
30
+ * An editable combobox (HAR-1588): type to filter, choose with the keyboard or the pointer. It
31
+ * follows the WAI-ARIA APG combobox with a listbox popup, at parity with `Select`: the list is a
32
+ * top-layer popover placed with `useAnchoredPosition`, so a Drawer, a Popover or an
33
+ * `overflow: hidden` cell never clips it; ↑ ↓ PageUp PageDown move (Home and End too while an
34
+ * option is active), disabled options are skipped and the list does not wrap; Enter chooses;
35
+ * Escape closes the list and puts the chosen option's text back, as leaving the field does.
36
+ * Groups render as `role="group"`; the no-match row sits outside the listbox.
37
+ */
38
+ declare function Combobox({ options, value, defaultValue, onValueChange, label, placeholder, emptyLabel, name, disabled, className, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, inputId, placement, ...props }: ComboboxProps): react.JSX.Element;
18
39
 
19
- export { Combobox, ComboboxOption, type ComboboxProps };
40
+ export { Combobox, ComboboxGroup, ComboboxOption, type ComboboxProps };