pixelize-design-library 2.4.2-beta.58 → 2.4.2-beta.60

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 (244) hide show
  1. package/CHANGELOG.md +149 -5
  2. package/COMPONENT-GOTCHAS.md +205 -18
  3. package/DESIGN-GUIDE.md +43 -9
  4. package/dist/Components/AppSwitcher/AppSwitcher.js +5 -1
  5. package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +2 -0
  6. package/dist/Components/AppSwitcher/AppSwitcherProps.js +7 -4
  7. package/dist/Components/AudioRecorder/AudioRecorder.d.ts +8 -0
  8. package/dist/Components/AudioRecorder/AudioRecorder.js +169 -0
  9. package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +20 -0
  10. package/dist/Components/AudioRecorder/AudioRecorderProps.js +2 -0
  11. package/dist/Components/Breadcrumbs/Breadcrumbs.d.ts +1 -1
  12. package/dist/Components/Breadcrumbs/Breadcrumbs.js +33 -10
  13. package/dist/Components/Breadcrumbs/BreadcrumbsProps.d.ts +16 -2
  14. package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +3 -0
  15. package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +43 -0
  16. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +35 -0
  17. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.js +2 -0
  18. package/dist/Components/BulkSelectionBar/index.d.ts +2 -0
  19. package/dist/Components/BulkSelectionBar/index.js +8 -0
  20. package/dist/Components/CalendarGrid/CalendarGrid.d.ts +3 -0
  21. package/dist/Components/CalendarGrid/CalendarGrid.js +173 -0
  22. package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +54 -0
  23. package/dist/Components/CalendarGrid/CalendarGridProps.js +2 -0
  24. package/dist/Components/CalendarGrid/calendarMath.d.ts +49 -0
  25. package/dist/Components/CalendarGrid/calendarMath.js +178 -0
  26. package/dist/Components/Card/Card.d.ts +3 -1
  27. package/dist/Components/Card/Card.js +12 -2
  28. package/dist/Components/Card/CardProps.d.ts +5 -1
  29. package/dist/Components/CircularProgress/CircularProgress.d.ts +4 -0
  30. package/dist/Components/CircularProgress/CircularProgress.js +39 -0
  31. package/dist/Components/CircularProgress/CircularProgressProps.d.ts +22 -0
  32. package/dist/Components/CircularProgress/CircularProgressProps.js +2 -0
  33. package/dist/Components/CircularProgress/arc.d.ts +12 -0
  34. package/dist/Components/CircularProgress/arc.js +16 -0
  35. package/dist/Components/Common/fieldStyles.d.ts +1 -0
  36. package/dist/Components/Common/fieldStyles.js +2 -1
  37. package/dist/Components/Drawer/Drawer.d.ts +1 -1
  38. package/dist/Components/Drawer/Drawer.js +4 -2
  39. package/dist/Components/Drawer/DrawerProps.d.ts +3 -1
  40. package/dist/Components/Editor/Editor.js +50 -2
  41. package/dist/Components/Editor/EditorFieldBar.d.ts +15 -0
  42. package/dist/Components/Editor/EditorFieldBar.js +19 -0
  43. package/dist/Components/Editor/EditorProps.d.ts +74 -1
  44. package/dist/Components/Editor/MakeDynamicDialog.d.ts +12 -0
  45. package/dist/Components/Editor/MakeDynamicDialog.js +59 -0
  46. package/dist/Components/Editor/editorLabels.d.ts +4 -0
  47. package/dist/Components/Editor/editorLabels.js +24 -0
  48. package/dist/Components/Editor/editorTokenStyles.d.ts +28 -0
  49. package/dist/Components/Editor/editorTokenStyles.js +30 -0
  50. package/dist/Components/Editor/editorTokens.d.ts +26 -0
  51. package/dist/Components/Editor/editorTokens.js +134 -0
  52. package/dist/Components/Editor/useEditorBridge.d.ts +40 -0
  53. package/dist/Components/Editor/useEditorBridge.js +141 -0
  54. package/dist/Components/EmptyState/EmptyState.js +2 -1
  55. package/dist/Components/EmptyState/EmptyStateProps.d.ts +2 -2
  56. package/dist/Components/FormDrawer/FormDrawer.d.ts +2 -0
  57. package/dist/Components/FormDrawer/FormDrawer.js +97 -0
  58. package/dist/Components/FormDrawer/FormDrawerProps.d.ts +55 -0
  59. package/dist/Components/FormDrawer/FormDrawerProps.js +2 -0
  60. package/dist/Components/Kbd/Kbd.d.ts +6 -0
  61. package/dist/Components/Kbd/Kbd.js +33 -0
  62. package/dist/Components/Kbd/KbdProps.d.ts +17 -0
  63. package/dist/Components/Kbd/KbdProps.js +2 -0
  64. package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +4 -0
  65. package/dist/Components/LauncherTrigger/LauncherTrigger.js +48 -0
  66. package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +18 -0
  67. package/dist/Components/LauncherTrigger/LauncherTriggerProps.js +2 -0
  68. package/dist/Components/Modal/Modal.d.ts +1 -1
  69. package/dist/Components/Modal/Modal.js +4 -2
  70. package/dist/Components/Modal/ModalProps.d.ts +3 -1
  71. package/dist/Components/Modal/warnDroppedSlotChildren.d.ts +2 -0
  72. package/dist/Components/Modal/warnDroppedSlotChildren.js +45 -0
  73. package/dist/Components/NavLauncher/NavLauncher.d.ts +3 -0
  74. package/dist/Components/NavLauncher/NavLauncher.js +452 -0
  75. package/dist/Components/NavLauncher/NavLauncherProps.d.ts +187 -0
  76. package/dist/Components/NavLauncher/NavLauncherProps.js +2 -0
  77. package/dist/Components/NavLauncher/NewTabLink.d.ts +3 -0
  78. package/dist/Components/NavLauncher/NewTabLink.js +14 -0
  79. package/dist/Components/NavLauncher/PinToggle.d.ts +3 -0
  80. package/dist/Components/NavLauncher/PinToggle.js +21 -0
  81. package/dist/Components/NavLauncher/launcherParts.d.ts +152 -0
  82. package/dist/Components/NavLauncher/launcherParts.js +212 -0
  83. package/dist/Components/NavLauncher/match.d.ts +53 -0
  84. package/dist/Components/NavLauncher/match.js +178 -0
  85. package/dist/Components/NavLauncher/revealStyles.d.ts +4 -0
  86. package/dist/Components/NavLauncher/revealStyles.js +19 -0
  87. package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -1
  88. package/dist/Components/NavigationBar/NavigationBar.js +2 -2
  89. package/dist/Components/NavigationBar/NavigationBarProps.d.ts +2 -0
  90. package/dist/Components/PageHeader/PageHeader.d.ts +1 -1
  91. package/dist/Components/PageHeader/PageHeader.js +9 -2
  92. package/dist/Components/PageHeader/PageHeaderProps.d.ts +8 -0
  93. package/dist/Components/PdfViewer/PdfViewer.d.ts +21 -4
  94. package/dist/Components/PdfViewer/PdfViewer.js +124 -3
  95. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  96. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewerProps.d.ts +1 -1
  97. package/dist/Components/QueryState/QueryStateBoundary.d.ts +2 -2
  98. package/dist/Components/QueryState/QueryStateBoundary.js +9 -3
  99. package/dist/Components/QueryState/QueryStateProps.d.ts +19 -2
  100. package/dist/Components/Search/Search.js +2 -1
  101. package/dist/Components/SectionRail/SectionRail.d.ts +3 -0
  102. package/dist/Components/SectionRail/SectionRail.js +49 -0
  103. package/dist/Components/SectionRail/SectionRailProps.d.ts +23 -0
  104. package/dist/Components/SectionRail/SectionRailProps.js +2 -0
  105. package/dist/Components/SectionRail/index.d.ts +2 -0
  106. package/dist/Components/SectionRail/index.js +8 -0
  107. package/dist/Components/SegmentedControl/SegmentedControl.js +9 -14
  108. package/dist/Components/SegmentedControl/segmentedInk.d.ts +0 -2
  109. package/dist/Components/SegmentedControl/segmentedInk.js +1 -5
  110. package/dist/Components/SideBar/SecondaryBar.js +4 -3
  111. package/dist/Components/SideBar/SideBar.d.ts +1 -1
  112. package/dist/Components/SideBar/SideBar.js +30 -9
  113. package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
  114. package/dist/Components/SideBar/components/MenuItemBox.d.ts +6 -0
  115. package/dist/Components/SideBar/components/MenuItemBox.js +8 -4
  116. package/dist/Components/SideBar/components/MenuPopoverContent.js +3 -2
  117. package/dist/Components/SideBar/components/OtherApps.js +2 -1
  118. package/dist/Components/SideBar/components/PinnedSection.d.ts +17 -0
  119. package/dist/Components/SideBar/components/PinnedSection.js +98 -0
  120. package/dist/Components/SideBar/sidebarFocus.d.ts +23 -0
  121. package/dist/Components/SideBar/sidebarFocus.js +20 -0
  122. package/dist/Components/StageProgress/StageProgress.js +2 -2
  123. package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
  124. package/dist/Components/StageProgress/StepperStage.js +5 -2
  125. package/dist/Components/StatCard/StatCard.d.ts +1 -1
  126. package/dist/Components/StatCard/StatCard.js +6 -2
  127. package/dist/Components/StatCard/StatCardProps.d.ts +3 -0
  128. package/dist/Components/StatusTag/StatusTag.d.ts +4 -0
  129. package/dist/Components/StatusTag/StatusTag.js +24 -0
  130. package/dist/Components/StatusTag/StatusTagProps.d.ts +20 -0
  131. package/dist/Components/StatusTag/StatusTagProps.js +2 -0
  132. package/dist/Components/Table/Table.d.ts +1 -1
  133. package/dist/Components/Table/Table.js +6 -2
  134. package/dist/Components/Table/TableProps.d.ts +20 -0
  135. package/dist/Components/Table/components/TableBody.d.ts +1 -1
  136. package/dist/Components/Table/components/TableBody.js +43 -18
  137. package/dist/Components/Table/components/TableHeader.d.ts +1 -1
  138. package/dist/Components/Table/components/TableHeader.js +7 -5
  139. package/dist/Components/Table/components/rowDetail.d.ts +32 -0
  140. package/dist/Components/Table/components/rowDetail.js +20 -0
  141. package/dist/Components/Table/hooks/useRowExpansion.d.ts +7 -0
  142. package/dist/Components/Table/hooks/useRowExpansion.js +27 -0
  143. package/dist/Components/Tabs/Tabs.d.ts +1 -1
  144. package/dist/Components/Tabs/Tabs.js +15 -2
  145. package/dist/Components/Tabs/TabsProps.d.ts +6 -2
  146. package/dist/Components/Tabs/components/Tab.js +4 -33
  147. package/dist/Components/Tabs/components/TabLinkList.d.ts +7 -0
  148. package/dist/Components/Tabs/components/TabLinkList.js +34 -0
  149. package/dist/Components/Tabs/useTabAppearance.d.ts +17 -0
  150. package/dist/Components/Tabs/useTabAppearance.js +67 -0
  151. package/dist/Components/Tag/Tag.js +11 -1
  152. package/dist/Components/Tag/TagProps.d.ts +4 -0
  153. package/dist/Components/Tag/tagDot.d.ts +3 -0
  154. package/dist/Components/Tag/tagDot.js +21 -0
  155. package/dist/Components/WizardDialog/WizardDialog.d.ts +9 -0
  156. package/dist/Components/WizardDialog/WizardDialog.js +102 -0
  157. package/dist/Components/WizardDialog/WizardDialogProps.d.ts +48 -0
  158. package/dist/Components/WizardDialog/WizardDialogProps.js +2 -0
  159. package/dist/Components/WizardDialog/useWizardFlow.d.ts +22 -0
  160. package/dist/Components/WizardDialog/useWizardFlow.js +47 -0
  161. package/dist/Components/WizardDialog/wizardSteps.d.ts +31 -0
  162. package/dist/Components/WizardDialog/wizardSteps.js +40 -0
  163. package/dist/esm/Components/AppSwitcher/AppSwitcher.js +2 -1
  164. package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +5 -3
  165. package/dist/esm/Components/AudioRecorder/AudioRecorder.js +161 -0
  166. package/dist/esm/Components/AudioRecorder/AudioRecorderProps.js +1 -0
  167. package/dist/esm/Components/Breadcrumbs/Breadcrumbs.js +35 -12
  168. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +38 -0
  169. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBarProps.js +1 -0
  170. package/dist/esm/Components/BulkSelectionBar/index.js +1 -0
  171. package/dist/esm/Components/CalendarGrid/CalendarGrid.js +168 -0
  172. package/dist/esm/Components/CalendarGrid/CalendarGridProps.js +1 -0
  173. package/dist/esm/Components/CalendarGrid/calendarMath.js +155 -0
  174. package/dist/esm/Components/Card/Card.js +12 -2
  175. package/dist/esm/Components/CircularProgress/CircularProgress.js +37 -0
  176. package/dist/esm/Components/CircularProgress/CircularProgressProps.js +1 -0
  177. package/dist/esm/Components/CircularProgress/arc.js +11 -0
  178. package/dist/esm/Components/Common/fieldStyles.js +2 -1
  179. package/dist/esm/Components/Drawer/Drawer.js +4 -2
  180. package/dist/esm/Components/Editor/Editor.js +48 -3
  181. package/dist/esm/Components/Editor/EditorFieldBar.js +13 -0
  182. package/dist/esm/Components/Editor/MakeDynamicDialog.js +20 -0
  183. package/dist/esm/Components/Editor/editorLabels.js +20 -0
  184. package/dist/esm/Components/Editor/editorTokenStyles.js +25 -0
  185. package/dist/esm/Components/Editor/editorTokens.js +121 -0
  186. package/dist/esm/Components/Editor/useEditorBridge.js +138 -0
  187. package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
  188. package/dist/esm/Components/FormDrawer/FormDrawer.js +58 -0
  189. package/dist/esm/Components/FormDrawer/FormDrawerProps.js +1 -0
  190. package/dist/esm/Components/Kbd/Kbd.js +28 -0
  191. package/dist/esm/Components/Kbd/KbdProps.js +1 -0
  192. package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +41 -0
  193. package/dist/esm/Components/LauncherTrigger/LauncherTriggerProps.js +1 -0
  194. package/dist/esm/Components/Modal/Modal.js +4 -2
  195. package/dist/esm/Components/Modal/warnDroppedSlotChildren.js +38 -0
  196. package/dist/esm/Components/NavLauncher/NavLauncher.js +413 -0
  197. package/dist/esm/Components/NavLauncher/NavLauncherProps.js +1 -0
  198. package/dist/esm/Components/NavLauncher/NewTabLink.js +11 -0
  199. package/dist/esm/Components/NavLauncher/PinToggle.js +18 -0
  200. package/dist/esm/Components/NavLauncher/launcherParts.js +187 -0
  201. package/dist/esm/Components/NavLauncher/match.js +172 -0
  202. package/dist/esm/Components/NavLauncher/revealStyles.js +15 -0
  203. package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
  204. package/dist/esm/Components/PageHeader/PageHeader.js +9 -2
  205. package/dist/esm/Components/PdfViewer/PdfViewer.js +123 -2
  206. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  207. package/dist/esm/Components/QueryState/QueryStateBoundary.js +10 -4
  208. package/dist/esm/Components/Search/Search.js +3 -2
  209. package/dist/esm/Components/SectionRail/SectionRail.js +44 -0
  210. package/dist/esm/Components/SectionRail/SectionRailProps.js +1 -0
  211. package/dist/esm/Components/SectionRail/index.js +1 -0
  212. package/dist/esm/Components/SegmentedControl/SegmentedControl.js +11 -16
  213. package/dist/esm/Components/SegmentedControl/segmentedInk.js +0 -3
  214. package/dist/esm/Components/SideBar/SecondaryBar.js +4 -3
  215. package/dist/esm/Components/SideBar/SideBar.js +31 -10
  216. package/dist/esm/Components/SideBar/components/MenuItemBox.js +4 -2
  217. package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +3 -2
  218. package/dist/esm/Components/SideBar/components/OtherApps.js +2 -1
  219. package/dist/esm/Components/SideBar/components/PinnedSection.js +92 -0
  220. package/dist/esm/Components/SideBar/sidebarFocus.js +15 -0
  221. package/dist/esm/Components/StageProgress/StageProgress.js +2 -2
  222. package/dist/esm/Components/StageProgress/StepperStage.js +5 -2
  223. package/dist/esm/Components/StatCard/StatCard.js +6 -2
  224. package/dist/esm/Components/StatusTag/StatusTag.js +19 -0
  225. package/dist/esm/Components/StatusTag/StatusTagProps.js +1 -0
  226. package/dist/esm/Components/Table/Table.js +7 -3
  227. package/dist/esm/Components/Table/components/TableBody.js +43 -18
  228. package/dist/esm/Components/Table/components/TableHeader.js +7 -5
  229. package/dist/esm/Components/Table/components/rowDetail.js +14 -0
  230. package/dist/esm/Components/Table/hooks/useRowExpansion.js +23 -0
  231. package/dist/esm/Components/Tabs/Tabs.js +15 -2
  232. package/dist/esm/Components/Tabs/components/Tab.js +4 -33
  233. package/dist/esm/Components/Tabs/components/TabLinkList.js +29 -0
  234. package/dist/esm/Components/Tabs/useTabAppearance.js +63 -0
  235. package/dist/esm/Components/Tag/Tag.js +12 -2
  236. package/dist/esm/Components/Tag/tagDot.js +17 -0
  237. package/dist/esm/Components/WizardDialog/WizardDialog.js +58 -0
  238. package/dist/esm/Components/WizardDialog/WizardDialogProps.js +1 -0
  239. package/dist/esm/Components/WizardDialog/useWizardFlow.js +44 -0
  240. package/dist/esm/Components/WizardDialog/wizardSteps.js +35 -0
  241. package/dist/esm/index.js +14 -0
  242. package/dist/index.d.ts +35 -6
  243. package/dist/index.js +38 -4
  244. package/package.json +1 -1
@@ -0,0 +1,161 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
4
+ import Button from "../Button/Button.js";
5
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
+ const DEFAULT_LABELS = {
7
+ start: "Start recording",
8
+ stop: "Stop recording",
9
+ recording: "Recording",
10
+ recorded: "Recorded",
11
+ permissionDenied: "Microphone access was denied. Allow it in your browser settings and try again.",
12
+ unsupported: "Audio recording is not supported in this browser.",
13
+ };
14
+ const PREFERRED_MIME = "audio/webm";
15
+ const TICK_MS = 250;
16
+ const EXTENSIONS = {
17
+ "audio/webm": "webm",
18
+ "audio/wav": "wav",
19
+ "audio/wave": "wav",
20
+ "audio/x-wav": "wav",
21
+ "audio/ogg": "ogg",
22
+ "audio/mp4": "m4a",
23
+ "audio/mpeg": "mp3",
24
+ };
25
+ export const formatElapsed = (ms) => {
26
+ const total = Math.floor(ms / 1000);
27
+ return `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
28
+ };
29
+ export const recordingFileName = (mime, now) => {
30
+ var _a, _b;
31
+ const type = mime.split(";")[0].trim().toLowerCase() || PREFERRED_MIME;
32
+ return { type, name: `recording-${now}.${(_a = EXTENSIONS[type]) !== null && _a !== void 0 ? _a : (((_b = type.split("/")[1]) === null || _b === void 0 ? void 0 : _b.replace(/^x-/, "")) || "webm")}` };
33
+ };
34
+ const isSupported = () => { var _a; return typeof navigator !== "undefined" && !!((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) && typeof MediaRecorder !== "undefined"; };
35
+ const AudioRecorder = ({ onRecorded, maxDurationSec, isDisabled = false, labels }) => {
36
+ const { colors } = useCustomTheme();
37
+ const text = { ...DEFAULT_LABELS, ...labels };
38
+ const [status, setStatus] = useState("idle");
39
+ const [elapsedMs, setElapsedMs] = useState(0);
40
+ const [announcement, setAnnouncement] = useState("");
41
+ const recorderRef = useRef(null);
42
+ const streamRef = useRef(null);
43
+ const chunksRef = useRef([]);
44
+ const timerRef = useRef(null);
45
+ const startedAtRef = useRef(0);
46
+ const mountedRef = useRef(true);
47
+ const startingRef = useRef(false);
48
+ const onRecordedRef = useRef(onRecorded);
49
+ const textRef = useRef(text);
50
+ onRecordedRef.current = onRecorded;
51
+ textRef.current = text;
52
+ const release = useCallback(() => {
53
+ var _a;
54
+ if (timerRef.current !== null)
55
+ clearInterval(timerRef.current);
56
+ timerRef.current = null;
57
+ (_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
58
+ streamRef.current = null;
59
+ recorderRef.current = null;
60
+ }, []);
61
+ useEffect(() => {
62
+ mountedRef.current = true;
63
+ if (!isSupported())
64
+ setStatus("unsupported");
65
+ return () => {
66
+ mountedRef.current = false;
67
+ const recorder = recorderRef.current;
68
+ if (recorder) {
69
+ recorder.ondataavailable = null;
70
+ recorder.onstop = null;
71
+ recorder.onerror = null;
72
+ if (recorder.state !== "inactive") {
73
+ try {
74
+ recorder.stop();
75
+ }
76
+ catch {
77
+ // NOTE: a recorder the browser already tore down throws on stop.
78
+ }
79
+ }
80
+ }
81
+ release();
82
+ };
83
+ }, [release]);
84
+ const stop = useCallback(() => {
85
+ const recorder = recorderRef.current;
86
+ if (recorder && recorder.state !== "inactive")
87
+ recorder.stop();
88
+ }, []);
89
+ const start = useCallback(async () => {
90
+ var _a, _b;
91
+ if (startingRef.current)
92
+ return;
93
+ if (!isSupported()) {
94
+ setStatus("unsupported");
95
+ return;
96
+ }
97
+ startingRef.current = true;
98
+ try {
99
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
100
+ if (!mountedRef.current) {
101
+ stream.getTracks().forEach((track) => track.stop());
102
+ return;
103
+ }
104
+ streamRef.current = stream;
105
+ chunksRef.current = [];
106
+ const useWebm = (_b = (_a = MediaRecorder.isTypeSupported) === null || _a === void 0 ? void 0 : _a.call(MediaRecorder, PREFERRED_MIME)) !== null && _b !== void 0 ? _b : false;
107
+ const recorder = new MediaRecorder(stream, useWebm ? { mimeType: PREFERRED_MIME } : undefined);
108
+ recorderRef.current = recorder;
109
+ recorder.ondataavailable = (event) => {
110
+ if (event.data.size > 0)
111
+ chunksRef.current.push(event.data);
112
+ };
113
+ recorder.onerror = () => {
114
+ release();
115
+ setStatus("idle");
116
+ };
117
+ recorder.onstop = () => {
118
+ const duration = Date.now() - startedAtRef.current;
119
+ const { type, name } = recordingFileName(recorder.mimeType || PREFERRED_MIME, Date.now());
120
+ const file = new File(chunksRef.current, name, { type });
121
+ release();
122
+ setStatus("idle");
123
+ setElapsedMs(duration);
124
+ setAnnouncement(`${textRef.current.recorded} ${formatElapsed(duration)}`);
125
+ if (file.size > 0)
126
+ onRecordedRef.current(file);
127
+ };
128
+ recorder.start();
129
+ startedAtRef.current = Date.now();
130
+ setElapsedMs(0);
131
+ setStatus("recording");
132
+ setAnnouncement(textRef.current.recording);
133
+ timerRef.current = setInterval(() => {
134
+ const elapsed = Date.now() - startedAtRef.current;
135
+ if (maxDurationSec !== undefined && elapsed >= maxDurationSec * 1000) {
136
+ setElapsedMs(maxDurationSec * 1000);
137
+ stop();
138
+ return;
139
+ }
140
+ setElapsedMs(elapsed);
141
+ }, TICK_MS);
142
+ }
143
+ catch (error) {
144
+ release();
145
+ if (!mountedRef.current)
146
+ return;
147
+ const name = error === null || error === void 0 ? void 0 : error.name;
148
+ setStatus(name === "NotFoundError" ? "unsupported" : "denied");
149
+ }
150
+ finally {
151
+ startingRef.current = false;
152
+ }
153
+ }, [maxDurationSec, release, stop]);
154
+ const isRecording = status === "recording";
155
+ const blocked = status === "unsupported";
156
+ return (_jsxs(Box, { children: [_jsxs(Flex, { align: "center", gap: 3, children: [_jsx(Button, { label: isRecording ? text.stop : text.start, size: "sm", colorScheme: isRecording ? "red" : undefined, variant: isRecording ? "solid" : "outline", "aria-pressed": isRecording, isDisabled: isRecording ? false : isDisabled || blocked, onClick: isRecording ? stop : () => void start() }), isRecording ? (_jsxs(Flex, { align: "center", gap: 2, children: [_jsx(Box, { "data-testid": "audio-recorder-dot", boxSize: "0.5rem", borderRadius: "full", bg: colors.semanticText.error, "aria-hidden": true, sx: {
157
+ "@media (prefers-reduced-motion: no-preference)": { animation: "audio-recorder-pulse 1.2s ease-in-out infinite" },
158
+ "@keyframes audio-recorder-pulse": { "0%, 100%": { opacity: 1 }, "50%": { opacity: 0.35 } },
159
+ } }), _jsx(Text, { as: "span", role: "timer", fontSize: "0.875rem", color: colors.header[900], sx: { fontVariantNumeric: "tabular-nums" }, children: formatElapsed(elapsedMs) })] })) : null] }), _jsx(VisuallyHidden, { role: "status", "aria-live": "polite", children: announcement }), status === "denied" || status === "unsupported" ? (_jsx(Text, { role: "alert", mt: 1, fontSize: "0.75rem", color: colors.semanticText.error, children: status === "denied" ? text.permissionDenied : text.unsupported })) : null] }));
160
+ };
161
+ export default AudioRecorder;
@@ -1,19 +1,42 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Breadcrumb, BreadcrumbItem, BreadcrumbLink, useTheme } from "@chakra-ui/react";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Menu, MenuButton, MenuItem, MenuList, useTheme, } from "@chakra-ui/react";
3
3
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
4
+ import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
4
5
  import { breadcrumbInk } from "./breadcrumbInk.js";
5
- export default function Breadcrumbs({ spacing, separator, fontWeight, fontSize, isCurrentPage, handleClick, items, }) {
6
+ const DEFAULT_MAX_ITEMS = 3;
7
+ const MIN_MAX_ITEMS = 2;
8
+ const OVERFLOW_SX = {
9
+ nowrap: undefined,
10
+ wrap: { "& ol": { flexWrap: "wrap", rowGap: "4px" }, "& li": { maxWidth: "100%" } },
11
+ truncate: { "& ol": { flexWrap: "nowrap" }, "& li": { minWidth: 0 } },
12
+ };
13
+ const collapse = (entries, maxItems) => {
14
+ const keep = Math.max(MIN_MAX_ITEMS, Math.floor(maxItems));
15
+ if (entries.length <= keep)
16
+ return { head: entries, hidden: [], tail: [] };
17
+ return { head: entries.slice(0, 1), hidden: entries.slice(1, entries.length - (keep - 1)), tail: entries.slice(entries.length - (keep - 1)) };
18
+ };
19
+ export default function Breadcrumbs({ spacing, separator, fontWeight, fontSize, isCurrentPage, handleClick, items, overflow = "nowrap", maxItems = DEFAULT_MAX_ITEMS, labels, "data-testid": testId, }) {
6
20
  const theme = useTheme();
7
21
  const { colors } = useCustomTheme();
8
22
  const ink = breadcrumbInk(colors);
9
23
  const effectiveSeparator = separator !== null && separator !== void 0 ? separator : (_jsx(Box, { as: "span", color: ink.separator, children: "/" }));
10
- return (_jsx(Breadcrumb, { spacing: spacing, separator: effectiveSeparator, fontWeight: fontWeight, fontSize: fontSize, fontFamily: theme.fonts.body, children: items.map((item, index) => {
11
- const isLast = isCurrentPage && index === items.length - 1;
12
- return (_jsx(BreadcrumbItem, { isCurrentPage: isLast, children: _jsx(BreadcrumbLink, { href: item.path, color: isLast ? ink.current : ink.link, fontWeight: isLast ? 600 : fontWeight, sx: {
13
- "&:hover, &:active": { textDecoration: "none", color: isLast ? ink.current : ink.linkHover },
14
- }, onClick: (event) => {
15
- event.preventDefault();
16
- handleClick(item.path);
17
- }, children: item.label }) }, index));
18
- }) }));
24
+ const clip = overflow !== "nowrap";
25
+ const clipProps = clip ? { noOfLines: 1 } : {};
26
+ const renderCrumb = ({ crumb, index }) => {
27
+ const isLast = Boolean(isCurrentPage) && index === items.length - 1;
28
+ const color = isLast ? ink.current : ink.link;
29
+ const common = { ...clipProps, title: clip ? crumb.label : undefined, color, fontWeight: isLast ? 600 : fontWeight };
30
+ return (_jsx(BreadcrumbItem, { isCurrentPage: isLast, children: crumb.path === undefined ? (_jsx(Box, { as: "span", "aria-current": isLast ? "page" : undefined, ...common, children: crumb.label })) : (_jsx(BreadcrumbLink, { href: crumb.path, ...common, sx: { "&:hover, &:active": { textDecoration: "none", color: isLast ? ink.current : ink.linkHover } }, onClick: (event) => {
31
+ event.preventDefault();
32
+ handleClick(crumb.path);
33
+ }, children: crumb.label })) }, index));
34
+ };
35
+ const renderMenu = (hidden) => {
36
+ var _a;
37
+ return (_jsx(BreadcrumbItem, { sx: { flexShrink: 0 }, children: _jsxs(Menu, { isLazy: true, placement: "bottom-start", children: [_jsx(MenuButton, { type: "button", "aria-label": ((_a = labels === null || labels === void 0 ? void 0 : labels.showMore) !== null && _a !== void 0 ? _a : ((n) => `Show ${n} more`))(hidden.length), color: ink.link, borderRadius: "md", px: 1, _hover: { color: ink.linkHover }, sx: focusVisibleStyles(colors), children: "\u2026" }), _jsx(MenuList, { children: hidden.map(({ crumb, index }) => (_jsx(MenuItem, { isDisabled: crumb.path === undefined, onClick: () => crumb.path !== undefined && handleClick(crumb.path), children: crumb.label }, index))) })] }) }, "overflow"));
38
+ };
39
+ const entries = items.map((crumb, index) => ({ crumb, index }));
40
+ const { head, hidden, tail } = overflow === "truncate" ? collapse(entries, maxItems) : { head: entries, hidden: [], tail: [] };
41
+ return (_jsxs(Breadcrumb, { spacing: spacing, separator: effectiveSeparator, fontWeight: fontWeight, fontSize: fontSize, fontFamily: theme.fonts.body, sx: OVERFLOW_SX[overflow], "data-testid": testId, children: [head.map(renderCrumb), hidden.length > 0 && renderMenu(hidden), tail.map(renderCrumb)] }));
19
42
  }
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
4
+ import { X } from "lucide-react";
5
+ import Button from "../Button/Button.js";
6
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
7
+ const FACT_SEPARATOR = " · ";
8
+ const VARIANT_BY_TONE = {
9
+ default: "outline",
10
+ primary: "solid",
11
+ danger: "outline",
12
+ };
13
+ const SCHEME_BY_TONE = {
14
+ default: "gray",
15
+ primary: "primary",
16
+ danger: "red",
17
+ };
18
+ const TOUCH_SX = { "@media (pointer: coarse)": { minHeight: "44px", minWidth: "44px" } };
19
+ const BulkSelectionBar = ({ selectedCount, facts, actions, onClearSelection, clearLabel = "Clear selection", countLabel, placement = "sticky", "aria-label": ariaLabel = "Selection actions", "data-testid": testId = "bulk-selection-bar", }) => {
20
+ const { colors } = useCustomTheme();
21
+ const isActive = selectedCount > 0;
22
+ const summary = countLabel !== null && countLabel !== void 0 ? countLabel : `${selectedCount} selected`;
23
+ const [announcement, setAnnouncement] = useState("");
24
+ useEffect(() => {
25
+ setAnnouncement(isActive ? summary : "");
26
+ }, [isActive, summary]);
27
+ if (!isActive)
28
+ return null;
29
+ const resolvedFacts = (facts !== null && facts !== void 0 ? facts : []).filter(Boolean);
30
+ const isSticky = placement === "sticky";
31
+ return (_jsxs(Flex, { role: "region", "aria-label": ariaLabel, "data-testid": testId, align: "center", gap: 3, wrap: "wrap", px: 4, py: 2, w: "100%", borderRadius: "lg", borderWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, position: isSticky ? "sticky" : "relative", bottom: isSticky ? 0 : undefined, zIndex: isSticky ? 2 : undefined, children: [_jsx(VisuallyHidden, { role: "status", "aria-live": "polite", "data-testid": "bulk-selection-announcement", children: announcement }), _jsxs(Flex, { align: "baseline", gap: 1, flex: "1", minW: "180px", wrap: "wrap", children: [_jsx(Text, { fontSize: "sm", fontWeight: 600, color: colors.text[800], "data-testid": "bulk-selection-count", children: summary }), resolvedFacts.length ? (_jsxs(Text, { fontSize: "sm", color: colors.textMuted, "data-testid": "bulk-selection-facts", children: [FACT_SEPARATOR, resolvedFacts.join(FACT_SEPARATOR)] })) : null] }), _jsxs(Flex, { align: "center", gap: 2, wrap: "wrap", children: [actions.map((action) => {
32
+ var _a;
33
+ const tone = (_a = action.tone) !== null && _a !== void 0 ? _a : "default";
34
+ const Icon = action.icon;
35
+ return (_jsx(Button, { size: "sm", variant: VARIANT_BY_TONE[tone], colorScheme: SCHEME_BY_TONE[tone], onClick: action.onClick, isDisabled: action.isDisabled, isLoading: action.isLoading, loadingText: action.label, leftIcon: Icon ? _jsx(Icon, { size: 16, "aria-hidden": "true" }) : undefined, sx: TOUCH_SX, "data-testid": `bulk-action-${action.id}`, "data-tone": tone, children: action.label }, action.id));
36
+ }), _jsx(Box, { children: _jsx(Button, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: onClearSelection, leftIcon: _jsx(X, { size: 16, "aria-hidden": "true" }), sx: TOUCH_SX, "data-testid": "bulk-selection-clear", children: clearLabel }) })] })] }));
37
+ };
38
+ export default BulkSelectionBar;
@@ -0,0 +1 @@
1
+ export { default as BulkSelectionBar } from "./BulkSelectionBar.js";
@@ -0,0 +1,168 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { Box, Flex, Grid, Text } from "@chakra-ui/react";
4
+ import { ChevronLeft, ChevronRight } from "lucide-react";
5
+ import Button from "../Button/Button.js";
6
+ import Skeletons from "../Skeletons/Skeletons.js";
7
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
8
+ import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
9
+ import { resolveSoftTone, toneScale } from "../../Theme/tokens/builders/tone.js";
10
+ import { dayKey, eventsOnDay, formatFullDate, formatTitle, isNavKey, isSameDay, isWeekend, layoutWeek, moveFocus, shiftAnchor, startOfDay, visibleWeeks, weekdayLabels, } from "./calendarMath.js";
11
+ const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
12
+ const DEFAULT_LABELS = {
13
+ today: "Today",
14
+ previous: "Previous",
15
+ next: "Next",
16
+ month: "Month",
17
+ week: "Week",
18
+ day: "Day",
19
+ more: (count) => `+${count} more`,
20
+ moreLabel: (count, date) => `${plural(count, "more event", "more events")} on ${date}`,
21
+ eventCount: (count) => plural(count, "event", "events"),
22
+ };
23
+ const VIEWS = ["month", "week", "day"];
24
+ const MAX_LANES = { month: 3, week: 6, day: Infinity };
25
+ const MIN_CELL_HEIGHT = { month: "6.5rem", week: "12rem", day: "16rem" };
26
+ const LANE_HEIGHT = "1.5rem";
27
+ const CHIP_HEIGHT = "1.375rem";
28
+ const CHIP_INSET = "2px";
29
+ const EventChip = ({ event, colors, surface, onEventClick, segment }) => {
30
+ var _a;
31
+ const tone = resolveSoftTone(colors, toneScale(colors, (_a = event.tone) !== null && _a !== void 0 ? _a : "neutral"), surface);
32
+ const left = (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? "0px" : CHIP_INSET;
33
+ const right = (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? "0px" : CHIP_INSET;
34
+ const span = segment ? segment.endCol - segment.startCol + 1 : 1;
35
+ const placement = segment
36
+ ? {
37
+ position: "absolute",
38
+ zIndex: 1,
39
+ top: `calc(${segment.lane} * ${LANE_HEIGHT})`,
40
+ left,
41
+ width: `calc(${span * 100}% + ${span - 1}px - ${left} - ${right})`,
42
+ }
43
+ : { width: "100%" };
44
+ const common = {
45
+ ...placement,
46
+ display: "flex",
47
+ alignItems: "center",
48
+ h: CHIP_HEIGHT,
49
+ px: 2,
50
+ fontSize: "0.75rem",
51
+ fontWeight: 500,
52
+ lineHeight: 1,
53
+ textAlign: "left",
54
+ whiteSpace: "nowrap",
55
+ overflow: "hidden",
56
+ textOverflow: "ellipsis",
57
+ textDecoration: "none",
58
+ cursor: "pointer",
59
+ bg: tone.bg,
60
+ color: tone.fg,
61
+ border: "1px solid",
62
+ borderColor: tone.border,
63
+ borderLeftRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? 0 : "md",
64
+ borderRightRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? 0 : "md",
65
+ title: event.label,
66
+ "data-event-id": event.id,
67
+ onClick: () => onEventClick === null || onEventClick === void 0 ? void 0 : onEventClick(event),
68
+ _hover: { bg: tone.hoverBg, borderColor: tone.hoverBorder },
69
+ _active: { bg: tone.activeBg, borderColor: tone.activeBorder },
70
+ sx: focusVisibleStyles(colors),
71
+ };
72
+ return event.href ? (_jsx(Box, { as: "a", href: event.href, ...common, children: event.label })) : (_jsx(Box, { as: "button", type: "button", ...common, children: event.label }));
73
+ };
74
+ const CalendarGrid = ({ view, date, events, onViewChange, onDateChange, onEventClick, renderDayCell, weekStartsOn = 0, locale, isLoading = false, labels, }) => {
75
+ const { colors } = useCustomTheme();
76
+ const text = { ...DEFAULT_LABELS, ...labels };
77
+ const today = new Date();
78
+ const anchor = startOfDay(date);
79
+ const anchorKey = dayKey(anchor);
80
+ const weeks = visibleWeeks(view, anchor, weekStartsOn);
81
+ const columns = weeks[0].length;
82
+ const visibleKeys = new Set(weeks.flat().map(dayKey));
83
+ const shownEvents = isLoading ? [] : events;
84
+ const maxLanes = MAX_LANES[view];
85
+ const [focusedKey, setFocusedKey] = useState(anchorKey);
86
+ const pendingFocus = useRef(false);
87
+ const cellRefs = useRef(new Map());
88
+ const activeKey = visibleKeys.has(focusedKey) ? focusedKey : visibleKeys.has(anchorKey) ? anchorKey : dayKey(weeks[0][0]);
89
+ useEffect(() => {
90
+ setFocusedKey(anchorKey);
91
+ }, [anchorKey]);
92
+ useEffect(() => {
93
+ if (!pendingFocus.current)
94
+ return;
95
+ const el = cellRefs.current.get(activeKey);
96
+ if (!el)
97
+ return;
98
+ pendingFocus.current = false;
99
+ el.focus();
100
+ });
101
+ const handleCellKeyDown = (e, cellDate) => {
102
+ if (e.target !== e.currentTarget)
103
+ return;
104
+ if (isNavKey(e.key)) {
105
+ e.preventDefault();
106
+ const next = moveFocus(view, cellDate, e.key, weekStartsOn);
107
+ const inRange = visibleKeys.has(dayKey(next));
108
+ if (!inRange && !onDateChange)
109
+ return;
110
+ setFocusedKey(dayKey(next));
111
+ pendingFocus.current = true;
112
+ if (!inRange)
113
+ onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(next);
114
+ return;
115
+ }
116
+ if ((e.key === "Enter" || e.key === " ") && onDateChange) {
117
+ e.preventDefault();
118
+ onDateChange(cellDate);
119
+ }
120
+ };
121
+ const drillIntoDay = (day) => {
122
+ onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(day);
123
+ onViewChange === null || onViewChange === void 0 ? void 0 : onViewChange("day");
124
+ };
125
+ const weekdayShort = weekdayLabels(locale, weekStartsOn, "short");
126
+ const weekdayLong = weekdayLabels(locale, weekStartsOn, "long");
127
+ const headerDays = view === "day" ? [(anchor.getDay() - weekStartsOn + 7) % 7] : weekdayShort.map((_, i) => i);
128
+ const title = formatTitle(view, anchor, locale, weekStartsOn);
129
+ const template = `repeat(${columns}, minmax(0, 1fr))`;
130
+ const border = colors.boxborder[200];
131
+ const renderCell = (day, col, weekIndex, layout) => {
132
+ const key = dayKey(day);
133
+ const isToday = isSameDay(day, today);
134
+ const inMonth = view !== "month" || day.getMonth() === anchor.getMonth();
135
+ const surface = isWeekend(day) ? colors.backgroundColor.secondary : colors.backgroundColor.main;
136
+ const dayEvents = eventsOnDay(shownEvents, day);
137
+ const hidden = layout.hiddenByCol[col];
138
+ const active = key === activeKey;
139
+ const label = `${formatFullDate(day, locale)}${dayEvents.length ? `, ${text.eventCount(dayEvents.length)}` : ""}`;
140
+ const ownSegments = layout.segments.filter((s) => s.startCol === col && s.lane < maxLanes);
141
+ let body = null;
142
+ if (isLoading) {
143
+ body = (_jsx(Box, { mt: 1, "aria-hidden": true, children: (weekIndex * 7 + col) % 3 === 0 ? _jsx(Skeletons, { type: "skeleton", height: CHIP_HEIGHT, borderRadius: "md" }) : null }));
144
+ }
145
+ else if (renderDayCell) {
146
+ body = _jsx(Box, { mt: 1, children: renderDayCell(day, dayEvents) });
147
+ }
148
+ else if (view === "day") {
149
+ body = (_jsx(Flex, { direction: "column", gap: 1, mt: 2, children: dayEvents.map((event) => (_jsx(EventChip, { event: event, colors: colors, surface: surface, onEventClick: onEventClick }, event.id))) }));
150
+ }
151
+ else {
152
+ body = (_jsxs(_Fragment, { children: [_jsx(Box, { position: "relative", mt: 1, h: `calc(${layout.visibleLanes} * ${LANE_HEIGHT})`, children: ownSegments.map((s) => (_jsx(EventChip, { event: s.event, colors: colors, surface: surface, onEventClick: onEventClick, segment: s }, s.event.id))) }), hidden > 0 ? (_jsx(Box, { as: "button", type: "button", "aria-label": text.moreLabel(hidden, formatFullDate(day, locale)), onClick: () => drillIntoDay(day), px: 1, fontSize: "0.75rem", fontWeight: 600, color: colors.header[900], textAlign: "left", textDecoration: "underline", sx: focusVisibleStyles(colors), children: text.more(hidden) })) : null] }));
153
+ }
154
+ return (_jsxs(Box, { role: "gridcell", ref: (el) => {
155
+ if (el)
156
+ cellRefs.current.set(key, el);
157
+ else
158
+ cellRefs.current.delete(key);
159
+ }, "data-date": key, "aria-label": label, "aria-current": isToday ? "date" : undefined, tabIndex: active ? 0 : -1, onFocus: () => setFocusedKey(key), onKeyDown: (e) => handleCellKeyDown(e, day), position: "relative", minH: MIN_CELL_HEIGHT[view], p: 1, bg: surface, borderRight: "1px solid", borderBottom: "1px solid", borderColor: border, outline: "none", _focusVisible: {
160
+ boxShadow: `inset 0 0 0 2px ${colors.focusRing.halo}, inset 0 0 0 4px ${colors.focusRing.ring}`,
161
+ }, children: [_jsx(Text, { as: "span", display: "inline-flex", alignItems: "center", justifyContent: "center", minW: "1.5rem", h: "1.5rem", px: 1, borderRadius: "full", fontSize: "0.8125rem", fontWeight: isToday ? 700 : 500, color: isToday ? colors.accentText : inMonth ? colors.header[900] : colors.textMuted, boxShadow: isToday ? `inset 0 0 0 1.5px ${colors.accentText}` : undefined, "aria-hidden": true, children: day.getDate() }), body] }, key));
162
+ };
163
+ return (_jsxs(Box, { children: [_jsxs(Flex, { align: "center", justify: "space-between", wrap: "wrap", gap: 2, mb: 3, children: [_jsxs(Flex, { align: "center", gap: 2, children: [onDateChange ? (_jsxs(_Fragment, { children: [_jsx(Button, { label: text.today, size: "sm", variant: "outline", onClick: () => onDateChange(startOfDay(new Date())) }), _jsx(Button, { "aria-label": text.previous, size: "sm", variant: "ghost", leftIcon: _jsx(ChevronLeft, { size: 16, "aria-hidden": true }), onClick: () => onDateChange(shiftAnchor(view, anchor, -1)) }), _jsx(Button, { "aria-label": text.next, size: "sm", variant: "ghost", leftIcon: _jsx(ChevronRight, { size: 16, "aria-hidden": true }), onClick: () => onDateChange(shiftAnchor(view, anchor, 1)) })] })) : null, _jsx(Text, { as: "h2", fontSize: "1rem", fontWeight: 600, color: colors.header[900], children: title })] }), onViewChange ? (_jsx(Flex, { role: "group", "aria-label": text[view], gap: 1, children: VIEWS.map((v) => (_jsx(Button, { label: text[v], size: "sm", variant: v === view ? "solid" : "outline", "aria-pressed": v === view, onClick: () => onViewChange(v) }, v))) })) : null] }), _jsx(Box, { overflowX: "auto", children: _jsxs(Box, { role: "grid", "aria-label": title, "aria-busy": isLoading || undefined, "data-view": view, minW: view === "day" ? undefined : "42rem", borderTop: "1px solid", borderLeft: "1px solid", borderColor: border, borderRadius: "lg", overflow: "hidden", children: [_jsx(Grid, { role: "row", templateColumns: template, children: headerDays.map((i) => (_jsx(Box, { role: "columnheader", "aria-label": weekdayLong[i], px: 2, py: 1, fontSize: "0.75rem", fontWeight: 600, color: colors.textMuted, bg: colors.backgroundColor.main, borderRight: "1px solid", borderBottom: "1px solid", borderColor: border, children: weekdayShort[i] }, i))) }), weeks.map((week, weekIndex) => {
164
+ const layout = layoutWeek(week, shownEvents, maxLanes);
165
+ return (_jsx(Grid, { role: "row", templateColumns: template, children: week.map((day, col) => renderCell(day, col, weekIndex, layout)) }, dayKey(week[0])));
166
+ })] }) })] }));
167
+ };
168
+ export default CalendarGrid;
@@ -0,0 +1,155 @@
1
+ const MS_PER_DAY = 86400000;
2
+ export const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
3
+ export const addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
4
+ export const addMonths = (d, n) => {
5
+ const target = new Date(d.getFullYear(), d.getMonth() + n, 1);
6
+ const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
7
+ return new Date(target.getFullYear(), target.getMonth(), Math.min(d.getDate(), lastDay));
8
+ };
9
+ export const isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
10
+ export const dayKey = (d) => `${String(d.getFullYear()).padStart(4, "0")}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
11
+ export const isWeekend = (d) => d.getDay() === 0 || d.getDay() === 6;
12
+ /** Whole calendar days from `a` to `b`, counted on date components so a DST day is still one day. */
13
+ export const diffDays = (a, b) => Math.round((Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()) - Date.UTC(a.getFullYear(), a.getMonth(), a.getDate())) /
14
+ MS_PER_DAY);
15
+ export const startOfWeek = (d, weekStartsOn) => addDays(d, -((d.getDay() - weekStartsOn + 7) % 7));
16
+ export const weekDays = (d, weekStartsOn) => {
17
+ const first = startOfWeek(d, weekStartsOn);
18
+ return Array.from({ length: 7 }, (_, i) => addDays(first, i));
19
+ };
20
+ /** Whole weeks that cover the month of `d`, padded with the neighbouring months' days. */
21
+ export const monthWeeks = (d, weekStartsOn) => {
22
+ const first = new Date(d.getFullYear(), d.getMonth(), 1);
23
+ const last = new Date(d.getFullYear(), d.getMonth() + 1, 0);
24
+ const gridStart = startOfWeek(first, weekStartsOn);
25
+ const weekCount = Math.ceil((diffDays(gridStart, last) + 1) / 7);
26
+ return Array.from({ length: weekCount }, (_, w) => Array.from({ length: 7 }, (_, i) => addDays(gridStart, w * 7 + i)));
27
+ };
28
+ export const visibleWeeks = (view, d, weekStartsOn) => {
29
+ if (view === "month")
30
+ return monthWeeks(d, weekStartsOn);
31
+ if (view === "week")
32
+ return [weekDays(d, weekStartsOn)];
33
+ return [[startOfDay(d)]];
34
+ };
35
+ export const shiftAnchor = (view, d, direction) => {
36
+ if (view === "month")
37
+ return addMonths(d, direction);
38
+ return addDays(d, view === "week" ? 7 * direction : direction);
39
+ };
40
+ export const eventRange = (e) => {
41
+ const start = startOfDay(e.start);
42
+ const end = e.end ? startOfDay(e.end) : start;
43
+ return end < start ? { start: end, end: start } : { start, end };
44
+ };
45
+ export const eventsOnDay = (events, day) => {
46
+ const d = startOfDay(day);
47
+ return events.filter((e) => {
48
+ const r = eventRange(e);
49
+ return r.start <= d && d <= r.end;
50
+ });
51
+ };
52
+ /** Lays events out in lanes across one week row; lanes past `maxLanes` are counted per column, not drawn. */
53
+ export const layoutWeek = (days, events, maxLanes) => {
54
+ const first = days[0];
55
+ const lastCol = days.length - 1;
56
+ const candidates = events
57
+ .map((event) => {
58
+ const r = eventRange(event);
59
+ const rawStart = diffDays(first, r.start);
60
+ const rawEnd = diffDays(first, r.end);
61
+ if (rawEnd < 0 || rawStart > lastCol)
62
+ return null;
63
+ return {
64
+ event,
65
+ rawStart,
66
+ length: rawEnd - rawStart,
67
+ startCol: Math.max(0, rawStart),
68
+ endCol: Math.min(lastCol, rawEnd),
69
+ continuesBefore: rawStart < 0,
70
+ continuesAfter: rawEnd > lastCol,
71
+ };
72
+ })
73
+ .filter((c) => c !== null)
74
+ .sort((a, b) => a.rawStart - b.rawStart ||
75
+ b.length - a.length ||
76
+ a.event.label.localeCompare(b.event.label) ||
77
+ a.event.id.localeCompare(b.event.id));
78
+ const occupied = [];
79
+ const segments = [];
80
+ for (const c of candidates) {
81
+ let lane = occupied.findIndex((row) => row.slice(c.startCol, c.endCol + 1).every((taken) => !taken));
82
+ if (lane === -1) {
83
+ lane = occupied.length;
84
+ occupied.push(new Array(days.length).fill(false));
85
+ }
86
+ for (let col = c.startCol; col <= c.endCol; col += 1)
87
+ occupied[lane][col] = true;
88
+ segments.push({
89
+ event: c.event,
90
+ startCol: c.startCol,
91
+ endCol: c.endCol,
92
+ lane,
93
+ continuesBefore: c.continuesBefore,
94
+ continuesAfter: c.continuesAfter,
95
+ });
96
+ }
97
+ const hiddenByCol = new Array(days.length).fill(0);
98
+ for (const s of segments) {
99
+ if (s.lane < maxLanes)
100
+ continue;
101
+ for (let col = s.startCol; col <= s.endCol; col += 1)
102
+ hiddenByCol[col] += 1;
103
+ }
104
+ return { segments, visibleLanes: Math.min(occupied.length, maxLanes), hiddenByCol };
105
+ };
106
+ export const NAV_KEYS = [
107
+ "ArrowLeft",
108
+ "ArrowRight",
109
+ "ArrowUp",
110
+ "ArrowDown",
111
+ "Home",
112
+ "End",
113
+ "PageUp",
114
+ "PageDown",
115
+ ];
116
+ export const isNavKey = (key) => NAV_KEYS.includes(key);
117
+ export const moveFocus = (view, d, key, weekStartsOn) => {
118
+ const columns = view === "day" ? 1 : 7;
119
+ switch (key) {
120
+ case "ArrowLeft":
121
+ return addDays(d, -1);
122
+ case "ArrowRight":
123
+ return addDays(d, 1);
124
+ case "ArrowUp":
125
+ return addDays(d, -columns);
126
+ case "ArrowDown":
127
+ return addDays(d, columns);
128
+ case "Home":
129
+ return view === "day" ? d : startOfWeek(d, weekStartsOn);
130
+ case "End":
131
+ return view === "day" ? d : addDays(startOfWeek(d, weekStartsOn), 6);
132
+ case "PageUp":
133
+ return shiftAnchor(view, d, -1);
134
+ default:
135
+ return shiftAnchor(view, d, 1);
136
+ }
137
+ };
138
+ const probe = (weekday) => new Date(2024, 0, 7 + weekday);
139
+ export const weekdayLabels = (locale, weekStartsOn, style = "short") => {
140
+ const fmt = new Intl.DateTimeFormat(locale, { weekday: style });
141
+ return Array.from({ length: 7 }, (_, i) => fmt.format(probe((i + weekStartsOn) % 7)));
142
+ };
143
+ export const formatTitle = (view, d, locale, weekStartsOn) => {
144
+ if (view === "month")
145
+ return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(d);
146
+ if (view === "day")
147
+ return new Intl.DateTimeFormat(locale, { weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
148
+ const days = weekDays(d, weekStartsOn);
149
+ const opts = { day: "numeric", month: "short", year: "numeric" };
150
+ const fmt = new Intl.DateTimeFormat(locale, opts);
151
+ return typeof fmt.formatRange === "function"
152
+ ? fmt.formatRange(days[0], days[6])
153
+ : `${fmt.format(days[0])} - ${fmt.format(days[6])}`;
154
+ };
155
+ export const formatFullDate = (d, locale) => new Intl.DateTimeFormat(locale, { weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
@@ -1,11 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { Card as ChakraCard, CardBody, CardFooter, CardHeader, } from "@chakra-ui/react";
4
- const Card = forwardRef(function Card({ header, footer, children, color, colorScheme, direction = "column", align, justify, bodyProps, ...rest }, ref) {
4
+ const HEADER_PADDING_WHEN_FLUSH = "0.75rem";
5
+ const PADDING_VALUES = {
6
+ none: "0",
7
+ "2xs": "0.375rem",
8
+ xs: "0.75rem",
9
+ sm: "0.75rem 1rem",
10
+ md: "1rem 1.25rem",
11
+ lg: "1.25rem 1.5rem",
12
+ };
13
+ const Card = forwardRef(function Card({ header, footer, children, color, colorScheme, direction = "column", align, justify, bodyProps, padding, ...rest }, ref) {
5
14
  const cardProps = { ...rest };
6
15
  delete cardProps.dividercolor;
7
16
  delete cardProps.dividersize;
8
17
  delete cardProps.dividervariant;
9
- return (_jsxs(ChakraCard, { ref: ref, colorScheme: colorScheme !== null && colorScheme !== void 0 ? colorScheme : color, ...cardProps, children: [header && _jsx(CardHeader, { children: header }), _jsx(CardBody, { display: "flex", flexDirection: direction, alignItems: align, justifyContent: justify, ...bodyProps, children: children }), footer && _jsx(CardFooter, { children: footer })] }));
18
+ const sx = padding ? { "--card-padding": PADDING_VALUES[padding], ...cardProps.sx } : cardProps.sx;
19
+ return (_jsxs(ChakraCard, { ref: ref, colorScheme: colorScheme !== null && colorScheme !== void 0 ? colorScheme : color, ...cardProps, sx: sx, children: [header && (_jsx(CardHeader, { sx: padding === "none" ? { "--card-padding": HEADER_PADDING_WHEN_FLUSH } : undefined, children: header })), _jsx(CardBody, { display: "flex", flexDirection: direction, alignItems: align, justifyContent: justify, ...bodyProps, children: children }), footer && _jsx(CardFooter, { children: footer })] }));
10
20
  });
11
21
  export default Card;