@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,76 @@
1
+ /** What a span of a resource's day is: open to book, booked, or not working. */
2
+ type SchedulingAvailabilityKind = 'free' | 'booked' | 'off';
3
+ /** One column of the resource day, e.g. a practitioner. */
4
+ interface SchedulingResource {
5
+ id: string;
6
+ /** The column heading and the start of every slot's accessible name. */
7
+ label: string;
8
+ /** A second line under the heading, e.g. the speciality or the room. */
9
+ description?: string;
10
+ }
11
+ /** A stretch of one resource's day, as the server composed it. */
12
+ interface SchedulingResourceSpan {
13
+ id: string;
14
+ resourceId: string;
15
+ /** ISO 8601 instants, half-open `[start, end)`. */
16
+ start: string;
17
+ end: string;
18
+ kind: SchedulingAvailabilityKind;
19
+ /** Shown on the span, e.g. the patient's name on a booking or "Lunch" on time off. */
20
+ label?: string;
21
+ /** The complete accessible name, in the consumer's words. Replaces the built one. */
22
+ accessibleName?: string;
23
+ }
24
+ /** One row of the slot axis. */
25
+ interface ResourceDaySlot {
26
+ /** Epoch milliseconds of the slot's start and end. */
27
+ start: number;
28
+ end: number;
29
+ /** The local time it starts, `HH:MM`, in the display zone. */
30
+ label: string;
31
+ }
32
+ interface ResourceDaySlotOptions {
33
+ /** The first local time shown, `HH:MM`. Default `'07:00'`. */
34
+ dayStart?: string;
35
+ /** The local time the axis ends, `HH:MM` (exclusive). Default `'19:00'`. */
36
+ dayEnd?: string;
37
+ /** Minutes per slot. Default 15. */
38
+ slotMinutes?: number;
39
+ }
40
+ /** One drawn piece of a column: a bookable slot of a free span, a booking, or time off. */
41
+ interface ResourceDayItem {
42
+ key: string;
43
+ span: SchedulingResourceSpan;
44
+ kind: SchedulingAvailabilityKind;
45
+ /** Epoch milliseconds of what this piece covers (a free span is cut at slot edges). */
46
+ start: number;
47
+ end: number;
48
+ /** Slots from the top of the axis to the piece's start (fractional). */
49
+ offset: number;
50
+ /** Slots the piece is long (fractional). */
51
+ length: number;
52
+ lane: number;
53
+ lanes: number;
54
+ /** The span starts before or ends after the axis. */
55
+ continuesBefore: boolean;
56
+ continuesAfter: boolean;
57
+ }
58
+ /**
59
+ * The slot axis of `date` in `timeZone`: one slot per `slotMinutes` of real time from the first
60
+ * local time at or after `dayStart` to the last before `dayEnd`. On a clock-change day the axis
61
+ * is as long as the day really is (a skipped hour has no slots; a repeated hour has two sets).
62
+ */
63
+ declare function resourceDaySlots(date: string, timeZone: string, options?: ResourceDaySlotOptions): ResourceDaySlot[];
64
+ /**
65
+ * Where the spans of one resource go on the slot axis. Free spans are cut at slot edges, so each
66
+ * bookable slot is its own piece; bookings and time off stay whole. Pieces outside the axis are
67
+ * dropped, pieces across its edges are clipped. Overlapping pieces (a double booking) get lanes.
68
+ */
69
+ declare function layoutResourceColumn(spans: readonly SchedulingResourceSpan[], slots: readonly ResourceDaySlot[]): ResourceDayItem[];
70
+ /** The stretches of the axis no span covers: shown as "no schedule", never as free. */
71
+ declare function resourceDayGaps(items: readonly ResourceDayItem[], slots: readonly ResourceDaySlot[]): Array<{
72
+ start: number;
73
+ end: number;
74
+ }>;
75
+
76
+ export { type ResourceDayItem, type ResourceDaySlot, type ResourceDaySlotOptions, type SchedulingAvailabilityKind, type SchedulingResource, type SchedulingResourceSpan, layoutResourceColumn, resourceDayGaps, resourceDaySlots };
@@ -0,0 +1,74 @@
1
+ import { packLanes, zonedDayBounds, zonedTimeOfDay } from "./calendar-model.js";
2
+ const MINUTE = 6e4;
3
+ const TIME = /^([01]\d|2[0-3]):[0-5]\d$/;
4
+ function resourceDaySlots(date, timeZone, options = {}) {
5
+ const dayStart = options.dayStart && TIME.test(options.dayStart) ? options.dayStart : "07:00";
6
+ const dayEnd = options.dayEnd && TIME.test(options.dayEnd) ? options.dayEnd : "19:00";
7
+ const minutes = Number.isFinite(options.slotMinutes) && options.slotMinutes >= 5 ? Math.floor(options.slotMinutes) : 15;
8
+ const step = minutes * MINUTE;
9
+ const bounds = zonedDayBounds(date, timeZone);
10
+ const all = [];
11
+ for (let at = bounds.start.getTime(); at < bounds.end.getTime(); at += step) {
12
+ all.push({ start: at, end: Math.min(at + step, bounds.end.getTime()), label: zonedTimeOfDay(at, timeZone) });
13
+ }
14
+ const first = all.findIndex((slot) => slot.label >= dayStart);
15
+ let last = -1;
16
+ all.forEach((slot, index) => {
17
+ if (slot.label < dayEnd) last = index;
18
+ });
19
+ if (first === -1 || last < first) return [];
20
+ return all.slice(first, last + 1);
21
+ }
22
+ function layoutResourceColumn(spans, slots) {
23
+ if (slots.length === 0) return [];
24
+ const axisStart = slots[0].start;
25
+ const axisEnd = slots[slots.length - 1].end;
26
+ const slotMs = slots[0].end - slots[0].start;
27
+ const pieces = [];
28
+ const seen = /* @__PURE__ */ new Set();
29
+ for (const span of spans) {
30
+ if (seen.has(span.id)) continue;
31
+ seen.add(span.id);
32
+ const start = new Date(span.start).getTime();
33
+ const end = new Date(span.end).getTime();
34
+ if (!(end > start) || end <= axisStart || start >= axisEnd) continue;
35
+ const from = Math.max(start, axisStart);
36
+ const to = Math.min(end, axisEnd);
37
+ const edges = { continuesBefore: start < axisStart, continuesAfter: end > axisEnd };
38
+ if (span.kind === "free") {
39
+ for (const slot of slots) {
40
+ const a = Math.max(from, slot.start);
41
+ const b = Math.min(to, slot.end);
42
+ if (b > a) pieces.push({ key: `${span.id}@${a}`, span, kind: "free", start: a, end: b, ...edges });
43
+ }
44
+ } else pieces.push({ key: span.id, span, kind: span.kind, start: from, end: to, ...edges });
45
+ }
46
+ const packed = packLanes(pieces.map((piece) => ({ id: piece.key, start: piece.start, end: piece.end })), Infinity);
47
+ return pieces.map((piece) => {
48
+ const placement = packed.lanes[piece.key];
49
+ return {
50
+ ...piece,
51
+ offset: (piece.start - axisStart) / slotMs,
52
+ length: (piece.end - piece.start) / slotMs,
53
+ lane: placement.lane ?? 0,
54
+ lanes: packed.clusters[placement.cluster].laneCount || 1
55
+ };
56
+ }).sort((a, b) => a.start - b.start || a.lane - b.lane);
57
+ }
58
+ function resourceDayGaps(items, slots) {
59
+ if (slots.length === 0) return [];
60
+ const gaps = [];
61
+ let at = slots[0].start;
62
+ for (const item of [...items].sort((a, b) => a.start - b.start)) {
63
+ if (item.start > at) gaps.push({ start: at, end: item.start });
64
+ at = Math.max(at, item.end);
65
+ }
66
+ const axisEnd = slots[slots.length - 1].end;
67
+ if (at < axisEnd) gaps.push({ start: at, end: axisEnd });
68
+ return gaps;
69
+ }
70
+ export {
71
+ layoutResourceColumn,
72
+ resourceDayGaps,
73
+ resourceDaySlots
74
+ };
@@ -1,11 +1,13 @@
1
1
  const COMBINING_MARK = /\p{M}/u;
2
+ const STROKED = { "\u0111": "d", "\xF8": "o", "\u0142": "l", "\u0131": "i", "\u0127": "h", "\u0167": "t" };
2
3
  function foldForSearch(text) {
3
4
  let folded = "";
4
5
  const source = [];
5
6
  const sourceEnd = [];
6
7
  let index = 0;
7
8
  for (const char of text) {
8
- const base = char === "\xDF" || char === "\u1E9E" ? "ss" : char.toLowerCase().normalize("NFD");
9
+ const lower = char.toLowerCase();
10
+ const base = char === "\xDF" || char === "\u1E9E" ? "ss" : STROKED[lower] ?? lower.normalize("NFD");
9
11
  for (const part of base) {
10
12
  if (COMBINING_MARK.test(part)) continue;
11
13
  folded += part;
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * True inside AppShell's phone navigation drawer (HAR-1371). The sidebar there is shown full,
5
+ * whatever tier a person chose on a wide screen: a Sidebar and an AccountMenu read this and drop
6
+ * their rail form. Internal: not exported from the package.
7
+ */
8
+ declare const ShellDrawerContext: react.Context<boolean>;
9
+ declare const useInShellDrawer: () => boolean;
10
+
11
+ export { ShellDrawerContext, useInShellDrawer };
@@ -0,0 +1,7 @@
1
+ import { createContext, useContext } from "react";
2
+ const ShellDrawerContext = createContext(false);
3
+ const useInShellDrawer = () => useContext(ShellDrawerContext);
4
+ export {
5
+ ShellDrawerContext,
6
+ useInShellDrawer
7
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * uix time-field model — pure helpers behind `TimePicker` (HAR-1835): how a locale writes a time
3
+ * of day, how a typed time is read, and how the arrow keys step it. No DOM, no React, no runtime
4
+ * import, so the unit test loads this file directly (time-field-model.test.mjs).
5
+ *
6
+ * A time is a wall-clock `HH:mm` string (what `<input type="time">` holds): 24-hour, zero-padded,
7
+ * no seconds, no zone.
8
+ */
9
+ /** Whether `HH:mm` names a time of day (00:00 to 23:59). */
10
+ declare function isValidTimeKey(value: string | null | undefined): value is string;
11
+ /** Minutes since midnight of `HH:mm`. */
12
+ declare const timeToMinutes: (value: string) => number;
13
+ /** `HH:mm` of a minute of the day (wrapped into 0 … 1439). */
14
+ declare const minutesToTime: (minutes: number) => string;
15
+ /** Whether `locale` writes times on a 12-hour clock (en-US: yes; de, en-GB, bs: no). */
16
+ declare function uses12Hour(locale?: string | readonly string[]): boolean;
17
+ /** A time in the locale's own shape: 14:30 in German, 2:30 PM in en-US. `hour12` overrides the locale. */
18
+ declare function formatTimeKey(value: string, locale?: string | readonly string[], hour12?: boolean): string;
19
+ /**
20
+ * Where the hour and the minutes end in `formatTimeKey(value, locale, hour12)` (character offsets),
21
+ * found from the formatter's own parts, so "오후 2:30" (ko, the day period first) and "2:30 PM"
22
+ * both work. `null` when the value is not a time.
23
+ */
24
+ declare function timePartEnds(value: string, locale?: string | readonly string[], hour12?: boolean): {
25
+ hour: number;
26
+ minute: number;
27
+ } | null;
28
+ /**
29
+ * Read a typed time. Accepts `14:30`, `14.30`, `14h30`, `1430`, `930`, `9` and, with a day period,
30
+ * `2:30 pm`, `2pm`, `2:30 p.m.` (and the locale's own words). Without a day period the hour is
31
+ * read on the 24-hour clock. Returns `HH:mm`, or `null` when the text is not a time.
32
+ */
33
+ declare function parseTimeInput(text: string, locale?: string | readonly string[]): string | null;
34
+ /** Whether `min` is after `max`: an overnight range (22:00 to 06:00) that wraps round midnight. */
35
+ declare const isOvernightRange: (min?: string, max?: string) => boolean;
36
+ /** Whether `value` is outside `min` / `max` (both `HH:mm`, inclusive; `min` after `max` wraps midnight). */
37
+ declare function isTimeOutOfRange(value: string, min?: string, max?: string): boolean;
38
+ /**
39
+ * The time `delta` minutes from `value`, for the arrow keys. Between `min` and `max` it stops at
40
+ * the ends; with neither it wraps round midnight. From an empty field it starts at `min`, or 00:00.
41
+ */
42
+ declare function stepTime(value: string | null, delta: number, min?: string, max?: string): string;
43
+ /** The times a list offers: every `step` minutes from `min` (or 00:00) to `max` (or 23:59). */
44
+ declare function timeSlots(step: number, min?: string, max?: string): string[];
45
+
46
+ export { formatTimeKey, isOvernightRange, isTimeOutOfRange, isValidTimeKey, minutesToTime, parseTimeInput, stepTime, timePartEnds, timeSlots, timeToMinutes, uses12Hour };
@@ -0,0 +1,152 @@
1
+ const pad = (n) => String(n).padStart(2, "0");
2
+ function isValidTimeKey(value) {
3
+ const match = value ? /^(\d{2}):(\d{2})$/.exec(value) : null;
4
+ return !!match && Number(match[1]) < 24 && Number(match[2]) < 60;
5
+ }
6
+ const timeToMinutes = (value) => Number(value.slice(0, 2)) * 60 + Number(value.slice(3, 5));
7
+ const minutesToTime = (minutes) => {
8
+ const m = (Math.round(minutes) % 1440 + 1440) % 1440;
9
+ return `${pad(Math.floor(m / 60))}:${pad(m % 60)}`;
10
+ };
11
+ function uses12Hour(locale) {
12
+ try {
13
+ const options = new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions();
14
+ const cycle = options.hourCycle;
15
+ return cycle === "h11" || cycle === "h12";
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+ const at = (value) => new Date(Date.UTC(2026, 0, 1, Number(value.slice(0, 2)), Number(value.slice(3, 5))));
21
+ function formatTimeKey(value, locale, hour12) {
22
+ if (!isValidTimeKey(value)) return value;
23
+ const twelve = hour12 ?? uses12Hour(locale);
24
+ try {
25
+ return new Intl.DateTimeFormat(locale, {
26
+ hour: twelve ? "numeric" : "2-digit",
27
+ minute: "2-digit",
28
+ hourCycle: twelve ? "h12" : "h23",
29
+ timeZone: "UTC"
30
+ }).format(at(value));
31
+ } catch {
32
+ return value;
33
+ }
34
+ }
35
+ function timePartEnds(value, locale, hour12) {
36
+ if (!isValidTimeKey(value)) return null;
37
+ const twelve = hour12 ?? uses12Hour(locale);
38
+ try {
39
+ const parts = new Intl.DateTimeFormat(locale, {
40
+ hour: twelve ? "numeric" : "2-digit",
41
+ minute: "2-digit",
42
+ hourCycle: twelve ? "h12" : "h23",
43
+ timeZone: "UTC"
44
+ }).formatToParts(at(value));
45
+ let offset = 0;
46
+ let hour = -1;
47
+ let minute = -1;
48
+ for (const part of parts) {
49
+ offset += part.value.length;
50
+ if (part.type === "hour") hour = offset;
51
+ if (part.type === "minute") minute = offset;
52
+ }
53
+ return hour >= 0 && minute >= 0 ? { hour, minute } : null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ function dayPeriods(locale) {
59
+ const am = ["am", "a.m.", "a"];
60
+ const pm = ["pm", "p.m.", "p"];
61
+ try {
62
+ const fmt = new Intl.DateTimeFormat(locale, { hour: "numeric", hourCycle: "h12", timeZone: "UTC" });
63
+ const word = (hour) => fmt.formatToParts(at(`${pad(hour)}:00`)).find((p2) => p2.type === "dayPeriod")?.value.trim().toLowerCase();
64
+ const a = word(1);
65
+ const p = word(13);
66
+ if (a && !am.includes(a)) am.unshift(a);
67
+ if (p && !pm.includes(p)) pm.unshift(p);
68
+ } catch {
69
+ }
70
+ return { am, pm };
71
+ }
72
+ function parseTimeInput(text, locale) {
73
+ let rest = text.normalize("NFKC").trim().toLowerCase().replace(/\s+/g, " ");
74
+ if (!rest) return null;
75
+ let period = null;
76
+ const { am, pm } = dayPeriods(locale);
77
+ const words = [...am.map((w) => [w, "am"]), ...pm.map((w) => [w, "pm"])].sort((x, y) => y[0].length - x[0].length);
78
+ for (const [word, which] of words) {
79
+ if (rest.endsWith(word) && /[\d\s.]$/.test(rest.slice(0, -word.length) || " ")) {
80
+ period = which;
81
+ rest = rest.slice(0, -word.length).trim();
82
+ break;
83
+ }
84
+ if (rest.startsWith(word) && /^[\s\d]/.test(rest.slice(word.length))) {
85
+ period = which;
86
+ rest = rest.slice(word.length).trim();
87
+ break;
88
+ }
89
+ }
90
+ let hour;
91
+ let minute;
92
+ const split = /^(\d{1,2})\s*(?:[:.h]\s*(\d{1,2}))?\s*(?:h|uhr)?$/.exec(rest);
93
+ const packed = /^(\d{3,4})$/.exec(rest);
94
+ if (split && (split[2] === void 0 || split[2].length === 2)) {
95
+ hour = Number(split[1]);
96
+ minute = split[2] === void 0 ? 0 : Number(split[2]);
97
+ } else if (packed) {
98
+ hour = Number(packed[1].slice(0, -2));
99
+ minute = Number(packed[1].slice(-2));
100
+ } else {
101
+ return null;
102
+ }
103
+ if (minute > 59) return null;
104
+ if (period) {
105
+ if (hour < 1 || hour > 12) return null;
106
+ hour = hour % 12 + (period === "pm" ? 12 : 0);
107
+ } else if (hour > 23) {
108
+ return null;
109
+ }
110
+ return `${pad(hour)}:${pad(minute)}`;
111
+ }
112
+ const isOvernightRange = (min, max) => !!min && !!max && isValidTimeKey(min) && isValidTimeKey(max) && min > max;
113
+ function isTimeOutOfRange(value, min, max) {
114
+ if (isOvernightRange(min, max)) return value < min && value > max;
115
+ return !!min && isValidTimeKey(min) && value < min || !!max && isValidTimeKey(max) && value > max;
116
+ }
117
+ function stepTime(value, delta, min, max) {
118
+ const lo = min && isValidTimeKey(min) ? timeToMinutes(min) : null;
119
+ const hi = max && isValidTimeKey(max) ? timeToMinutes(max) : null;
120
+ if (!value || !isValidTimeKey(value)) return minutesToTime(lo ?? 0);
121
+ if (lo !== null && hi !== null && lo > hi) {
122
+ const wrapped = ((timeToMinutes(value) + delta) % 1440 + 1440) % 1440;
123
+ return minutesToTime(wrapped >= lo || wrapped <= hi ? wrapped : delta > 0 ? hi : lo);
124
+ }
125
+ let next = timeToMinutes(value) + delta;
126
+ if (lo === null && hi === null) return minutesToTime(next);
127
+ if (lo !== null && next < lo) next = lo;
128
+ if (hi !== null && next > hi) next = hi;
129
+ return minutesToTime(Math.max(0, Math.min(1439, next)));
130
+ }
131
+ function timeSlots(step, min, max) {
132
+ const size = Math.max(1, Math.round(step));
133
+ const lo = min && isValidTimeKey(min) ? timeToMinutes(min) : 0;
134
+ const hi = max && isValidTimeKey(max) ? timeToMinutes(max) : 1439;
135
+ const out = [];
136
+ const end = lo > hi ? hi + 1440 : hi;
137
+ for (let m = lo; m <= end; m += size) out.push(minutesToTime(m));
138
+ return out;
139
+ }
140
+ export {
141
+ formatTimeKey,
142
+ isOvernightRange,
143
+ isTimeOutOfRange,
144
+ isValidTimeKey,
145
+ minutesToTime,
146
+ parseTimeInput,
147
+ stepTime,
148
+ timePartEnds,
149
+ timeSlots,
150
+ timeToMinutes,
151
+ uses12Hour
152
+ };
@@ -25,6 +25,13 @@ interface ToastOptions {
25
25
  * Defaults: success/info/default 5 s, warning 8 s, error and loading stay.
26
26
  */
27
27
  duration?: number | null;
28
+ /**
29
+ * `false`: a fixed timer (HAR-1831). The toast closes `duration` after it appears, even while the
30
+ * pointer or focus is on it or the tab is hidden; for an undo window that must end on time
31
+ * (TENSOR's `useUndoableAction` commits after exactly 5 s). A toast waiting beyond the
32
+ * Toaster's `limit` starts its timer when it appears. Default `true`: the timer pauses.
33
+ */
34
+ pauseOnHover?: boolean;
28
35
  action?: ToastAction;
29
36
  /** Replaces the tone glyph. */
30
37
  icon?: ReactNode;
@@ -37,6 +44,8 @@ interface ToastRecord {
37
44
  message: ReactNode;
38
45
  description?: ReactNode;
39
46
  duration: number | null;
47
+ /** `false`: the timer does not pause (see {@link ToastOptions.pauseOnHover}). */
48
+ pauseOnHover?: boolean;
40
49
  action?: ToastAction;
41
50
  icon?: ReactNode;
42
51
  /** Set while the leave animation runs; the record is removed after it. */
@@ -65,8 +74,11 @@ interface ToastStore {
65
74
  subscribe: (listener: () => void) => () => void;
66
75
  getSnapshot: () => readonly ToastRecord[];
67
76
  }
68
- /** How long the leave animation gets before the record is removed. */
69
- declare const TOAST_LEAVE_MS = 200;
77
+ /**
78
+ * How long the leave transition gets before the record is removed when no stylesheet answers:
79
+ * the store reads `--uix-dur-exit` (toast.css leaves on it), so the two never disagree (HAR-2578).
80
+ */
81
+ declare const TOAST_LEAVE_MS = 110;
70
82
  /** A separate queue, e.g. per test or per embedded app. Most products use the default `toast`. */
71
83
  declare function createToastStore(): ToastStore;
72
84
  interface ToastApi {
@@ -84,9 +96,16 @@ interface ToastApi {
84
96
  promise: <T>(promise: Promise<T>, messages: ToastPromiseMessages<T>, options?: ToastOptions) => Promise<T>;
85
97
  /**
86
98
  * An optimistic action with an Undo button (TENSOR C2 `useUndoableAction`): `onCommit` runs
87
- * when the toast closes without Undo; `onUndo` runs instead when Undo is pressed.
99
+ * when the toast closes without Undo; `onUndo` runs instead when Undo is pressed. Pass
100
+ * `pauseOnHover: false` for a commit that happens exactly `duration` after the toast appears.
88
101
  */
89
102
  undoable: (message: ReactNode, options: UndoableToastOptions) => string;
103
+ /**
104
+ * Undo an `undoable` toast from code, as if its Undo were pressed (HAR-1831): `onUndo` runs,
105
+ * `onCommit` does not, and the toast closes. Returns `false` when `id` is not an open undoable
106
+ * toast (it was already undone or committed). `dismiss(id)` commits instead.
107
+ */
108
+ undo: (id: string) => boolean;
90
109
  /** The store this API writes to; pass it to `<Toaster store>` for a non-default queue. */
91
110
  store: ToastStore;
92
111
  }
@@ -1,3 +1,4 @@
1
+ import { tokenMs } from "./motion.js";
1
2
  const DEFAULT_DURATION = {
2
3
  success: 5e3,
3
4
  info: 5e3,
@@ -6,7 +7,7 @@ const DEFAULT_DURATION = {
6
7
  error: null,
7
8
  loading: null
8
9
  };
9
- const TOAST_LEAVE_MS = 200;
10
+ const TOAST_LEAVE_MS = 110;
10
11
  let counter = 0;
11
12
  function createToastStore() {
12
13
  let records = [];
@@ -26,6 +27,7 @@ function createToastStore() {
26
27
  message,
27
28
  description: options.description,
28
29
  duration: options.duration === void 0 ? DEFAULT_DURATION[kind] : options.duration,
30
+ pauseOnHover: options.pauseOnHover,
29
31
  action: options.action,
30
32
  icon: options.icon,
31
33
  onDismiss: options.onDismiss,
@@ -51,7 +53,7 @@ function createToastStore() {
51
53
  set(records.map((r) => ids.has(r.id) ? { ...r, leaving: true } : r));
52
54
  for (const r of closing) r.onDismiss?.(reason);
53
55
  const remove = () => set(records.filter((r) => !(ids.has(r.id) && r.leaving)));
54
- if (typeof setTimeout === "function") setTimeout(remove, TOAST_LEAVE_MS);
56
+ if (typeof setTimeout === "function") setTimeout(remove, tokenMs("--uix-dur-exit", TOAST_LEAVE_MS));
55
57
  else remove();
56
58
  };
57
59
  return {
@@ -84,19 +86,34 @@ function createToastApi(store) {
84
86
  );
85
87
  return promise;
86
88
  };
89
+ const undoers = /* @__PURE__ */ new Map();
87
90
  api.undoable = (message, { onUndo, onCommit, undoLabel = "Undo", duration = 6e3, ...options }) => {
88
- let undone = false;
89
- return store.show("default", message, {
91
+ let settled = false;
92
+ const undo = () => {
93
+ if (settled) return;
94
+ settled = true;
95
+ onUndo();
96
+ };
97
+ const id = store.show("default", message, {
90
98
  ...options,
91
99
  duration,
92
- action: { label: undoLabel, onClick: () => {
93
- undone = true;
94
- onUndo();
95
- } },
100
+ action: { label: undoLabel, onClick: undo },
96
101
  onDismiss: () => {
97
- if (!undone) onCommit();
102
+ undoers.delete(id);
103
+ if (settled) return;
104
+ settled = true;
105
+ onCommit();
98
106
  }
99
107
  });
108
+ undoers.set(id, undo);
109
+ return id;
110
+ };
111
+ api.undo = (id) => {
112
+ const undo = undoers.get(id);
113
+ if (!undo) return false;
114
+ undo();
115
+ store.dismiss(id, "action");
116
+ return true;
100
117
  };
101
118
  api.store = store;
102
119
  return api;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/react",
3
- "version": "2.47.0",
3
+ "version": "2.49.0",
4
4
  "description": "Thin React wrappers over the UIx v2 CSS component library.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -149,6 +149,7 @@
149
149
  "emojibase-data": "17.0.0",
150
150
  "esbuild": "^0.27.0",
151
151
  "jsdom": "30.1.0",
152
+ "jsqr": "1.4.0",
152
153
  "marked": "17.0.6",
153
154
  "tsup": "^8.0.0",
154
155
  "typescript": "^5.0.0"