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.
- package/CHANGELOG.md +110 -13
- package/COMPONENT-GOTCHAS.md +159 -21
- package/DESIGN-GUIDE.md +26 -7
- package/dist/Components/AudioRecorder/AudioRecorder.d.ts +8 -0
- package/dist/Components/AudioRecorder/AudioRecorder.js +169 -0
- package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +20 -0
- package/dist/Components/AudioRecorder/AudioRecorderProps.js +2 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +3 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +43 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +35 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.js +2 -0
- package/dist/Components/BulkSelectionBar/index.d.ts +2 -0
- package/dist/Components/BulkSelectionBar/index.js +8 -0
- package/dist/Components/CalendarGrid/CalendarGrid.d.ts +3 -0
- package/dist/Components/CalendarGrid/CalendarGrid.js +173 -0
- package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +54 -0
- package/dist/Components/CalendarGrid/CalendarGridProps.js +2 -0
- package/dist/Components/CalendarGrid/calendarMath.d.ts +49 -0
- package/dist/Components/CalendarGrid/calendarMath.js +178 -0
- package/dist/Components/CircularProgress/CircularProgress.d.ts +4 -0
- package/dist/Components/CircularProgress/CircularProgress.js +39 -0
- package/dist/Components/CircularProgress/CircularProgressProps.d.ts +22 -0
- package/dist/Components/CircularProgress/CircularProgressProps.js +2 -0
- package/dist/Components/CircularProgress/arc.d.ts +12 -0
- package/dist/Components/CircularProgress/arc.js +16 -0
- package/dist/Components/Common/fieldStyles.d.ts +1 -0
- package/dist/Components/Common/fieldStyles.js +2 -1
- package/dist/Components/Drawer/Drawer.d.ts +1 -1
- package/dist/Components/Drawer/Drawer.js +4 -2
- package/dist/Components/Drawer/DrawerProps.d.ts +3 -1
- package/dist/Components/EmptyState/EmptyState.js +2 -1
- package/dist/Components/EmptyState/EmptyStateProps.d.ts +2 -2
- package/dist/Components/FormDrawer/FormDrawer.d.ts +2 -0
- package/dist/Components/FormDrawer/FormDrawer.js +97 -0
- package/dist/Components/FormDrawer/FormDrawerProps.d.ts +55 -0
- package/dist/Components/FormDrawer/FormDrawerProps.js +2 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +1 -1
- package/dist/Components/LauncherTrigger/LauncherTrigger.js +8 -1
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +2 -0
- package/dist/Components/Modal/Modal.d.ts +1 -1
- package/dist/Components/Modal/Modal.js +4 -2
- package/dist/Components/Modal/ModalProps.d.ts +3 -1
- package/dist/Components/Modal/warnDroppedSlotChildren.d.ts +2 -0
- package/dist/Components/Modal/warnDroppedSlotChildren.js +45 -0
- package/dist/Components/NavLauncher/LauncherTabs.d.ts +18 -0
- package/dist/Components/NavLauncher/LauncherTabs.js +64 -0
- package/dist/Components/NavLauncher/NavLauncher.d.ts +1 -1
- package/dist/Components/NavLauncher/NavLauncher.js +268 -61
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +29 -10
- package/dist/Components/NavLauncher/NewTabLink.d.ts +2 -2
- package/dist/Components/NavLauncher/NewTabLink.js +7 -3
- package/dist/Components/NavLauncher/launcherParts.d.ts +64 -2
- package/dist/Components/NavLauncher/launcherParts.js +103 -13
- package/dist/Components/NavLauncher/resultNavigation.d.ts +3 -0
- package/dist/Components/NavLauncher/resultNavigation.js +65 -0
- package/dist/Components/NavLauncher/revealStyles.d.ts +1 -1
- package/dist/Components/NavLauncher/revealStyles.js +2 -2
- package/dist/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/Components/NavigationBar/NavigationBarProps.d.ts +1 -0
- package/dist/Components/OrgSwitcher/OrgSwitcher.js +18 -6
- package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +4 -0
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewerProps.d.ts +1 -1
- package/dist/Components/QueryState/QueryStateBoundary.d.ts +2 -2
- package/dist/Components/QueryState/QueryStateBoundary.js +9 -3
- package/dist/Components/QueryState/QueryStateProps.d.ts +19 -2
- package/dist/Components/SectionRail/SectionRail.d.ts +3 -0
- package/dist/Components/SectionRail/SectionRail.js +49 -0
- package/dist/Components/SectionRail/SectionRailProps.d.ts +23 -0
- package/dist/Components/SectionRail/SectionRailProps.js +2 -0
- package/dist/Components/SectionRail/index.d.ts +2 -0
- package/dist/Components/SectionRail/index.js +8 -0
- package/dist/Components/SegmentedControl/SegmentedControl.js +9 -14
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +0 -2
- package/dist/Components/SegmentedControl/segmentedInk.js +1 -5
- package/dist/Components/StatCard/StatCard.d.ts +1 -1
- package/dist/Components/StatCard/StatCard.js +6 -2
- package/dist/Components/StatCard/StatCardProps.d.ts +3 -0
- package/dist/Components/StatusTag/StatusTag.d.ts +4 -0
- package/dist/Components/StatusTag/StatusTag.js +24 -0
- package/dist/Components/StatusTag/StatusTagProps.d.ts +20 -0
- package/dist/Components/StatusTag/StatusTagProps.js +2 -0
- package/dist/Components/Table/Table.d.ts +1 -1
- package/dist/Components/Table/Table.js +6 -2
- package/dist/Components/Table/TableProps.d.ts +20 -0
- package/dist/Components/Table/components/TableBody.d.ts +1 -1
- package/dist/Components/Table/components/TableBody.js +43 -18
- package/dist/Components/Table/components/TableHeader.d.ts +1 -1
- package/dist/Components/Table/components/TableHeader.js +7 -5
- package/dist/Components/Table/components/rowDetail.d.ts +32 -0
- package/dist/Components/Table/components/rowDetail.js +20 -0
- package/dist/Components/Table/hooks/useRowExpansion.d.ts +7 -0
- package/dist/Components/Table/hooks/useRowExpansion.js +27 -0
- package/dist/Components/Tabs/Tabs.d.ts +1 -1
- package/dist/Components/Tabs/Tabs.js +15 -2
- package/dist/Components/Tabs/TabsProps.d.ts +6 -2
- package/dist/Components/Tabs/components/Tab.js +4 -33
- package/dist/Components/Tabs/components/TabLinkList.d.ts +7 -0
- package/dist/Components/Tabs/components/TabLinkList.js +34 -0
- package/dist/Components/Tabs/useTabAppearance.d.ts +17 -0
- package/dist/Components/Tabs/useTabAppearance.js +67 -0
- package/dist/Components/Tag/Tag.js +11 -1
- package/dist/Components/Tag/TagProps.d.ts +4 -0
- package/dist/Components/Tag/tagDot.d.ts +3 -0
- package/dist/Components/Tag/tagDot.js +21 -0
- package/dist/esm/Components/AudioRecorder/AudioRecorder.js +161 -0
- package/dist/esm/Components/AudioRecorder/AudioRecorderProps.js +1 -0
- package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +38 -0
- package/dist/esm/Components/BulkSelectionBar/BulkSelectionBarProps.js +1 -0
- package/dist/esm/Components/BulkSelectionBar/index.js +1 -0
- package/dist/esm/Components/CalendarGrid/CalendarGrid.js +168 -0
- package/dist/esm/Components/CalendarGrid/CalendarGridProps.js +1 -0
- package/dist/esm/Components/CalendarGrid/calendarMath.js +155 -0
- package/dist/esm/Components/CircularProgress/CircularProgress.js +37 -0
- package/dist/esm/Components/CircularProgress/CircularProgressProps.js +1 -0
- package/dist/esm/Components/CircularProgress/arc.js +11 -0
- package/dist/esm/Components/Common/fieldStyles.js +2 -1
- package/dist/esm/Components/Drawer/Drawer.js +4 -2
- package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
- package/dist/esm/Components/FormDrawer/FormDrawer.js +58 -0
- package/dist/esm/Components/FormDrawer/FormDrawerProps.js +1 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +5 -1
- package/dist/esm/Components/Modal/Modal.js +4 -2
- package/dist/esm/Components/Modal/warnDroppedSlotChildren.js +38 -0
- package/dist/esm/Components/NavLauncher/LauncherTabs.js +58 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +270 -63
- package/dist/esm/Components/NavLauncher/NewTabLink.js +4 -3
- package/dist/esm/Components/NavLauncher/launcherParts.js +98 -14
- package/dist/esm/Components/NavLauncher/resultNavigation.js +61 -0
- package/dist/esm/Components/NavLauncher/revealStyles.js +2 -2
- package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +16 -7
- package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
- package/dist/esm/Components/QueryState/QueryStateBoundary.js +10 -4
- package/dist/esm/Components/SectionRail/SectionRail.js +44 -0
- package/dist/esm/Components/SectionRail/SectionRailProps.js +1 -0
- package/dist/esm/Components/SectionRail/index.js +1 -0
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +11 -16
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +0 -3
- package/dist/esm/Components/StatCard/StatCard.js +6 -2
- package/dist/esm/Components/StatusTag/StatusTag.js +19 -0
- package/dist/esm/Components/StatusTag/StatusTagProps.js +1 -0
- package/dist/esm/Components/Table/Table.js +7 -3
- package/dist/esm/Components/Table/components/TableBody.js +43 -18
- package/dist/esm/Components/Table/components/TableHeader.js +7 -5
- package/dist/esm/Components/Table/components/rowDetail.js +14 -0
- package/dist/esm/Components/Table/hooks/useRowExpansion.js +23 -0
- package/dist/esm/Components/Tabs/Tabs.js +15 -2
- package/dist/esm/Components/Tabs/components/Tab.js +4 -33
- package/dist/esm/Components/Tabs/components/TabLinkList.js +29 -0
- package/dist/esm/Components/Tabs/useTabAppearance.js +63 -0
- package/dist/esm/Components/Tag/Tag.js +12 -2
- package/dist/esm/Components/Tag/tagDot.js +17 -0
- package/dist/esm/index.js +7 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +18 -4
- package/package.json +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatFullDate = exports.formatTitle = exports.weekdayLabels = exports.moveFocus = exports.isNavKey = exports.NAV_KEYS = exports.layoutWeek = exports.eventsOnDay = exports.eventRange = exports.shiftAnchor = exports.visibleWeeks = exports.monthWeeks = exports.weekDays = exports.startOfWeek = exports.diffDays = exports.isWeekend = exports.dayKey = exports.isSameDay = exports.addMonths = exports.addDays = exports.startOfDay = void 0;
|
|
4
|
+
const MS_PER_DAY = 86400000;
|
|
5
|
+
const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
6
|
+
exports.startOfDay = startOfDay;
|
|
7
|
+
const addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
|
|
8
|
+
exports.addDays = addDays;
|
|
9
|
+
const addMonths = (d, n) => {
|
|
10
|
+
const target = new Date(d.getFullYear(), d.getMonth() + n, 1);
|
|
11
|
+
const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
|
|
12
|
+
return new Date(target.getFullYear(), target.getMonth(), Math.min(d.getDate(), lastDay));
|
|
13
|
+
};
|
|
14
|
+
exports.addMonths = addMonths;
|
|
15
|
+
const isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
16
|
+
exports.isSameDay = isSameDay;
|
|
17
|
+
const dayKey = (d) => `${String(d.getFullYear()).padStart(4, "0")}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
18
|
+
exports.dayKey = dayKey;
|
|
19
|
+
const isWeekend = (d) => d.getDay() === 0 || d.getDay() === 6;
|
|
20
|
+
exports.isWeekend = isWeekend;
|
|
21
|
+
/** Whole calendar days from `a` to `b`, counted on date components so a DST day is still one day. */
|
|
22
|
+
const diffDays = (a, b) => Math.round((Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()) - Date.UTC(a.getFullYear(), a.getMonth(), a.getDate())) /
|
|
23
|
+
MS_PER_DAY);
|
|
24
|
+
exports.diffDays = diffDays;
|
|
25
|
+
const startOfWeek = (d, weekStartsOn) => (0, exports.addDays)(d, -((d.getDay() - weekStartsOn + 7) % 7));
|
|
26
|
+
exports.startOfWeek = startOfWeek;
|
|
27
|
+
const weekDays = (d, weekStartsOn) => {
|
|
28
|
+
const first = (0, exports.startOfWeek)(d, weekStartsOn);
|
|
29
|
+
return Array.from({ length: 7 }, (_, i) => (0, exports.addDays)(first, i));
|
|
30
|
+
};
|
|
31
|
+
exports.weekDays = weekDays;
|
|
32
|
+
/** Whole weeks that cover the month of `d`, padded with the neighbouring months' days. */
|
|
33
|
+
const monthWeeks = (d, weekStartsOn) => {
|
|
34
|
+
const first = new Date(d.getFullYear(), d.getMonth(), 1);
|
|
35
|
+
const last = new Date(d.getFullYear(), d.getMonth() + 1, 0);
|
|
36
|
+
const gridStart = (0, exports.startOfWeek)(first, weekStartsOn);
|
|
37
|
+
const weekCount = Math.ceil(((0, exports.diffDays)(gridStart, last) + 1) / 7);
|
|
38
|
+
return Array.from({ length: weekCount }, (_, w) => Array.from({ length: 7 }, (_, i) => (0, exports.addDays)(gridStart, w * 7 + i)));
|
|
39
|
+
};
|
|
40
|
+
exports.monthWeeks = monthWeeks;
|
|
41
|
+
const visibleWeeks = (view, d, weekStartsOn) => {
|
|
42
|
+
if (view === "month")
|
|
43
|
+
return (0, exports.monthWeeks)(d, weekStartsOn);
|
|
44
|
+
if (view === "week")
|
|
45
|
+
return [(0, exports.weekDays)(d, weekStartsOn)];
|
|
46
|
+
return [[(0, exports.startOfDay)(d)]];
|
|
47
|
+
};
|
|
48
|
+
exports.visibleWeeks = visibleWeeks;
|
|
49
|
+
const shiftAnchor = (view, d, direction) => {
|
|
50
|
+
if (view === "month")
|
|
51
|
+
return (0, exports.addMonths)(d, direction);
|
|
52
|
+
return (0, exports.addDays)(d, view === "week" ? 7 * direction : direction);
|
|
53
|
+
};
|
|
54
|
+
exports.shiftAnchor = shiftAnchor;
|
|
55
|
+
const eventRange = (e) => {
|
|
56
|
+
const start = (0, exports.startOfDay)(e.start);
|
|
57
|
+
const end = e.end ? (0, exports.startOfDay)(e.end) : start;
|
|
58
|
+
return end < start ? { start: end, end: start } : { start, end };
|
|
59
|
+
};
|
|
60
|
+
exports.eventRange = eventRange;
|
|
61
|
+
const eventsOnDay = (events, day) => {
|
|
62
|
+
const d = (0, exports.startOfDay)(day);
|
|
63
|
+
return events.filter((e) => {
|
|
64
|
+
const r = (0, exports.eventRange)(e);
|
|
65
|
+
return r.start <= d && d <= r.end;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
exports.eventsOnDay = eventsOnDay;
|
|
69
|
+
/** Lays events out in lanes across one week row; lanes past `maxLanes` are counted per column, not drawn. */
|
|
70
|
+
const layoutWeek = (days, events, maxLanes) => {
|
|
71
|
+
const first = days[0];
|
|
72
|
+
const lastCol = days.length - 1;
|
|
73
|
+
const candidates = events
|
|
74
|
+
.map((event) => {
|
|
75
|
+
const r = (0, exports.eventRange)(event);
|
|
76
|
+
const rawStart = (0, exports.diffDays)(first, r.start);
|
|
77
|
+
const rawEnd = (0, exports.diffDays)(first, r.end);
|
|
78
|
+
if (rawEnd < 0 || rawStart > lastCol)
|
|
79
|
+
return null;
|
|
80
|
+
return {
|
|
81
|
+
event,
|
|
82
|
+
rawStart,
|
|
83
|
+
length: rawEnd - rawStart,
|
|
84
|
+
startCol: Math.max(0, rawStart),
|
|
85
|
+
endCol: Math.min(lastCol, rawEnd),
|
|
86
|
+
continuesBefore: rawStart < 0,
|
|
87
|
+
continuesAfter: rawEnd > lastCol,
|
|
88
|
+
};
|
|
89
|
+
})
|
|
90
|
+
.filter((c) => c !== null)
|
|
91
|
+
.sort((a, b) => a.rawStart - b.rawStart ||
|
|
92
|
+
b.length - a.length ||
|
|
93
|
+
a.event.label.localeCompare(b.event.label) ||
|
|
94
|
+
a.event.id.localeCompare(b.event.id));
|
|
95
|
+
const occupied = [];
|
|
96
|
+
const segments = [];
|
|
97
|
+
for (const c of candidates) {
|
|
98
|
+
let lane = occupied.findIndex((row) => row.slice(c.startCol, c.endCol + 1).every((taken) => !taken));
|
|
99
|
+
if (lane === -1) {
|
|
100
|
+
lane = occupied.length;
|
|
101
|
+
occupied.push(new Array(days.length).fill(false));
|
|
102
|
+
}
|
|
103
|
+
for (let col = c.startCol; col <= c.endCol; col += 1)
|
|
104
|
+
occupied[lane][col] = true;
|
|
105
|
+
segments.push({
|
|
106
|
+
event: c.event,
|
|
107
|
+
startCol: c.startCol,
|
|
108
|
+
endCol: c.endCol,
|
|
109
|
+
lane,
|
|
110
|
+
continuesBefore: c.continuesBefore,
|
|
111
|
+
continuesAfter: c.continuesAfter,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const hiddenByCol = new Array(days.length).fill(0);
|
|
115
|
+
for (const s of segments) {
|
|
116
|
+
if (s.lane < maxLanes)
|
|
117
|
+
continue;
|
|
118
|
+
for (let col = s.startCol; col <= s.endCol; col += 1)
|
|
119
|
+
hiddenByCol[col] += 1;
|
|
120
|
+
}
|
|
121
|
+
return { segments, visibleLanes: Math.min(occupied.length, maxLanes), hiddenByCol };
|
|
122
|
+
};
|
|
123
|
+
exports.layoutWeek = layoutWeek;
|
|
124
|
+
exports.NAV_KEYS = [
|
|
125
|
+
"ArrowLeft",
|
|
126
|
+
"ArrowRight",
|
|
127
|
+
"ArrowUp",
|
|
128
|
+
"ArrowDown",
|
|
129
|
+
"Home",
|
|
130
|
+
"End",
|
|
131
|
+
"PageUp",
|
|
132
|
+
"PageDown",
|
|
133
|
+
];
|
|
134
|
+
const isNavKey = (key) => exports.NAV_KEYS.includes(key);
|
|
135
|
+
exports.isNavKey = isNavKey;
|
|
136
|
+
const moveFocus = (view, d, key, weekStartsOn) => {
|
|
137
|
+
const columns = view === "day" ? 1 : 7;
|
|
138
|
+
switch (key) {
|
|
139
|
+
case "ArrowLeft":
|
|
140
|
+
return (0, exports.addDays)(d, -1);
|
|
141
|
+
case "ArrowRight":
|
|
142
|
+
return (0, exports.addDays)(d, 1);
|
|
143
|
+
case "ArrowUp":
|
|
144
|
+
return (0, exports.addDays)(d, -columns);
|
|
145
|
+
case "ArrowDown":
|
|
146
|
+
return (0, exports.addDays)(d, columns);
|
|
147
|
+
case "Home":
|
|
148
|
+
return view === "day" ? d : (0, exports.startOfWeek)(d, weekStartsOn);
|
|
149
|
+
case "End":
|
|
150
|
+
return view === "day" ? d : (0, exports.addDays)((0, exports.startOfWeek)(d, weekStartsOn), 6);
|
|
151
|
+
case "PageUp":
|
|
152
|
+
return (0, exports.shiftAnchor)(view, d, -1);
|
|
153
|
+
default:
|
|
154
|
+
return (0, exports.shiftAnchor)(view, d, 1);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
exports.moveFocus = moveFocus;
|
|
158
|
+
const probe = (weekday) => new Date(2024, 0, 7 + weekday);
|
|
159
|
+
const weekdayLabels = (locale, weekStartsOn, style = "short") => {
|
|
160
|
+
const fmt = new Intl.DateTimeFormat(locale, { weekday: style });
|
|
161
|
+
return Array.from({ length: 7 }, (_, i) => fmt.format(probe((i + weekStartsOn) % 7)));
|
|
162
|
+
};
|
|
163
|
+
exports.weekdayLabels = weekdayLabels;
|
|
164
|
+
const formatTitle = (view, d, locale, weekStartsOn) => {
|
|
165
|
+
if (view === "month")
|
|
166
|
+
return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(d);
|
|
167
|
+
if (view === "day")
|
|
168
|
+
return new Intl.DateTimeFormat(locale, { weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
|
|
169
|
+
const days = (0, exports.weekDays)(d, weekStartsOn);
|
|
170
|
+
const opts = { day: "numeric", month: "short", year: "numeric" };
|
|
171
|
+
const fmt = new Intl.DateTimeFormat(locale, opts);
|
|
172
|
+
return typeof fmt.formatRange === "function"
|
|
173
|
+
? fmt.formatRange(days[0], days[6])
|
|
174
|
+
: `${fmt.format(days[0])} - ${fmt.format(days[6])}`;
|
|
175
|
+
};
|
|
176
|
+
exports.formatTitle = formatTitle;
|
|
177
|
+
const formatFullDate = (d, locale) => new Intl.DateTimeFormat(locale, { weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
|
|
178
|
+
exports.formatFullDate = formatFullDate;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CircularProgressProps } from "./CircularProgressProps";
|
|
2
|
+
/** Ring-shaped determinate progress with a centred label — for a linear bar use ProgressBar. */
|
|
3
|
+
declare const CircularProgress: ({ value, max, size, thickness, tone, label, "aria-label": ariaLabel, "data-testid": testId, }: CircularProgressProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export default CircularProgress;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("@chakra-ui/react");
|
|
5
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
6
|
+
const tone_1 = require("../../Theme/tokens/builders/tone");
|
|
7
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
8
|
+
const motion_2 = require("../../Theme/chakra/motion");
|
|
9
|
+
const arc_1 = require("./arc");
|
|
10
|
+
const Circle = (0, react_1.chakra)("circle");
|
|
11
|
+
const LABEL_FONT = {
|
|
12
|
+
sm: "0.625rem",
|
|
13
|
+
md: "0.75rem",
|
|
14
|
+
lg: "1rem",
|
|
15
|
+
};
|
|
16
|
+
const scaleFor = (colors, tone) => tone === "info" ? colors.primary : (0, tone_1.toneScale)(colors, tone);
|
|
17
|
+
/** Ring-shaped determinate progress with a centred label — for a linear bar use ProgressBar. */
|
|
18
|
+
const CircularProgress = ({ value, max = 100, size = "md", thickness, tone = "info", label, "aria-label": ariaLabel, "data-testid": testId, }) => {
|
|
19
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
20
|
+
const diameter = arc_1.SIZE_PX[size];
|
|
21
|
+
const stroke = thickness !== null && thickness !== void 0 ? thickness : arc_1.THICKNESS_PX[size];
|
|
22
|
+
const { radius, circumference, fraction, clamped, safeMax, dashOffset } = (0, arc_1.arcGeometry)(diameter, stroke, value, max);
|
|
23
|
+
const ink = (0, tone_1.resolveSoftTone)(colors, scaleFor(colors, tone), colors.background[50]).fg;
|
|
24
|
+
const center = diameter / 2;
|
|
25
|
+
const valueText = typeof label === "string" || typeof label === "number" ? String(label) : undefined;
|
|
26
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { role: "progressbar", "aria-label": ariaLabel, "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": safeMax, "aria-valuetext": valueText, "data-testid": testId, position: "relative", display: "inline-flex", flexShrink: 0, boxSize: `${diameter}px`, children: [(0, jsx_runtime_1.jsxs)("svg", { width: diameter, height: diameter, viewBox: `0 0 ${diameter} ${diameter}`, "aria-hidden": true, focusable: "false", children: [(0, jsx_runtime_1.jsx)(Circle, { cx: center, cy: center, r: radius, fill: "none", sx: { stroke: colors.backgroundColor.subtle, strokeWidth: stroke } }), (0, jsx_runtime_1.jsx)(Circle, { cx: center, cy: center, r: radius, fill: "none", "data-testid": testId ? `${testId}-arc` : undefined, sx: {
|
|
27
|
+
stroke: ink,
|
|
28
|
+
strokeWidth: stroke,
|
|
29
|
+
strokeLinecap: "round",
|
|
30
|
+
strokeDasharray: circumference,
|
|
31
|
+
strokeDashoffset: dashOffset,
|
|
32
|
+
strokeOpacity: fraction > 0 ? 1 : 0,
|
|
33
|
+
transform: "rotate(-90deg)",
|
|
34
|
+
transformOrigin: "50% 50%",
|
|
35
|
+
transition: (0, motion_1.transitionFor)(["stroke-dashoffset"]),
|
|
36
|
+
...(0, motion_2.reducedMotionSx)(),
|
|
37
|
+
} })] }), label !== undefined && label !== null && ((0, jsx_runtime_1.jsx)(react_1.Flex, { position: "absolute", inset: 0, align: "center", justify: "center", fontSize: LABEL_FONT[size], fontWeight: 600, lineHeight: 1, color: colors.header[900], sx: { fontVariantNumeric: "tabular-nums" }, children: label }))] }));
|
|
38
|
+
};
|
|
39
|
+
exports.default = CircularProgress;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Tone } from "../../Theme/tokens/builders/tone";
|
|
3
|
+
/** Diameter preset: sm 32px, md 48px, lg 72px. */
|
|
4
|
+
export type CircularProgressSize = "sm" | "md" | "lg";
|
|
5
|
+
/** Props for the SVG ring CircularProgress; the arc starts at 12 o'clock and fills clockwise. */
|
|
6
|
+
export interface CircularProgressProps {
|
|
7
|
+
/** Current progress, clamped to 0..max. */
|
|
8
|
+
value: number;
|
|
9
|
+
/** Upper bound of the range; default 100. */
|
|
10
|
+
max?: number;
|
|
11
|
+
/** Diameter preset (32 / 48 / 72px); default "md". */
|
|
12
|
+
size?: CircularProgressSize;
|
|
13
|
+
/** Ring stroke in px; defaults by size (4 / 5 / 7). */
|
|
14
|
+
thickness?: number;
|
|
15
|
+
/** Fill colour; default "info", which paints the brand primary rather than blue. */
|
|
16
|
+
tone?: Tone;
|
|
17
|
+
/** Centred content such as "7/10"; a string or number is also spoken as aria-valuetext. */
|
|
18
|
+
label?: ReactNode;
|
|
19
|
+
/** Required accessible name; the ring has no visible caption of its own. */
|
|
20
|
+
"aria-label": string;
|
|
21
|
+
"data-testid"?: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CircularProgressSize } from "./CircularProgressProps";
|
|
2
|
+
export declare const SIZE_PX: Record<CircularProgressSize, number>;
|
|
3
|
+
export declare const THICKNESS_PX: Record<CircularProgressSize, number>;
|
|
4
|
+
export declare const clampValue: (value: number, max: number) => number;
|
|
5
|
+
export declare const arcGeometry: (diameter: number, thickness: number, value: number, max: number) => {
|
|
6
|
+
radius: number;
|
|
7
|
+
circumference: number;
|
|
8
|
+
fraction: number;
|
|
9
|
+
clamped: number;
|
|
10
|
+
safeMax: number;
|
|
11
|
+
dashOffset: number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.arcGeometry = exports.clampValue = exports.THICKNESS_PX = exports.SIZE_PX = void 0;
|
|
4
|
+
exports.SIZE_PX = { sm: 32, md: 48, lg: 72 };
|
|
5
|
+
exports.THICKNESS_PX = { sm: 4, md: 5, lg: 7 };
|
|
6
|
+
const clampValue = (value, max) => Number.isFinite(value) ? Math.min(Math.max(value, 0), max) : 0;
|
|
7
|
+
exports.clampValue = clampValue;
|
|
8
|
+
const arcGeometry = (diameter, thickness, value, max) => {
|
|
9
|
+
const safeMax = max > 0 ? max : 100;
|
|
10
|
+
const clamped = (0, exports.clampValue)(value, safeMax);
|
|
11
|
+
const radius = Math.max((diameter - thickness) / 2, 0);
|
|
12
|
+
const circumference = 2 * Math.PI * radius;
|
|
13
|
+
const fraction = clamped / safeMax;
|
|
14
|
+
return { radius, circumference, fraction, clamped, safeMax, dashOffset: circumference * (1 - fraction) };
|
|
15
|
+
};
|
|
16
|
+
exports.arcGeometry = arcGeometry;
|
|
@@ -4,6 +4,7 @@ import type { PaletteProps } from "../../Theme/tokens/types";
|
|
|
4
4
|
import { type ControlSize } from "../../Theme/tokens/primitives/controlSizes";
|
|
5
5
|
export declare const FIELD_RADIUS = "lg";
|
|
6
6
|
export declare const FIELD_BORDER_WIDTH = "1px";
|
|
7
|
+
/** No box-shadow: the focus ring must be present the frame focus lands, not fade in. */
|
|
7
8
|
export declare const FIELD_TRANSITION: string;
|
|
8
9
|
/** Chakra `h`/`fontSize`/`px` for a field or select of this size. */
|
|
9
10
|
export declare const controlSizeStyles: (size: ControlSize) => {
|
|
@@ -8,7 +8,8 @@ const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
|
8
8
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
9
9
|
exports.FIELD_RADIUS = "lg";
|
|
10
10
|
exports.FIELD_BORDER_WIDTH = "1px";
|
|
11
|
-
|
|
11
|
+
/** No box-shadow: the focus ring must be present the frame focus lands, not fade in. */
|
|
12
|
+
exports.FIELD_TRANSITION = (0, motion_1.transitionFor)(["border-color", "background-color"]);
|
|
12
13
|
/** Chakra `h`/`fontSize`/`px` for a field or select of this size. */
|
|
13
14
|
const controlSizeStyles = (size) => {
|
|
14
15
|
const spec = controlSizes_1.CONTROL_SIZES[size];
|
|
@@ -3,4 +3,4 @@ import { DrawerProps, DrawerHeaderSlotProps, DrawerBodySlotProps, DrawerFooterSl
|
|
|
3
3
|
export declare const DrawerHeader: React.FC<DrawerHeaderSlotProps>;
|
|
4
4
|
export declare const DrawerBody: React.FC<DrawerBodySlotProps>;
|
|
5
5
|
export declare const DrawerFooter: React.FC<DrawerFooterSlotProps>;
|
|
6
|
-
export default function Drawer({ isOpen, onClose, placement, size, id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, children, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default function Drawer({ isOpen, onClose, placement, size, id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, portalChildren, children, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -8,6 +8,7 @@ exports.default = Drawer;
|
|
|
8
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
9
|
const react_1 = __importDefault(require("react"));
|
|
10
10
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
11
|
+
const warnDroppedSlotChildren_1 = require("../Modal/warnDroppedSlotChildren");
|
|
11
12
|
const react_2 = require("@chakra-ui/react");
|
|
12
13
|
const DrawerHeader = ({ children }) => {
|
|
13
14
|
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
@@ -37,11 +38,12 @@ const findSlot = (children, slot) => {
|
|
|
37
38
|
}
|
|
38
39
|
return undefined;
|
|
39
40
|
};
|
|
40
|
-
function Drawer({ isOpen, onClose, placement = "right", size = "xl", id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, children, }) {
|
|
41
|
+
function Drawer({ isOpen, onClose, placement = "right", size = "xl", id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, portalChildren, children, }) {
|
|
41
42
|
var _a, _b;
|
|
42
43
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
43
44
|
const headerSlot = findSlot(children, exports.DrawerHeader);
|
|
44
45
|
const bodySlot = findSlot(children, exports.DrawerBody);
|
|
45
46
|
const footerSlot = findSlot(children, exports.DrawerFooter);
|
|
46
|
-
|
|
47
|
+
(0, warnDroppedSlotChildren_1.warnDroppedSlotChildren)("Drawer", children, [exports.DrawerHeader, exports.DrawerBody, exports.DrawerFooter]);
|
|
48
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Drawer, { isOpen: isOpen, placement: placement, onClose: onClose, size: size, variant: (_a = variant !== null && variant !== void 0 ? variant : vaiant) !== null && _a !== void 0 ? _a : "solid", id: id, initialFocusRef: initialFocusRef, finalFocusRef: finalFocusRef, closeOnOverlayClick: closeOnOverlayClick, closeOnEsc: closeOnEsc, children: [(0, jsx_runtime_1.jsx)(react_2.DrawerOverlay, {}), (0, jsx_runtime_1.jsxs)(react_2.DrawerContent, { ...contentProps, children: [!hideCloseButton && (0, jsx_runtime_1.jsx)(react_2.DrawerCloseButton, {}), headerSlot && ((0, jsx_runtime_1.jsx)(react_2.DrawerHeader, { borderBottomWidth: "1px", borderColor: colors.boxborder[200], ...headerSlot.props, children: headerSlot })), (0, jsx_runtime_1.jsx)(react_2.DrawerBody, { ...((_b = bodySlot === null || bodySlot === void 0 ? void 0 : bodySlot.props) !== null && _b !== void 0 ? _b : {}), children: bodySlot }), footerSlot && ((0, jsx_runtime_1.jsx)(react_2.DrawerFooter, { borderTopWidth: "1px", borderColor: colors.boxborder[200], ...footerSlot.props, children: footerSlot })), portalChildren] })] }));
|
|
47
49
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DrawerProps as ChakraDrawerProps, DrawerContentProps, ModalHeaderProps, ModalBodyProps, ModalFooterProps } from "@chakra-ui/react";
|
|
2
|
-
/** Only DrawerHeader/Body/Footer children render, matched by type one fragment deep; any other
|
|
2
|
+
/** Only DrawerHeader/Body/Footer children render, matched by type one fragment deep; any other child is dropped (dev console.warn) — use `portalChildren`. */
|
|
3
3
|
export type DrawerProps = Pick<ChakraDrawerProps, "isOpen" | "onClose" | "placement" | "size" | "id" | "variant" | "initialFocusRef" | "finalFocusRef" | "closeOnOverlayClick" | "closeOnEsc"> & {
|
|
4
4
|
/** Defaults to "xl". */
|
|
5
5
|
size?: "xs" | "sm" | "md" | "lg" | "xl" | "full";
|
|
@@ -12,6 +12,8 @@ export type DrawerProps = Pick<ChakraDrawerProps, "isOpen" | "onClose" | "placem
|
|
|
12
12
|
contentProps?: DrawerContentProps;
|
|
13
13
|
/** Drops the built-in close button, for a drawer dismissed only by its own footer action. */
|
|
14
14
|
hideCloseButton?: boolean;
|
|
15
|
+
/** Rendered inside the drawer's dialog after the slots (so live regions stay announced); nested dialogs portal out and keep focus here. Unmounts with the drawer. */
|
|
16
|
+
portalChildren?: React.ReactNode;
|
|
15
17
|
};
|
|
16
18
|
/** Chakra style props (padding, borderBottom, gap, sx, ...) spread onto the rendered DrawerHeader; omit to keep theme defaults. Chakra's Drawer reuses Modal's header part internally. */
|
|
17
19
|
export type DrawerHeaderSlotProps = ModalHeaderProps;
|
|
@@ -26,6 +26,7 @@ const scaleFor = (colors, scheme) => scheme === "primary" || scheme === "seconda
|
|
|
26
26
|
const EmptyState = ({ title = "No data found", titleAs = "h3", description, icon: IconComp = lucide_react_1.Inbox, image, imageAlt = "", size = "md", colorScheme = "primary", variant = "default", action, animate = false, minH = "15rem", sx, }) => {
|
|
27
27
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
28
28
|
const s = sizeMap[size];
|
|
29
|
+
const hasDescription = description !== undefined && description !== null && description !== false && description !== "";
|
|
29
30
|
const palette = scaleFor(theme.colors, colorScheme);
|
|
30
31
|
const tone = (0, tone_1.resolveSoftTone)(theme.colors, palette, theme.colors.background[50]);
|
|
31
32
|
const accentSoft = tone.bg;
|
|
@@ -48,6 +49,6 @@ const EmptyState = ({ title = "No data found", titleAs = "h3", description, icon
|
|
|
48
49
|
bg: accentSoft,
|
|
49
50
|
opacity: HALO_OPACITY,
|
|
50
51
|
zIndex: -1,
|
|
51
|
-
}, sx: (0, motion_1.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(IconComp, { size: s.icon, strokeWidth: 1.75, "aria-hidden": true }) })), (0, jsx_runtime_1.jsxs)(react_1.Box, { children: [(0, jsx_runtime_1.jsx)(react_1.Text, { as: titleAs, fontSize: s.title, fontWeight: 600, color: theme.colors.header[900], mt: 1, children: title }),
|
|
52
|
+
}, sx: (0, motion_1.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(IconComp, { size: s.icon, strokeWidth: 1.75, "aria-hidden": true }) })), (0, jsx_runtime_1.jsxs)(react_1.Box, { children: [(0, jsx_runtime_1.jsx)(react_1.Text, { as: titleAs, fontSize: s.title, fontWeight: 600, color: theme.colors.header[900], mt: 1, children: title }), hasDescription && ((0, jsx_runtime_1.jsx)(react_1.Text, { as: typeof description === "string" ? undefined : "div", fontSize: s.desc, color: theme.colors.textMuted, mt: 1, maxW: "23.75rem", children: description }))] }), action && ((0, jsx_runtime_1.jsx)(react_1.Flex, { gap: 3, mt: 2, wrap: "wrap", justify: "center", children: action }))] }));
|
|
52
53
|
};
|
|
53
54
|
exports.default = EmptyState;
|
|
@@ -10,8 +10,8 @@ export interface EmptyStateProps {
|
|
|
10
10
|
title?: string;
|
|
11
11
|
/** Heading element for the title; defaults to "h3" — pick the level that fits the page outline. */
|
|
12
12
|
titleAs?: "h2" | "h3" | "h4" | "h5" | "h6" | "p";
|
|
13
|
-
/** Supporting
|
|
14
|
-
description?:
|
|
13
|
+
/** Supporting text under the title; a node (bold, link) is wrapped in the same muted block and renders above `action`. */
|
|
14
|
+
description?: ReactNode;
|
|
15
15
|
/** A lucide-react (or any) icon component rendered inside the badge. */
|
|
16
16
|
icon?: ElementType;
|
|
17
17
|
/** Illustration image URL. When provided it replaces the icon badge. */
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { FormDrawerProps } from "./FormDrawerProps";
|
|
2
|
+
export default function FormDrawer({ isOpen, onClose, title, description, sections, errors, isDirty, onSubmit, isSubmitting, submitLabel, cancelLabel, secondaryAction, size, labels, "data-testid": testId, }: FormDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = FormDrawer;
|
|
40
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
|
+
const react_1 = require("react");
|
|
42
|
+
const react_2 = require("@chakra-ui/react");
|
|
43
|
+
const Drawer_1 = __importStar(require("../Drawer/Drawer"));
|
|
44
|
+
const AlertDialog_1 = __importDefault(require("../AlertDialog/AlertDialog"));
|
|
45
|
+
const Button_1 = __importDefault(require("../Button/Button"));
|
|
46
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
47
|
+
const DEFAULT_LABELS = {
|
|
48
|
+
fixErrors: "Please fix the following before saving",
|
|
49
|
+
discardTitle: "Discard changes?",
|
|
50
|
+
discardBody: "You have unsaved changes. If you close now, they will be lost.",
|
|
51
|
+
discard: "Discard",
|
|
52
|
+
keepEditing: "Keep editing",
|
|
53
|
+
};
|
|
54
|
+
function FormDrawer({ isOpen, onClose, title, description, sections, errors = [], isDirty, onSubmit, isSubmitting = false, submitLabel = "Save", cancelLabel = "Cancel", secondaryAction, size = "md", labels, "data-testid": testId = "form-drawer", }) {
|
|
55
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
56
|
+
const uid = (0, react_1.useId)();
|
|
57
|
+
const formId = `${uid}-form`;
|
|
58
|
+
const titleId = `${uid}-title`;
|
|
59
|
+
const text = { ...DEFAULT_LABELS, ...labels };
|
|
60
|
+
const [confirmDiscard, setConfirmDiscard] = (0, react_1.useState)(false);
|
|
61
|
+
const [attempt, setAttempt] = (0, react_1.useState)(0);
|
|
62
|
+
const keepEditingRef = (0, react_1.useRef)(null);
|
|
63
|
+
const summaryRef = (0, react_1.useRef)(null);
|
|
64
|
+
const focusPending = (0, react_1.useRef)(false);
|
|
65
|
+
(0, react_1.useEffect)(() => {
|
|
66
|
+
if (!isOpen)
|
|
67
|
+
setConfirmDiscard(false);
|
|
68
|
+
}, [isOpen]);
|
|
69
|
+
(0, react_1.useEffect)(() => {
|
|
70
|
+
var _a;
|
|
71
|
+
if (!focusPending.current || isSubmitting)
|
|
72
|
+
return;
|
|
73
|
+
focusPending.current = false;
|
|
74
|
+
if (errors.length)
|
|
75
|
+
(_a = summaryRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
76
|
+
}, [attempt, errors, isSubmitting]);
|
|
77
|
+
const requestClose = () => {
|
|
78
|
+
if (isSubmitting)
|
|
79
|
+
return;
|
|
80
|
+
if (isDirty)
|
|
81
|
+
setConfirmDiscard(true);
|
|
82
|
+
else
|
|
83
|
+
onClose();
|
|
84
|
+
};
|
|
85
|
+
const submit = (event) => {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
if (isSubmitting)
|
|
88
|
+
return;
|
|
89
|
+
focusPending.current = true;
|
|
90
|
+
setAttempt((n) => n + 1);
|
|
91
|
+
onSubmit();
|
|
92
|
+
};
|
|
93
|
+
return ((0, jsx_runtime_1.jsxs)(Drawer_1.default, { isOpen: isOpen, onClose: requestClose, size: size, closeOnEsc: !isSubmitting, closeOnOverlayClick: !isSubmitting, portalChildren: (0, jsx_runtime_1.jsx)(AlertDialog_1.default, { isOpen: confirmDiscard, onClose: () => setConfirmDiscard(false), leastDestructiveRef: keepEditingRef, isCentered: true, tone: "danger", title: text.discardTitle, content: (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", color: colors.text[800], children: text.discardBody }), cancelButtonText: text.keepEditing, confirmButtonText: text.discard, onConfirm: () => {
|
|
94
|
+
setConfirmDiscard(false);
|
|
95
|
+
onClose();
|
|
96
|
+
} }), children: [(0, jsx_runtime_1.jsxs)(Drawer_1.DrawerHeader, { children: [(0, jsx_runtime_1.jsx)(react_2.Heading, { id: titleId, as: "h2", fontSize: "md", fontWeight: 600, color: colors.header[900], children: title }), description ? ((0, jsx_runtime_1.jsx)(react_2.Text, { mt: 0.5, fontSize: "sm", fontWeight: 400, color: colors.textMuted, children: description })) : null] }), (0, jsx_runtime_1.jsx)(Drawer_1.DrawerBody, { children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: "form", id: formId, "aria-labelledby": titleId, onSubmit: submit, noValidate: true, "data-testid": testId, children: (0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", gap: 5, pt: 2, children: [errors.length ? ((0, jsx_runtime_1.jsxs)(react_2.Alert, { ref: summaryRef, tabIndex: -1, status: "error", variant: "subtle", borderRadius: "md", alignItems: "flex-start", "data-testid": `${testId}-errors`, children: [(0, jsx_runtime_1.jsx)(react_2.AlertIcon, {}), (0, jsx_runtime_1.jsxs)(react_2.Box, { children: [(0, jsx_runtime_1.jsx)(react_2.AlertTitle, { fontSize: "sm", children: text.fixErrors }), (0, jsx_runtime_1.jsx)(react_2.AlertDescription, { fontSize: "sm", children: (0, jsx_runtime_1.jsx)(react_2.UnorderedList, { mt: 1, children: errors.map((message) => ((0, jsx_runtime_1.jsx)(react_2.ListItem, { children: message }, message))) }) })] })] })) : null, sections.map((section, index) => ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [index > 0 ? (0, jsx_runtime_1.jsx)(react_2.Divider, { borderColor: colors.boxborder[200] }) : null, (0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "section", direction: "column", gap: 4, "aria-label": section.title, children: [section.title ? ((0, jsx_runtime_1.jsxs)(react_2.Box, { children: [(0, jsx_runtime_1.jsx)(react_2.Heading, { as: "h3", fontSize: "sm", fontWeight: 600, color: colors.header[900], children: section.title }), section.description ? ((0, jsx_runtime_1.jsx)(react_2.Text, { mt: 0.5, fontSize: "xs", color: colors.textMuted, children: section.description })) : null] })) : null, section.content] })] }, section.id)))] }) }) }), (0, jsx_runtime_1.jsx)(Drawer_1.DrawerFooter, { children: (0, jsx_runtime_1.jsxs)(react_2.Flex, { gap: 2, w: "100%", justify: "flex-end", wrap: "wrap", children: [secondaryAction, (0, jsx_runtime_1.jsx)(Button_1.default, { variant: "ghost", colorScheme: "gray", onClick: requestClose, isDisabled: isSubmitting, label: cancelLabel }), (0, jsx_runtime_1.jsx)(Button_1.default, { variant: "solid", colorScheme: "primary", type: "submit", form: formId, isLoading: isSubmitting, loadingText: submitLabel, label: submitLabel, "data-testid": `${testId}-submit` })] }) })] }));
|
|
97
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** One block of the form; sections after the first are separated by a divider. */
|
|
3
|
+
export type FormDrawerSection = {
|
|
4
|
+
/** Stable React key. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Renders an h3 above the content; also names the section region. */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Muted line under `title`; ignored without a title. */
|
|
9
|
+
description?: string;
|
|
10
|
+
content: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
/** Every user-facing string FormDrawer owns; pass the ones you localise. */
|
|
13
|
+
export type FormDrawerLabels = {
|
|
14
|
+
/** Heading of the error summary. Defaults to "Please fix the following before saving". */
|
|
15
|
+
fixErrors: string;
|
|
16
|
+
/** Defaults to "Discard changes?". */
|
|
17
|
+
discardTitle: string;
|
|
18
|
+
/** Defaults to "You have unsaved changes. If you close now, they will be lost.". */
|
|
19
|
+
discardBody: string;
|
|
20
|
+
/** Confirm button of the discard dialog. Defaults to "Discard". */
|
|
21
|
+
discard: string;
|
|
22
|
+
/** Cancel button of the discard dialog, focused on open. Defaults to "Keep editing". */
|
|
23
|
+
keepEditing: string;
|
|
24
|
+
};
|
|
25
|
+
/** Sectioned create/edit form in a Drawer: error summary, sticky footer, and a discard-changes confirm when closing while dirty. */
|
|
26
|
+
export interface FormDrawerProps {
|
|
27
|
+
isOpen: boolean;
|
|
28
|
+
/** Called once the user has really closed: clean close, or Discard confirmed. Never called while `isSubmitting`. */
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
/** Drawer heading; also the accessible name of the dialog and of the form. */
|
|
31
|
+
title: string;
|
|
32
|
+
/** Muted line under the title. */
|
|
33
|
+
description?: string;
|
|
34
|
+
sections: FormDrawerSection[];
|
|
35
|
+
/** Validation messages shown as a summary above the form; the summary takes focus after a submit that leaves any. */
|
|
36
|
+
errors?: string[];
|
|
37
|
+
/** Closing (X, Esc, overlay, Cancel) asks for confirmation while true. */
|
|
38
|
+
isDirty: boolean;
|
|
39
|
+
/** Fires on submit (button or Enter in a field); the browser's own validation is off, so validate in the handler. */
|
|
40
|
+
onSubmit: () => void;
|
|
41
|
+
/** Spins the submit button, disables Cancel and blocks every close path. Set it synchronously in `onSubmit` so error focus waits for the result. */
|
|
42
|
+
isSubmitting?: boolean;
|
|
43
|
+
/** Defaults to "Save". */
|
|
44
|
+
submitLabel?: string;
|
|
45
|
+
/** Defaults to "Cancel". */
|
|
46
|
+
cancelLabel?: string;
|
|
47
|
+
/** Rendered left of Cancel, e.g. a ghost "Save as draft" Button. */
|
|
48
|
+
secondaryAction?: ReactNode;
|
|
49
|
+
/** Drawer width. Defaults to "md". */
|
|
50
|
+
size?: "sm" | "md" | "lg";
|
|
51
|
+
/** Overrides for the built-in strings. */
|
|
52
|
+
labels?: Partial<FormDrawerLabels>;
|
|
53
|
+
/** Set on the form; the error summary and submit button get `-errors` / `-submit` suffixes. Defaults to "form-drawer". */
|
|
54
|
+
"data-testid"?: string;
|
|
55
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { LauncherTriggerProps } from "./LauncherTriggerProps";
|
|
2
2
|
export declare const keyShortcutsFor: (shortcut: string) => string;
|
|
3
|
-
declare const LauncherTrigger: ({ onClick, isOpen, label, shortcut, icon: Icon, "aria-label": ariaLabel, "data-testid": testId, }: LauncherTriggerProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
declare const LauncherTrigger: ({ onClick, isOpen, label, shortcut, icon: Icon, display, "aria-label": ariaLabel, "data-testid": testId, }: LauncherTriggerProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export default LauncherTrigger;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.keyShortcutsFor = void 0;
|
|
4
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
@@ -11,6 +14,7 @@ const motion_2 = require("../../Theme/tokens/primitives/motion");
|
|
|
11
14
|
const useMatchMedia_1 = require("../../Hooks/useMatchMedia");
|
|
12
15
|
const Kbd_1 = require("../Kbd/Kbd");
|
|
13
16
|
const AppSwitcherProps_1 = require("../AppSwitcher/AppSwitcherProps");
|
|
17
|
+
const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
|
|
14
18
|
const MD_QUERY = "(min-width: 48em)";
|
|
15
19
|
const KEY_NAMES = {
|
|
16
20
|
mod: ["Meta", "Control"],
|
|
@@ -31,11 +35,14 @@ const keyShortcutsFor = (shortcut) => {
|
|
|
31
35
|
return Array.from(new Set(chords)).join(" ");
|
|
32
36
|
};
|
|
33
37
|
exports.keyShortcutsFor = keyShortcutsFor;
|
|
34
|
-
const LauncherTrigger = ({ onClick, isOpen, label = "Menu", shortcut = "mod+k", icon: Icon = lucide_react_1.Compass, "aria-label": ariaLabel, "data-testid": testId, }) => {
|
|
38
|
+
const LauncherTrigger = ({ onClick, isOpen, label = "Menu", shortcut = "mod+k", icon: Icon = lucide_react_1.Compass, display = "chip", "aria-label": ariaLabel, "data-testid": testId, }) => {
|
|
35
39
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
36
40
|
const showHint = (0, useMatchMedia_1.useMatchMedia)(MD_QUERY, true);
|
|
37
41
|
const ring = (0, focusRing_styles_1.focusVisibleRing)(colors);
|
|
38
42
|
const hint = shortcut ? (0, AppSwitcherProps_1.formatShortcut)(shortcut, (0, Kbd_1.detectApple)()) : "";
|
|
43
|
+
if (display === "icon") {
|
|
44
|
+
return ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: hint ? `${label} (${hint})` : label, placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", "data-testid": testId, onClick: onClick, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : label, "aria-keyshortcuts": shortcut ? (0, exports.keyShortcutsFor)(shortcut) : undefined, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, w: "36px", h: "36px", borderRadius: "10px", borderWidth: "1px", borderColor: colors.primary[100], bg: colors.primary[50], color: colors.accentText, cursor: "pointer", transition: (0, motion_2.transitionFor)(["background-color", "border-color", "box-shadow"]), _hover: { borderColor: colors.primary[300] }, sx: { _focusVisible: ring, [focusRing_styles_1.FOCUS_VISIBLE_OVER_STATE]: ring, ...(0, motion_1.reducedMotionSx)() }, children: (0, jsx_runtime_1.jsx)(Icon, { size: 17, "aria-hidden": "true" }) }) }));
|
|
45
|
+
}
|
|
39
46
|
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { as: "button", type: "button", "data-testid": testId, onClick: onClick, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-label": ariaLabel, "aria-keyshortcuts": shortcut ? (0, exports.keyShortcutsFor)(shortcut) : undefined, display: "inline-flex", alignItems: "center", gap: 2, flexShrink: 0, h: "2.25rem", pl: "0.625rem", pr: "0.375rem", borderRadius: "10px", borderWidth: "1px", borderColor: colors.primary[100], bg: colors.primary[50], color: colors.accentText, fontSize: "0.84375rem", fontWeight: 600, lineHeight: 1, cursor: "pointer", transition: (0, motion_2.transitionFor)(["background-color", "border-color", "box-shadow"]), _hover: { borderColor: colors.primary[300] }, sx: { _focusVisible: ring, [focusRing_styles_1.FOCUS_VISIBLE_OVER_STATE]: ring, ...(0, motion_1.reducedMotionSx)() }, children: [(0, jsx_runtime_1.jsxs)(react_1.Box, { as: "span", display: "inline-flex", alignItems: "center", gap: 2, "aria-hidden": ariaLabel ? true : undefined, children: [(0, jsx_runtime_1.jsx)(Icon, { size: 17, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: label })] }), hint && showHint && ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "kbd", "aria-hidden": "true", fontFamily: "inherit", fontSize: "0.6875rem", fontWeight: 600, lineHeight: "normal", px: "6px", py: "2px", borderRadius: "6px", borderWidth: "1px", borderColor: colors.primary[100], bg: colors.surfaceRaised, children: hint }))] }));
|
|
40
47
|
};
|
|
41
48
|
exports.default = LauncherTrigger;
|
|
@@ -10,6 +10,8 @@ export interface LauncherTriggerProps {
|
|
|
10
10
|
shortcut?: string | null;
|
|
11
11
|
/** Component rendered at 17px before the label. Default lucide `Compass`. */
|
|
12
12
|
icon?: ElementType;
|
|
13
|
+
/** `"chip"` shows icon, label and chord hint; `"icon"` is a 36px square whose label is the accessible name and tooltip. Default "chip". */
|
|
14
|
+
display?: "chip" | "icon";
|
|
13
15
|
/** Overrides the accessible name; the visible label and hint then become `aria-hidden`. */
|
|
14
16
|
"aria-label"?: string;
|
|
15
17
|
"data-testid"?: string;
|
|
@@ -3,4 +3,4 @@ import { ChakraModelProps, ModalHeaderSlotProps, ModalBodySlotProps, ModalFooter
|
|
|
3
3
|
export declare const ModalHeader: React.FC<ModalHeaderSlotProps>;
|
|
4
4
|
export declare const ModalBody: React.FC<ModalBodySlotProps>;
|
|
5
5
|
export declare const ModalFooter: React.FC<ModalFooterSlotProps>;
|
|
6
|
-
export default function Modal({ size, isOpen, onClose, overlaybg, overlaybackdropFilter, overlaybackdropInvert, overlaybackdropBlur, isCentered, finalFocusRef, blockScrollOnMount, initialFocusRef, closeOnOverlayClick, closeOnEsc, trapFocus, motionPreset, scrollBehavior, children, isLoading, title, subtitle, hideCloseButton, contentProps, }: ChakraModelProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default function Modal({ size, isOpen, onClose, overlaybg, overlaybackdropFilter, overlaybackdropInvert, overlaybackdropBlur, isCentered, finalFocusRef, blockScrollOnMount, initialFocusRef, closeOnOverlayClick, closeOnEsc, trapFocus, motionPreset, scrollBehavior, children, isLoading, title, subtitle, hideCloseButton, contentProps, portalChildren, }: ChakraModelProps): import("react/jsx-runtime").JSX.Element;
|