pixelize-design-library 2.4.2-beta.59 → 2.4.2-beta.61

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 (157) hide show
  1. package/CHANGELOG.md +110 -13
  2. package/COMPONENT-GOTCHAS.md +159 -21
  3. package/DESIGN-GUIDE.md +26 -7
  4. package/dist/Components/AudioRecorder/AudioRecorder.d.ts +8 -0
  5. package/dist/Components/AudioRecorder/AudioRecorder.js +169 -0
  6. package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +20 -0
  7. package/dist/Components/AudioRecorder/AudioRecorderProps.js +2 -0
  8. package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +3 -0
  9. package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +43 -0
  10. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +35 -0
  11. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.js +2 -0
  12. package/dist/Components/BulkSelectionBar/index.d.ts +2 -0
  13. package/dist/Components/BulkSelectionBar/index.js +8 -0
  14. package/dist/Components/CalendarGrid/CalendarGrid.d.ts +3 -0
  15. package/dist/Components/CalendarGrid/CalendarGrid.js +173 -0
  16. package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +54 -0
  17. package/dist/Components/CalendarGrid/CalendarGridProps.js +2 -0
  18. package/dist/Components/CalendarGrid/calendarMath.d.ts +49 -0
  19. package/dist/Components/CalendarGrid/calendarMath.js +178 -0
  20. package/dist/Components/CircularProgress/CircularProgress.d.ts +4 -0
  21. package/dist/Components/CircularProgress/CircularProgress.js +39 -0
  22. package/dist/Components/CircularProgress/CircularProgressProps.d.ts +22 -0
  23. package/dist/Components/CircularProgress/CircularProgressProps.js +2 -0
  24. package/dist/Components/CircularProgress/arc.d.ts +12 -0
  25. package/dist/Components/CircularProgress/arc.js +16 -0
  26. package/dist/Components/Common/fieldStyles.d.ts +1 -0
  27. package/dist/Components/Common/fieldStyles.js +2 -1
  28. package/dist/Components/Drawer/Drawer.d.ts +1 -1
  29. package/dist/Components/Drawer/Drawer.js +4 -2
  30. package/dist/Components/Drawer/DrawerProps.d.ts +3 -1
  31. package/dist/Components/EmptyState/EmptyState.js +2 -1
  32. package/dist/Components/EmptyState/EmptyStateProps.d.ts +2 -2
  33. package/dist/Components/FormDrawer/FormDrawer.d.ts +2 -0
  34. package/dist/Components/FormDrawer/FormDrawer.js +97 -0
  35. package/dist/Components/FormDrawer/FormDrawerProps.d.ts +55 -0
  36. package/dist/Components/FormDrawer/FormDrawerProps.js +2 -0
  37. package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +1 -1
  38. package/dist/Components/LauncherTrigger/LauncherTrigger.js +8 -1
  39. package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +2 -0
  40. package/dist/Components/Modal/Modal.d.ts +1 -1
  41. package/dist/Components/Modal/Modal.js +4 -2
  42. package/dist/Components/Modal/ModalProps.d.ts +3 -1
  43. package/dist/Components/Modal/warnDroppedSlotChildren.d.ts +2 -0
  44. package/dist/Components/Modal/warnDroppedSlotChildren.js +45 -0
  45. package/dist/Components/NavLauncher/LauncherTabs.d.ts +18 -0
  46. package/dist/Components/NavLauncher/LauncherTabs.js +64 -0
  47. package/dist/Components/NavLauncher/NavLauncher.d.ts +1 -1
  48. package/dist/Components/NavLauncher/NavLauncher.js +268 -61
  49. package/dist/Components/NavLauncher/NavLauncherProps.d.ts +29 -10
  50. package/dist/Components/NavLauncher/NewTabLink.d.ts +2 -2
  51. package/dist/Components/NavLauncher/NewTabLink.js +7 -3
  52. package/dist/Components/NavLauncher/launcherParts.d.ts +64 -2
  53. package/dist/Components/NavLauncher/launcherParts.js +103 -13
  54. package/dist/Components/NavLauncher/resultNavigation.d.ts +3 -0
  55. package/dist/Components/NavLauncher/resultNavigation.js +65 -0
  56. package/dist/Components/NavLauncher/revealStyles.d.ts +1 -1
  57. package/dist/Components/NavLauncher/revealStyles.js +2 -2
  58. package/dist/Components/NavigationBar/NavigationBar.js +2 -1
  59. package/dist/Components/NavigationBar/NavigationBarProps.d.ts +1 -0
  60. package/dist/Components/OrgSwitcher/OrgSwitcher.js +18 -6
  61. package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +4 -0
  62. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  63. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewerProps.d.ts +1 -1
  64. package/dist/Components/QueryState/QueryStateBoundary.d.ts +2 -2
  65. package/dist/Components/QueryState/QueryStateBoundary.js +9 -3
  66. package/dist/Components/QueryState/QueryStateProps.d.ts +19 -2
  67. package/dist/Components/SectionRail/SectionRail.d.ts +3 -0
  68. package/dist/Components/SectionRail/SectionRail.js +49 -0
  69. package/dist/Components/SectionRail/SectionRailProps.d.ts +23 -0
  70. package/dist/Components/SectionRail/SectionRailProps.js +2 -0
  71. package/dist/Components/SectionRail/index.d.ts +2 -0
  72. package/dist/Components/SectionRail/index.js +8 -0
  73. package/dist/Components/SegmentedControl/SegmentedControl.js +9 -14
  74. package/dist/Components/SegmentedControl/segmentedInk.d.ts +0 -2
  75. package/dist/Components/SegmentedControl/segmentedInk.js +1 -5
  76. package/dist/Components/StatCard/StatCard.d.ts +1 -1
  77. package/dist/Components/StatCard/StatCard.js +6 -2
  78. package/dist/Components/StatCard/StatCardProps.d.ts +3 -0
  79. package/dist/Components/StatusTag/StatusTag.d.ts +4 -0
  80. package/dist/Components/StatusTag/StatusTag.js +24 -0
  81. package/dist/Components/StatusTag/StatusTagProps.d.ts +20 -0
  82. package/dist/Components/StatusTag/StatusTagProps.js +2 -0
  83. package/dist/Components/Table/Table.d.ts +1 -1
  84. package/dist/Components/Table/Table.js +6 -2
  85. package/dist/Components/Table/TableProps.d.ts +20 -0
  86. package/dist/Components/Table/components/TableBody.d.ts +1 -1
  87. package/dist/Components/Table/components/TableBody.js +43 -18
  88. package/dist/Components/Table/components/TableHeader.d.ts +1 -1
  89. package/dist/Components/Table/components/TableHeader.js +7 -5
  90. package/dist/Components/Table/components/rowDetail.d.ts +32 -0
  91. package/dist/Components/Table/components/rowDetail.js +20 -0
  92. package/dist/Components/Table/hooks/useRowExpansion.d.ts +7 -0
  93. package/dist/Components/Table/hooks/useRowExpansion.js +27 -0
  94. package/dist/Components/Tabs/Tabs.d.ts +1 -1
  95. package/dist/Components/Tabs/Tabs.js +15 -2
  96. package/dist/Components/Tabs/TabsProps.d.ts +6 -2
  97. package/dist/Components/Tabs/components/Tab.js +4 -33
  98. package/dist/Components/Tabs/components/TabLinkList.d.ts +7 -0
  99. package/dist/Components/Tabs/components/TabLinkList.js +34 -0
  100. package/dist/Components/Tabs/useTabAppearance.d.ts +17 -0
  101. package/dist/Components/Tabs/useTabAppearance.js +67 -0
  102. package/dist/Components/Tag/Tag.js +11 -1
  103. package/dist/Components/Tag/TagProps.d.ts +4 -0
  104. package/dist/Components/Tag/tagDot.d.ts +3 -0
  105. package/dist/Components/Tag/tagDot.js +21 -0
  106. package/dist/esm/Components/AudioRecorder/AudioRecorder.js +161 -0
  107. package/dist/esm/Components/AudioRecorder/AudioRecorderProps.js +1 -0
  108. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +38 -0
  109. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBarProps.js +1 -0
  110. package/dist/esm/Components/BulkSelectionBar/index.js +1 -0
  111. package/dist/esm/Components/CalendarGrid/CalendarGrid.js +168 -0
  112. package/dist/esm/Components/CalendarGrid/CalendarGridProps.js +1 -0
  113. package/dist/esm/Components/CalendarGrid/calendarMath.js +155 -0
  114. package/dist/esm/Components/CircularProgress/CircularProgress.js +37 -0
  115. package/dist/esm/Components/CircularProgress/CircularProgressProps.js +1 -0
  116. package/dist/esm/Components/CircularProgress/arc.js +11 -0
  117. package/dist/esm/Components/Common/fieldStyles.js +2 -1
  118. package/dist/esm/Components/Drawer/Drawer.js +4 -2
  119. package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
  120. package/dist/esm/Components/FormDrawer/FormDrawer.js +58 -0
  121. package/dist/esm/Components/FormDrawer/FormDrawerProps.js +1 -0
  122. package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +5 -1
  123. package/dist/esm/Components/Modal/Modal.js +4 -2
  124. package/dist/esm/Components/Modal/warnDroppedSlotChildren.js +38 -0
  125. package/dist/esm/Components/NavLauncher/LauncherTabs.js +58 -0
  126. package/dist/esm/Components/NavLauncher/NavLauncher.js +270 -63
  127. package/dist/esm/Components/NavLauncher/NewTabLink.js +4 -3
  128. package/dist/esm/Components/NavLauncher/launcherParts.js +98 -14
  129. package/dist/esm/Components/NavLauncher/resultNavigation.js +61 -0
  130. package/dist/esm/Components/NavLauncher/revealStyles.js +2 -2
  131. package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
  132. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +16 -7
  133. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  134. package/dist/esm/Components/QueryState/QueryStateBoundary.js +10 -4
  135. package/dist/esm/Components/SectionRail/SectionRail.js +44 -0
  136. package/dist/esm/Components/SectionRail/SectionRailProps.js +1 -0
  137. package/dist/esm/Components/SectionRail/index.js +1 -0
  138. package/dist/esm/Components/SegmentedControl/SegmentedControl.js +11 -16
  139. package/dist/esm/Components/SegmentedControl/segmentedInk.js +0 -3
  140. package/dist/esm/Components/StatCard/StatCard.js +6 -2
  141. package/dist/esm/Components/StatusTag/StatusTag.js +19 -0
  142. package/dist/esm/Components/StatusTag/StatusTagProps.js +1 -0
  143. package/dist/esm/Components/Table/Table.js +7 -3
  144. package/dist/esm/Components/Table/components/TableBody.js +43 -18
  145. package/dist/esm/Components/Table/components/TableHeader.js +7 -5
  146. package/dist/esm/Components/Table/components/rowDetail.js +14 -0
  147. package/dist/esm/Components/Table/hooks/useRowExpansion.js +23 -0
  148. package/dist/esm/Components/Tabs/Tabs.js +15 -2
  149. package/dist/esm/Components/Tabs/components/Tab.js +4 -33
  150. package/dist/esm/Components/Tabs/components/TabLinkList.js +29 -0
  151. package/dist/esm/Components/Tabs/useTabAppearance.js +63 -0
  152. package/dist/esm/Components/Tag/Tag.js +12 -2
  153. package/dist/esm/Components/Tag/tagDot.js +17 -0
  154. package/dist/esm/index.js +7 -0
  155. package/dist/index.d.ts +17 -2
  156. package/dist/index.js +18 -4
  157. package/package.json +1 -1
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.recordingFileName = exports.formatElapsed = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const react_2 = require("@chakra-ui/react");
10
+ const Button_1 = __importDefault(require("../Button/Button"));
11
+ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
12
+ const DEFAULT_LABELS = {
13
+ start: "Start recording",
14
+ stop: "Stop recording",
15
+ recording: "Recording",
16
+ recorded: "Recorded",
17
+ permissionDenied: "Microphone access was denied. Allow it in your browser settings and try again.",
18
+ unsupported: "Audio recording is not supported in this browser.",
19
+ };
20
+ const PREFERRED_MIME = "audio/webm";
21
+ const TICK_MS = 250;
22
+ const EXTENSIONS = {
23
+ "audio/webm": "webm",
24
+ "audio/wav": "wav",
25
+ "audio/wave": "wav",
26
+ "audio/x-wav": "wav",
27
+ "audio/ogg": "ogg",
28
+ "audio/mp4": "m4a",
29
+ "audio/mpeg": "mp3",
30
+ };
31
+ const formatElapsed = (ms) => {
32
+ const total = Math.floor(ms / 1000);
33
+ return `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
34
+ };
35
+ exports.formatElapsed = formatElapsed;
36
+ const recordingFileName = (mime, now) => {
37
+ var _a, _b;
38
+ const type = mime.split(";")[0].trim().toLowerCase() || PREFERRED_MIME;
39
+ 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")}` };
40
+ };
41
+ exports.recordingFileName = recordingFileName;
42
+ const isSupported = () => { var _a; return typeof navigator !== "undefined" && !!((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) && typeof MediaRecorder !== "undefined"; };
43
+ const AudioRecorder = ({ onRecorded, maxDurationSec, isDisabled = false, labels }) => {
44
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
45
+ const text = { ...DEFAULT_LABELS, ...labels };
46
+ const [status, setStatus] = (0, react_1.useState)("idle");
47
+ const [elapsedMs, setElapsedMs] = (0, react_1.useState)(0);
48
+ const [announcement, setAnnouncement] = (0, react_1.useState)("");
49
+ const recorderRef = (0, react_1.useRef)(null);
50
+ const streamRef = (0, react_1.useRef)(null);
51
+ const chunksRef = (0, react_1.useRef)([]);
52
+ const timerRef = (0, react_1.useRef)(null);
53
+ const startedAtRef = (0, react_1.useRef)(0);
54
+ const mountedRef = (0, react_1.useRef)(true);
55
+ const startingRef = (0, react_1.useRef)(false);
56
+ const onRecordedRef = (0, react_1.useRef)(onRecorded);
57
+ const textRef = (0, react_1.useRef)(text);
58
+ onRecordedRef.current = onRecorded;
59
+ textRef.current = text;
60
+ const release = (0, react_1.useCallback)(() => {
61
+ var _a;
62
+ if (timerRef.current !== null)
63
+ clearInterval(timerRef.current);
64
+ timerRef.current = null;
65
+ (_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
66
+ streamRef.current = null;
67
+ recorderRef.current = null;
68
+ }, []);
69
+ (0, react_1.useEffect)(() => {
70
+ mountedRef.current = true;
71
+ if (!isSupported())
72
+ setStatus("unsupported");
73
+ return () => {
74
+ mountedRef.current = false;
75
+ const recorder = recorderRef.current;
76
+ if (recorder) {
77
+ recorder.ondataavailable = null;
78
+ recorder.onstop = null;
79
+ recorder.onerror = null;
80
+ if (recorder.state !== "inactive") {
81
+ try {
82
+ recorder.stop();
83
+ }
84
+ catch {
85
+ // NOTE: a recorder the browser already tore down throws on stop.
86
+ }
87
+ }
88
+ }
89
+ release();
90
+ };
91
+ }, [release]);
92
+ const stop = (0, react_1.useCallback)(() => {
93
+ const recorder = recorderRef.current;
94
+ if (recorder && recorder.state !== "inactive")
95
+ recorder.stop();
96
+ }, []);
97
+ const start = (0, react_1.useCallback)(async () => {
98
+ var _a, _b;
99
+ if (startingRef.current)
100
+ return;
101
+ if (!isSupported()) {
102
+ setStatus("unsupported");
103
+ return;
104
+ }
105
+ startingRef.current = true;
106
+ try {
107
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
108
+ if (!mountedRef.current) {
109
+ stream.getTracks().forEach((track) => track.stop());
110
+ return;
111
+ }
112
+ streamRef.current = stream;
113
+ chunksRef.current = [];
114
+ const useWebm = (_b = (_a = MediaRecorder.isTypeSupported) === null || _a === void 0 ? void 0 : _a.call(MediaRecorder, PREFERRED_MIME)) !== null && _b !== void 0 ? _b : false;
115
+ const recorder = new MediaRecorder(stream, useWebm ? { mimeType: PREFERRED_MIME } : undefined);
116
+ recorderRef.current = recorder;
117
+ recorder.ondataavailable = (event) => {
118
+ if (event.data.size > 0)
119
+ chunksRef.current.push(event.data);
120
+ };
121
+ recorder.onerror = () => {
122
+ release();
123
+ setStatus("idle");
124
+ };
125
+ recorder.onstop = () => {
126
+ const duration = Date.now() - startedAtRef.current;
127
+ const { type, name } = (0, exports.recordingFileName)(recorder.mimeType || PREFERRED_MIME, Date.now());
128
+ const file = new File(chunksRef.current, name, { type });
129
+ release();
130
+ setStatus("idle");
131
+ setElapsedMs(duration);
132
+ setAnnouncement(`${textRef.current.recorded} ${(0, exports.formatElapsed)(duration)}`);
133
+ if (file.size > 0)
134
+ onRecordedRef.current(file);
135
+ };
136
+ recorder.start();
137
+ startedAtRef.current = Date.now();
138
+ setElapsedMs(0);
139
+ setStatus("recording");
140
+ setAnnouncement(textRef.current.recording);
141
+ timerRef.current = setInterval(() => {
142
+ const elapsed = Date.now() - startedAtRef.current;
143
+ if (maxDurationSec !== undefined && elapsed >= maxDurationSec * 1000) {
144
+ setElapsedMs(maxDurationSec * 1000);
145
+ stop();
146
+ return;
147
+ }
148
+ setElapsedMs(elapsed);
149
+ }, TICK_MS);
150
+ }
151
+ catch (error) {
152
+ release();
153
+ if (!mountedRef.current)
154
+ return;
155
+ const name = error === null || error === void 0 ? void 0 : error.name;
156
+ setStatus(name === "NotFoundError" ? "unsupported" : "denied");
157
+ }
158
+ finally {
159
+ startingRef.current = false;
160
+ }
161
+ }, [maxDurationSec, release, stop]);
162
+ const isRecording = status === "recording";
163
+ const blocked = status === "unsupported";
164
+ return ((0, jsx_runtime_1.jsxs)(react_2.Box, { children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 3, children: [(0, jsx_runtime_1.jsx)(Button_1.default, { 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 ? ((0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { "data-testid": "audio-recorder-dot", boxSize: "0.5rem", borderRadius: "full", bg: colors.semanticText.error, "aria-hidden": true, sx: {
165
+ "@media (prefers-reduced-motion: no-preference)": { animation: "audio-recorder-pulse 1.2s ease-in-out infinite" },
166
+ "@keyframes audio-recorder-pulse": { "0%, 100%": { opacity: 1 }, "50%": { opacity: 0.35 } },
167
+ } }), (0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", role: "timer", fontSize: "0.875rem", color: colors.header[900], sx: { fontVariantNumeric: "tabular-nums" }, children: (0, exports.formatElapsed)(elapsedMs) })] })) : null] }), (0, jsx_runtime_1.jsx)(react_2.VisuallyHidden, { role: "status", "aria-live": "polite", children: announcement }), status === "denied" || status === "unsupported" ? ((0, jsx_runtime_1.jsx)(react_2.Text, { role: "alert", mt: 1, fontSize: "0.75rem", color: colors.semanticText.error, children: status === "denied" ? text.permissionDenied : text.unsupported })) : null] }));
168
+ };
169
+ exports.default = AudioRecorder;
@@ -0,0 +1,20 @@
1
+ /** Visible text and announcements; every key falls back to English. */
2
+ export type AudioRecorderLabels = {
3
+ start: string;
4
+ stop: string;
5
+ recording: string;
6
+ /** Announced when a recording finishes, followed by its duration. */
7
+ recorded: string;
8
+ permissionDenied: string;
9
+ unsupported: string;
10
+ };
11
+ /** Props for the microphone recorder: one toggle button, a timer, and a `File` on stop. */
12
+ export interface AudioRecorderProps {
13
+ /** Receives the finished recording; never called for an empty one or after unmount. */
14
+ onRecorded: (file: File) => void;
15
+ /** Stops the recording by itself after this many seconds. No limit when omitted. */
16
+ maxDurationSec?: number;
17
+ /** Blocks Start only; a recording already in progress can always be stopped. */
18
+ isDisabled?: boolean;
19
+ labels?: Partial<AudioRecorderLabels>;
20
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import type { BulkSelectionBarProps } from "./BulkSelectionBarProps";
2
+ declare const BulkSelectionBar: ({ selectedCount, facts, actions, onClearSelection, clearLabel, countLabel, placement, "aria-label": ariaLabel, "data-testid": testId, }: BulkSelectionBarProps) => import("react/jsx-runtime").JSX.Element | null;
3
+ export default BulkSelectionBar;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const react_2 = require("@chakra-ui/react");
9
+ const lucide_react_1 = require("lucide-react");
10
+ const Button_1 = __importDefault(require("../Button/Button"));
11
+ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
12
+ const FACT_SEPARATOR = " · ";
13
+ const VARIANT_BY_TONE = {
14
+ default: "outline",
15
+ primary: "solid",
16
+ danger: "outline",
17
+ };
18
+ const SCHEME_BY_TONE = {
19
+ default: "gray",
20
+ primary: "primary",
21
+ danger: "red",
22
+ };
23
+ const TOUCH_SX = { "@media (pointer: coarse)": { minHeight: "44px", minWidth: "44px" } };
24
+ const BulkSelectionBar = ({ selectedCount, facts, actions, onClearSelection, clearLabel = "Clear selection", countLabel, placement = "sticky", "aria-label": ariaLabel = "Selection actions", "data-testid": testId = "bulk-selection-bar", }) => {
25
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
26
+ const isActive = selectedCount > 0;
27
+ const summary = countLabel !== null && countLabel !== void 0 ? countLabel : `${selectedCount} selected`;
28
+ const [announcement, setAnnouncement] = (0, react_1.useState)("");
29
+ (0, react_1.useEffect)(() => {
30
+ setAnnouncement(isActive ? summary : "");
31
+ }, [isActive, summary]);
32
+ if (!isActive)
33
+ return null;
34
+ const resolvedFacts = (facts !== null && facts !== void 0 ? facts : []).filter(Boolean);
35
+ const isSticky = placement === "sticky";
36
+ return ((0, jsx_runtime_1.jsxs)(react_2.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: [(0, jsx_runtime_1.jsx)(react_2.VisuallyHidden, { role: "status", "aria-live": "polite", "data-testid": "bulk-selection-announcement", children: announcement }), (0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "baseline", gap: 1, flex: "1", minW: "180px", wrap: "wrap", children: [(0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", fontWeight: 600, color: colors.text[800], "data-testid": "bulk-selection-count", children: summary }), resolvedFacts.length ? ((0, jsx_runtime_1.jsxs)(react_2.Text, { fontSize: "sm", color: colors.textMuted, "data-testid": "bulk-selection-facts", children: [FACT_SEPARATOR, resolvedFacts.join(FACT_SEPARATOR)] })) : null] }), (0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, wrap: "wrap", children: [actions.map((action) => {
37
+ var _a;
38
+ const tone = (_a = action.tone) !== null && _a !== void 0 ? _a : "default";
39
+ const Icon = action.icon;
40
+ return ((0, jsx_runtime_1.jsx)(Button_1.default, { 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 ? (0, jsx_runtime_1.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));
41
+ }), (0, jsx_runtime_1.jsx)(react_2.Box, { children: (0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: onClearSelection, leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, "aria-hidden": "true" }), sx: TOUCH_SX, "data-testid": "bulk-selection-clear", children: clearLabel }) })] })] }));
42
+ };
43
+ exports.default = BulkSelectionBar;
@@ -0,0 +1,35 @@
1
+ import type { ElementType } from "react";
2
+ /** Emphasis of a bulk action: "primary" is the affirmative one, "danger" the destructive one, "default" the rest. */
3
+ export type BulkActionTone = "default" | "primary" | "danger";
4
+ /** One button in the bar; actions render in array order. */
5
+ export interface BulkAction {
6
+ /** Stable key; also the suffix of the button's `data-testid` (`bulk-action-<id>`). */
7
+ id: string;
8
+ label: string;
9
+ onClick: () => void;
10
+ /** Defaults to "default". */
11
+ tone?: BulkActionTone;
12
+ /** Icon component (e.g. a lucide icon); rendered at 16px, decorative. */
13
+ icon?: ElementType;
14
+ isDisabled?: boolean;
15
+ /** Spinner on this action; the label stays as the loading text. */
16
+ isLoading?: boolean;
17
+ }
18
+ /** Props for the selection bar shown while table rows are selected; it renders nothing at zero. */
19
+ export interface BulkSelectionBarProps {
20
+ /** Rows selected; the bar renders nothing at 0 and announces each change politely. */
21
+ selectedCount: number;
22
+ /** Short claims true of the whole selection, e.g. ["all leave", "no clashes"]; joined after the count with " · ". */
23
+ facts?: string[];
24
+ actions: BulkAction[];
25
+ onClearSelection: () => void;
26
+ /** Clear button text; defaults to "Clear selection". */
27
+ clearLabel?: string;
28
+ /** Replaces the default "{n} selected" text, and what is announced. */
29
+ countLabel?: string;
30
+ /** "sticky" (default) pins to the bottom of the nearest scroll container; "inline" stays in flow. */
31
+ placement?: "inline" | "sticky";
32
+ /** Accessible name of the region; defaults to "Selection actions". */
33
+ "aria-label"?: string;
34
+ "data-testid"?: string;
35
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export { default as BulkSelectionBar } from "./BulkSelectionBar";
2
+ export type { BulkAction, BulkActionTone, BulkSelectionBarProps } from "./BulkSelectionBarProps";
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BulkSelectionBar = void 0;
7
+ var BulkSelectionBar_1 = require("./BulkSelectionBar");
8
+ Object.defineProperty(exports, "BulkSelectionBar", { enumerable: true, get: function () { return __importDefault(BulkSelectionBar_1).default; } });
@@ -0,0 +1,3 @@
1
+ import type { CalendarGridProps } from "./CalendarGridProps";
2
+ declare const CalendarGrid: ({ view, date, events, onViewChange, onDateChange, onEventClick, renderDayCell, weekStartsOn, locale, isLoading, labels, }: CalendarGridProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default CalendarGrid;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const react_2 = require("@chakra-ui/react");
9
+ const lucide_react_1 = require("lucide-react");
10
+ const Button_1 = __importDefault(require("../Button/Button"));
11
+ const Skeletons_1 = __importDefault(require("../Skeletons/Skeletons"));
12
+ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
13
+ const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
14
+ const tone_1 = require("../../Theme/tokens/builders/tone");
15
+ const calendarMath_1 = require("./calendarMath");
16
+ const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
17
+ const DEFAULT_LABELS = {
18
+ today: "Today",
19
+ previous: "Previous",
20
+ next: "Next",
21
+ month: "Month",
22
+ week: "Week",
23
+ day: "Day",
24
+ more: (count) => `+${count} more`,
25
+ moreLabel: (count, date) => `${plural(count, "more event", "more events")} on ${date}`,
26
+ eventCount: (count) => plural(count, "event", "events"),
27
+ };
28
+ const VIEWS = ["month", "week", "day"];
29
+ const MAX_LANES = { month: 3, week: 6, day: Infinity };
30
+ const MIN_CELL_HEIGHT = { month: "6.5rem", week: "12rem", day: "16rem" };
31
+ const LANE_HEIGHT = "1.5rem";
32
+ const CHIP_HEIGHT = "1.375rem";
33
+ const CHIP_INSET = "2px";
34
+ const EventChip = ({ event, colors, surface, onEventClick, segment }) => {
35
+ var _a;
36
+ const tone = (0, tone_1.resolveSoftTone)(colors, (0, tone_1.toneScale)(colors, (_a = event.tone) !== null && _a !== void 0 ? _a : "neutral"), surface);
37
+ const left = (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? "0px" : CHIP_INSET;
38
+ const right = (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? "0px" : CHIP_INSET;
39
+ const span = segment ? segment.endCol - segment.startCol + 1 : 1;
40
+ const placement = segment
41
+ ? {
42
+ position: "absolute",
43
+ zIndex: 1,
44
+ top: `calc(${segment.lane} * ${LANE_HEIGHT})`,
45
+ left,
46
+ width: `calc(${span * 100}% + ${span - 1}px - ${left} - ${right})`,
47
+ }
48
+ : { width: "100%" };
49
+ const common = {
50
+ ...placement,
51
+ display: "flex",
52
+ alignItems: "center",
53
+ h: CHIP_HEIGHT,
54
+ px: 2,
55
+ fontSize: "0.75rem",
56
+ fontWeight: 500,
57
+ lineHeight: 1,
58
+ textAlign: "left",
59
+ whiteSpace: "nowrap",
60
+ overflow: "hidden",
61
+ textOverflow: "ellipsis",
62
+ textDecoration: "none",
63
+ cursor: "pointer",
64
+ bg: tone.bg,
65
+ color: tone.fg,
66
+ border: "1px solid",
67
+ borderColor: tone.border,
68
+ borderLeftRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? 0 : "md",
69
+ borderRightRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? 0 : "md",
70
+ title: event.label,
71
+ "data-event-id": event.id,
72
+ onClick: () => onEventClick === null || onEventClick === void 0 ? void 0 : onEventClick(event),
73
+ _hover: { bg: tone.hoverBg, borderColor: tone.hoverBorder },
74
+ _active: { bg: tone.activeBg, borderColor: tone.activeBorder },
75
+ sx: (0, focusRing_styles_1.focusVisibleStyles)(colors),
76
+ };
77
+ return event.href ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "a", href: event.href, ...common, children: event.label })) : ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", ...common, children: event.label }));
78
+ };
79
+ const CalendarGrid = ({ view, date, events, onViewChange, onDateChange, onEventClick, renderDayCell, weekStartsOn = 0, locale, isLoading = false, labels, }) => {
80
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
81
+ const text = { ...DEFAULT_LABELS, ...labels };
82
+ const today = new Date();
83
+ const anchor = (0, calendarMath_1.startOfDay)(date);
84
+ const anchorKey = (0, calendarMath_1.dayKey)(anchor);
85
+ const weeks = (0, calendarMath_1.visibleWeeks)(view, anchor, weekStartsOn);
86
+ const columns = weeks[0].length;
87
+ const visibleKeys = new Set(weeks.flat().map(calendarMath_1.dayKey));
88
+ const shownEvents = isLoading ? [] : events;
89
+ const maxLanes = MAX_LANES[view];
90
+ const [focusedKey, setFocusedKey] = (0, react_1.useState)(anchorKey);
91
+ const pendingFocus = (0, react_1.useRef)(false);
92
+ const cellRefs = (0, react_1.useRef)(new Map());
93
+ const activeKey = visibleKeys.has(focusedKey) ? focusedKey : visibleKeys.has(anchorKey) ? anchorKey : (0, calendarMath_1.dayKey)(weeks[0][0]);
94
+ (0, react_1.useEffect)(() => {
95
+ setFocusedKey(anchorKey);
96
+ }, [anchorKey]);
97
+ (0, react_1.useEffect)(() => {
98
+ if (!pendingFocus.current)
99
+ return;
100
+ const el = cellRefs.current.get(activeKey);
101
+ if (!el)
102
+ return;
103
+ pendingFocus.current = false;
104
+ el.focus();
105
+ });
106
+ const handleCellKeyDown = (e, cellDate) => {
107
+ if (e.target !== e.currentTarget)
108
+ return;
109
+ if ((0, calendarMath_1.isNavKey)(e.key)) {
110
+ e.preventDefault();
111
+ const next = (0, calendarMath_1.moveFocus)(view, cellDate, e.key, weekStartsOn);
112
+ const inRange = visibleKeys.has((0, calendarMath_1.dayKey)(next));
113
+ if (!inRange && !onDateChange)
114
+ return;
115
+ setFocusedKey((0, calendarMath_1.dayKey)(next));
116
+ pendingFocus.current = true;
117
+ if (!inRange)
118
+ onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(next);
119
+ return;
120
+ }
121
+ if ((e.key === "Enter" || e.key === " ") && onDateChange) {
122
+ e.preventDefault();
123
+ onDateChange(cellDate);
124
+ }
125
+ };
126
+ const drillIntoDay = (day) => {
127
+ onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(day);
128
+ onViewChange === null || onViewChange === void 0 ? void 0 : onViewChange("day");
129
+ };
130
+ const weekdayShort = (0, calendarMath_1.weekdayLabels)(locale, weekStartsOn, "short");
131
+ const weekdayLong = (0, calendarMath_1.weekdayLabels)(locale, weekStartsOn, "long");
132
+ const headerDays = view === "day" ? [(anchor.getDay() - weekStartsOn + 7) % 7] : weekdayShort.map((_, i) => i);
133
+ const title = (0, calendarMath_1.formatTitle)(view, anchor, locale, weekStartsOn);
134
+ const template = `repeat(${columns}, minmax(0, 1fr))`;
135
+ const border = colors.boxborder[200];
136
+ const renderCell = (day, col, weekIndex, layout) => {
137
+ const key = (0, calendarMath_1.dayKey)(day);
138
+ const isToday = (0, calendarMath_1.isSameDay)(day, today);
139
+ const inMonth = view !== "month" || day.getMonth() === anchor.getMonth();
140
+ const surface = (0, calendarMath_1.isWeekend)(day) ? colors.backgroundColor.secondary : colors.backgroundColor.main;
141
+ const dayEvents = (0, calendarMath_1.eventsOnDay)(shownEvents, day);
142
+ const hidden = layout.hiddenByCol[col];
143
+ const active = key === activeKey;
144
+ const label = `${(0, calendarMath_1.formatFullDate)(day, locale)}${dayEvents.length ? `, ${text.eventCount(dayEvents.length)}` : ""}`;
145
+ const ownSegments = layout.segments.filter((s) => s.startCol === col && s.lane < maxLanes);
146
+ let body = null;
147
+ if (isLoading) {
148
+ body = ((0, jsx_runtime_1.jsx)(react_2.Box, { mt: 1, "aria-hidden": true, children: (weekIndex * 7 + col) % 3 === 0 ? (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: CHIP_HEIGHT, borderRadius: "md" }) : null }));
149
+ }
150
+ else if (renderDayCell) {
151
+ body = (0, jsx_runtime_1.jsx)(react_2.Box, { mt: 1, children: renderDayCell(day, dayEvents) });
152
+ }
153
+ else if (view === "day") {
154
+ body = ((0, jsx_runtime_1.jsx)(react_2.Flex, { direction: "column", gap: 1, mt: 2, children: dayEvents.map((event) => ((0, jsx_runtime_1.jsx)(EventChip, { event: event, colors: colors, surface: surface, onEventClick: onEventClick }, event.id))) }));
155
+ }
156
+ else {
157
+ body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Box, { position: "relative", mt: 1, h: `calc(${layout.visibleLanes} * ${LANE_HEIGHT})`, children: ownSegments.map((s) => ((0, jsx_runtime_1.jsx)(EventChip, { event: s.event, colors: colors, surface: surface, onEventClick: onEventClick, segment: s }, s.event.id))) }), hidden > 0 ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", "aria-label": text.moreLabel(hidden, (0, calendarMath_1.formatFullDate)(day, locale)), onClick: () => drillIntoDay(day), px: 1, fontSize: "0.75rem", fontWeight: 600, color: colors.header[900], textAlign: "left", textDecoration: "underline", sx: (0, focusRing_styles_1.focusVisibleStyles)(colors), children: text.more(hidden) })) : null] }));
158
+ }
159
+ return ((0, jsx_runtime_1.jsxs)(react_2.Box, { role: "gridcell", ref: (el) => {
160
+ if (el)
161
+ cellRefs.current.set(key, el);
162
+ else
163
+ cellRefs.current.delete(key);
164
+ }, "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: {
165
+ boxShadow: `inset 0 0 0 2px ${colors.focusRing.halo}, inset 0 0 0 4px ${colors.focusRing.ring}`,
166
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.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));
167
+ };
168
+ return ((0, jsx_runtime_1.jsxs)(react_2.Box, { children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", justify: "space-between", wrap: "wrap", gap: 2, mb: 3, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, children: [onDateChange ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Button_1.default, { label: text.today, size: "sm", variant: "outline", onClick: () => onDateChange((0, calendarMath_1.startOfDay)(new Date())) }), (0, jsx_runtime_1.jsx)(Button_1.default, { "aria-label": text.previous, size: "sm", variant: "ghost", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { size: 16, "aria-hidden": true }), onClick: () => onDateChange((0, calendarMath_1.shiftAnchor)(view, anchor, -1)) }), (0, jsx_runtime_1.jsx)(Button_1.default, { "aria-label": text.next, size: "sm", variant: "ghost", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 16, "aria-hidden": true }), onClick: () => onDateChange((0, calendarMath_1.shiftAnchor)(view, anchor, 1)) })] })) : null, (0, jsx_runtime_1.jsx)(react_2.Text, { as: "h2", fontSize: "1rem", fontWeight: 600, color: colors.header[900], children: title })] }), onViewChange ? ((0, jsx_runtime_1.jsx)(react_2.Flex, { role: "group", "aria-label": text[view], gap: 1, children: VIEWS.map((v) => ((0, jsx_runtime_1.jsx)(Button_1.default, { label: text[v], size: "sm", variant: v === view ? "solid" : "outline", "aria-pressed": v === view, onClick: () => onViewChange(v) }, v))) })) : null] }), (0, jsx_runtime_1.jsx)(react_2.Box, { overflowX: "auto", children: (0, jsx_runtime_1.jsxs)(react_2.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: [(0, jsx_runtime_1.jsx)(react_2.Grid, { role: "row", templateColumns: template, children: headerDays.map((i) => ((0, jsx_runtime_1.jsx)(react_2.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) => {
169
+ const layout = (0, calendarMath_1.layoutWeek)(week, shownEvents, maxLanes);
170
+ return ((0, jsx_runtime_1.jsx)(react_2.Grid, { role: "row", templateColumns: template, children: week.map((day, col) => renderCell(day, col, weekIndex, layout)) }, (0, calendarMath_1.dayKey)(week[0])));
171
+ })] }) })] }));
172
+ };
173
+ exports.default = CalendarGrid;
@@ -0,0 +1,54 @@
1
+ import type { ReactNode } from "react";
2
+ import type { Tone } from "../../Theme/tokens/builders/tone";
3
+ /** Which span the grid shows: a month of weeks, one week, or a single day. */
4
+ export type CalendarGridView = "month" | "week" | "day";
5
+ /** A date-granularity event; time of day is ignored. */
6
+ export type CalendarEvent = {
7
+ id: string;
8
+ /** First day covered; only the date part is read. */
9
+ start: Date;
10
+ /** Last day covered, inclusive. Omit for a single-day event; a value before `start` is swapped. */
11
+ end?: Date;
12
+ label: string;
13
+ /** Status tint of the chip, same tones as Tag. Defaults to "neutral". */
14
+ tone?: Tone;
15
+ /** Renders the chip as a link; without it the chip is a button that fires `onEventClick`. */
16
+ href?: string;
17
+ };
18
+ /** Visible text and accessible names; every key falls back to English. */
19
+ export type CalendarGridLabels = {
20
+ today: string;
21
+ previous: string;
22
+ next: string;
23
+ month: string;
24
+ week: string;
25
+ day: string;
26
+ /** Receives the number of hidden events, e.g. "+3 more". */
27
+ more: (count: number) => string;
28
+ /** Accessible name of the "more" button, e.g. "3 more events on Tuesday, 14 October 2026". */
29
+ moreLabel: (count: number, date: string) => string;
30
+ /** Appended to a day cell's accessible name, e.g. "3 events". */
31
+ eventCount: (count: number) => string;
32
+ };
33
+ /** Props for the month/week/day event grid; fully controlled through `view` and `date`. */
34
+ export interface CalendarGridProps {
35
+ view: CalendarGridView;
36
+ /** Anchor date: any day inside the month, week or day being shown. */
37
+ date: Date;
38
+ events: CalendarEvent[];
39
+ /** Called by the toolbar's view switcher and by a cell's "+N more" button (with "day"). */
40
+ onViewChange?: (view: CalendarGridView) => void;
41
+ /** Called when the anchor should move: toolbar, arrow keys leaving the visible range, Enter on a cell, "+N more". */
42
+ onDateChange?: (date: Date) => void;
43
+ /** Called when an event chip is activated; link chips still navigate to `href`. */
44
+ onEventClick?: (event: CalendarEvent) => void;
45
+ /** Replaces a cell's event chips (and the multi-day spanning) with your own content, under the day number. */
46
+ renderDayCell?: (date: Date, events: CalendarEvent[]) => ReactNode;
47
+ /** First column of the week; defaults to 0 (Sunday). */
48
+ weekStartsOn?: 0 | 1;
49
+ /** BCP 47 tag for month, weekday and date names via Intl; defaults to the runtime locale. */
50
+ locale?: string;
51
+ /** Keeps the grid's shape and swaps the events for skeleton bars; sets aria-busy. */
52
+ isLoading?: boolean;
53
+ labels?: Partial<CalendarGridLabels>;
54
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,49 @@
1
+ export type CalendarView = "month" | "week" | "day";
2
+ export type WeekStart = 0 | 1;
3
+ export type SpanEvent = {
4
+ id: string;
5
+ start: Date;
6
+ end?: Date;
7
+ label: string;
8
+ };
9
+ export type WeekSegment<E extends SpanEvent> = {
10
+ event: E;
11
+ startCol: number;
12
+ endCol: number;
13
+ lane: number;
14
+ continuesBefore: boolean;
15
+ continuesAfter: boolean;
16
+ };
17
+ export type WeekLayout<E extends SpanEvent> = {
18
+ segments: WeekSegment<E>[];
19
+ visibleLanes: number;
20
+ hiddenByCol: number[];
21
+ };
22
+ export declare const startOfDay: (d: Date) => Date;
23
+ export declare const addDays: (d: Date, n: number) => Date;
24
+ export declare const addMonths: (d: Date, n: number) => Date;
25
+ export declare const isSameDay: (a: Date, b: Date) => boolean;
26
+ export declare const dayKey: (d: Date) => string;
27
+ export declare const isWeekend: (d: Date) => boolean;
28
+ /** Whole calendar days from `a` to `b`, counted on date components so a DST day is still one day. */
29
+ export declare const diffDays: (a: Date, b: Date) => number;
30
+ export declare const startOfWeek: (d: Date, weekStartsOn: WeekStart) => Date;
31
+ export declare const weekDays: (d: Date, weekStartsOn: WeekStart) => Date[];
32
+ /** Whole weeks that cover the month of `d`, padded with the neighbouring months' days. */
33
+ export declare const monthWeeks: (d: Date, weekStartsOn: WeekStart) => Date[][];
34
+ export declare const visibleWeeks: (view: CalendarView, d: Date, weekStartsOn: WeekStart) => Date[][];
35
+ export declare const shiftAnchor: (view: CalendarView, d: Date, direction: 1 | -1) => Date;
36
+ export declare const eventRange: (e: Pick<SpanEvent, "start" | "end">) => {
37
+ start: Date;
38
+ end: Date;
39
+ };
40
+ export declare const eventsOnDay: <E extends SpanEvent>(events: readonly E[], day: Date) => E[];
41
+ /** Lays events out in lanes across one week row; lanes past `maxLanes` are counted per column, not drawn. */
42
+ export declare const layoutWeek: <E extends SpanEvent>(days: readonly Date[], events: readonly E[], maxLanes: number) => WeekLayout<E>;
43
+ export type NavKey = "ArrowLeft" | "ArrowRight" | "ArrowUp" | "ArrowDown" | "Home" | "End" | "PageUp" | "PageDown";
44
+ export declare const NAV_KEYS: readonly string[];
45
+ export declare const isNavKey: (key: string) => key is NavKey;
46
+ export declare const moveFocus: (view: CalendarView, d: Date, key: NavKey, weekStartsOn: WeekStart) => Date;
47
+ export declare const weekdayLabels: (locale: string | undefined, weekStartsOn: WeekStart, style?: "short" | "long") => string[];
48
+ export declare const formatTitle: (view: CalendarView, d: Date, locale: string | undefined, weekStartsOn: WeekStart) => string;
49
+ export declare const formatFullDate: (d: Date, locale: string | undefined) => string;