@exsto/ds 0.0.1-rc.13 → 0.0.1-rc.15

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 (184) hide show
  1. package/dist/date-picker.js +1 -344
  2. package/dist/index.js +75 -4513
  3. package/dist/rich-text-editor.js +3 -904
  4. package/dist/source-hash.txt +1 -1
  5. package/dist/styles.css +9 -8
  6. package/dist/ui/src/components/edyn/animated-number/animated-number.js +108 -0
  7. package/dist/ui/src/components/edyn/animated-number/animated-number.js.map +1 -0
  8. package/dist/ui/src/components/edyn/audio-player/audio-player.js +86 -0
  9. package/dist/ui/src/components/edyn/audio-player/audio-player.js.map +1 -0
  10. package/dist/ui/src/components/edyn/date-picker/date-picker.js +250 -0
  11. package/dist/ui/src/components/edyn/date-picker/date-picker.js.map +1 -0
  12. package/dist/ui/src/components/edyn/date-picker/date-text.js +105 -0
  13. package/dist/ui/src/components/edyn/date-picker/date-text.js.map +1 -0
  14. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js +59 -0
  15. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js.map +1 -0
  16. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js +86 -0
  17. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js.map +1 -0
  18. package/dist/ui/src/components/edyn/multi-select/multi-select.js +332 -0
  19. package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -0
  20. package/dist/ui/src/components/edyn/progress-line/progress-line.js +100 -0
  21. package/dist/ui/src/components/edyn/progress-line/progress-line.js.map +1 -0
  22. package/dist/ui/src/components/edyn/rich-text-content/allow-list.js +37 -0
  23. package/dist/ui/src/components/edyn/rich-text-content/allow-list.js.map +1 -0
  24. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js +124 -0
  25. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js.map +1 -0
  26. package/dist/ui/src/components/edyn/rich-text-editor/extensions.js +124 -0
  27. package/dist/ui/src/components/edyn/rich-text-editor/extensions.js.map +1 -0
  28. package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js +232 -0
  29. package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js.map +1 -0
  30. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js +244 -0
  31. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js.map +1 -0
  32. package/dist/ui/src/components/edyn/rich-text-editor/serialize.js +55 -0
  33. package/dist/ui/src/components/edyn/rich-text-editor/serialize.js.map +1 -0
  34. package/dist/ui/src/components/edyn/rich-text-editor/sync.js +34 -0
  35. package/dist/ui/src/components/edyn/rich-text-editor/sync.js.map +1 -0
  36. package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js +235 -0
  37. package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js.map +1 -0
  38. package/dist/ui/src/components/edyn/rich-text-editor/types.js +26 -0
  39. package/dist/ui/src/components/edyn/rich-text-editor/types.js.map +1 -0
  40. package/dist/ui/src/components/edyn/video-player/video-player.js +132 -0
  41. package/dist/ui/src/components/edyn/video-player/video-player.js.map +1 -0
  42. package/dist/ui/src/components/ui/accordion/accordion.js +83 -0
  43. package/dist/ui/src/components/ui/accordion/accordion.js.map +1 -0
  44. package/dist/ui/src/components/ui/alert/alert.js +47 -0
  45. package/dist/ui/src/components/ui/alert/alert.js.map +1 -0
  46. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js +95 -0
  47. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js.map +1 -0
  48. package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js +15 -0
  49. package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js.map +1 -0
  50. package/dist/ui/src/components/ui/attachment/attachment.js +105 -0
  51. package/dist/ui/src/components/ui/attachment/attachment.js.map +1 -0
  52. package/dist/ui/src/components/ui/avatar/avatar.js +51 -0
  53. package/dist/ui/src/components/ui/avatar/avatar.js.map +1 -0
  54. package/dist/ui/src/components/ui/badge/badge.js +47 -0
  55. package/dist/ui/src/components/ui/badge/badge.js.map +1 -0
  56. package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js +73 -0
  57. package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js.map +1 -0
  58. package/dist/ui/src/components/ui/bubble/bubble.js +74 -0
  59. package/dist/ui/src/components/ui/bubble/bubble.js.map +1 -0
  60. package/dist/{chunks/input-syWxjY2U.js → ui/src/components/ui/button/button.js} +3 -82
  61. package/dist/ui/src/components/ui/button/button.js.map +1 -0
  62. package/dist/ui/src/components/ui/button-group/button-group.js +37 -0
  63. package/dist/ui/src/components/ui/button-group/button-group.js.map +1 -0
  64. package/dist/{chunks/date-range-picker-CWVcHgy2.js → ui/src/components/ui/calendar/calendar.js} +6 -100
  65. package/dist/ui/src/components/ui/calendar/calendar.js.map +1 -0
  66. package/dist/ui/src/components/ui/card/card.js +57 -0
  67. package/dist/ui/src/components/ui/card/card.js.map +1 -0
  68. package/dist/ui/src/components/ui/carousel/carousel.js +136 -0
  69. package/dist/ui/src/components/ui/carousel/carousel.js.map +1 -0
  70. package/dist/ui/src/components/ui/chart/chart.js +168 -0
  71. package/dist/ui/src/components/ui/chart/chart.js.map +1 -0
  72. package/dist/ui/src/components/ui/checkbox/checkbox.js +21 -0
  73. package/dist/ui/src/components/ui/checkbox/checkbox.js.map +1 -0
  74. package/dist/ui/src/components/ui/collapsible/collapsible.js +26 -0
  75. package/dist/ui/src/components/ui/collapsible/collapsible.js.map +1 -0
  76. package/dist/ui/src/components/ui/combobox/combobox.js +85 -0
  77. package/dist/ui/src/components/ui/combobox/combobox.js.map +1 -0
  78. package/dist/ui/src/components/ui/command/command.js +118 -0
  79. package/dist/ui/src/components/ui/command/command.js.map +1 -0
  80. package/dist/ui/src/components/ui/context-menu/context-menu.js +134 -0
  81. package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -0
  82. package/dist/ui/src/components/ui/dialog/dialog.js +92 -0
  83. package/dist/ui/src/components/ui/dialog/dialog.js.map +1 -0
  84. package/dist/ui/src/components/ui/direction/direction.js +2 -0
  85. package/dist/ui/src/components/ui/drawer/drawer.js +86 -0
  86. package/dist/ui/src/components/ui/drawer/drawer.js.map +1 -0
  87. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +160 -0
  88. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -0
  89. package/dist/ui/src/components/ui/empty/empty.js +61 -0
  90. package/dist/ui/src/components/ui/empty/empty.js.map +1 -0
  91. package/dist/ui/src/components/ui/field/field.js +36 -0
  92. package/dist/ui/src/components/ui/field/field.js.map +1 -0
  93. package/dist/ui/src/components/ui/hover-card/hover-card.js +38 -0
  94. package/dist/ui/src/components/ui/hover-card/hover-card.js.map +1 -0
  95. package/dist/ui/src/components/ui/indicator/indicator.js +79 -0
  96. package/dist/ui/src/components/ui/indicator/indicator.js.map +1 -0
  97. package/dist/ui/src/components/ui/input/input.js +16 -0
  98. package/dist/ui/src/components/ui/input/input.js.map +1 -0
  99. package/dist/ui/src/components/ui/input-group/input-group.js +77 -0
  100. package/dist/ui/src/components/ui/input-group/input-group.js.map +1 -0
  101. package/dist/ui/src/components/ui/input-otp/input-otp.js +48 -0
  102. package/dist/ui/src/components/ui/input-otp/input-otp.js.map +1 -0
  103. package/dist/ui/src/components/ui/item/item.js +122 -0
  104. package/dist/ui/src/components/ui/item/item.js.map +1 -0
  105. package/dist/ui/src/components/ui/kbd/kbd.js +21 -0
  106. package/dist/ui/src/components/ui/kbd/kbd.js.map +1 -0
  107. package/dist/ui/src/components/ui/label/label.js +14 -0
  108. package/dist/ui/src/components/ui/label/label.js.map +1 -0
  109. package/dist/ui/src/components/ui/marker/marker.js +44 -0
  110. package/dist/ui/src/components/ui/marker/marker.js.map +1 -0
  111. package/dist/ui/src/components/ui/menubar/menubar.js +137 -0
  112. package/dist/ui/src/components/ui/menubar/menubar.js.map +1 -0
  113. package/dist/ui/src/components/ui/message/message.js +50 -0
  114. package/dist/ui/src/components/ui/message/message.js.map +1 -0
  115. package/dist/ui/src/components/ui/message-scroller/message-scroller.js +61 -0
  116. package/dist/ui/src/components/ui/message-scroller/message-scroller.js.map +1 -0
  117. package/dist/ui/src/components/ui/native-select/native-select.js +39 -0
  118. package/dist/ui/src/components/ui/native-select/native-select.js.map +1 -0
  119. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js +84 -0
  120. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js.map +1 -0
  121. package/dist/ui/src/components/ui/pagination/pagination.js +104 -0
  122. package/dist/ui/src/components/ui/pagination/pagination.js.map +1 -0
  123. package/dist/ui/src/components/ui/popover/popover.js +56 -0
  124. package/dist/ui/src/components/ui/popover/popover.js.map +1 -0
  125. package/dist/ui/src/components/ui/progress/progress.js +70 -0
  126. package/dist/ui/src/components/ui/progress/progress.js.map +1 -0
  127. package/dist/ui/src/components/ui/questionnaire/questionnaire.js +171 -0
  128. package/dist/ui/src/components/ui/questionnaire/questionnaire.js.map +1 -0
  129. package/dist/ui/src/components/ui/radio-group/radio-group.js +29 -0
  130. package/dist/ui/src/components/ui/radio-group/radio-group.js.map +1 -0
  131. package/dist/ui/src/components/ui/resizable/resizable.js +29 -0
  132. package/dist/ui/src/components/ui/resizable/resizable.js.map +1 -0
  133. package/dist/ui/src/components/ui/scroll-area/scroll-area.js +119 -0
  134. package/dist/ui/src/components/ui/scroll-area/scroll-area.js.map +1 -0
  135. package/dist/ui/src/components/ui/select/select.js +99 -0
  136. package/dist/ui/src/components/ui/select/select.js.map +1 -0
  137. package/dist/ui/src/components/ui/separator/separator.js +30 -0
  138. package/dist/ui/src/components/ui/separator/separator.js.map +1 -0
  139. package/dist/ui/src/components/ui/sheet/sheet.js +89 -0
  140. package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -0
  141. package/dist/ui/src/components/ui/sidebar/sidebar.js +383 -0
  142. package/dist/ui/src/components/ui/sidebar/sidebar.js.map +1 -0
  143. package/dist/ui/src/components/ui/skeleton/skeleton.js +14 -0
  144. package/dist/ui/src/components/ui/skeleton/skeleton.js.map +1 -0
  145. package/dist/ui/src/components/ui/slider/slider.js +49 -0
  146. package/dist/ui/src/components/ui/slider/slider.js.map +1 -0
  147. package/dist/ui/src/components/ui/spinner/spinner.js +28 -0
  148. package/dist/ui/src/components/ui/spinner/spinner.js.map +1 -0
  149. package/dist/ui/src/components/ui/switch/switch.js +20 -0
  150. package/dist/ui/src/components/ui/switch/switch.js.map +1 -0
  151. package/dist/ui/src/components/ui/table/table.js +68 -0
  152. package/dist/ui/src/components/ui/table/table.js.map +1 -0
  153. package/dist/ui/src/components/ui/tabs/tabs.js +50 -0
  154. package/dist/ui/src/components/ui/tabs/tabs.js.map +1 -0
  155. package/dist/ui/src/components/ui/textarea/textarea.js +14 -0
  156. package/dist/ui/src/components/ui/textarea/textarea.js.map +1 -0
  157. package/dist/ui/src/components/ui/toast/toast.js +124 -0
  158. package/dist/ui/src/components/ui/toast/toast.js.map +1 -0
  159. package/dist/ui/src/components/ui/toggle/toggle.js +37 -0
  160. package/dist/ui/src/components/ui/toggle/toggle.js.map +1 -0
  161. package/dist/ui/src/components/ui/toggle-group/toggle-group.js +56 -0
  162. package/dist/ui/src/components/ui/toggle-group/toggle-group.js.map +1 -0
  163. package/dist/ui/src/components/ui/tooltip/tooltip.js +42 -0
  164. package/dist/ui/src/components/ui/tooltip/tooltip.js.map +1 -0
  165. package/dist/ui/src/hooks/use-media-player.js +140 -0
  166. package/dist/ui/src/hooks/use-media-player.js.map +1 -0
  167. package/dist/ui/src/hooks/use-merged-refs.js +18 -0
  168. package/dist/ui/src/hooks/use-merged-refs.js.map +1 -0
  169. package/dist/ui/src/hooks/use-mobile.js +20 -0
  170. package/dist/ui/src/hooks/use-mobile.js.map +1 -0
  171. package/dist/ui/src/lib/first-weekday.js +34 -0
  172. package/dist/ui/src/lib/first-weekday.js.map +1 -0
  173. package/dist/ui/src/lib/popup-list.js +26 -0
  174. package/dist/ui/src/lib/popup-list.js.map +1 -0
  175. package/dist/ui/src/lib/utils.js +21 -0
  176. package/dist/ui/src/lib/utils.js.map +1 -0
  177. package/package.json +1 -1
  178. package/dist/chunks/date-range-picker-CWVcHgy2.js.map +0 -1
  179. package/dist/chunks/input-syWxjY2U.js.map +0 -1
  180. package/dist/chunks/toggle-Bxg8P-61.js +0 -490
  181. package/dist/chunks/toggle-Bxg8P-61.js.map +0 -1
  182. package/dist/date-picker.js.map +0 -1
  183. package/dist/index.js.map +0 -1
  184. package/dist/rich-text-editor.js.map +0 -1
@@ -1 +1 @@
1
- baf2d34d8256d4f9ca45b9a3adab201d955f45d438dc72f74b30fcf41b3dbd6f
1
+ 51bef8069ad2575c9c422f14d92fe68c65cfbd0cb8727824d68ce82165e7e6b3
package/dist/styles.css CHANGED
@@ -120,6 +120,7 @@
120
120
  --font-sans: "Inter", sans-serif;
121
121
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
122
122
  "Courier New", monospace;
123
+ --color-red-300: oklch(80.8% .114 19.571);
123
124
  --spacing: .25rem;
124
125
  --container-xs: 20rem;
125
126
  --container-sm: 24rem;
@@ -2975,14 +2976,6 @@
2975
2976
  color: var(--accent-foreground);
2976
2977
  }
2977
2978
 
2978
- .group-has-disabled\/field\:pointer-events-none:is(:where(.group\/field):has(:disabled) *) {
2979
- pointer-events: none;
2980
- }
2981
-
2982
- .group-has-disabled\/field\:opacity-50:is(:where(.group\/field):has(:disabled) *) {
2983
- opacity: .5;
2984
- }
2985
-
2986
2979
  .group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar="menu-action"]) *) {
2987
2980
  padding-right: calc(var(--spacing) * 8);
2988
2981
  }
@@ -5882,6 +5875,10 @@
5882
5875
  color: var(--muted-foreground);
5883
5876
  }
5884
5877
 
5878
+ .dark\:text-red-300:is(.dark *) {
5879
+ color: var(--color-red-300);
5880
+ }
5881
+
5885
5882
  .dark\:group-data-checked\/questionnaire-choice\:bg-primary:is(.dark *):is(:is(:where(.group\/questionnaire-choice):where([data-state="checked"]), :where(.group\/questionnaire-choice):where([data-checked]:not([data-checked="false"]))) *) {
5886
5883
  background-color: var(--primary);
5887
5884
  }
@@ -6326,6 +6323,10 @@
6326
6323
  }
6327
6324
  }
6328
6325
 
6326
+ .data-disabled\:text-muted-foreground:where([data-disabled="true"]), .data-disabled\:text-muted-foreground:where([data-disabled]:not([data-disabled="false"])) {
6327
+ color: var(--muted-foreground);
6328
+ }
6329
+
6329
6330
  .data-disabled\:opacity-50:where([data-disabled="true"]), .data-disabled\:opacity-50:where([data-disabled]:not([data-disabled="false"])) {
6330
6331
  opacity: .5;
6331
6332
  }
@@ -0,0 +1,108 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region ../ui/src/components/edyn/animated-number/animated-number.tsx
4
+ var DURATION = 800;
5
+ var easeInOutCubic = (t) => t < .5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2;
6
+ var reducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7
+ var NUMBER = new Intl.NumberFormat("en-GB");
8
+ /** Thousands separated, rounded to a whole number: the default way a count reads. */
9
+ var formatCount = (value) => NUMBER.format(Math.round(value) || 0);
10
+ var useSeen = (element) => {
11
+ const [seen, setSeen] = useState(() => typeof IntersectionObserver === "undefined");
12
+ useEffect(() => {
13
+ const target = element.current;
14
+ if (seen || target === null) return;
15
+ const observer = new IntersectionObserver((entries) => {
16
+ if (entries.some((entry) => entry.isIntersecting)) {
17
+ setSeen(true);
18
+ observer.disconnect();
19
+ }
20
+ });
21
+ observer.observe(target);
22
+ return () => observer.disconnect();
23
+ }, [element, seen]);
24
+ return seen;
25
+ };
26
+ /**
27
+ * The values to show for `targets` this frame, all on one clock and one curve, so a
28
+ * count and its bar move together. They start from 0 and move once `element` has
29
+ * been on screen.
30
+ */
31
+ var useAnimatedValues = (targets, element) => {
32
+ const seen = useSeen(element);
33
+ const [shown, setShown] = useState(() => targets.map(() => 0));
34
+ const refCurrent = useRef(shown);
35
+ const key = targets.join(",");
36
+ useEffect(() => {
37
+ if (!seen) return;
38
+ const goal = key.split(",").map(Number);
39
+ const from = goal.map((_, index) => refCurrent.current[index] ?? 0);
40
+ if (from.every((value, index) => value === goal[index])) return;
41
+ if (reducedMotion()) {
42
+ refCurrent.current = goal;
43
+ setShown(goal);
44
+ return;
45
+ }
46
+ const start = performance.now();
47
+ let frame = 0;
48
+ const tick = (now) => {
49
+ const progress = Math.min(1, (now - start) / DURATION);
50
+ const eased = easeInOutCubic(progress);
51
+ const next = progress === 1 ? goal : goal.map((value, index) => (from[index] ?? 0) + (value - (from[index] ?? 0)) * eased);
52
+ refCurrent.current = next;
53
+ setShown(next);
54
+ if (progress < 1) frame = requestAnimationFrame(tick);
55
+ };
56
+ frame = requestAnimationFrame(tick);
57
+ return () => cancelAnimationFrame(frame);
58
+ }, [key, seen]);
59
+ return shown;
60
+ };
61
+ /**
62
+ * A number that moves to its new value. Its width holds the widest of where it starts
63
+ * and where it lands, in tabular figures, so nothing beside it moves while the digits
64
+ * change. Assistive technology reads only the final value.
65
+ */
66
+ var AnimatedNumber = ({ value, format = formatCount, className, ...props }) => {
67
+ const refElement = useRef(null);
68
+ const [shown = 0] = useAnimatedValues([value], refElement);
69
+ return /* @__PURE__ */ jsx(NumberText, {
70
+ element: refElement,
71
+ shown,
72
+ value,
73
+ format,
74
+ className,
75
+ ...props
76
+ });
77
+ };
78
+ /** The text of a moving number: what it shows now, held at its widest, with the final value for assistive technology. */
79
+ var NumberText = ({ element, shown, value, format = formatCount, className, ...props }) => {
80
+ const now = format(shown);
81
+ const final = format(value);
82
+ const widest = now.length > final.length ? now : final;
83
+ return /* @__PURE__ */ jsxs("span", {
84
+ "data-slot": "animated-number",
85
+ ...props,
86
+ ref: element,
87
+ className,
88
+ children: [/* @__PURE__ */ jsxs("span", {
89
+ "aria-hidden": "true",
90
+ className: "inline-grid tabular-nums",
91
+ children: [/* @__PURE__ */ jsx("span", {
92
+ "data-ghost": widest,
93
+ className: "invisible col-start-1 row-start-1 ghost-text"
94
+ }), /* @__PURE__ */ jsx("span", {
95
+ "data-slot": "animated-number-value",
96
+ className: "col-start-1 row-start-1",
97
+ children: now
98
+ })]
99
+ }), /* @__PURE__ */ jsx("span", {
100
+ className: "sr-only",
101
+ children: final
102
+ })]
103
+ });
104
+ };
105
+ //#endregion
106
+ export { AnimatedNumber, NumberText, formatCount, useAnimatedValues };
107
+
108
+ //# sourceMappingURL=animated-number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-number.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/animated-number/animated-number.tsx"],"sourcesContent":["import { type ComponentPropsWithoutRef, type RefObject, useEffect, useRef, useState } from 'react';\n\n// Numbers that move instead of jumping when a tab or a filter changes them: from the\n// value shown to the new one over 800ms, easing in and out; the first time, from 0\n// once it scrolls into view. Interrupted by another change, it carries on from where\n// it is, never back. With reduced motion, or when the value has not changed (a refetch\n// with the same answer), nothing moves.\n\nconst DURATION = 800;\n\nconst easeInOutCubic = (t: number): number => (t < 0.5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2);\n\nconst reducedMotion = (): boolean => window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\nconst NUMBER = new Intl.NumberFormat('en-GB');\n\n/** Thousands separated, rounded to a whole number: the default way a count reads. */\nexport const formatCount = (value: number): string => NUMBER.format(Math.round(value) || 0);\n\n// Whether the element has been on screen yet; once it has, it stays counted as seen.\nconst useSeen = (element: RefObject<Element | null>): boolean => {\n const [seen, setSeen] = useState(() => typeof IntersectionObserver === 'undefined');\n useEffect(() => {\n const target = element.current;\n if (seen || target === null) {\n return;\n }\n const observer = new IntersectionObserver((entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n setSeen(true);\n observer.disconnect();\n }\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [element, seen]);\n return seen;\n};\n\n/**\n * The values to show for `targets` this frame, all on one clock and one curve, so a\n * count and its bar move together. They start from 0 and move once `element` has\n * been on screen.\n */\nexport const useAnimatedValues = (targets: number[], element: RefObject<Element | null>): number[] => {\n const seen = useSeen(element);\n const [shown, setShown] = useState<number[]>(() => targets.map(() => 0));\n const refCurrent = useRef(shown);\n const key = targets.join(',');\n useEffect(() => {\n if (!seen) {\n return;\n }\n const goal = key.split(',').map(Number);\n const from = goal.map((_, index) => refCurrent.current[index] ?? 0);\n if (from.every((value, index) => value === goal[index])) {\n return;\n }\n if (reducedMotion()) {\n refCurrent.current = goal;\n setShown(goal);\n return;\n }\n const start = performance.now();\n let frame = 0;\n const tick = (now: number): void => {\n const progress = Math.min(1, (now - start) / DURATION);\n const eased = easeInOutCubic(progress);\n // Exactly the goal at the end, never a float a hair beside it.\n const next = progress === 1 ? goal : goal.map((value, index) => (from[index] ?? 0) + (value - (from[index] ?? 0)) * eased);\n refCurrent.current = next;\n setShown(next);\n if (progress < 1) {\n frame = requestAnimationFrame(tick);\n }\n };\n frame = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frame);\n }, [key, seen]);\n return shown;\n};\n\nexport interface IAnimatedNumberProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children' | 'className'> {\n value: number;\n /** What it shows while it moves and once there (formatCount by default; a percentage formatter for shares). */\n format?: (value: number) => string;\n className?: string;\n}\n\n/**\n * A number that moves to its new value. Its width holds the widest of where it starts\n * and where it lands, in tabular figures, so nothing beside it moves while the digits\n * change. Assistive technology reads only the final value.\n */\nexport const AnimatedNumber = ({ value, format = formatCount, className, ...props }: IAnimatedNumberProps) => {\n const refElement = useRef<HTMLSpanElement>(null);\n const [shown = 0] = useAnimatedValues([value], refElement);\n return <NumberText element={refElement} shown={shown} value={value} format={format} className={className} {...props} />;\n};\n\nexport interface INumberTextProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children' | 'className'> {\n element?: RefObject<HTMLSpanElement | null>;\n shown: number;\n value: number;\n format?: (value: number) => string;\n className?: string;\n}\n\n/** The text of a moving number: what it shows now, held at its widest, with the final value for assistive technology. */\nexport const NumberText = ({ element, shown, value, format = formatCount, className, ...props }: INumberTextProps) => {\n const now = format(shown);\n const final = format(value);\n // The widest of the two, laid invisibly under the number: it holds the width of the\n // real figures (tabular digits are not exactly 1ch), so the number grows or shrinks\n // only once it has landed.\n const widest = now.length > final.length ? now : final;\n return (\n <span data-slot=\"animated-number\" {...props} ref={element} className={className}>\n <span aria-hidden=\"true\" className=\"inline-grid tabular-nums\">\n <span data-ghost={widest} className=\"invisible col-start-1 row-start-1 ghost-text\" />\n <span data-slot=\"animated-number-value\" className=\"col-start-1 row-start-1\">\n {now}\n </span>\n </span>\n <span className=\"sr-only\">{final}</span>\n </span>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { cn, cssLength } from "../../../lib/utils.js";
2
+ import { Button } from "../../ui/button/button.js";
3
+ import { Slider } from "../../ui/slider/slider.js";
4
+ import { formatMediaTime, useMediaPlayer } from "../../../hooks/use-media-player.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { IconPlayerPauseFilled, IconPlayerPlayFilled, IconVolume, IconVolumeOff } from "@tabler/icons-react";
7
+ //#region ../ui/src/components/edyn/audio-player/audio-player.tsx
8
+ /**
9
+ * Compact inline media control for audio content: play/pause, elapsed and total
10
+ * time, a seekable timeline, and volume. Four elements in one rounded row, per
11
+ * the design-system anatomy.
12
+ */
13
+ var AudioPlayer = ({ src, muted = false, loop = false, disabled = false, width, className, "aria-label": ariaLabel = "Audio player", style, onPlay, onPause, onEnded, onTimeUpdate, ...props }) => {
14
+ const { refMedia, playing, currentTime, duration, muted: isMuted, toggle, seek, toggleMute } = useMediaPlayer({
15
+ onPlay,
16
+ onPause,
17
+ onEnded,
18
+ onTimeUpdate
19
+ });
20
+ const PlaybackIcon = playing ? IconPlayerPauseFilled : IconPlayerPlayFilled;
21
+ const VolumeIcon = isMuted ? IconVolumeOff : IconVolume;
22
+ return /* @__PURE__ */ jsxs("div", {
23
+ "data-slot": "audio-player",
24
+ "data-playing": playing || void 0,
25
+ role: "group",
26
+ ...props,
27
+ "aria-label": ariaLabel,
28
+ style: {
29
+ width: cssLength(width),
30
+ ...style
31
+ },
32
+ className: cn("flex h-12 w-full items-center gap-2 rounded-lg bg-muted px-3 py-1.5", className),
33
+ children: [
34
+ /* @__PURE__ */ jsx(Button, {
35
+ type: "button",
36
+ variant: "ghost",
37
+ size: "icon",
38
+ disabled,
39
+ "aria-label": playing ? "Pause" : "Play",
40
+ onClick: toggle,
41
+ children: /* @__PURE__ */ jsx(PlaybackIcon, {})
42
+ }),
43
+ /* @__PURE__ */ jsxs("span", {
44
+ "data-slot": "audio-player-time",
45
+ className: "shrink-0 text-xs tabular-nums text-foreground/70",
46
+ children: [
47
+ formatMediaTime(currentTime),
48
+ " / ",
49
+ formatMediaTime(duration)
50
+ ]
51
+ }),
52
+ /* @__PURE__ */ jsx(Slider, {
53
+ className: "flex-1 [&_[data-slot=slider-control]]:py-1.5 [&_[data-slot=slider-thumb]]:size-3 [&_[data-slot=slider-track]]:h-1 [&_[data-slot=slider-track]]:bg-foreground/15",
54
+ value: Math.min(currentTime, duration || 0),
55
+ min: 0,
56
+ max: duration || 0,
57
+ step: 1,
58
+ disabled: disabled || duration === 0,
59
+ "aria-label": "Seek",
60
+ onValueChange: (next) => {
61
+ seek(next);
62
+ }
63
+ }),
64
+ /* @__PURE__ */ jsx(Button, {
65
+ type: "button",
66
+ variant: "ghost",
67
+ size: "icon",
68
+ disabled,
69
+ "aria-label": isMuted ? "Unmute" : "Mute",
70
+ onClick: toggleMute,
71
+ children: /* @__PURE__ */ jsx(VolumeIcon, {})
72
+ }),
73
+ /* @__PURE__ */ jsx("audio", {
74
+ ref: refMedia,
75
+ src,
76
+ muted,
77
+ loop,
78
+ preload: "metadata"
79
+ })
80
+ ]
81
+ });
82
+ };
83
+ //#endregion
84
+ export { AudioPlayer };
85
+
86
+ //# sourceMappingURL=audio-player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-player.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/audio-player/audio-player.tsx"],"sourcesContent":["import { Button } from '@edyn/ui/components/ui/button';\nimport { Slider } from '@edyn/ui/components/ui/slider';\nimport { formatMediaTime, useMediaPlayer } from '@edyn/ui/hooks/use-media-player';\nimport type { TCssLength } from '@edyn/ui/lib/utils';\nimport { cn, cssLength } from '@edyn/ui/lib/utils';\nimport { IconPlayerPauseFilled, IconPlayerPlayFilled, IconVolume, IconVolumeOff } from '@tabler/icons-react';\nimport type { ComponentPropsWithoutRef } from 'react';\n\nexport interface IAudioPlayerProps\n extends Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'className' | 'onPlay' | 'onPause' | 'onEnded' | 'onTimeUpdate' | 'role'> {\n /** Audio source URL. */\n src?: string;\n /** Start muted. The volume control still works from here. */\n muted?: boolean;\n /** Loop playback when the track ends. */\n loop?: boolean;\n /** Disable every control without hiding the player. */\n disabled?: boolean;\n /**\n * Bar width. A number is px, a string is any CSS length (`'100%'`, `'34rem'`).\n * Omitted, the bar fills the width it is given. The height is fixed at 48px by\n * the design: the row has to line up with the other form-height controls.\n */\n width?: TCssLength;\n className?: string;\n 'aria-label'?: string;\n onPlay?: () => void;\n onPause?: () => void;\n onEnded?: () => void;\n onTimeUpdate?: (currentTime: number, duration: number) => void;\n}\n\n/**\n * Compact inline media control for audio content: play/pause, elapsed and total\n * time, a seekable timeline, and volume. Four elements in one rounded row, per\n * the design-system anatomy.\n */\nexport const AudioPlayer = ({\n src,\n muted = false,\n loop = false,\n disabled = false,\n width,\n className,\n 'aria-label': ariaLabel = 'Audio player',\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n ...props\n}: IAudioPlayerProps) => {\n const {\n refMedia,\n playing,\n currentTime,\n duration,\n muted: isMuted,\n toggle,\n seek,\n toggleMute,\n } = useMediaPlayer<HTMLAudioElement>({\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n });\n\n const PlaybackIcon = playing ? IconPlayerPauseFilled : IconPlayerPlayFilled;\n const VolumeIcon = isMuted ? IconVolumeOff : IconVolume;\n\n return (\n // biome-ignore lint/a11y/useSemanticElements: <fieldset> carries form semantics and UA styling this player does not want; role=\"group\" is the correct ARIA for a labelled set of related controls.\n <div\n data-slot=\"audio-player\"\n data-playing={playing || undefined}\n role=\"group\"\n {...props}\n aria-label={ariaLabel}\n style={{ width: cssLength(width), ...style }}\n className={cn('flex h-12 w-full items-center gap-2 rounded-lg bg-muted px-3 py-1.5', className)}\n >\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled} aria-label={playing ? 'Pause' : 'Play'} onClick={toggle}>\n <PlaybackIcon />\n </Button>\n\n {/* `text-foreground/70` not `text-muted-foreground`: the latter on `bg-muted` is only 4.34:1, below AA. */}\n <span data-slot=\"audio-player-time\" className=\"shrink-0 text-xs tabular-nums text-foreground/70\">\n {formatMediaTime(currentTime)} / {formatMediaTime(duration)}\n </span>\n\n {/* Scope the form-sized Slider down to a timeline: thinner track, smaller thumb, and a track tint that\n reads against this bar (the Slider's default `bg-muted` track is invisible on a `bg-muted` container). */}\n <Slider\n className=\"flex-1 [&_[data-slot=slider-control]]:py-1.5 [&_[data-slot=slider-thumb]]:size-3 [&_[data-slot=slider-track]]:h-1 [&_[data-slot=slider-track]]:bg-foreground/15\"\n value={Math.min(currentTime, duration || 0)}\n min={0}\n max={duration || 0}\n step={1}\n disabled={disabled || duration === 0}\n aria-label=\"Seek\"\n onValueChange={(next) => {\n seek(next);\n }}\n />\n\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled} aria-label={isMuted ? 'Unmute' : 'Mute'} onClick={toggleMute}>\n <VolumeIcon />\n </Button>\n\n {/* Visually hidden: the row above is the entire control surface. */}\n {/* No class needed: the UA stylesheet already hides `audio:not([controls])`, and that rule is `!important` so author CSS cannot change it either way. */}\n <audio ref={refMedia} src={src} muted={muted} loop={loop} preload=\"metadata\" />\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAqCA,IAAa,eAAe,EAC1B,KACA,QAAQ,OACR,OAAO,OACP,WAAW,OACX,OACA,WACA,cAAc,YAAY,gBAC1B,OACA,QACA,SACA,SACA,cACA,GAAG,YACoB;CACvB,MAAM,EACJ,UACA,SACA,aACA,UACA,OAAO,SACP,QACA,MACA,eACE,eAAiC;EACnC;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,eAAe,UAAU,wBAAwB;CACvD,MAAM,aAAa,UAAU,gBAAgB;CAE7C,OAEE,qBAAC,OAAD;EACE,aAAU;EACV,gBAAc,WAAW,KAAA;EACzB,MAAK;EACL,GAAI;EACJ,cAAY;EACZ,OAAO;GAAE,OAAO,UAAU,KAAK;GAAG,GAAG;EAAM;EAC3C,WAAW,GAAG,uEAAuE,SAAS;EAPhG,UAAA;GASE,oBAAC,QAAD;IAAQ,MAAK;IAAS,SAAQ;IAAQ,MAAK;IAAiB;IAAU,cAAY,UAAU,UAAU;IAAQ,SAAS;IACrH,UAAA,oBAAC,cAAD,CAAe,CAAA;GACT,CAAA;GAGR,qBAAC,QAAD;IAAM,aAAU;IAAoB,WAAU;IAA9C,UAAA;KACG,gBAAgB,WAAW;KAAE;KAAI,gBAAgB,QAAQ;IACtD;;GAIN,oBAAC,QAAD;IACE,WAAU;IACV,OAAO,KAAK,IAAI,aAAa,YAAY,CAAC;IAC1C,KAAK;IACL,KAAK,YAAY;IACjB,MAAM;IACN,UAAU,YAAY,aAAa;IACnC,cAAW;IACX,gBAAgB,SAAS;KACvB,KAAK,IAAI;IACX;GACD,CAAA;GAED,oBAAC,QAAD;IAAQ,MAAK;IAAS,SAAQ;IAAQ,MAAK;IAAiB;IAAU,cAAY,UAAU,WAAW;IAAQ,SAAS;IACtH,UAAA,oBAAC,YAAD,CAAa,CAAA;GACP,CAAA;GAIR,oBAAC,SAAD;IAAO,KAAK;IAAe;IAAY;IAAa;IAAM,SAAQ;GAAY,CAAA;EAC3E;;AAET"}
@@ -0,0 +1,250 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { Button } from "../../ui/button/button.js";
3
+ import { Calendar } from "../../ui/calendar/calendar.js";
4
+ import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover/popover.js";
5
+ import { firstWeekdayOf } from "../../../lib/first-weekday.js";
6
+ import { dateToDay, dayToDate } from "../date-range-picker/date-range-picker.js";
7
+ import { Input } from "../../ui/input/input.js";
8
+ import { datePatternOf, dayProblem, formatDay, parseDateText } from "./date-text.js";
9
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { IconCalendar } from "@tabler/icons-react";
12
+ //#region ../ui/src/components/edyn/date-picker/date-picker.tsx
13
+ var DEFAULT_DATE_PICKER_LABELS = {
14
+ choose: "Choose date",
15
+ format: (pattern) => `Enter a date as ${pattern}.`,
16
+ notReal: "That date does not exist.",
17
+ beforeMin: (min) => `Choose ${min} or later.`,
18
+ afterMax: (max) => `Choose ${max} or earlier.`,
19
+ unavailable: "That date is not available."
20
+ };
21
+ var problemText = (problem, labels, locale, limits, pattern) => {
22
+ switch (problem) {
23
+ case "format": return labels.format(pattern);
24
+ case "notReal": return labels.notReal;
25
+ case "beforeMin": return labels.beforeMin(limits.min === void 0 ? "" : formatDay(limits.min, locale));
26
+ case "afterMax": return labels.afterMax(limits.max === void 0 ? "" : formatDay(limits.max, locale));
27
+ default: return labels.unavailable;
28
+ }
29
+ };
30
+ /**
31
+ * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it
32
+ * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.
33
+ */
34
+ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled = false, locale = "en-GB", calendarLocale, placeholder, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, labels: labelOverrides, className }) => {
35
+ const labels = useMemo(() => ({
36
+ ...DEFAULT_DATE_PICKER_LABELS,
37
+ ...labelOverrides
38
+ }), [labelOverrides]);
39
+ const pattern = useMemo(() => datePatternOf(locale), [locale]);
40
+ const current = value === "" ? void 0 : value;
41
+ const errorId = useId();
42
+ const refField = useRef(null);
43
+ const refInput = useRef(null);
44
+ const refPointer = useRef(false);
45
+ const refKnown = useRef(current);
46
+ const refRefused = useRef(null);
47
+ const [text, setText] = useState(current === void 0 ? "" : formatDay(current, locale));
48
+ const [problem, setProblem] = useState(null);
49
+ const [open, setOpen] = useState(false);
50
+ useEffect(() => {
51
+ if (current !== refKnown.current) {
52
+ refKnown.current = current;
53
+ refRefused.current = null;
54
+ setText(current === void 0 ? "" : formatDay(current, locale));
55
+ setProblem(null);
56
+ }
57
+ }, [current, locale]);
58
+ const report = (day, state) => {
59
+ refKnown.current = day;
60
+ refRefused.current = state.kind === "refused" ? `${state.text}\u0000${state.problem}` : null;
61
+ onValueChange(day, state);
62
+ };
63
+ /**
64
+ * Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,
65
+ * with the state saying whether the field is empty or holds text it refused.
66
+ */
67
+ const commit = (typed) => {
68
+ const parsed = parseDateText(typed, locale);
69
+ if (parsed.kind === "empty") {
70
+ setProblem(null);
71
+ if (current !== void 0 || refRefused.current !== null) report(void 0, { kind: "empty" });
72
+ return;
73
+ }
74
+ const found = parsed.kind === "problem" ? parsed.problem : dayProblem(parsed.day, {
75
+ min,
76
+ max,
77
+ disabledDates
78
+ });
79
+ if (found !== void 0) {
80
+ setProblem(found);
81
+ const message = problemText(found, labels, locale, {
82
+ min,
83
+ max
84
+ }, pattern);
85
+ if (current !== void 0 || refRefused.current !== `${typed}\u0000${message}`) report(void 0, {
86
+ kind: "refused",
87
+ text: typed,
88
+ problem: message
89
+ });
90
+ return;
91
+ }
92
+ if (parsed.kind === "day") {
93
+ setProblem(null);
94
+ setText(formatDay(parsed.day, locale));
95
+ if (parsed.day !== current || refRefused.current !== null) report(parsed.day, { kind: "day" });
96
+ }
97
+ };
98
+ const pick = (date) => {
99
+ const day = dateToDay(date);
100
+ if (day === void 0) return;
101
+ setProblem(null);
102
+ setText(formatDay(day, locale));
103
+ if (day !== current || refRefused.current !== null) report(day, { kind: "day" });
104
+ setOpen(false);
105
+ };
106
+ /**
107
+ * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a
108
+ * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus
109
+ * going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else
110
+ * reads whatever is typed.
111
+ */
112
+ const handleBlur = (event) => {
113
+ const next = event.relatedTarget;
114
+ const intoCalendar = next?.closest("[data-slot=\"popover-content\"]") != null;
115
+ const intoButton = next?.closest("[data-slot=\"date-picker-trigger\"]") != null;
116
+ const typed = refInput.current?.value ?? "";
117
+ if (!(intoCalendar || intoButton || next !== null && refField.current?.contains(next) === true)) {
118
+ commit(typed);
119
+ return;
120
+ }
121
+ if (event.target !== refInput.current) return;
122
+ const onTheWay = intoCalendar || intoButton && refPointer.current;
123
+ const parsed = parseDateText(typed, locale);
124
+ if (parsed.kind === "empty" || parsed.kind === "day" && dayProblem(parsed.day, {
125
+ min,
126
+ max,
127
+ disabledDates
128
+ }) === void 0 || !onTheWay) commit(typed);
129
+ };
130
+ const handleKeyDown = (event) => {
131
+ if (event.key === "ArrowDown" && event.altKey && !disabled) {
132
+ event.preventDefault();
133
+ setOpen(true);
134
+ return;
135
+ }
136
+ if (event.key === "Escape" && open) {
137
+ event.preventDefault();
138
+ event.stopPropagation();
139
+ setOpen(false);
140
+ return;
141
+ }
142
+ if (event.key === "Enter") {
143
+ const parsed = parseDateText(event.currentTarget.value, locale);
144
+ if (parsed.kind !== "empty" && (parsed.kind === "problem" || dayProblem(parsed.day, {
145
+ min,
146
+ max,
147
+ disabledDates
148
+ }) !== void 0)) event.preventDefault();
149
+ commit(event.currentTarget.value);
150
+ }
151
+ };
152
+ const selected = dayToDate(current);
153
+ const limits = {
154
+ min,
155
+ max
156
+ };
157
+ const invalid = problem !== null || ariaInvalid === true || ariaInvalid === "true";
158
+ const describedBy = [ariaDescribedBy, problem === null ? void 0 : errorId].filter(Boolean).join(" ") || void 0;
159
+ const isOff = (date) => {
160
+ const day = dateToDay(date);
161
+ return day !== void 0 && dayProblem(day, {
162
+ min,
163
+ max,
164
+ disabledDates
165
+ }) !== void 0;
166
+ };
167
+ const today = /* @__PURE__ */ new Date();
168
+ const start = dayToDate(min);
169
+ const end = dayToDate(max);
170
+ const month = selected ?? (start !== void 0 && today < start ? start : end !== void 0 && today > end ? end : today);
171
+ return /* @__PURE__ */ jsxs("div", {
172
+ "data-slot": "date-picker",
173
+ className: cn("flex flex-col gap-1.5", className),
174
+ children: [/* @__PURE__ */ jsxs("div", {
175
+ ref: refField,
176
+ className: "relative",
177
+ onPointerDownCapture: () => {
178
+ refPointer.current = true;
179
+ },
180
+ onPointerUpCapture: () => {
181
+ refPointer.current = false;
182
+ },
183
+ children: [/* @__PURE__ */ jsx(Input, {
184
+ ref: refInput,
185
+ id,
186
+ type: "text",
187
+ inputMode: "numeric",
188
+ autoComplete: "off",
189
+ "data-slot": "date-picker-input",
190
+ value: text,
191
+ placeholder: placeholder ?? pattern,
192
+ disabled,
193
+ "aria-label": ariaLabel,
194
+ "aria-invalid": invalid ? true : void 0,
195
+ "aria-describedby": describedBy,
196
+ className: "pr-9",
197
+ onChange: (event) => {
198
+ setText(event.target.value);
199
+ setProblem(null);
200
+ },
201
+ onBlur: handleBlur,
202
+ onKeyDown: handleKeyDown
203
+ }), /* @__PURE__ */ jsxs(Popover, {
204
+ open,
205
+ onOpenChange: setOpen,
206
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
207
+ render: /* @__PURE__ */ jsx(Button, {
208
+ variant: "ghost",
209
+ size: "icon-sm",
210
+ "data-slot": "date-picker-trigger",
211
+ "aria-label": labels.choose,
212
+ disabled,
213
+ className: "absolute top-0.5 right-0.5",
214
+ onBlur: handleBlur
215
+ }),
216
+ children: /* @__PURE__ */ jsx(IconCalendar, { "aria-hidden": "true" })
217
+ }), /* @__PURE__ */ jsx(PopoverContent, {
218
+ anchor: refField,
219
+ align: "start",
220
+ className: "w-auto p-0",
221
+ finalFocus: refInput,
222
+ onBlur: handleBlur,
223
+ children: /* @__PURE__ */ jsx(Calendar, {
224
+ mode: "single",
225
+ required: true,
226
+ autoFocus: true,
227
+ locale: calendarLocale,
228
+ weekStartsOn: firstWeekdayOf(locale),
229
+ defaultMonth: month,
230
+ startMonth: start,
231
+ endMonth: end,
232
+ selected,
233
+ disabled: isOff,
234
+ onSelect: pick
235
+ })
236
+ })]
237
+ })]
238
+ }), problem !== null && /* @__PURE__ */ jsx("p", {
239
+ id: errorId,
240
+ "data-slot": "date-picker-error",
241
+ role: "alert",
242
+ className: "text-sm text-destructive",
243
+ children: problemText(problem, labels, locale, limits, pattern)
244
+ })]
245
+ });
246
+ };
247
+ //#endregion
248
+ export { DEFAULT_DATE_PICKER_LABELS, DatePicker };
249
+
250
+ //# sourceMappingURL=date-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-picker.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/date-picker/date-picker.tsx"],"sourcesContent":["import { dateToDay, dayToDate } from '@edyn/ui/components/edyn/date-range-picker';\nimport { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Input } from '@edyn/ui/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport { type ChangeEvent, type FocusEvent, type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport type { Locale } from 'react-day-picker';\nimport { datePatternOf, dayProblem, formatDay, parseDateText, type TDateProblem, type TDisabledDays } from './date-text';\n\n/** Every word the component prints, so a product can translate it. */\nexport interface IDatePickerLabels {\n /** The calendar button's accessible name. */\n choose: string;\n /** Said when the text is not a date; `pattern` is what to type, written the locale's way (`DD/MM/YYYY`). */\n format: (pattern: string) => string;\n notReal: string;\n /** `min` and `max` are written the locale's way. */\n beforeMin: (min: string) => string;\n afterMax: (max: string) => string;\n unavailable: string;\n}\n\nexport const DEFAULT_DATE_PICKER_LABELS: IDatePickerLabels = {\n choose: 'Choose date',\n format: (pattern) => `Enter a date as ${pattern}.`,\n notReal: 'That date does not exist.',\n beforeMin: (min) => `Choose ${min} or later.`,\n afterMax: (max) => `Choose ${max} or earlier.`,\n unavailable: 'That date is not available.',\n};\n\n/**\n * What the field holds when it reports, so a parent can tell three things apart that all arrive as `undefined`:\n * - `day`: a usable day, typed or picked.\n * - `empty`: the field was emptied, or nothing has been put in it. A required field is right to ask for a date.\n * - `refused`: the field holds text it will not take (not a day, not a real one, or outside `min`, `max` or\n * `disabledDates`). `text` is what was typed and `problem` the message the picker shows under the field, already\n * in the picker's `labels`. The picker is saying so itself, so the page should not add a second message.\n */\nexport type TDatePickerState = { kind: 'day' } | { kind: 'empty' } | { kind: 'refused'; text: string; problem: string };\n\nexport interface IDatePickerProps {\n /** For a Label's htmlFor; inside a Field the Field's label is already wired. */\n id?: string;\n /**\n * The chosen day as `yyyy-mm-dd`, or `undefined` for none (an empty string counts as none, so a value kept for an\n * `<input type=\"date\">` drops in). A string and not a Date, on purpose: a Date is an instant, and the instant for\n * \"5 March\" is another calendar day in another time zone, so a Date serialised for an API drifts a day. The text is\n * what `<input type=\"date\">`, JSON and a .NET `DateOnly` use, and it compares as a day.\n */\n value: string | undefined;\n /**\n * Called with a valid day, or with `undefined` when the field is emptied or its text is not a usable day. The second\n * argument says which of the two it was (see `TDatePickerState`); a caller that has no use for it can ignore it.\n * Text that is refused is reported each time it changes, also when the field held no day before, so a form that\n * tracks the state hears about it. Emptying the field after that reports `empty`.\n */\n onValueChange: (value: string | undefined, state: TDatePickerState) => void;\n /** The earliest and latest day that can be chosen, as `yyyy-mm-dd`, both included. */\n min?: string;\n max?: string;\n /** Days that cannot be chosen: a list of `yyyy-mm-dd`, or a function that says so for one (weekends, say). */\n disabledDates?: TDisabledDays;\n disabled?: boolean;\n /** How a day is written and in which order it is typed (BCP 47); en-GB reads 05/03/2026. */\n locale?: string;\n /** The calendar's own words (month and weekday names), as a date-fns locale; English by default. The first weekday follows `locale`. */\n calendarLocale?: Locale;\n /** What the empty field shows; the pattern for the locale (`DD/MM/YYYY`) by default. */\n placeholder?: string;\n /** An accessible name, when there is no visible label to point at. */\n 'aria-label'?: string;\n /** Marks the field invalid (the kit's destructive border), as a Field's invalid state does. */\n 'aria-invalid'?: boolean | 'true' | 'false';\n /** The id of the text that explains it. Added to what a Field names already, and to the picker's own error. */\n 'aria-describedby'?: string;\n labels?: Partial<IDatePickerLabels>;\n /** Layout only (a width); the look is the kit's. */\n className?: string;\n}\n\nconst problemText = (\n problem: TDateProblem,\n labels: IDatePickerLabels,\n locale: string,\n limits: { min?: string; max?: string },\n pattern: string,\n): string => {\n switch (problem) {\n case 'format':\n return labels.format(pattern);\n case 'notReal':\n return labels.notReal;\n case 'beforeMin':\n return labels.beforeMin(limits.min === undefined ? '' : formatDay(limits.min, locale));\n case 'afterMax':\n return labels.afterMax(limits.max === undefined ? '' : formatDay(limits.max, locale));\n default:\n return labels.unavailable;\n }\n};\n\n/**\n * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it\n * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.\n */\nexport const DatePicker = ({\n id,\n value,\n onValueChange,\n min,\n max,\n disabledDates,\n disabled = false,\n locale = 'en-GB',\n calendarLocale,\n placeholder,\n 'aria-label': ariaLabel,\n 'aria-invalid': ariaInvalid,\n 'aria-describedby': ariaDescribedBy,\n labels: labelOverrides,\n className,\n}: IDatePickerProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_DATE_PICKER_LABELS, ...labelOverrides }), [labelOverrides]);\n const pattern = useMemo(() => datePatternOf(locale), [locale]);\n const current = value === '' ? undefined : value;\n const errorId = useId();\n const refField = useRef<HTMLDivElement | null>(null);\n const refInput = useRef<HTMLInputElement | null>(null);\n // The mouse is down inside the picker, so a blur of the field is a click on something in it, not a Tab.\n const refPointer = useRef(false);\n // The day the field last agreed on with the page, so a change from outside is told from our own report of it.\n const refKnown = useRef<string | undefined>(current);\n // The refused text last reported (with its message), or null: told apart from a fresh refusal so it is reported once,\n // and so that emptying a field that held a refused text is reported although it held no day.\n const refRefused = useRef<string | null>(null);\n const [text, setText] = useState(current === undefined ? '' : formatDay(current, locale));\n const [problem, setProblem] = useState<TDateProblem | null>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (current !== refKnown.current) {\n refKnown.current = current;\n refRefused.current = null;\n setText(current === undefined ? '' : formatDay(current, locale));\n setProblem(null);\n }\n }, [current, locale]);\n\n const report = (day: string | undefined, state: TDatePickerState) => {\n refKnown.current = day;\n refRefused.current = state.kind === 'refused' ? `${state.text}\\u0000${state.problem}` : null;\n onValueChange(day, state);\n };\n\n /**\n * Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,\n * with the state saying whether the field is empty or holds text it refused.\n */\n const commit = (typed: string) => {\n const parsed = parseDateText(typed, locale);\n if (parsed.kind === 'empty') {\n setProblem(null);\n if (current !== undefined || refRefused.current !== null) {\n report(undefined, { kind: 'empty' });\n }\n return;\n }\n const found = parsed.kind === 'problem' ? parsed.problem : dayProblem(parsed.day, { min, max, disabledDates });\n if (found !== undefined) {\n setProblem(found);\n const message = problemText(found, labels, locale, { min, max }, pattern);\n if (current !== undefined || refRefused.current !== `${typed}\\u0000${message}`) {\n report(undefined, { kind: 'refused', text: typed, problem: message });\n }\n return;\n }\n if (parsed.kind === 'day') {\n setProblem(null);\n setText(formatDay(parsed.day, locale));\n if (parsed.day !== current || refRefused.current !== null) {\n report(parsed.day, { kind: 'day' });\n }\n }\n };\n\n const pick = (date: Date | undefined) => {\n const day = dateToDay(date);\n if (day === undefined) {\n return;\n }\n setProblem(null);\n setText(formatDay(day, locale));\n if (day !== current || refRefused.current !== null) {\n report(day, { kind: 'day' });\n }\n setOpen(false);\n };\n\n /**\n * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a\n * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus\n * going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else\n * reads whatever is typed.\n */\n const handleBlur = (event: FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget as Element | null;\n const intoCalendar = next?.closest('[data-slot=\"popover-content\"]') != null;\n const intoButton = next?.closest('[data-slot=\"date-picker-trigger\"]') != null;\n const typed = refInput.current?.value ?? '';\n if (!(intoCalendar || intoButton || (next !== null && refField.current?.contains(next) === true))) {\n commit(typed);\n return;\n }\n if (event.target !== refInput.current) {\n return;\n }\n const onTheWay = intoCalendar || (intoButton && refPointer.current);\n const parsed = parseDateText(typed, locale);\n const usable = parsed.kind === 'empty' || (parsed.kind === 'day' && dayProblem(parsed.day, { min, max, disabledDates }) === undefined);\n if (usable || !onTheWay) {\n commit(typed);\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown' && event.altKey && !disabled) {\n event.preventDefault();\n setOpen(true);\n return;\n }\n // The field has no key handler of Base UI's to close the calendar, so the key would reach the Sheet or Dialog around\n // it and close that too. While the calendar is open it is this field's key; when it is closed it is the page's.\n if (event.key === 'Escape' && open) {\n event.preventDefault();\n event.stopPropagation();\n setOpen(false);\n return;\n }\n if (event.key === 'Enter') {\n const parsed = parseDateText(event.currentTarget.value, locale);\n if (parsed.kind !== 'empty' && (parsed.kind === 'problem' || dayProblem(parsed.day, { min, max, disabledDates }) !== undefined)) {\n event.preventDefault();\n }\n commit(event.currentTarget.value);\n }\n };\n\n const selected = dayToDate(current);\n const limits = { min, max };\n const invalid = problem !== null || ariaInvalid === true || ariaInvalid === 'true';\n const describedBy = [ariaDescribedBy, problem === null ? undefined : errorId].filter(Boolean).join(' ') || undefined;\n const isOff = (date: Date): boolean => {\n const day = dateToDay(date);\n return day !== undefined && dayProblem(day, { min, max, disabledDates }) !== undefined;\n };\n const today = new Date();\n const start = dayToDate(min);\n const end = dayToDate(max);\n const month = selected ?? (start !== undefined && today < start ? start : end !== undefined && today > end ? end : today);\n\n return (\n <div data-slot=\"date-picker\" className={cn('flex flex-col gap-1.5', className)}>\n <div\n ref={refField}\n className=\"relative\"\n onPointerDownCapture={() => {\n refPointer.current = true;\n }}\n onPointerUpCapture={() => {\n refPointer.current = false;\n }}\n >\n <Input\n ref={refInput}\n id={id}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n data-slot=\"date-picker-input\"\n value={text}\n placeholder={placeholder ?? pattern}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={invalid ? true : undefined}\n aria-describedby={describedBy}\n className=\"pr-9\"\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setText(event.target.value);\n setProblem(null);\n }}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n />\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n data-slot=\"date-picker-trigger\"\n aria-label={labels.choose}\n disabled={disabled}\n className=\"absolute top-0.5 right-0.5\"\n onBlur={handleBlur}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n </PopoverTrigger>\n <PopoverContent anchor={refField} align=\"start\" className=\"w-auto p-0\" finalFocus={refInput} onBlur={handleBlur}>\n <Calendar\n mode=\"single\"\n required\n autoFocus\n locale={calendarLocale}\n weekStartsOn={firstWeekdayOf(locale)}\n defaultMonth={month}\n startMonth={start}\n endMonth={end}\n selected={selected}\n disabled={isOff}\n onSelect={pick}\n />\n </PopoverContent>\n </Popover>\n </div>\n {problem !== null && (\n <p id={errorId} data-slot=\"date-picker-error\" role=\"alert\" className=\"text-sm text-destructive\">\n {problemText(problem, labels, locale, limits, pattern)}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":"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"}