@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
@@ -0,0 +1,80 @@
1
+ import * as react from 'react';
2
+ import { AriaAttributes, CSSProperties } from 'react';
3
+ import { Placement } from '../overlay-position.js';
4
+
5
+ /** Every word `TimePicker` renders or announces. */
6
+ interface TimePickerLabels {
7
+ /** The list button's name while no time is set. */
8
+ open: string;
9
+ /** The list button's name with a time set; `{time}` is the time as the field shows it. */
10
+ openSelected: string;
11
+ /** The list of times (the popover's and the listbox's name). */
12
+ list: string;
13
+ /** Shown when the typed text is not a time; `{example}` is a time in the field's own shape. */
14
+ invalidTime: string;
15
+ /** Shown when the typed time is before `min`; `{min}`. */
16
+ tooEarly: string;
17
+ /** Shown when the typed time is after `max`; `{max}`. */
18
+ tooLate: string;
19
+ /** Shown when the typed time is outside an overnight range (`min` after `max`); `{min}`, `{max}`. */
20
+ outside: string;
21
+ }
22
+ declare const DEFAULT_TIME_PICKER_LABELS: TimePickerLabels;
23
+ /** Why typed text was refused: not a time, or a time before `min` / after `max`. */
24
+ type TimeInputProblem = 'format' | 'early' | 'late' | 'outside';
25
+ interface TimePickerProps {
26
+ /** The time as `HH:mm` (24-hour), or `null` for none. */
27
+ value: string | null;
28
+ onValueChange: (value: string | null) => void;
29
+ /** Minutes the arrow keys step the minutes by, and between the times in the list. Default 15. */
30
+ minuteStep?: number;
31
+ /**
32
+ * First and last time that can be chosen (`HH:mm`, inclusive). A `min` after `max` (22:00 to
33
+ * 06:00) is an overnight range: it wraps round midnight, in the keys, the list and the check.
34
+ */
35
+ min?: string;
36
+ max?: string;
37
+ /**
38
+ * The locale that writes the time: 14:30 in `de`, 2:30 PM in `en-US`. Default: `UixLabelsProvider`
39
+ * `dateFormat.locale`, then the viewer's.
40
+ */
41
+ locale?: string;
42
+ /** `true` writes 2:30 PM, `false` 14:30, whatever the locale. */
43
+ hour12?: boolean;
44
+ placeholder?: string;
45
+ size?: 'sm' | 'md';
46
+ disabled?: boolean;
47
+ readOnly?: boolean;
48
+ required?: boolean;
49
+ invalid?: boolean;
50
+ /** Submits the `HH:mm` value under this name (a hidden input), whatever the field shows. */
51
+ name?: string;
52
+ /** Lands on the text input, so a `<label htmlFor>` or `<Field>` names it. */
53
+ id?: string;
54
+ 'aria-label'?: string;
55
+ 'aria-labelledby'?: string;
56
+ 'aria-describedby'?: string;
57
+ 'aria-invalid'?: AriaAttributes['aria-invalid'];
58
+ 'aria-required'?: AriaAttributes['aria-required'];
59
+ /** Where the list opens. Default `'bottom-start'`; it flips and shifts to stay on screen. */
60
+ placement?: Placement;
61
+ /** Told when typed text is refused, and `null` once the field is fine again. */
62
+ onInputProblem?: (problem: TimeInputProblem | null) => void;
63
+ onOpenChange?: (open: boolean) => void;
64
+ /** Translatable words; English defaults in {@link DEFAULT_TIME_PICKER_LABELS}. */
65
+ labels?: Partial<TimePickerLabels>;
66
+ className?: string;
67
+ style?: CSSProperties;
68
+ }
69
+ /**
70
+ * A time of day as a form field (HAR-1835), beside `DatePicker`: a text input that shows the time
71
+ * in the locale's shape (14:30 in German, 2:30 PM in en-US) and reads a typed one (`1430`, `9`,
72
+ * `2:30 pm`) on Enter and when focus leaves it; a clock button opens the times at `minuteStep` in a
73
+ * list. ArrowUp / ArrowDown step the part the caret is in (the hour, or the minutes by
74
+ * `minuteStep`), PageUp / PageDown step the hour, Alt+ArrowDown opens the list. Text that is not a
75
+ * time, or a time outside `min` / `max`, marks the field invalid, says why and leaves `value` alone.
76
+ * The value is `HH:mm`, what `<input type="time">` holds.
77
+ */
78
+ declare const TimePicker: react.ForwardRefExoticComponent<TimePickerProps & react.RefAttributes<HTMLInputElement>>;
79
+
80
+ export { DEFAULT_TIME_PICKER_LABELS, type TimeInputProblem, TimePicker, type TimePickerLabels, type TimePickerProps };
@@ -0,0 +1,347 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { fillLabel } from "../fill-label.js";
6
+ import { ClockIcon } from "../icons/components.js";
7
+ import { useUixLabels } from "../labels-context.js";
8
+ import {
9
+ formatTimeKey,
10
+ isOvernightRange,
11
+ isTimeOutOfRange,
12
+ isValidTimeKey,
13
+ parseTimeInput,
14
+ stepTime,
15
+ timePartEnds,
16
+ timeSlots,
17
+ timeToMinutes
18
+ } from "../time-field-model.js";
19
+ import { useFieldPopover } from "./DateFieldParts.js";
20
+ import { Popover } from "./Popover.js";
21
+ const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
22
+ const DEFAULT_TIME_PICKER_LABELS = {
23
+ open: "Choose time",
24
+ openSelected: "Change time, {time}",
25
+ list: "Times",
26
+ invalidTime: "Enter a time like {example}.",
27
+ tooEarly: "Choose {min} or later.",
28
+ tooLate: "Choose {max} or earlier.",
29
+ outside: "Choose a time from {min} to {max}."
30
+ };
31
+ const TimePicker = forwardRef(function TimePicker2({
32
+ value,
33
+ onValueChange,
34
+ minuteStep = 15,
35
+ min,
36
+ max,
37
+ locale: localeProp,
38
+ hour12,
39
+ placeholder,
40
+ size = "md",
41
+ disabled,
42
+ readOnly,
43
+ required,
44
+ invalid,
45
+ name,
46
+ id: idProp,
47
+ "aria-label": ariaLabel,
48
+ "aria-labelledby": ariaLabelledBy,
49
+ "aria-describedby": ariaDescribedBy,
50
+ "aria-invalid": ariaInvalid,
51
+ "aria-required": ariaRequired,
52
+ placement = "bottom-start",
53
+ onInputProblem,
54
+ onOpenChange,
55
+ labels: labelOverrides,
56
+ className,
57
+ style
58
+ }, forwardedRef) {
59
+ const uid = useId();
60
+ const id = idProp ?? `${uid}-input`;
61
+ const popoverId = `${uid}-times`;
62
+ const listId = `${uid}-list`;
63
+ const errorId = `${uid}-error`;
64
+ const uixLabels = useUixLabels();
65
+ const labels = { ...DEFAULT_TIME_PICKER_LABELS, ...uixLabels.timePicker, ...labelOverrides };
66
+ const locale = localeProp ?? uixLabels.dateFormat?.locale;
67
+ const anchorRef = useRef(null);
68
+ const inputRef = useRef(null);
69
+ const listRef = useRef(null);
70
+ useImperativeHandle(forwardedRef, () => inputRef.current);
71
+ const step = Math.max(1, Math.round(minuteStep));
72
+ const current = isValidTimeKey(value) ? value : null;
73
+ const [mounted, setMounted] = useState(false);
74
+ useEffect(() => setMounted(true), []);
75
+ const shownLocale = locale ?? (mounted ? void 0 : "en-GB");
76
+ const shownHour12 = hour12 ?? (locale || mounted ? void 0 : false);
77
+ const format = (time) => formatTimeKey(time, shownLocale, shownHour12);
78
+ const [text, setText] = useState(() => current ? format(current) : "");
79
+ const [problem, setProblemState] = useState(null);
80
+ const reported = useRef(null);
81
+ const setProblem = (next) => {
82
+ if (next !== reported.current) {
83
+ reported.current = next;
84
+ onInputProblem?.(next);
85
+ }
86
+ setProblemState(next);
87
+ };
88
+ useEffect(() => {
89
+ setText(current ? format(current) : "");
90
+ setProblem(null);
91
+ }, [current, locale, hour12, mounted]);
92
+ const { open, show, hide, triggerProps } = useFieldPopover(popoverId, () => inputRef.current?.focus({ preventScroll: true }), onOpenChange);
93
+ const locked = !!disabled || !!readOnly;
94
+ const commit = () => {
95
+ const typed = text.trim();
96
+ if (!typed) {
97
+ setProblem(null);
98
+ if (text) setText("");
99
+ if (value !== null) onValueChange(null);
100
+ return true;
101
+ }
102
+ if (current && typed === format(current)) {
103
+ setProblem(null);
104
+ return true;
105
+ }
106
+ const parsed = parseTimeInput(typed, locale);
107
+ if (!parsed) {
108
+ setProblem("format");
109
+ return false;
110
+ }
111
+ if (isTimeOutOfRange(parsed, min, max)) {
112
+ setProblem(isOvernightRange(min, max) ? "outside" : min && isValidTimeKey(min) && parsed < min ? "early" : "late");
113
+ return false;
114
+ }
115
+ setProblem(null);
116
+ setText(format(parsed));
117
+ if (parsed !== value) onValueChange(parsed);
118
+ return true;
119
+ };
120
+ const choose = (time) => {
121
+ setProblem(null);
122
+ setText(format(time));
123
+ if (time !== value) onValueChange(time);
124
+ };
125
+ const onInputKeyDown = (event) => {
126
+ if (locked || event.nativeEvent.isComposing || event.keyCode === 229) return;
127
+ const { key, altKey } = event;
128
+ if (key === "ArrowDown" && altKey) {
129
+ event.preventDefault();
130
+ show();
131
+ return;
132
+ }
133
+ if (key === "ArrowUp" || key === "ArrowDown" || key === "PageUp" || key === "PageDown") {
134
+ event.preventDefault();
135
+ const base = parseTimeInput(text, locale) ?? current;
136
+ const input = event.currentTarget;
137
+ const caret = input.selectionStart ?? 0;
138
+ const ends = base && input.value === format(base) ? timePartEnds(base, shownLocale, shownHour12) : null;
139
+ const hourEnd = ends?.hour ?? /^\s*\d{1,2}/.exec(input.value)?.[0].length ?? 0;
140
+ const onHour = key === "PageUp" || key === "PageDown" || !!base && caret <= hourEnd;
141
+ const sign = key === "ArrowUp" || key === "PageUp" ? 1 : -1;
142
+ let next;
143
+ if (!base) next = stepTime(null, 0, min, max);
144
+ else if (onHour) next = stepTime(base, sign * 60, min, max);
145
+ else {
146
+ const minutes = timeToMinutes(base);
147
+ const snapped = sign > 0 ? Math.floor(minutes / step) * step + step : Math.ceil(minutes / step) * step - step;
148
+ next = stepTime(base, snapped - minutes, min, max);
149
+ }
150
+ choose(next);
151
+ requestAnimationFrame(() => {
152
+ const el = inputRef.current;
153
+ if (!el || el.ownerDocument.activeElement !== el) return;
154
+ const moved = timePartEnds(next, shownLocale, shownHour12);
155
+ const at = moved ? onHour ? moved.hour : moved.minute : el.value.length;
156
+ try {
157
+ el.setSelectionRange(at, at);
158
+ } catch {
159
+ }
160
+ });
161
+ return;
162
+ }
163
+ if (key === "Enter" && text.trim() !== (current ? format(current) : "")) {
164
+ event.preventDefault();
165
+ commit();
166
+ }
167
+ };
168
+ const pick = (time) => {
169
+ choose(time);
170
+ hide(false);
171
+ inputRef.current?.focus({ preventScroll: true });
172
+ };
173
+ const describedBy = [ariaDescribedBy, problem ? errorId : void 0].filter(Boolean).join(" ") || void 0;
174
+ const isInvalid = !!invalid || !!problem || ariaInvalid === true || ariaInvalid === "true";
175
+ const example = format(min && isValidTimeKey(min) && min > "14:30" ? min : "14:30");
176
+ return /* @__PURE__ */ jsxs(
177
+ "div",
178
+ {
179
+ ref: anchorRef,
180
+ className: cx("uix-date-picker", "uix-time-picker", size === "sm" && "uix-date-picker--sm", className),
181
+ style,
182
+ onKeyDown: (event) => {
183
+ if (event.key !== "Escape" || !open) return;
184
+ event.stopPropagation();
185
+ event.preventDefault();
186
+ hide(false);
187
+ inputRef.current?.focus({ preventScroll: true });
188
+ },
189
+ children: [
190
+ /* @__PURE__ */ jsxs("div", { className: "uix-date-picker__control", children: [
191
+ /* @__PURE__ */ jsx(
192
+ "input",
193
+ {
194
+ ref: inputRef,
195
+ id,
196
+ type: "text",
197
+ autoComplete: "off",
198
+ spellCheck: false,
199
+ className: cx("uix-input", size === "sm" && "uix-input--sm", "uix-date-picker__input", "uix-time-picker__input"),
200
+ value: text,
201
+ placeholder: placeholder ?? example,
202
+ disabled,
203
+ readOnly,
204
+ required,
205
+ "aria-label": ariaLabel,
206
+ "aria-labelledby": ariaLabelledBy,
207
+ "aria-describedby": describedBy,
208
+ "aria-invalid": isInvalid || void 0,
209
+ "aria-required": ariaRequired,
210
+ onChange: (event) => {
211
+ setText(event.target.value);
212
+ if (problem) setProblem(null);
213
+ },
214
+ onBlur: () => {
215
+ if (!locked) commit();
216
+ },
217
+ onKeyDown: onInputKeyDown
218
+ }
219
+ ),
220
+ /* @__PURE__ */ jsx(
221
+ "button",
222
+ {
223
+ type: "button",
224
+ className: "uix-date-picker__toggle",
225
+ disabled: locked,
226
+ "aria-label": current ? fillLabel(labels.openSelected, { time: format(current) }) : labels.open,
227
+ ...triggerProps,
228
+ children: /* @__PURE__ */ jsx(ClockIcon, { size: "sm" })
229
+ }
230
+ )
231
+ ] }),
232
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "", disabled }),
233
+ problem && /* @__PURE__ */ jsx("span", { id: errorId, className: "uix-field__error uix-date-picker__error", role: "alert", children: problem === "format" ? fillLabel(labels.invalidTime, { example }) : problem === "outside" ? fillLabel(labels.outside, { min: format(min ?? ""), max: format(max ?? "") }) : problem === "early" ? fillLabel(labels.tooEarly, { min: format(min ?? "") }) : fillLabel(labels.tooLate, { max: format(max ?? "") }) }),
234
+ /* @__PURE__ */ jsx(
235
+ Popover,
236
+ {
237
+ id: popoverId,
238
+ anchor: anchorRef,
239
+ placement,
240
+ className: "uix-time-picker__popover",
241
+ role: "dialog",
242
+ "aria-label": labels.list,
243
+ capHeight: true,
244
+ closeWhenAnchorHidden: true,
245
+ children: open && /* @__PURE__ */ jsx(
246
+ TimeList,
247
+ {
248
+ ref: listRef,
249
+ id: listId,
250
+ value: current,
251
+ step,
252
+ min,
253
+ max,
254
+ label: labels.list,
255
+ format,
256
+ onPick: pick
257
+ }
258
+ )
259
+ }
260
+ )
261
+ ]
262
+ }
263
+ );
264
+ });
265
+ TimePicker.displayName = "TimePicker";
266
+ const TimeList = forwardRef(function TimeList2({ id, value, step, min, max, label, format, onPick }, ref) {
267
+ const slots = useMemo(() => timeSlots(step, min, max), [step, min, max]);
268
+ const nearest = () => {
269
+ if (!slots.length) return -1;
270
+ if (!value) return 0;
271
+ const exact = slots.indexOf(value);
272
+ if (exact >= 0) return exact;
273
+ const at = slots.findIndex((slot) => slot > value);
274
+ return at < 0 ? slots.length - 1 : Math.max(0, at - 1);
275
+ };
276
+ const [active, setActive] = useState(nearest);
277
+ const listRef = useRef(null);
278
+ useImperativeHandle(ref, () => listRef.current);
279
+ const optionId = (index) => `${id}-${index}`;
280
+ useEffect(() => {
281
+ listRef.current?.focus({ preventScroll: true });
282
+ }, []);
283
+ const placed = useRef(false);
284
+ useIsomorphicLayoutEffect(() => {
285
+ const list = listRef.current;
286
+ const el = list?.ownerDocument.getElementById(optionId(active));
287
+ if (!list || !el) return;
288
+ const top = el.offsetTop;
289
+ const bottom = top + el.offsetHeight;
290
+ if (!placed.current) {
291
+ placed.current = true;
292
+ list.scrollTop = top - (list.clientHeight - el.offsetHeight) / 2;
293
+ return;
294
+ }
295
+ if (top < list.scrollTop) list.scrollTop = top;
296
+ else if (bottom > list.scrollTop + list.clientHeight) list.scrollTop = bottom - list.clientHeight;
297
+ }, [active]);
298
+ const perHour = Math.max(1, Math.round(60 / step));
299
+ const onKeyDown = (event) => {
300
+ const last = slots.length - 1;
301
+ const moves = { ArrowDown: 1, ArrowUp: -1, PageDown: perHour, PageUp: -perHour };
302
+ if (event.key in moves) {
303
+ event.preventDefault();
304
+ setActive((index) => Math.max(0, Math.min(last, index + moves[event.key])));
305
+ } else if (event.key === "Home" || event.key === "End") {
306
+ event.preventDefault();
307
+ setActive(event.key === "Home" ? 0 : last);
308
+ } else if (event.key === "Enter" || event.key === " ") {
309
+ event.preventDefault();
310
+ const slot = slots[active];
311
+ if (slot) onPick(slot);
312
+ }
313
+ };
314
+ return /* @__PURE__ */ jsx(
315
+ "div",
316
+ {
317
+ ref: listRef,
318
+ id,
319
+ role: "listbox",
320
+ tabIndex: 0,
321
+ "aria-label": label,
322
+ className: "uix-listbox uix-time-picker__list",
323
+ "aria-activedescendant": active >= 0 ? optionId(active) : void 0,
324
+ onKeyDown,
325
+ children: slots.map((slot, index) => /* @__PURE__ */ jsx(
326
+ "div",
327
+ {
328
+ id: optionId(index),
329
+ role: "option",
330
+ className: "uix-listbox__option",
331
+ "aria-selected": slot === value,
332
+ "data-active": index === active || void 0,
333
+ onPointerMove: () => {
334
+ if (index !== active) setActive(index);
335
+ },
336
+ onClick: () => onPick(slot),
337
+ children: /* @__PURE__ */ jsx("span", { className: "uix-listbox__label", children: format(slot) })
338
+ },
339
+ slot
340
+ ))
341
+ }
342
+ );
343
+ });
344
+ export {
345
+ DEFAULT_TIME_PICKER_LABELS,
346
+ TimePicker
347
+ };
@@ -15,7 +15,7 @@ interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
15
15
  onClose?: () => void;
16
16
  leaving?: boolean;
17
17
  }
18
- declare function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLabel: dismissLabelProp, className, ...props }: ToastProps): react.JSX.Element;
18
+ declare function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLabel: dismissLabelProp, className, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...props }: ToastProps): react.JSX.Element;
19
19
  interface ToasterProps extends HTMLAttributes<HTMLDivElement> {
20
20
  /** TENSOR RX-125 (UIX-04): translatable; English default. */
21
21
  regionLabel?: string;
@@ -1,11 +1,91 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  import { useUixLabels } from "../labels-context.js";
6
+ import { prefersReducedMotion } from "../hooks/useReducedMotion.js";
7
+ import { tokenMs } from "../motion.js";
8
+ import { XIcon } from "../icons/components.js";
6
9
  import { toast as defaultToast } from "../toast-store.js";
7
10
  const ToasterContext = createContext(null);
8
- function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLabel: dismissLabelProp, className, ...props }) {
11
+ const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
12
+ const SWIPE_VELOCITY = 0.11;
13
+ const SWIPE_SLOP = 6;
14
+ const SWIPE_WINDOW = 100;
15
+ function releaseVelocity(samples, x, t) {
16
+ const recent = samples.filter((s) => t - s.t <= SWIPE_WINDOW);
17
+ if (recent.length === 0) return 0;
18
+ return (x - recent[0].x) / Math.max(1, t - recent[0].t);
19
+ }
20
+ function useSwipeToDismiss(onClose, own) {
21
+ const drag = useRef(null);
22
+ const end = (e) => {
23
+ const d = drag.current;
24
+ drag.current = null;
25
+ if (!d?.active || !onClose) return;
26
+ const el = e.currentTarget;
27
+ el.removeAttribute("data-swiping");
28
+ const v = releaseVelocity(d.samples, e.clientX, e.timeStamp);
29
+ const fling = Math.abs(v) > SWIPE_VELOCITY && Math.sign(v) === Math.sign(d.dx);
30
+ if (e.type === "pointerup" && (fling || Math.abs(d.dx) > el.offsetWidth / 3)) {
31
+ if (!prefersReducedMotion()) el.style.translate = `${Math.sign(d.dx) * (el.offsetWidth + 32)}px 0`;
32
+ onClose();
33
+ } else {
34
+ el.style.translate = "";
35
+ }
36
+ };
37
+ return {
38
+ onPointerDown: (e) => {
39
+ own.onPointerDown?.(e);
40
+ if (!onClose || e.defaultPrevented || e.button !== 0) return;
41
+ if (e.target.closest('button, a, input, textarea, select, [role="button"]')) return;
42
+ drag.current = { id: e.pointerId, x0: e.clientX, y0: e.clientY, dx: 0, samples: [{ x: e.clientX, t: e.timeStamp }], active: false };
43
+ },
44
+ onPointerMove: (e) => {
45
+ own.onPointerMove?.(e);
46
+ const d = drag.current;
47
+ if (!d || d.id !== e.pointerId) return;
48
+ const dx = e.clientX - d.x0;
49
+ if (!d.active) {
50
+ if (Math.abs(dx) < SWIPE_SLOP) return;
51
+ if (Math.abs(e.clientY - d.y0) > Math.abs(dx)) {
52
+ drag.current = null;
53
+ return;
54
+ }
55
+ d.active = true;
56
+ e.currentTarget.setPointerCapture?.(e.pointerId);
57
+ e.currentTarget.setAttribute("data-swiping", "");
58
+ }
59
+ d.samples = [...d.samples.filter((s) => e.timeStamp - s.t <= SWIPE_WINDOW), { x: e.clientX, t: e.timeStamp }];
60
+ d.dx = dx;
61
+ e.currentTarget.style.translate = `${dx}px 0`;
62
+ },
63
+ onPointerUp: (e) => {
64
+ own.onPointerUp?.(e);
65
+ end(e);
66
+ },
67
+ onPointerCancel: (e) => {
68
+ own.onPointerCancel?.(e);
69
+ end(e);
70
+ }
71
+ };
72
+ }
73
+ function Toast({
74
+ title,
75
+ message,
76
+ tone,
77
+ icon,
78
+ action,
79
+ onClose,
80
+ leaving,
81
+ dismissLabel: dismissLabelProp,
82
+ className,
83
+ onPointerDown,
84
+ onPointerMove,
85
+ onPointerUp,
86
+ onPointerCancel,
87
+ ...props
88
+ }) {
9
89
  const uixLabels = useUixLabels();
10
90
  const dismissLabel = dismissLabelProp ?? uixLabels.toast?.dismiss ?? "Dismiss";
11
91
  const assertive = tone === "danger";
@@ -28,6 +108,7 @@ function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLa
28
108
  }
29
109
  onClose?.();
30
110
  };
111
+ const swipe = useSwipeToDismiss(onClose, { onPointerDown, onPointerMove, onPointerUp, onPointerCancel });
31
112
  const ownLiveRegion = assertive || !toaster;
32
113
  return /* @__PURE__ */ jsxs(
33
114
  "div",
@@ -37,6 +118,7 @@ function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLa
37
118
  role: ownLiveRegion ? assertive ? "alert" : "status" : void 0,
38
119
  "aria-live": ownLiveRegion ? assertive ? "assertive" : "polite" : void 0,
39
120
  ...props,
121
+ ...swipe,
40
122
  children: [
41
123
  icon && /* @__PURE__ */ jsx("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
42
124
  /* @__PURE__ */ jsxs("div", { className: "uix-toast__body", ref: bodyRef, children: [
@@ -44,7 +126,7 @@ function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLa
44
126
  message && /* @__PURE__ */ jsx("div", { className: "uix-toast__msg", children: message })
45
127
  ] }),
46
128
  action != null && /* @__PURE__ */ jsx("div", { className: "uix-toast__action-slot", children: action }),
47
- onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children: "\xD7" })
129
+ onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children: /* @__PURE__ */ jsx(XIcon, {}) })
48
130
  ]
49
131
  }
50
132
  );
@@ -124,7 +206,7 @@ function useToastTimers(store, visible, paused) {
124
206
  timer = { version: record.version, remaining: record.duration, start: now };
125
207
  map.set(record.id, timer);
126
208
  }
127
- if (paused) {
209
+ if (paused && record.pauseOnHover !== false) {
128
210
  if (timer.handle != null) {
129
211
  clearTimeout(timer.handle);
130
212
  timer.handle = void 0;
@@ -168,6 +250,28 @@ function Toaster({ children, regionLabel: regionLabelProp, store = defaultToast.
168
250
  return () => document.removeEventListener("visibilitychange", sync);
169
251
  }, []);
170
252
  useToastTimers(store, visible, hovered || focused || hidden);
253
+ const regionRef = useRef(null);
254
+ const tops = useRef(/* @__PURE__ */ new Map());
255
+ useIsomorphicLayoutEffect(() => {
256
+ const region = regionRef.current;
257
+ if (!region) return;
258
+ const base = region.getBoundingClientRect().top;
259
+ const next = /* @__PURE__ */ new Map();
260
+ const glide = !prefersReducedMotion();
261
+ for (const el of Array.from(region.querySelectorAll(":scope > [data-toast-id]"))) {
262
+ const id = el.dataset.toastId;
263
+ const top = base + el.offsetTop;
264
+ next.set(id, top);
265
+ const before = tops.current.get(id);
266
+ if (!glide || before === void 0 || Math.abs(before - top) < 1 || typeof el.animate !== "function") continue;
267
+ el.animate([{ transform: `translateY(${before - top}px)` }, { transform: "translateY(0)" }], {
268
+ duration: tokenMs("--uix-dur", 160, el),
269
+ easing: getComputedStyle(el).getPropertyValue("--uix-ease-out").trim() || "ease-out",
270
+ composite: "add"
271
+ });
272
+ }
273
+ tops.current = next;
274
+ });
171
275
  const handleKeyDown = (event) => {
172
276
  onKeyDown?.(event);
173
277
  if (event.defaultPrevented || event.key !== "Escape") return;
@@ -180,6 +284,7 @@ function Toaster({ children, regionLabel: regionLabelProp, store = defaultToast.
180
284
  return /* @__PURE__ */ jsx(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
181
285
  "div",
182
286
  {
287
+ ref: regionRef,
183
288
  className: cx("uix-toaster", className),
184
289
  role: "region",
185
290
  "aria-label": regionLabel,
@@ -16,10 +16,11 @@ interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'>
16
16
  placement?: Placement;
17
17
  /**
18
18
  * The trigger's keyboard shortcut, in `KbdCombo` key names, e.g. `['Mod', 'K']` (HAR-2031):
19
- * shown with `Kbd` after the text, and set as the trigger's `aria-keyshortcuts` for the
20
- * viewer's platform. Default: the `shortcut` of a `Button` child, so a Tooltip around a Button
21
- * with a shortcut shows one bubble with the label and the keys. The kit only shows it: the
22
- * product binds the key.
19
+ * shown with `Kbd` after the text. Screen readers hear it once, in the trigger's description
20
+ * after the text ("Keyboard shortcuts, Shift+?"), the same rule as a `Button` with `shortcut`
21
+ * (HAR-2416). Default: the `shortcut` of a `Button` child, so a Tooltip around a Button with a
22
+ * shortcut shows one bubble with the label and the keys. The kit only shows it: the product
23
+ * binds the key.
23
24
  */
24
25
  shortcut?: KeyShortcut;
25
26
  children?: ReactNode;
@@ -31,6 +32,9 @@ interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'>
31
32
  * (flip/shift), shown on hover and keyboard focus, dismissed on blur or Escape
32
33
  * (document-level, per WCAG 1.4.13), and hoverable: the bubble stays open while
33
34
  * the pointer is over it (UIX-A11Y-1).
35
+ *
36
+ * While the bubble is open it takes the first Escape (HAR-2415), also on a combobox-style trigger
37
+ * whose list is open (a Select, a date field): one Escape closes the bubble, the next one the list.
34
38
  */
35
39
  declare function Tooltip({ label, content, placement, shortcut, className, children, ...props }: TooltipProps): react.JSX.Element;
36
40