@thangnqdev/ui 0.1.2 → 0.2.1
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/LICENSE +21 -0
- package/README.md +54 -206
- package/bin/cli.js +125 -0
- package/dist/bento/index.cjs.map +1 -1
- package/dist/bento/index.js +2 -2
- package/dist/bento/styles.css +2 -46
- package/dist/buttons/index.cjs +111 -18
- package/dist/buttons/index.cjs.map +1 -1
- package/dist/buttons/index.d.cts +1 -1
- package/dist/buttons/index.d.ts +1 -1
- package/dist/buttons/index.js +3 -3
- package/dist/buttons/styles.css +2 -6
- package/dist/{chunk-4S6L5V7X.js → chunk-2YJBW4E2.js} +2 -2
- package/dist/chunk-2YJBW4E2.js.map +1 -0
- package/dist/{chunk-F5Q7BTMH.js → chunk-4CRFNALF.js} +1011 -556
- package/dist/chunk-4CRFNALF.js.map +1 -0
- package/dist/{chunk-LWFRUJRU.js → chunk-4MS6ZHFQ.js} +225 -41
- package/dist/chunk-4MS6ZHFQ.js.map +1 -0
- package/dist/{chunk-2OXZK6IP.js → chunk-77KTYOMQ.js} +3 -3
- package/dist/chunk-77KTYOMQ.js.map +1 -0
- package/dist/{chunk-IABPUCAQ.js → chunk-ADO3SVUA.js} +2 -2
- package/dist/chunk-ADO3SVUA.js.map +1 -0
- package/dist/{chunk-GXI2H5XA.js → chunk-D7FB3TCA.js} +3 -3
- package/dist/chunk-D7FB3TCA.js.map +1 -0
- package/dist/chunk-DSEL7ELZ.js +314 -0
- package/dist/chunk-DSEL7ELZ.js.map +1 -0
- package/dist/{chunk-KJRBRTQE.js → chunk-EXFR2BFJ.js} +37 -28
- package/dist/chunk-EXFR2BFJ.js.map +1 -0
- package/dist/{chunk-PF4ZHGVW.js → chunk-GXWB6BSY.js} +116 -23
- package/dist/chunk-GXWB6BSY.js.map +1 -0
- package/dist/{chunk-66VWUJEI.js → chunk-I2FQBMVW.js} +3 -3
- package/dist/chunk-I2FQBMVW.js.map +1 -0
- package/dist/{chunk-EU2LPUYP.js → chunk-I5O2WCGF.js} +3 -3
- package/dist/chunk-I5O2WCGF.js.map +1 -0
- package/dist/{chunk-ATYGDTMK.js → chunk-ME4XILG5.js} +5 -5
- package/dist/chunk-ME4XILG5.js.map +1 -0
- package/dist/{chunk-5FZ6ZFII.js → chunk-PC5SZFZC.js} +373 -150
- package/dist/chunk-PC5SZFZC.js.map +1 -0
- package/dist/{chunk-C4SUXMI3.js → chunk-S6XJMYME.js} +3 -3
- package/dist/chunk-S6XJMYME.js.map +1 -0
- package/dist/{chunk-A7P3O5Z5.js → chunk-V52JOSDF.js} +3 -3
- package/dist/chunk-V52JOSDF.js.map +1 -0
- package/dist/{chunk-NFVONJUK.js → chunk-W5WNWTQC.js} +455 -211
- package/dist/chunk-W5WNWTQC.js.map +1 -0
- package/dist/{chunk-WAFHC4DQ.js → chunk-YJ64RI7J.js} +3 -3
- package/dist/chunk-YJ64RI7J.js.map +1 -0
- package/dist/chunk-YOCGBORV.js +155 -0
- package/dist/chunk-YOCGBORV.js.map +1 -0
- package/dist/chunk-ZEDXARXW.js +96 -0
- package/dist/chunk-ZEDXARXW.js.map +1 -0
- package/dist/components.css +2 -0
- package/dist/core/index.cjs +33 -24
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/styles.css +2 -134
- package/dist/data-display/index.cjs +770 -247
- package/dist/data-display/index.cjs.map +1 -1
- package/dist/data-display/index.d.cts +1 -1
- package/dist/data-display/index.d.ts +1 -1
- package/dist/data-display/index.js +5 -4
- package/dist/data-display/styles.css +2 -4
- package/dist/feedback/index.cjs.map +1 -1
- package/dist/feedback/index.js +2 -2
- package/dist/feedback/styles.css +2 -4
- package/dist/forms/index.cjs +1211 -459
- package/dist/forms/index.cjs.map +1 -1
- package/dist/forms/index.d.cts +1 -1
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.js +8 -8
- package/dist/forms/styles.css +2 -6
- package/dist/glass/index.cjs.map +1 -1
- package/dist/glass/index.js +2 -2
- package/dist/glass/styles.css +2 -81
- package/dist/{index-D6fE7w-b.d.cts → index-BGt_hecO.d.cts} +1 -0
- package/dist/{index-D6fE7w-b.d.ts → index-BGt_hecO.d.ts} +1 -0
- package/dist/{index-DzcWh1Hs.d.cts → index-BtDvpZCf.d.cts} +7 -0
- package/dist/{index-DzcWh1Hs.d.ts → index-BtDvpZCf.d.ts} +7 -0
- package/dist/{index-Cle6QzQl.d.ts → index-COX1sBUN.d.ts} +27 -3
- package/dist/{index-BkRqGNG1.d.ts → index-Cg8MuP7u.d.cts} +40 -13
- package/dist/{index-BkRqGNG1.d.cts → index-Cg8MuP7u.d.ts} +40 -13
- package/dist/{index-CenuF_P2.d.ts → index-DHkPRTvE.d.cts} +39 -8
- package/dist/{index-CenuF_P2.d.cts → index-DHkPRTvE.d.ts} +39 -8
- package/dist/{index-TyK-Ldpy.d.cts → index-Du_oht9k.d.cts} +27 -3
- package/dist/index.cjs +2481 -917
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +19 -19
- package/dist/layout/index.cjs.map +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/styles.css +2 -8
- package/dist/navigation/index.cjs +288 -48
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +1 -1
- package/dist/navigation/index.d.ts +1 -1
- package/dist/navigation/index.js +6 -6
- package/dist/navigation/styles.css +2 -4
- package/dist/overlays/index.cjs +566 -229
- package/dist/overlays/index.cjs.map +1 -1
- package/dist/overlays/index.d.cts +1 -1
- package/dist/overlays/index.d.ts +1 -1
- package/dist/overlays/index.js +7 -7
- package/dist/overlays/styles.css +2 -8
- package/dist/reset.css +45 -0
- package/dist/styles.css +1 -1
- package/dist/tokens.css +11 -0
- package/dist/typography/index.cjs.map +1 -1
- package/dist/typography/index.js +4 -4
- package/dist/typography/styles.css +2 -5
- package/llms.txt +211 -113
- package/package.json +167 -116
- package/scripts/postinstall.js +125 -0
- package/src/bento/BentoCard.tsx +140 -140
- package/src/bento/BentoGrid.tsx +50 -50
- package/src/bento/index.ts +2 -2
- package/src/bento/tokens.css +46 -46
- package/src/buttons/Button.tsx +104 -104
- package/src/buttons/ButtonGroup.tsx +43 -43
- package/src/buttons/IconButton.tsx +79 -79
- package/src/buttons/SplitButton.tsx +186 -86
- package/src/buttons/ToggleButton.tsx +157 -157
- package/src/buttons/index.ts +5 -5
- package/src/buttons/tokens.css +6 -6
- package/src/core/ThemeProvider.tsx +135 -123
- package/src/core/index.ts +2 -2
- package/src/core/tokens.css +134 -134
- package/src/data-display/Accordion.tsx +147 -147
- package/src/data-display/Avatar.tsx +134 -134
- package/src/data-display/Card.tsx +112 -112
- package/src/data-display/Chip.tsx +74 -74
- package/src/data-display/DataTablePro.tsx +299 -121
- package/src/data-display/StatCard.tsx +60 -60
- package/src/data-display/Table.tsx +74 -74
- package/src/data-display/Timeline.tsx +62 -62
- package/src/data-display/TreeView.tsx +243 -104
- package/src/data-display/tokens.css +4 -4
- package/src/feedback/Alert.tsx +80 -80
- package/src/feedback/EmptyState.tsx +45 -45
- package/src/feedback/Skeleton.tsx +66 -66
- package/src/feedback/Spinner.tsx +65 -65
- package/src/feedback/index.ts +5 -5
- package/src/feedback/tokens.css +4 -4
- package/src/forms/Checkbox.tsx +149 -69
- package/src/forms/CheckboxGroup.tsx +76 -29
- package/src/forms/Combobox.tsx +172 -28
- package/src/forms/DatePicker.tsx +385 -81
- package/src/forms/FileUpload.tsx +51 -7
- package/src/forms/FormField.tsx +115 -115
- package/src/forms/Input.tsx +151 -151
- package/src/forms/NumberInput.tsx +34 -20
- package/src/forms/PasswordInput.tsx +35 -35
- package/src/forms/PinInput.tsx +139 -125
- package/src/forms/Radio.tsx +142 -142
- package/src/forms/RangeSlider.tsx +237 -111
- package/src/forms/SearchInput.tsx +34 -34
- package/src/forms/Select.tsx +103 -103
- package/src/forms/Slider.tsx +85 -85
- package/src/forms/Textarea.tsx +114 -114
- package/src/forms/tokens.css +6 -6
- package/src/glass/GlassButton.tsx +59 -59
- package/src/glass/GlassCard.tsx +71 -71
- package/src/glass/index.ts +3 -3
- package/src/glass/tokens.css +81 -81
- package/src/index.ts +26 -26
- package/src/layout/AspectRatio.tsx +39 -39
- package/src/layout/Container.tsx +53 -53
- package/src/layout/Divider.tsx +61 -61
- package/src/layout/Flex.tsx +79 -79
- package/src/layout/Grid.tsx +124 -124
- package/src/layout/PageLayout.tsx +59 -59
- package/src/layout/Section.tsx +47 -47
- package/src/layout/Spacer.tsx +30 -30
- package/src/layout/Stack.tsx +88 -88
- package/src/layout/tokens.css +8 -8
- package/src/navigation/Breadcrumb.tsx +73 -73
- package/src/navigation/CommandPalette.tsx +142 -142
- package/src/navigation/DropdownMenu.tsx +244 -160
- package/src/navigation/Pagination.tsx +114 -114
- package/src/navigation/Stepper.tsx +93 -93
- package/src/navigation/Tabs.tsx +265 -133
- package/src/navigation/index.ts +6 -6
- package/src/navigation/tokens.css +4 -4
- package/src/overlays/AlertDialog.tsx +63 -54
- package/src/overlays/BottomSheet.tsx +30 -10
- package/src/overlays/ContextMenu.tsx +51 -9
- package/src/overlays/Drawer.tsx +160 -145
- package/src/overlays/FocusTrap.tsx +122 -49
- package/src/overlays/Modal.tsx +296 -219
- package/src/overlays/Popover.tsx +55 -25
- package/src/overlays/Portal.tsx +23 -23
- package/src/overlays/Toast.tsx +175 -137
- package/src/overlays/Tooltip.tsx +125 -100
- package/src/overlays/tokens.css +8 -8
- package/src/registry/components.ts +1368 -0
- package/src/styles/components.css +39 -0
- package/src/styles/index.css +2 -93
- package/src/styles/reset.css +45 -0
- package/src/styles/tokens.css +11 -2
- package/src/typography/Blockquote.tsx +32 -32
- package/src/typography/Code.tsx +89 -89
- package/src/typography/Heading.tsx +56 -56
- package/src/typography/Kbd.tsx +25 -25
- package/src/typography/Label.tsx +47 -47
- package/src/typography/Link.tsx +56 -56
- package/src/typography/Text.tsx +62 -62
- package/src/typography/index.ts +7 -7
- package/src/typography/tokens.css +5 -5
- package/src/utils/cn.ts +6 -6
- package/src/utils/scroll-lock.ts +19 -0
- package/src/utils/useControllableState.ts +54 -0
- package/ui-contract.json +192 -35
- package/dist/chunk-2OXZK6IP.js.map +0 -1
- package/dist/chunk-4S6L5V7X.js.map +0 -1
- package/dist/chunk-5FZ6ZFII.js.map +0 -1
- package/dist/chunk-66VWUJEI.js.map +0 -1
- package/dist/chunk-A7P3O5Z5.js.map +0 -1
- package/dist/chunk-ATYGDTMK.js.map +0 -1
- package/dist/chunk-C4SUXMI3.js.map +0 -1
- package/dist/chunk-EU2LPUYP.js.map +0 -1
- package/dist/chunk-F5Q7BTMH.js.map +0 -1
- package/dist/chunk-GXI2H5XA.js.map +0 -1
- package/dist/chunk-H3LCNPMC.js +0 -101
- package/dist/chunk-H3LCNPMC.js.map +0 -1
- package/dist/chunk-IABPUCAQ.js.map +0 -1
- package/dist/chunk-KJRBRTQE.js.map +0 -1
- package/dist/chunk-LBUBXUYQ.js +0 -40
- package/dist/chunk-LBUBXUYQ.js.map +0 -1
- package/dist/chunk-LWFRUJRU.js.map +0 -1
- package/dist/chunk-NFVONJUK.js.map +0 -1
- package/dist/chunk-PF4ZHGVW.js.map +0 -1
- package/dist/chunk-WAFHC4DQ.js.map +0 -1
- package/dist/chunk-X76KBH4Y.js +0 -71
- package/dist/chunk-X76KBH4Y.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -33,25 +33,32 @@ function ThemeProvider({
|
|
|
33
33
|
attribute = "data-theme",
|
|
34
34
|
enableSystem = true
|
|
35
35
|
}) {
|
|
36
|
-
const [theme, setThemeState] = React66.useState(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return defaultTheme;
|
|
45
|
-
});
|
|
46
|
-
const [resolvedTheme, setResolvedTheme] = React66.useState("dark");
|
|
47
|
-
React66.useEffect(() => {
|
|
48
|
-
const root = document.documentElement;
|
|
49
|
-
const computeResolved = (t) => {
|
|
36
|
+
const [theme, setThemeState] = React66.useState(defaultTheme);
|
|
37
|
+
const [resolvedTheme, setResolvedTheme] = React66.useState(
|
|
38
|
+
defaultTheme === "light" ? "light" : "dark"
|
|
39
|
+
);
|
|
40
|
+
const computeResolved = React66.useCallback(
|
|
41
|
+
(t) => {
|
|
50
42
|
if (t === "system" && enableSystem && typeof window !== "undefined" && window.matchMedia) {
|
|
51
43
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
52
44
|
}
|
|
53
45
|
return t === "dark" ? "dark" : "light";
|
|
54
|
-
}
|
|
46
|
+
},
|
|
47
|
+
[enableSystem]
|
|
48
|
+
);
|
|
49
|
+
React66.useEffect(() => {
|
|
50
|
+
if (typeof window === "undefined") return;
|
|
51
|
+
try {
|
|
52
|
+
const stored = window.localStorage?.getItem(storageKey);
|
|
53
|
+
if (stored && stored !== theme) {
|
|
54
|
+
setThemeState(stored);
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}, [storageKey, theme]);
|
|
59
|
+
React66.useEffect(() => {
|
|
60
|
+
if (typeof document === "undefined") return;
|
|
61
|
+
const root = document.documentElement;
|
|
55
62
|
const currentResolved = computeResolved(theme);
|
|
56
63
|
setResolvedTheme(currentResolved);
|
|
57
64
|
root.setAttribute(attribute, currentResolved);
|
|
@@ -68,20 +75,22 @@ function ThemeProvider({
|
|
|
68
75
|
} catch {
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
|
-
}, [theme, attribute,
|
|
78
|
+
}, [theme, attribute, computeResolved, storageKey]);
|
|
72
79
|
React66.useEffect(() => {
|
|
73
|
-
if (theme !== "system" || !enableSystem) return;
|
|
80
|
+
if (theme !== "system" || !enableSystem || typeof window === "undefined" || !window.matchMedia) return;
|
|
74
81
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
75
82
|
const handleChange = () => {
|
|
76
83
|
const newResolved = mediaQuery.matches ? "dark" : "light";
|
|
77
84
|
setResolvedTheme(newResolved);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
if (typeof document !== "undefined") {
|
|
86
|
+
document.documentElement.setAttribute(attribute, newResolved);
|
|
87
|
+
if (newResolved === "dark") {
|
|
88
|
+
document.documentElement.classList.add("dark");
|
|
89
|
+
document.documentElement.classList.remove("light");
|
|
90
|
+
} else {
|
|
91
|
+
document.documentElement.classList.add("light");
|
|
92
|
+
document.documentElement.classList.remove("dark");
|
|
93
|
+
}
|
|
85
94
|
}
|
|
86
95
|
};
|
|
87
96
|
mediaQuery.addEventListener("change", handleChange);
|
|
@@ -1207,9 +1216,24 @@ var ToggleGroup = React66.forwardRef(
|
|
|
1207
1216
|
);
|
|
1208
1217
|
ToggleGroup.displayName = "ToggleGroup";
|
|
1209
1218
|
var SplitButton = React66.forwardRef(
|
|
1210
|
-
({
|
|
1219
|
+
({
|
|
1220
|
+
className,
|
|
1221
|
+
items,
|
|
1222
|
+
onMainClick,
|
|
1223
|
+
variant = "primary",
|
|
1224
|
+
size = "md",
|
|
1225
|
+
children,
|
|
1226
|
+
disabled,
|
|
1227
|
+
menuAriaLabel = "More actions",
|
|
1228
|
+
...props
|
|
1229
|
+
}, ref) => {
|
|
1211
1230
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
1231
|
+
const [focusedIndex, setFocusedIndex] = React66.useState(0);
|
|
1212
1232
|
const containerRef = React66.useRef(null);
|
|
1233
|
+
const triggerBtnRef = React66.useRef(null);
|
|
1234
|
+
const menuRef = React66.useRef(null);
|
|
1235
|
+
const menuId = `split-button-menu-${React66.useId()}`;
|
|
1236
|
+
const enabledItemsIndices = items.map((item, idx) => !item.disabled ? idx : -1).filter((idx) => idx !== -1);
|
|
1213
1237
|
React66.useEffect(() => {
|
|
1214
1238
|
const handleOutside = (e) => {
|
|
1215
1239
|
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
@@ -1221,6 +1245,56 @@ var SplitButton = React66.forwardRef(
|
|
|
1221
1245
|
}
|
|
1222
1246
|
return () => document.removeEventListener("mousedown", handleOutside);
|
|
1223
1247
|
}, [isOpen]);
|
|
1248
|
+
React66.useEffect(() => {
|
|
1249
|
+
if (isOpen && enabledItemsIndices.length > 0) {
|
|
1250
|
+
setFocusedIndex(enabledItemsIndices[0]);
|
|
1251
|
+
const timer = setTimeout(() => {
|
|
1252
|
+
const itemEl = menuRef.current?.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
|
|
1253
|
+
itemEl?.focus();
|
|
1254
|
+
}, 10);
|
|
1255
|
+
return () => clearTimeout(timer);
|
|
1256
|
+
}
|
|
1257
|
+
}, [isOpen]);
|
|
1258
|
+
const handleMenuKeyDown = (e) => {
|
|
1259
|
+
if (!isOpen) return;
|
|
1260
|
+
if (e.key === "ArrowDown") {
|
|
1261
|
+
e.preventDefault();
|
|
1262
|
+
const currentPos = enabledItemsIndices.indexOf(focusedIndex);
|
|
1263
|
+
const nextPos = (currentPos + 1) % enabledItemsIndices.length;
|
|
1264
|
+
const nextIdx = enabledItemsIndices[nextPos];
|
|
1265
|
+
setFocusedIndex(nextIdx);
|
|
1266
|
+
const buttons = menuRef.current?.querySelectorAll('[role="menuitem"]');
|
|
1267
|
+
buttons?.[nextIdx]?.focus();
|
|
1268
|
+
} else if (e.key === "ArrowUp") {
|
|
1269
|
+
e.preventDefault();
|
|
1270
|
+
const currentPos = enabledItemsIndices.indexOf(focusedIndex);
|
|
1271
|
+
const prevPos = (currentPos - 1 + enabledItemsIndices.length) % enabledItemsIndices.length;
|
|
1272
|
+
const prevIdx = enabledItemsIndices[prevPos];
|
|
1273
|
+
setFocusedIndex(prevIdx);
|
|
1274
|
+
const buttons = menuRef.current?.querySelectorAll('[role="menuitem"]');
|
|
1275
|
+
buttons?.[prevIdx]?.focus();
|
|
1276
|
+
} else if (e.key === "Home") {
|
|
1277
|
+
e.preventDefault();
|
|
1278
|
+
if (enabledItemsIndices.length > 0) {
|
|
1279
|
+
const firstIdx = enabledItemsIndices[0];
|
|
1280
|
+
setFocusedIndex(firstIdx);
|
|
1281
|
+
const buttons = menuRef.current?.querySelectorAll('[role="menuitem"]');
|
|
1282
|
+
buttons?.[firstIdx]?.focus();
|
|
1283
|
+
}
|
|
1284
|
+
} else if (e.key === "End") {
|
|
1285
|
+
e.preventDefault();
|
|
1286
|
+
if (enabledItemsIndices.length > 0) {
|
|
1287
|
+
const lastIdx = enabledItemsIndices[enabledItemsIndices.length - 1];
|
|
1288
|
+
setFocusedIndex(lastIdx);
|
|
1289
|
+
const buttons = menuRef.current?.querySelectorAll('[role="menuitem"]');
|
|
1290
|
+
buttons?.[lastIdx]?.focus();
|
|
1291
|
+
}
|
|
1292
|
+
} else if (e.key === "Escape") {
|
|
1293
|
+
e.preventDefault();
|
|
1294
|
+
setIsOpen(false);
|
|
1295
|
+
triggerBtnRef.current?.focus();
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1224
1298
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative inline-flex", className), children: [
|
|
1225
1299
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: "inline-flex rounded-[var(--ui-radius-md)] shadow-sm", children: [
|
|
1226
1300
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1238,34 +1312,62 @@ var SplitButton = React66.forwardRef(
|
|
|
1238
1312
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1239
1313
|
Button,
|
|
1240
1314
|
{
|
|
1315
|
+
ref: triggerBtnRef,
|
|
1241
1316
|
variant,
|
|
1242
1317
|
size,
|
|
1243
1318
|
disabled,
|
|
1244
1319
|
onClick: () => setIsOpen(!isOpen),
|
|
1320
|
+
onKeyDown: (e) => {
|
|
1321
|
+
if (e.key === "ArrowDown") {
|
|
1322
|
+
e.preventDefault();
|
|
1323
|
+
setIsOpen(true);
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1245
1326
|
"aria-expanded": isOpen,
|
|
1246
1327
|
"aria-haspopup": "menu",
|
|
1247
|
-
|
|
1328
|
+
"aria-controls": isOpen ? menuId : void 0,
|
|
1329
|
+
"aria-label": menuAriaLabel,
|
|
1330
|
+
className: "rounded-l-none px-2 cursor-pointer",
|
|
1248
1331
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("w-4 h-4 transition-transform duration-200", isOpen && "rotate-180") })
|
|
1249
1332
|
}
|
|
1250
1333
|
)
|
|
1251
1334
|
] }),
|
|
1252
|
-
isOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1253
|
-
"
|
|
1335
|
+
isOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1336
|
+
"div",
|
|
1254
1337
|
{
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
className: "
|
|
1262
|
-
children:
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1338
|
+
ref: menuRef,
|
|
1339
|
+
id: menuId,
|
|
1340
|
+
role: "menu",
|
|
1341
|
+
"aria-orientation": "vertical",
|
|
1342
|
+
tabIndex: -1,
|
|
1343
|
+
onKeyDown: handleMenuKeyDown,
|
|
1344
|
+
className: "absolute top-full right-0 mt-1 w-48 rounded-[var(--ui-radius-md)] bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] shadow-lg py-1 z-50 animate-in fade-in zoom-in-95 focus:outline-none",
|
|
1345
|
+
children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1346
|
+
"button",
|
|
1347
|
+
{
|
|
1348
|
+
type: "button",
|
|
1349
|
+
role: "menuitem",
|
|
1350
|
+
tabIndex: focusedIndex === i ? 0 : -1,
|
|
1351
|
+
"aria-disabled": item.disabled,
|
|
1352
|
+
disabled: item.disabled,
|
|
1353
|
+
onClick: () => {
|
|
1354
|
+
item.onClick();
|
|
1355
|
+
setIsOpen(false);
|
|
1356
|
+
triggerBtnRef.current?.focus();
|
|
1357
|
+
},
|
|
1358
|
+
className: cn(
|
|
1359
|
+
"w-full flex items-center gap-2 px-3 py-2 text-left text-sm text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] focus:bg-[var(--ui-bg-tertiary)] focus:outline-none disabled:opacity-50 disabled:pointer-events-none transition-colors cursor-pointer",
|
|
1360
|
+
focusedIndex === i && "bg-[var(--ui-bg-tertiary)]"
|
|
1361
|
+
),
|
|
1362
|
+
children: [
|
|
1363
|
+
item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: item.icon }),
|
|
1364
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: item.label })
|
|
1365
|
+
]
|
|
1366
|
+
},
|
|
1367
|
+
i
|
|
1368
|
+
))
|
|
1369
|
+
}
|
|
1370
|
+
)
|
|
1269
1371
|
] });
|
|
1270
1372
|
}
|
|
1271
1373
|
);
|
|
@@ -1276,6 +1378,7 @@ var forms_exports = {};
|
|
|
1276
1378
|
__export(forms_exports, {
|
|
1277
1379
|
Checkbox: () => Checkbox,
|
|
1278
1380
|
CheckboxGroup: () => CheckboxGroup,
|
|
1381
|
+
CheckboxGroupContext: () => CheckboxGroupContext,
|
|
1279
1382
|
Combobox: () => Combobox,
|
|
1280
1383
|
CurrencyInput: () => CurrencyInput,
|
|
1281
1384
|
DatePicker: () => DatePicker,
|
|
@@ -1312,6 +1415,7 @@ __export(forms_exports, {
|
|
|
1312
1415
|
switchThumbVariants: () => switchThumbVariants,
|
|
1313
1416
|
switchVariants: () => switchVariants,
|
|
1314
1417
|
textareaVariants: () => textareaVariants,
|
|
1418
|
+
useCheckboxGroup: () => useCheckboxGroup,
|
|
1315
1419
|
useFormField: () => useFormField
|
|
1316
1420
|
});
|
|
1317
1421
|
var FormFieldContext = React66.createContext(null);
|
|
@@ -1710,8 +1814,59 @@ var Select = React66.forwardRef(
|
|
|
1710
1814
|
}
|
|
1711
1815
|
);
|
|
1712
1816
|
Select.displayName = "Select";
|
|
1817
|
+
var CheckboxGroupContext = React66.createContext(null);
|
|
1818
|
+
var useCheckboxGroup = () => React66.useContext(CheckboxGroupContext);
|
|
1819
|
+
var CheckboxGroup = React66.forwardRef(
|
|
1820
|
+
({
|
|
1821
|
+
className,
|
|
1822
|
+
orientation = "vertical",
|
|
1823
|
+
children,
|
|
1824
|
+
value: controlledValue,
|
|
1825
|
+
defaultValue = [],
|
|
1826
|
+
onChange,
|
|
1827
|
+
disabled,
|
|
1828
|
+
name,
|
|
1829
|
+
...props
|
|
1830
|
+
}, ref) => {
|
|
1831
|
+
const [internalValue, setInternalValue] = React66.useState(defaultValue);
|
|
1832
|
+
const isControlled = controlledValue !== void 0;
|
|
1833
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
1834
|
+
const handleValueChange = (nextValue) => {
|
|
1835
|
+
if (!isControlled) {
|
|
1836
|
+
setInternalValue(nextValue);
|
|
1837
|
+
}
|
|
1838
|
+
onChange?.(nextValue);
|
|
1839
|
+
};
|
|
1840
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1841
|
+
CheckboxGroupContext.Provider,
|
|
1842
|
+
{
|
|
1843
|
+
value: {
|
|
1844
|
+
value,
|
|
1845
|
+
onChange: handleValueChange,
|
|
1846
|
+
disabled,
|
|
1847
|
+
name
|
|
1848
|
+
},
|
|
1849
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1850
|
+
"div",
|
|
1851
|
+
{
|
|
1852
|
+
ref,
|
|
1853
|
+
role: "group",
|
|
1854
|
+
className: cn(
|
|
1855
|
+
"flex gap-2",
|
|
1856
|
+
orientation === "vertical" ? "flex-col" : "flex-row flex-wrap items-center",
|
|
1857
|
+
className
|
|
1858
|
+
),
|
|
1859
|
+
...props,
|
|
1860
|
+
children
|
|
1861
|
+
}
|
|
1862
|
+
)
|
|
1863
|
+
}
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
);
|
|
1867
|
+
CheckboxGroup.displayName = "CheckboxGroup";
|
|
1713
1868
|
var checkboxVariants = classVarianceAuthority.cva(
|
|
1714
|
-
"peer shrink-0 rounded-[var(--ui-radius-sm)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)]
|
|
1869
|
+
"peer shrink-0 rounded-[var(--ui-radius-sm)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] transition-all flex items-center justify-center text-[var(--ui-primary-fg)] peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--ui-primary)] peer-focus-visible:ring-offset-2",
|
|
1715
1870
|
{
|
|
1716
1871
|
variants: {
|
|
1717
1872
|
size: {
|
|
@@ -1726,61 +1881,96 @@ var checkboxVariants = classVarianceAuthority.cva(
|
|
|
1726
1881
|
}
|
|
1727
1882
|
);
|
|
1728
1883
|
var Checkbox = React66.forwardRef(
|
|
1729
|
-
({
|
|
1884
|
+
({
|
|
1885
|
+
className,
|
|
1886
|
+
size,
|
|
1887
|
+
indeterminate = false,
|
|
1888
|
+
label,
|
|
1889
|
+
checked,
|
|
1890
|
+
defaultChecked = false,
|
|
1891
|
+
disabled,
|
|
1892
|
+
id,
|
|
1893
|
+
name,
|
|
1894
|
+
value: propValue,
|
|
1895
|
+
onChange,
|
|
1896
|
+
onCheckedChange,
|
|
1897
|
+
...props
|
|
1898
|
+
}, ref) => {
|
|
1730
1899
|
const formField = useFormField();
|
|
1731
|
-
const
|
|
1900
|
+
const group = useCheckboxGroup();
|
|
1901
|
+
const isGroupMember = group !== null && propValue !== void 0;
|
|
1902
|
+
const isDisabled = disabled ?? group?.disabled ?? formField?.isDisabled ?? false;
|
|
1732
1903
|
const checkboxId = id || formField?.id;
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
label
|
|
1761
|
-
] });
|
|
1762
|
-
}
|
|
1763
|
-
);
|
|
1764
|
-
Checkbox.displayName = "Checkbox";
|
|
1765
|
-
var CheckboxGroup = React66.forwardRef(
|
|
1766
|
-
({ className, orientation = "vertical", children, value, onChange, ...props }, ref) => {
|
|
1767
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1768
|
-
"div",
|
|
1904
|
+
const checkboxName = name || group?.name;
|
|
1905
|
+
const isControlled = checked !== void 0;
|
|
1906
|
+
const [internalChecked, setInternalChecked] = React66.useState(defaultChecked);
|
|
1907
|
+
const isChecked = isGroupMember ? group.value.includes(String(propValue)) : isControlled ? Boolean(checked) : internalChecked;
|
|
1908
|
+
const innerRef = React66.useRef(null);
|
|
1909
|
+
React66.useImperativeHandle(ref, () => innerRef.current);
|
|
1910
|
+
React66.useEffect(() => {
|
|
1911
|
+
if (innerRef.current) {
|
|
1912
|
+
innerRef.current.indeterminate = Boolean(indeterminate);
|
|
1913
|
+
}
|
|
1914
|
+
}, [indeterminate]);
|
|
1915
|
+
const handleChange = (e) => {
|
|
1916
|
+
if (isDisabled) return;
|
|
1917
|
+
const newChecked = e.target.checked;
|
|
1918
|
+
if (!isControlled && !isGroupMember) {
|
|
1919
|
+
setInternalChecked(newChecked);
|
|
1920
|
+
}
|
|
1921
|
+
if (isGroupMember && group) {
|
|
1922
|
+
const val = String(propValue);
|
|
1923
|
+
const next = newChecked ? [...group.value, val] : group.value.filter((v) => v !== val);
|
|
1924
|
+
group.onChange(next);
|
|
1925
|
+
}
|
|
1926
|
+
onChange?.(e);
|
|
1927
|
+
onCheckedChange?.(newChecked);
|
|
1928
|
+
};
|
|
1929
|
+
const isVisualActive = isChecked || indeterminate;
|
|
1930
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1931
|
+
"label",
|
|
1769
1932
|
{
|
|
1770
|
-
ref,
|
|
1771
|
-
role: "group",
|
|
1772
1933
|
className: cn(
|
|
1773
|
-
"flex gap-2",
|
|
1774
|
-
|
|
1775
|
-
className
|
|
1934
|
+
"inline-flex items-center gap-2 select-none cursor-pointer group",
|
|
1935
|
+
isDisabled && "cursor-not-allowed opacity-50"
|
|
1776
1936
|
),
|
|
1777
|
-
|
|
1778
|
-
|
|
1937
|
+
children: [
|
|
1938
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
1939
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1940
|
+
"input",
|
|
1941
|
+
{
|
|
1942
|
+
ref: innerRef,
|
|
1943
|
+
type: "checkbox",
|
|
1944
|
+
id: checkboxId,
|
|
1945
|
+
name: checkboxName,
|
|
1946
|
+
value: propValue,
|
|
1947
|
+
disabled: isDisabled,
|
|
1948
|
+
checked: isChecked,
|
|
1949
|
+
onChange: handleChange,
|
|
1950
|
+
"aria-checked": indeterminate ? "mixed" : isChecked,
|
|
1951
|
+
className: "sr-only peer",
|
|
1952
|
+
...props
|
|
1953
|
+
}
|
|
1954
|
+
),
|
|
1955
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1956
|
+
"div",
|
|
1957
|
+
{
|
|
1958
|
+
className: cn(
|
|
1959
|
+
checkboxVariants({ size }),
|
|
1960
|
+
isVisualActive ? "bg-[var(--ui-primary)] border-[var(--ui-primary)] text-white" : "hover:border-[var(--ui-border-strong)]",
|
|
1961
|
+
className
|
|
1962
|
+
),
|
|
1963
|
+
children: indeterminate ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3.5 h-3.5 stroke-[3]" }) : isChecked && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 stroke-[3]" })
|
|
1964
|
+
}
|
|
1965
|
+
)
|
|
1966
|
+
] }),
|
|
1967
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-[var(--ui-text-primary)]", children: label })
|
|
1968
|
+
]
|
|
1779
1969
|
}
|
|
1780
1970
|
);
|
|
1781
1971
|
}
|
|
1782
1972
|
);
|
|
1783
|
-
|
|
1973
|
+
Checkbox.displayName = "Checkbox";
|
|
1784
1974
|
var RadioGroupContext = React66.createContext(null);
|
|
1785
1975
|
var radioVariants = classVarianceAuthority.cva(
|
|
1786
1976
|
"peer shrink-0 rounded-full border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] disabled:cursor-not-allowed disabled:opacity-50 transition-all cursor-pointer flex items-center justify-center",
|
|
@@ -2040,118 +2230,228 @@ var RangeSlider = React66.forwardRef(
|
|
|
2040
2230
|
showValue = false,
|
|
2041
2231
|
formatValue = (v) => `${v}`,
|
|
2042
2232
|
className,
|
|
2043
|
-
disabled = false
|
|
2044
|
-
}, ref) => {
|
|
2045
|
-
const [stateValue, setStateValue] = React66.useState(defaultValue);
|
|
2046
|
-
const [minVal, maxVal] = controlledValue || stateValue;
|
|
2047
|
-
const minPercentage = Math.min(100, Math.max(0, (minVal - min) / (max - min) * 100));
|
|
2048
|
-
const maxPercentage = Math.min(100, Math.max(0, (maxVal - min) / (max - min) * 100));
|
|
2049
|
-
const handleMinChange = (e) => {
|
|
2050
|
-
const val = Math.min(Number(e.target.value), maxVal - step);
|
|
2051
|
-
setStateValue([val, maxVal]);
|
|
2052
|
-
onChange?.([val, maxVal]);
|
|
2053
|
-
};
|
|
2054
|
-
const handleMaxChange = (e) => {
|
|
2055
|
-
const val = Math.max(Number(e.target.value), minVal + step);
|
|
2056
|
-
setStateValue([minVal, val]);
|
|
2057
|
-
onChange?.([minVal, val]);
|
|
2058
|
-
};
|
|
2059
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("w-full flex items-center gap-4", className), children: [
|
|
2060
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full flex items-center h-6 select-none touch-none", children: [
|
|
2061
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-2 rounded-full bg-[var(--ui-border-default)] overflow-hidden relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2062
|
-
"div",
|
|
2063
|
-
{
|
|
2064
|
-
className: "absolute h-full bg-[var(--ui-primary)]",
|
|
2065
|
-
style: {
|
|
2066
|
-
left: `${minPercentage}%`,
|
|
2067
|
-
width: `${maxPercentage - minPercentage}%`
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
) }),
|
|
2071
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2072
|
-
"input",
|
|
2073
|
-
{
|
|
2074
|
-
type: "range",
|
|
2075
|
-
min,
|
|
2076
|
-
max,
|
|
2077
|
-
step,
|
|
2078
|
-
value: minVal,
|
|
2079
|
-
disabled,
|
|
2080
|
-
onChange: handleMinChange,
|
|
2081
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-20"
|
|
2082
|
-
}
|
|
2083
|
-
),
|
|
2084
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2085
|
-
"input",
|
|
2086
|
-
{
|
|
2087
|
-
type: "range",
|
|
2088
|
-
min,
|
|
2089
|
-
max,
|
|
2090
|
-
step,
|
|
2091
|
-
value: maxVal,
|
|
2092
|
-
disabled,
|
|
2093
|
-
onChange: handleMaxChange,
|
|
2094
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-30"
|
|
2095
|
-
}
|
|
2096
|
-
),
|
|
2097
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2098
|
-
"div",
|
|
2099
|
-
{
|
|
2100
|
-
className: "absolute w-5 h-5 rounded-full bg-white border-2 border-[var(--ui-primary)] shadow-md pointer-events-none -translate-x-1/2 z-10",
|
|
2101
|
-
style: { left: `${minPercentage}%` }
|
|
2102
|
-
}
|
|
2103
|
-
),
|
|
2104
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2105
|
-
"div",
|
|
2106
|
-
{
|
|
2107
|
-
className: "absolute w-5 h-5 rounded-full bg-white border-2 border-[var(--ui-primary)] shadow-md pointer-events-none -translate-x-1/2 z-10",
|
|
2108
|
-
style: { left: `${maxPercentage}%` }
|
|
2109
|
-
}
|
|
2110
|
-
)
|
|
2111
|
-
] }),
|
|
2112
|
-
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
2113
|
-
formatValue(minVal),
|
|
2114
|
-
" - ",
|
|
2115
|
-
formatValue(maxVal)
|
|
2116
|
-
] })
|
|
2117
|
-
] });
|
|
2118
|
-
}
|
|
2119
|
-
);
|
|
2120
|
-
RangeSlider.displayName = "RangeSlider";
|
|
2121
|
-
var PinInput = React66.forwardRef(
|
|
2122
|
-
({
|
|
2123
|
-
className,
|
|
2124
|
-
length = 4,
|
|
2125
|
-
value: controlledValue,
|
|
2126
|
-
defaultValue = "",
|
|
2127
|
-
onChange,
|
|
2128
|
-
onComplete,
|
|
2129
|
-
mask = false,
|
|
2130
2233
|
disabled = false,
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
...props
|
|
2234
|
+
name,
|
|
2235
|
+
"aria-label": ariaLabel = "Range slider"
|
|
2134
2236
|
}, ref) => {
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2237
|
+
const isControlled = controlledValue !== void 0;
|
|
2238
|
+
const [internalValue, setInternalValue] = React66.useState(defaultValue);
|
|
2239
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
2240
|
+
const [minVal, maxVal] = [
|
|
2241
|
+
Math.max(min, Math.min(currentValue[0], currentValue[1])),
|
|
2242
|
+
Math.min(max, Math.max(currentValue[0], currentValue[1]))
|
|
2243
|
+
];
|
|
2244
|
+
const trackRef = React66.useRef(null);
|
|
2245
|
+
const draggingThumbRef = React66.useRef(null);
|
|
2246
|
+
const updateValues = React66.useCallback(
|
|
2247
|
+
(newMin, newMax) => {
|
|
2248
|
+
const clampedMin = Math.max(min, Math.min(newMin, newMax));
|
|
2249
|
+
const clampedMax = Math.min(max, Math.max(clampedMin, newMax));
|
|
2250
|
+
const next = [clampedMin, clampedMax];
|
|
2251
|
+
if (!isControlled) {
|
|
2252
|
+
setInternalValue(next);
|
|
2253
|
+
}
|
|
2254
|
+
onChange?.(next);
|
|
2255
|
+
},
|
|
2256
|
+
[min, max, isControlled, onChange]
|
|
2257
|
+
);
|
|
2258
|
+
const getValueFromPointer = React66.useCallback(
|
|
2259
|
+
(clientX) => {
|
|
2260
|
+
if (!trackRef.current) return min;
|
|
2261
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
2262
|
+
const percent = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
|
|
2263
|
+
const rawValue = min + percent * (max - min);
|
|
2264
|
+
const steppedValue = Math.round((rawValue - min) / step) * step + min;
|
|
2265
|
+
return Math.min(max, Math.max(min, Number(steppedValue.toFixed(4))));
|
|
2266
|
+
},
|
|
2267
|
+
[min, max, step]
|
|
2268
|
+
);
|
|
2269
|
+
const handlePointerDown = (e) => {
|
|
2270
|
+
if (disabled) return;
|
|
2271
|
+
const clickedVal = getValueFromPointer(e.clientX);
|
|
2272
|
+
const distToMin = Math.abs(clickedVal - minVal);
|
|
2273
|
+
const distToMax = Math.abs(clickedVal - maxVal);
|
|
2274
|
+
const targetThumb = distToMin <= distToMax ? "min" : "max";
|
|
2275
|
+
draggingThumbRef.current = targetThumb;
|
|
2276
|
+
if (targetThumb === "min") {
|
|
2277
|
+
updateValues(Math.min(clickedVal, maxVal), maxVal);
|
|
2278
|
+
} else {
|
|
2279
|
+
updateValues(minVal, Math.max(clickedVal, minVal));
|
|
2143
2280
|
}
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2281
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2282
|
+
};
|
|
2283
|
+
const handlePointerMove = (e) => {
|
|
2284
|
+
if (disabled || !draggingThumbRef.current) return;
|
|
2285
|
+
const moveVal = getValueFromPointer(e.clientX);
|
|
2286
|
+
if (draggingThumbRef.current === "min") {
|
|
2287
|
+
updateValues(Math.min(moveVal, maxVal), maxVal);
|
|
2288
|
+
} else {
|
|
2289
|
+
updateValues(minVal, Math.max(moveVal, minVal));
|
|
2148
2290
|
}
|
|
2149
|
-
}
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2291
|
+
};
|
|
2292
|
+
const handlePointerUp = (e) => {
|
|
2293
|
+
if (draggingThumbRef.current) {
|
|
2294
|
+
draggingThumbRef.current = null;
|
|
2295
|
+
try {
|
|
2296
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
const handleKeyDownMin = (e) => {
|
|
2302
|
+
if (disabled) return;
|
|
2303
|
+
let nextMin = minVal;
|
|
2304
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
2305
|
+
e.preventDefault();
|
|
2306
|
+
nextMin = Math.min(minVal + step, maxVal);
|
|
2307
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
2308
|
+
e.preventDefault();
|
|
2309
|
+
nextMin = Math.max(minVal - step, min);
|
|
2310
|
+
} else if (e.key === "Home") {
|
|
2311
|
+
e.preventDefault();
|
|
2312
|
+
nextMin = min;
|
|
2313
|
+
} else if (e.key === "End") {
|
|
2314
|
+
e.preventDefault();
|
|
2315
|
+
nextMin = maxVal;
|
|
2316
|
+
}
|
|
2317
|
+
if (nextMin !== minVal) {
|
|
2318
|
+
updateValues(nextMin, maxVal);
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
const handleKeyDownMax = (e) => {
|
|
2322
|
+
if (disabled) return;
|
|
2323
|
+
let nextMax = maxVal;
|
|
2324
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
2325
|
+
e.preventDefault();
|
|
2326
|
+
nextMax = Math.min(maxVal + step, max);
|
|
2327
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
2328
|
+
e.preventDefault();
|
|
2329
|
+
nextMax = Math.max(maxVal - step, minVal);
|
|
2330
|
+
} else if (e.key === "Home") {
|
|
2331
|
+
e.preventDefault();
|
|
2332
|
+
nextMax = minVal;
|
|
2333
|
+
} else if (e.key === "End") {
|
|
2334
|
+
e.preventDefault();
|
|
2335
|
+
nextMax = max;
|
|
2336
|
+
}
|
|
2337
|
+
if (nextMax !== maxVal) {
|
|
2338
|
+
updateValues(minVal, nextMax);
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
const minPercentage = Math.min(100, Math.max(0, (minVal - min) / (max - min || 1) * 100));
|
|
2342
|
+
const maxPercentage = Math.min(100, Math.max(0, (maxVal - min) / (max - min || 1) * 100));
|
|
2343
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2344
|
+
"div",
|
|
2345
|
+
{
|
|
2346
|
+
ref,
|
|
2347
|
+
className: cn("w-full flex items-center gap-4", disabled && "opacity-50 cursor-not-allowed", className),
|
|
2348
|
+
children: [
|
|
2349
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2350
|
+
"div",
|
|
2351
|
+
{
|
|
2352
|
+
ref: trackRef,
|
|
2353
|
+
onPointerDown: handlePointerDown,
|
|
2354
|
+
onPointerMove: handlePointerMove,
|
|
2355
|
+
onPointerUp: handlePointerUp,
|
|
2356
|
+
className: "relative w-full flex items-center h-7 select-none touch-none cursor-pointer",
|
|
2357
|
+
children: [
|
|
2358
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-2 rounded-full bg-[var(--ui-border-default)] overflow-hidden relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2359
|
+
"div",
|
|
2360
|
+
{
|
|
2361
|
+
className: "absolute h-full bg-[var(--ui-primary)]",
|
|
2362
|
+
style: {
|
|
2363
|
+
left: `${minPercentage}%`,
|
|
2364
|
+
width: `${Math.max(0, maxPercentage - minPercentage)}%`
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
) }),
|
|
2368
|
+
name && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2369
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${name}_min`, value: minVal }),
|
|
2370
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${name}_max`, value: maxVal })
|
|
2371
|
+
] }),
|
|
2372
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2373
|
+
"div",
|
|
2374
|
+
{
|
|
2375
|
+
role: "slider",
|
|
2376
|
+
tabIndex: disabled ? -1 : 0,
|
|
2377
|
+
"aria-label": `${ariaLabel} minimum`,
|
|
2378
|
+
"aria-valuemin": min,
|
|
2379
|
+
"aria-valuemax": maxVal,
|
|
2380
|
+
"aria-valuenow": minVal,
|
|
2381
|
+
"aria-disabled": disabled,
|
|
2382
|
+
onKeyDown: handleKeyDownMin,
|
|
2383
|
+
className: cn(
|
|
2384
|
+
"absolute w-5 h-5 rounded-full bg-white border-2 border-[var(--ui-primary)] shadow-md -translate-x-1/2 z-20 cursor-grab active:cursor-grabbing transition-shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] focus-visible:ring-offset-2",
|
|
2385
|
+
disabled && "pointer-events-none"
|
|
2386
|
+
),
|
|
2387
|
+
style: { left: `${minPercentage}%` }
|
|
2388
|
+
}
|
|
2389
|
+
),
|
|
2390
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2391
|
+
"div",
|
|
2392
|
+
{
|
|
2393
|
+
role: "slider",
|
|
2394
|
+
tabIndex: disabled ? -1 : 0,
|
|
2395
|
+
"aria-label": `${ariaLabel} maximum`,
|
|
2396
|
+
"aria-valuemin": minVal,
|
|
2397
|
+
"aria-valuemax": max,
|
|
2398
|
+
"aria-valuenow": maxVal,
|
|
2399
|
+
"aria-disabled": disabled,
|
|
2400
|
+
onKeyDown: handleKeyDownMax,
|
|
2401
|
+
className: cn(
|
|
2402
|
+
"absolute w-5 h-5 rounded-full bg-white border-2 border-[var(--ui-primary)] shadow-md -translate-x-1/2 z-20 cursor-grab active:cursor-grabbing transition-shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] focus-visible:ring-offset-2",
|
|
2403
|
+
disabled && "pointer-events-none"
|
|
2404
|
+
),
|
|
2405
|
+
style: { left: `${maxPercentage}%` }
|
|
2406
|
+
}
|
|
2407
|
+
)
|
|
2408
|
+
]
|
|
2409
|
+
}
|
|
2410
|
+
),
|
|
2411
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
2412
|
+
formatValue(minVal),
|
|
2413
|
+
" - ",
|
|
2414
|
+
formatValue(maxVal)
|
|
2415
|
+
] })
|
|
2416
|
+
]
|
|
2417
|
+
}
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
);
|
|
2421
|
+
RangeSlider.displayName = "RangeSlider";
|
|
2422
|
+
var PinInput = React66.forwardRef(
|
|
2423
|
+
({
|
|
2424
|
+
className,
|
|
2425
|
+
length = 4,
|
|
2426
|
+
value: controlledValue,
|
|
2427
|
+
defaultValue = "",
|
|
2428
|
+
onChange,
|
|
2429
|
+
onComplete,
|
|
2430
|
+
mask = false,
|
|
2431
|
+
disabled = false,
|
|
2432
|
+
isInvalid = false,
|
|
2433
|
+
autoFocus = false,
|
|
2434
|
+
...props
|
|
2435
|
+
}, ref) => {
|
|
2436
|
+
const isControlled = controlledValue !== void 0;
|
|
2437
|
+
const [internalPins, setInternalPins] = React66.useState(() => {
|
|
2438
|
+
const initial = (controlledValue !== void 0 ? controlledValue : defaultValue).slice(0, length);
|
|
2439
|
+
return Array.from({ length }, (_, i) => initial[i] || "");
|
|
2440
|
+
});
|
|
2441
|
+
const pins = isControlled ? Array.from({ length }, (_, i) => controlledValue[i] || "") : internalPins;
|
|
2442
|
+
const inputsRef = React66.useRef([]);
|
|
2443
|
+
React66.useEffect(() => {
|
|
2444
|
+
if (autoFocus && inputsRef.current[0]) {
|
|
2445
|
+
inputsRef.current[0].focus();
|
|
2446
|
+
}
|
|
2447
|
+
}, [autoFocus]);
|
|
2448
|
+
const handleDigitChange = (index, val) => {
|
|
2449
|
+
const sanitized = val.replace(/[^0-9a-zA-Z]/g, "").slice(-1);
|
|
2152
2450
|
const newPins = [...pins];
|
|
2153
2451
|
newPins[index] = sanitized;
|
|
2154
|
-
|
|
2452
|
+
if (!isControlled) {
|
|
2453
|
+
setInternalPins(newPins);
|
|
2454
|
+
}
|
|
2155
2455
|
const combined = newPins.join("");
|
|
2156
2456
|
onChange?.(combined);
|
|
2157
2457
|
if (sanitized && index < length - 1) {
|
|
@@ -2162,16 +2462,28 @@ var PinInput = React66.forwardRef(
|
|
|
2162
2462
|
}
|
|
2163
2463
|
};
|
|
2164
2464
|
const handleKeyDown = (index, e) => {
|
|
2165
|
-
if (e.key === "Backspace"
|
|
2465
|
+
if (e.key === "Backspace") {
|
|
2466
|
+
if (!pins[index] && index > 0) {
|
|
2467
|
+
const newPins = [...pins];
|
|
2468
|
+
newPins[index - 1] = "";
|
|
2469
|
+
if (!isControlled) setInternalPins(newPins);
|
|
2470
|
+
onChange?.(newPins.join(""));
|
|
2471
|
+
inputsRef.current[index - 1]?.focus();
|
|
2472
|
+
}
|
|
2473
|
+
} else if (e.key === "ArrowLeft" && index > 0) {
|
|
2474
|
+
e.preventDefault();
|
|
2166
2475
|
inputsRef.current[index - 1]?.focus();
|
|
2476
|
+
} else if (e.key === "ArrowRight" && index < length - 1) {
|
|
2477
|
+
e.preventDefault();
|
|
2478
|
+
inputsRef.current[index + 1]?.focus();
|
|
2167
2479
|
}
|
|
2168
2480
|
};
|
|
2169
2481
|
const handlePaste = (e) => {
|
|
2170
2482
|
e.preventDefault();
|
|
2171
|
-
const pasted = e.clipboardData.getData("text").slice(0, length);
|
|
2483
|
+
const pasted = e.clipboardData.getData("text").replace(/[^0-9a-zA-Z]/g, "").slice(0, length);
|
|
2172
2484
|
if (!pasted) return;
|
|
2173
2485
|
const newPins = Array.from({ length }, (_, i) => pasted[i] || "");
|
|
2174
|
-
|
|
2486
|
+
if (!isControlled) setInternalPins(newPins);
|
|
2175
2487
|
const combined = newPins.join("");
|
|
2176
2488
|
onChange?.(combined);
|
|
2177
2489
|
const nextEmptyIndex = newPins.findIndex((p) => !p);
|
|
@@ -2191,6 +2503,7 @@ var PinInput = React66.forwardRef(
|
|
|
2191
2503
|
inputMode: "numeric",
|
|
2192
2504
|
maxLength: 1,
|
|
2193
2505
|
disabled,
|
|
2506
|
+
"aria-label": `Digit ${index + 1} of ${length}`,
|
|
2194
2507
|
value: pins[index],
|
|
2195
2508
|
onChange: (e) => handleDigitChange(index, e.target.value),
|
|
2196
2509
|
onKeyDown: (e) => handleKeyDown(index, e),
|
|
@@ -2216,20 +2529,67 @@ function Portal({ children, container }) {
|
|
|
2216
2529
|
if (!target) return null;
|
|
2217
2530
|
return reactDom.createPortal(children, target);
|
|
2218
2531
|
}
|
|
2532
|
+
function useControllableState({
|
|
2533
|
+
value: controlledValue,
|
|
2534
|
+
defaultValue,
|
|
2535
|
+
onChange,
|
|
2536
|
+
name
|
|
2537
|
+
}) {
|
|
2538
|
+
const isControlled = controlledValue !== void 0;
|
|
2539
|
+
const isControlledRef = React66.useRef(isControlled);
|
|
2540
|
+
React66.useEffect(() => {
|
|
2541
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production" && isControlledRef.current !== isControlled) {
|
|
2542
|
+
console.warn(
|
|
2543
|
+
`[${name || "Component"}] is changing from ${isControlledRef.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}. Check the value/defaultValue props.`
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
isControlledRef.current = isControlled;
|
|
2547
|
+
}, [isControlled, name]);
|
|
2548
|
+
const [internalState, setInternalState] = React66.useState(defaultValue);
|
|
2549
|
+
const value = isControlled ? controlledValue : internalState;
|
|
2550
|
+
const setValue = React66.useCallback(
|
|
2551
|
+
(next) => {
|
|
2552
|
+
const nextValue = typeof next === "function" ? next(value) : next;
|
|
2553
|
+
if (!isControlled) {
|
|
2554
|
+
setInternalState(nextValue);
|
|
2555
|
+
}
|
|
2556
|
+
onChange?.(nextValue);
|
|
2557
|
+
},
|
|
2558
|
+
[isControlled, value, onChange]
|
|
2559
|
+
);
|
|
2560
|
+
return [value, setValue, isControlled];
|
|
2561
|
+
}
|
|
2219
2562
|
var Popover = ({
|
|
2220
2563
|
trigger,
|
|
2221
2564
|
children,
|
|
2222
|
-
|
|
2565
|
+
open: controlledOpen,
|
|
2566
|
+
isOpen: legacyIsOpen,
|
|
2567
|
+
defaultOpen = false,
|
|
2568
|
+
onOpenChange,
|
|
2223
2569
|
onClose,
|
|
2224
2570
|
placement = "bottom",
|
|
2225
2571
|
className
|
|
2226
2572
|
}) => {
|
|
2227
|
-
const
|
|
2228
|
-
const
|
|
2573
|
+
const activeControlled = controlledOpen !== void 0 ? controlledOpen : legacyIsOpen;
|
|
2574
|
+
const handleStateChange = React66.useCallback(
|
|
2575
|
+
(nextOpen) => {
|
|
2576
|
+
onOpenChange?.(nextOpen);
|
|
2577
|
+
if (!nextOpen) {
|
|
2578
|
+
onClose?.();
|
|
2579
|
+
}
|
|
2580
|
+
},
|
|
2581
|
+
[onOpenChange, onClose]
|
|
2582
|
+
);
|
|
2583
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
2584
|
+
value: activeControlled,
|
|
2585
|
+
defaultValue: defaultOpen,
|
|
2586
|
+
onChange: handleStateChange,
|
|
2587
|
+
name: "Popover"
|
|
2588
|
+
});
|
|
2229
2589
|
const [coords, setCoords] = React66.useState({ top: 0, left: 0 });
|
|
2230
2590
|
const triggerRef = React66.useRef(null);
|
|
2231
2591
|
const popoverRef = React66.useRef(null);
|
|
2232
|
-
const calculatePosition = () => {
|
|
2592
|
+
const calculatePosition = React66.useCallback(() => {
|
|
2233
2593
|
if (triggerRef.current) {
|
|
2234
2594
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
2235
2595
|
let top = 0;
|
|
@@ -2249,45 +2609,52 @@ var Popover = ({
|
|
|
2249
2609
|
}
|
|
2250
2610
|
setCoords({ top, left });
|
|
2251
2611
|
}
|
|
2252
|
-
};
|
|
2612
|
+
}, [placement]);
|
|
2253
2613
|
const handleToggle = () => {
|
|
2254
2614
|
if (!isOpen) {
|
|
2255
2615
|
calculatePosition();
|
|
2256
2616
|
}
|
|
2257
|
-
|
|
2258
|
-
if (isOpen) onClose?.();
|
|
2259
|
-
} else {
|
|
2260
|
-
setInternalIsOpen(!internalIsOpen);
|
|
2261
|
-
}
|
|
2617
|
+
setIsOpen(!isOpen);
|
|
2262
2618
|
};
|
|
2263
2619
|
React66.useEffect(() => {
|
|
2264
2620
|
if (isOpen) {
|
|
2265
2621
|
calculatePosition();
|
|
2266
2622
|
}
|
|
2267
|
-
}, [isOpen]);
|
|
2623
|
+
}, [isOpen, calculatePosition]);
|
|
2268
2624
|
React66.useEffect(() => {
|
|
2625
|
+
if (!isOpen) return;
|
|
2269
2626
|
const handleOutside = (e) => {
|
|
2270
2627
|
if (popoverRef.current && !popoverRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
2271
|
-
|
|
2272
|
-
onClose?.();
|
|
2273
|
-
} else {
|
|
2274
|
-
setInternalIsOpen(false);
|
|
2275
|
-
}
|
|
2628
|
+
setIsOpen(false);
|
|
2276
2629
|
}
|
|
2277
2630
|
};
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
}
|
|
2631
|
+
const handleKeyDown = (e) => {
|
|
2632
|
+
if (e.key === "Escape") {
|
|
2633
|
+
setIsOpen(false);
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
document.addEventListener("mousedown", handleOutside);
|
|
2637
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2638
|
+
window.addEventListener("resize", calculatePosition);
|
|
2639
|
+
window.addEventListener("scroll", calculatePosition);
|
|
2283
2640
|
return () => {
|
|
2284
2641
|
document.removeEventListener("mousedown", handleOutside);
|
|
2642
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
2285
2643
|
window.removeEventListener("resize", calculatePosition);
|
|
2286
2644
|
window.removeEventListener("scroll", calculatePosition);
|
|
2287
2645
|
};
|
|
2288
|
-
}, [isOpen,
|
|
2646
|
+
}, [isOpen, setIsOpen, calculatePosition]);
|
|
2289
2647
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2290
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2648
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2649
|
+
"div",
|
|
2650
|
+
{
|
|
2651
|
+
ref: triggerRef,
|
|
2652
|
+
onClick: handleToggle,
|
|
2653
|
+
className: "w-full inline-block cursor-pointer",
|
|
2654
|
+
"aria-expanded": isOpen,
|
|
2655
|
+
children: trigger
|
|
2656
|
+
}
|
|
2657
|
+
),
|
|
2291
2658
|
isOpen && /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2292
2659
|
"div",
|
|
2293
2660
|
{
|
|
@@ -2322,7 +2689,7 @@ function formatDisplayDate(d) {
|
|
|
2322
2689
|
var DatePicker = React66.forwardRef(
|
|
2323
2690
|
({
|
|
2324
2691
|
value: controlledValue,
|
|
2325
|
-
defaultValue,
|
|
2692
|
+
defaultValue = null,
|
|
2326
2693
|
onChange,
|
|
2327
2694
|
placeholder = "Select date...",
|
|
2328
2695
|
minDate,
|
|
@@ -2332,26 +2699,28 @@ var DatePicker = React66.forwardRef(
|
|
|
2332
2699
|
className,
|
|
2333
2700
|
presets
|
|
2334
2701
|
}, ref) => {
|
|
2335
|
-
const [
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2702
|
+
const [selectedDate, setSelectedDate] = useControllableState({
|
|
2703
|
+
value: controlledValue,
|
|
2704
|
+
defaultValue,
|
|
2705
|
+
onChange,
|
|
2706
|
+
name: "DatePicker"
|
|
2707
|
+
});
|
|
2339
2708
|
const [viewDate, setViewDate] = React66.useState(selectedDate || /* @__PURE__ */ new Date());
|
|
2340
2709
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
2710
|
+
const [focusedDate, setFocusedDate] = React66.useState(selectedDate || /* @__PURE__ */ new Date());
|
|
2341
2711
|
React66.useEffect(() => {
|
|
2342
2712
|
if (selectedDate) {
|
|
2343
2713
|
setViewDate(selectedDate);
|
|
2714
|
+
setFocusedDate(selectedDate);
|
|
2344
2715
|
}
|
|
2345
2716
|
}, [selectedDate]);
|
|
2346
2717
|
const handleSelectDate = (d) => {
|
|
2347
|
-
|
|
2348
|
-
onChange?.(d);
|
|
2718
|
+
setSelectedDate(d);
|
|
2349
2719
|
setIsOpen(false);
|
|
2350
2720
|
};
|
|
2351
2721
|
const handleClear = (e) => {
|
|
2352
2722
|
e.stopPropagation();
|
|
2353
|
-
|
|
2354
|
-
onChange?.(null);
|
|
2723
|
+
setSelectedDate(null);
|
|
2355
2724
|
};
|
|
2356
2725
|
const prevMonth = () => {
|
|
2357
2726
|
setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1));
|
|
@@ -2384,20 +2753,68 @@ var DatePicker = React66.forwardRef(
|
|
|
2384
2753
|
}
|
|
2385
2754
|
const todayStr = formatDate(/* @__PURE__ */ new Date());
|
|
2386
2755
|
const selectedStr = selectedDate ? formatDate(selectedDate) : "";
|
|
2756
|
+
const focusedStr = formatDate(focusedDate);
|
|
2757
|
+
const handleGridKeyDown = (e) => {
|
|
2758
|
+
let nextDate = new Date(focusedDate);
|
|
2759
|
+
let handled = false;
|
|
2760
|
+
if (e.key === "ArrowLeft") {
|
|
2761
|
+
nextDate.setDate(nextDate.getDate() - 1);
|
|
2762
|
+
handled = true;
|
|
2763
|
+
} else if (e.key === "ArrowRight") {
|
|
2764
|
+
nextDate.setDate(nextDate.getDate() + 1);
|
|
2765
|
+
handled = true;
|
|
2766
|
+
} else if (e.key === "ArrowUp") {
|
|
2767
|
+
nextDate.setDate(nextDate.getDate() - 7);
|
|
2768
|
+
handled = true;
|
|
2769
|
+
} else if (e.key === "ArrowDown") {
|
|
2770
|
+
nextDate.setDate(nextDate.getDate() + 7);
|
|
2771
|
+
handled = true;
|
|
2772
|
+
} else if (e.key === "PageUp") {
|
|
2773
|
+
nextDate.setMonth(nextDate.getMonth() - 1);
|
|
2774
|
+
handled = true;
|
|
2775
|
+
} else if (e.key === "PageDown") {
|
|
2776
|
+
nextDate.setMonth(nextDate.getMonth() + 1);
|
|
2777
|
+
handled = true;
|
|
2778
|
+
} else if (e.key === "Home") {
|
|
2779
|
+
nextDate.setDate(1);
|
|
2780
|
+
handled = true;
|
|
2781
|
+
} else if (e.key === "End") {
|
|
2782
|
+
const lastDay = new Date(nextDate.getFullYear(), nextDate.getMonth() + 1, 0).getDate();
|
|
2783
|
+
nextDate.setDate(lastDay);
|
|
2784
|
+
handled = true;
|
|
2785
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
2786
|
+
e.preventDefault();
|
|
2787
|
+
handleSelectDate(focusedDate);
|
|
2788
|
+
return;
|
|
2789
|
+
} else if (e.key === "Escape") {
|
|
2790
|
+
e.preventDefault();
|
|
2791
|
+
setIsOpen(false);
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (handled) {
|
|
2795
|
+
e.preventDefault();
|
|
2796
|
+
setFocusedDate(nextDate);
|
|
2797
|
+
if (nextDate.getMonth() !== viewDate.getMonth() || nextDate.getFullYear() !== viewDate.getFullYear()) {
|
|
2798
|
+
setViewDate(new Date(nextDate.getFullYear(), nextDate.getMonth(), 1));
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
};
|
|
2387
2802
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2388
2803
|
Popover,
|
|
2389
2804
|
{
|
|
2390
|
-
isOpen,
|
|
2391
|
-
|
|
2805
|
+
open: isOpen,
|
|
2806
|
+
onOpenChange: setIsOpen,
|
|
2392
2807
|
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2393
2808
|
"button",
|
|
2394
2809
|
{
|
|
2395
2810
|
type: "button",
|
|
2396
2811
|
disabled,
|
|
2397
|
-
|
|
2812
|
+
"aria-haspopup": "dialog",
|
|
2813
|
+
"aria-expanded": isOpen,
|
|
2814
|
+
"aria-label": selectedDate ? `Selected date: ${formatDisplayDate(selectedDate)}` : placeholder,
|
|
2398
2815
|
className: cn(
|
|
2399
|
-
"w-full h-10 px-3.5 rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)] text-left flex items-center justify-between text-sm transition-all shadow-inner cursor-pointer",
|
|
2400
|
-
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]
|
|
2816
|
+
"w-full h-10 px-3.5 rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)] text-left flex items-center justify-between text-sm transition-all shadow-inner cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
2817
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
2401
2818
|
),
|
|
2402
2819
|
children: [
|
|
2403
2820
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
@@ -2407,7 +2824,11 @@ var DatePicker = React66.forwardRef(
|
|
|
2407
2824
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: clearable && selectedDate && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2408
2825
|
"span",
|
|
2409
2826
|
{
|
|
2827
|
+
role: "button",
|
|
2828
|
+
tabIndex: 0,
|
|
2829
|
+
"aria-label": "Clear date",
|
|
2410
2830
|
onClick: handleClear,
|
|
2831
|
+
onKeyDown: (e) => e.key === "Enter" && handleClear(e),
|
|
2411
2832
|
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
2412
2833
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
2413
2834
|
}
|
|
@@ -2415,74 +2836,131 @@ var DatePicker = React66.forwardRef(
|
|
|
2415
2836
|
]
|
|
2416
2837
|
}
|
|
2417
2838
|
),
|
|
2418
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2839
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2840
|
+
"div",
|
|
2841
|
+
{
|
|
2842
|
+
role: "dialog",
|
|
2843
|
+
"aria-label": "Choose date",
|
|
2844
|
+
className: "p-4 w-72 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-3",
|
|
2845
|
+
onKeyDown: handleGridKeyDown,
|
|
2846
|
+
children: [
|
|
2847
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2848
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2849
|
+
"span",
|
|
2850
|
+
{
|
|
2851
|
+
id: "calendar-month-heading",
|
|
2852
|
+
className: "text-xs font-bold text-[var(--ui-text-primary)] font-mono uppercase tracking-wider",
|
|
2853
|
+
"aria-live": "polite",
|
|
2854
|
+
children: viewDate.toLocaleString("default", { month: "long", year: "numeric" })
|
|
2855
|
+
}
|
|
2856
|
+
),
|
|
2857
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2858
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2859
|
+
"button",
|
|
2860
|
+
{
|
|
2861
|
+
type: "button",
|
|
2862
|
+
onClick: prevMonth,
|
|
2863
|
+
"aria-label": "Previous month",
|
|
2864
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
2865
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
2866
|
+
}
|
|
2867
|
+
),
|
|
2868
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2869
|
+
"button",
|
|
2870
|
+
{
|
|
2871
|
+
type: "button",
|
|
2872
|
+
onClick: nextMonth,
|
|
2873
|
+
"aria-label": "Next month",
|
|
2874
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
2875
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
|
|
2876
|
+
}
|
|
2877
|
+
)
|
|
2878
|
+
] })
|
|
2879
|
+
] }),
|
|
2880
|
+
presets && presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 pb-2 border-b border-[var(--ui-border-subtle)]", children: presets.map((preset, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2423
2881
|
"button",
|
|
2424
2882
|
{
|
|
2425
2883
|
type: "button",
|
|
2426
|
-
onClick:
|
|
2427
|
-
className: "
|
|
2428
|
-
children:
|
|
2884
|
+
onClick: () => handleSelectDate(preset.date),
|
|
2885
|
+
className: "px-2 py-0.5 rounded-md text-[10px] font-semibold bg-[var(--ui-bg-secondary)] hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-secondary)] transition-colors cursor-pointer",
|
|
2886
|
+
children: preset.label
|
|
2887
|
+
},
|
|
2888
|
+
idx
|
|
2889
|
+
)) }),
|
|
2890
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2891
|
+
"div",
|
|
2892
|
+
{
|
|
2893
|
+
role: "row",
|
|
2894
|
+
className: "grid grid-cols-7 text-center text-[10px] font-bold text-[var(--ui-text-muted)] font-mono",
|
|
2895
|
+
children: [
|
|
2896
|
+
{ short: "Su", full: "Sunday" },
|
|
2897
|
+
{ short: "Mo", full: "Monday" },
|
|
2898
|
+
{ short: "Tu", full: "Tuesday" },
|
|
2899
|
+
{ short: "We", full: "Wednesday" },
|
|
2900
|
+
{ short: "Th", full: "Thursday" },
|
|
2901
|
+
{ short: "Fr", full: "Friday" },
|
|
2902
|
+
{ short: "Sa", full: "Saturday" }
|
|
2903
|
+
].map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "columnheader", "aria-label": d.full, className: "py-1", children: d.short }, d.short))
|
|
2429
2904
|
}
|
|
2430
2905
|
),
|
|
2431
2906
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2432
|
-
"
|
|
2907
|
+
"div",
|
|
2433
2908
|
{
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
className: "
|
|
2437
|
-
children:
|
|
2909
|
+
role: "grid",
|
|
2910
|
+
"aria-labelledby": "calendar-month-heading",
|
|
2911
|
+
className: "grid grid-cols-7 gap-1",
|
|
2912
|
+
children: days.map((item, idx) => {
|
|
2913
|
+
const itemStr = formatDate(item.date);
|
|
2914
|
+
const isSelected = itemStr === selectedStr;
|
|
2915
|
+
const isToday = itemStr === todayStr;
|
|
2916
|
+
const isFocused = itemStr === focusedStr;
|
|
2917
|
+
const fullDateLabel = item.date.toLocaleDateString("en-US", {
|
|
2918
|
+
weekday: "long",
|
|
2919
|
+
year: "numeric",
|
|
2920
|
+
month: "long",
|
|
2921
|
+
day: "numeric"
|
|
2922
|
+
});
|
|
2923
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2924
|
+
"button",
|
|
2925
|
+
{
|
|
2926
|
+
type: "button",
|
|
2927
|
+
role: "gridcell",
|
|
2928
|
+
disabled: item.disabled,
|
|
2929
|
+
"aria-selected": isSelected,
|
|
2930
|
+
"aria-current": isToday ? "date" : void 0,
|
|
2931
|
+
"aria-label": fullDateLabel,
|
|
2932
|
+
tabIndex: isFocused ? 0 : -1,
|
|
2933
|
+
onClick: () => handleSelectDate(item.date),
|
|
2934
|
+
className: cn(
|
|
2935
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer outline-none",
|
|
2936
|
+
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
2937
|
+
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
2938
|
+
isFocused && "ring-2 ring-[var(--ui-primary)]",
|
|
2939
|
+
isSelected ? "bg-[var(--ui-primary)] text-white font-bold shadow-md shadow-blue-500/30" : isToday ? "border border-[var(--ui-primary)] text-[var(--ui-primary)]" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
2940
|
+
),
|
|
2941
|
+
children: item.date.getDate()
|
|
2942
|
+
},
|
|
2943
|
+
idx
|
|
2944
|
+
);
|
|
2945
|
+
})
|
|
2438
2946
|
}
|
|
2439
2947
|
)
|
|
2440
|
-
]
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
"button",
|
|
2444
|
-
{
|
|
2445
|
-
type: "button",
|
|
2446
|
-
onClick: () => handleSelectDate(preset.date),
|
|
2447
|
-
className: "px-2 py-0.5 rounded-md text-[10px] font-semibold bg-[var(--ui-bg-secondary)] hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-secondary)] transition-colors",
|
|
2448
|
-
children: preset.label
|
|
2449
|
-
},
|
|
2450
|
-
idx
|
|
2451
|
-
)) }),
|
|
2452
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 text-center text-[10px] font-bold text-[var(--ui-text-muted)] font-mono", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: d }, d)) }),
|
|
2453
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((item, idx) => {
|
|
2454
|
-
const itemStr = formatDate(item.date);
|
|
2455
|
-
const isSelected = itemStr === selectedStr;
|
|
2456
|
-
const isToday = itemStr === todayStr;
|
|
2457
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2458
|
-
"button",
|
|
2459
|
-
{
|
|
2460
|
-
type: "button",
|
|
2461
|
-
disabled: item.disabled,
|
|
2462
|
-
onClick: () => handleSelectDate(item.date),
|
|
2463
|
-
className: cn(
|
|
2464
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
2465
|
-
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
2466
|
-
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
2467
|
-
isSelected ? "bg-[var(--ui-primary)] text-white font-bold shadow-md shadow-blue-500/30" : isToday ? "border border-[var(--ui-primary)] text-[var(--ui-primary)]" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
2468
|
-
),
|
|
2469
|
-
children: item.date.getDate()
|
|
2470
|
-
},
|
|
2471
|
-
idx
|
|
2472
|
-
);
|
|
2473
|
-
}) })
|
|
2474
|
-
] })
|
|
2948
|
+
]
|
|
2949
|
+
}
|
|
2950
|
+
)
|
|
2475
2951
|
}
|
|
2476
2952
|
) });
|
|
2477
2953
|
}
|
|
2478
2954
|
);
|
|
2479
2955
|
DatePicker.displayName = "DatePicker";
|
|
2480
2956
|
var DateRangePicker = React66.forwardRef(
|
|
2481
|
-
({ value: controlledVal, defaultValue, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
2482
|
-
const [
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2957
|
+
({ value: controlledVal, defaultValue = { from: null, to: null }, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
2958
|
+
const [range, setRange] = useControllableState({
|
|
2959
|
+
value: controlledVal,
|
|
2960
|
+
defaultValue,
|
|
2961
|
+
onChange,
|
|
2962
|
+
name: "DateRangePicker"
|
|
2963
|
+
});
|
|
2486
2964
|
const [viewDate, setViewDate] = React66.useState(range.from || /* @__PURE__ */ new Date());
|
|
2487
2965
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
2488
2966
|
const handleDayClick = (d) => {
|
|
@@ -2497,8 +2975,7 @@ var DateRangePicker = React66.forwardRef(
|
|
|
2497
2975
|
setIsOpen(false);
|
|
2498
2976
|
}
|
|
2499
2977
|
}
|
|
2500
|
-
|
|
2501
|
-
onChange?.(next);
|
|
2978
|
+
setRange(next);
|
|
2502
2979
|
};
|
|
2503
2980
|
const displayText = range.from && range.to ? `${formatDisplayDate(range.from)} \u2013 ${formatDisplayDate(range.to)}` : range.from ? `${formatDisplayDate(range.from)} \u2013 ...` : placeholder;
|
|
2504
2981
|
const year = viewDate.getFullYear();
|
|
@@ -2512,146 +2989,345 @@ var DateRangePicker = React66.forwardRef(
|
|
|
2512
2989
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2513
2990
|
Popover,
|
|
2514
2991
|
{
|
|
2515
|
-
isOpen,
|
|
2516
|
-
|
|
2992
|
+
open: isOpen,
|
|
2993
|
+
onOpenChange: setIsOpen,
|
|
2517
2994
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2518
2995
|
"button",
|
|
2519
2996
|
{
|
|
2520
2997
|
type: "button",
|
|
2521
2998
|
disabled,
|
|
2522
|
-
|
|
2523
|
-
|
|
2999
|
+
"aria-haspopup": "dialog",
|
|
3000
|
+
"aria-expanded": isOpen,
|
|
3001
|
+
"aria-label": displayText,
|
|
3002
|
+
className: "w-full h-10 px-3.5 rounded-[var(--ui-radius-md)] border border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)] bg-[var(--ui-bg-surface)] text-left flex items-center justify-between text-sm shadow-inner cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
2524
3003
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
2525
3004
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
2526
3005
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: range.from ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: displayText })
|
|
2527
3006
|
] })
|
|
2528
3007
|
}
|
|
2529
3008
|
),
|
|
2530
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
3009
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3010
|
+
"div",
|
|
3011
|
+
{
|
|
3012
|
+
role: "dialog",
|
|
3013
|
+
"aria-label": "Choose date range",
|
|
3014
|
+
className: "p-4 w-76 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-3",
|
|
3015
|
+
children: [
|
|
3016
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3017
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { id: "range-month-heading", className: "text-xs font-bold text-[var(--ui-text-primary)] font-mono uppercase tracking-wider", children: viewDate.toLocaleString("default", { month: "long", year: "numeric" }) }),
|
|
3018
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3019
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3020
|
+
"button",
|
|
3021
|
+
{
|
|
3022
|
+
type: "button",
|
|
3023
|
+
onClick: () => setViewDate(new Date(year, month - 1, 1)),
|
|
3024
|
+
"aria-label": "Previous month",
|
|
3025
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
3026
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
3027
|
+
}
|
|
3028
|
+
),
|
|
3029
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3030
|
+
"button",
|
|
3031
|
+
{
|
|
3032
|
+
type: "button",
|
|
3033
|
+
onClick: () => setViewDate(new Date(year, month + 1, 1)),
|
|
3034
|
+
"aria-label": "Next month",
|
|
3035
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
3036
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
|
|
3037
|
+
}
|
|
3038
|
+
)
|
|
3039
|
+
] })
|
|
3040
|
+
] }),
|
|
3041
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3042
|
+
"div",
|
|
2545
3043
|
{
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
className: "
|
|
2549
|
-
children:
|
|
3044
|
+
role: "grid",
|
|
3045
|
+
"aria-labelledby": "range-month-heading",
|
|
3046
|
+
className: "grid grid-cols-7 gap-1 text-center",
|
|
3047
|
+
children: [
|
|
3048
|
+
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", {}, `empty-${i}`)),
|
|
3049
|
+
days.map((d, idx) => {
|
|
3050
|
+
const dStr = formatDate(d);
|
|
3051
|
+
const fromStr = range.from ? formatDate(range.from) : "";
|
|
3052
|
+
const toStr = range.to ? formatDate(range.to) : "";
|
|
3053
|
+
const isFrom = dStr === fromStr;
|
|
3054
|
+
const isTo = dStr === toStr;
|
|
3055
|
+
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
3056
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3057
|
+
"button",
|
|
3058
|
+
{
|
|
3059
|
+
type: "button",
|
|
3060
|
+
role: "gridcell",
|
|
3061
|
+
"aria-selected": Boolean(isFrom || isTo || isInRange),
|
|
3062
|
+
"aria-label": d.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" }),
|
|
3063
|
+
onClick: () => handleDayClick(d),
|
|
3064
|
+
className: cn(
|
|
3065
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
3066
|
+
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
3067
|
+
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
3068
|
+
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
3069
|
+
),
|
|
3070
|
+
children: d.getDate()
|
|
3071
|
+
},
|
|
3072
|
+
idx
|
|
3073
|
+
);
|
|
3074
|
+
})
|
|
3075
|
+
]
|
|
2550
3076
|
}
|
|
2551
3077
|
)
|
|
2552
|
-
]
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", {}, `empty-${i}`)),
|
|
2556
|
-
days.map((d, idx) => {
|
|
2557
|
-
const dStr = formatDate(d);
|
|
2558
|
-
const fromStr = range.from ? formatDate(range.from) : "";
|
|
2559
|
-
const toStr = range.to ? formatDate(range.to) : "";
|
|
2560
|
-
const isFrom = dStr === fromStr;
|
|
2561
|
-
const isTo = dStr === toStr;
|
|
2562
|
-
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
2563
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2564
|
-
"button",
|
|
2565
|
-
{
|
|
2566
|
-
type: "button",
|
|
2567
|
-
onClick: () => handleDayClick(d),
|
|
2568
|
-
className: cn(
|
|
2569
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
2570
|
-
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
2571
|
-
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
2572
|
-
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
2573
|
-
),
|
|
2574
|
-
children: d.getDate()
|
|
2575
|
-
},
|
|
2576
|
-
idx
|
|
2577
|
-
);
|
|
2578
|
-
})
|
|
2579
|
-
] })
|
|
2580
|
-
] })
|
|
3078
|
+
]
|
|
3079
|
+
}
|
|
3080
|
+
)
|
|
2581
3081
|
}
|
|
2582
3082
|
) });
|
|
2583
3083
|
}
|
|
2584
3084
|
);
|
|
2585
3085
|
DateRangePicker.displayName = "DateRangePicker";
|
|
2586
3086
|
var TimePicker = React66.forwardRef(
|
|
2587
|
-
({
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
3087
|
+
({
|
|
3088
|
+
value: controlledVal,
|
|
3089
|
+
defaultValue,
|
|
3090
|
+
onChange,
|
|
3091
|
+
format = "24h",
|
|
3092
|
+
className,
|
|
3093
|
+
disabled = false,
|
|
3094
|
+
placeholder = "Select time..."
|
|
3095
|
+
}, ref) => {
|
|
3096
|
+
const is12h = format === "12h";
|
|
3097
|
+
const fallbackDefault = defaultValue ?? (is12h ? "12:00 PM" : "12:00");
|
|
3098
|
+
const [time, setTime] = useControllableState({
|
|
3099
|
+
value: controlledVal,
|
|
3100
|
+
defaultValue: fallbackDefault,
|
|
3101
|
+
onChange,
|
|
3102
|
+
name: "TimePicker"
|
|
3103
|
+
});
|
|
2592
3104
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
2593
|
-
const
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
3105
|
+
const parseTime = (str, is12) => {
|
|
3106
|
+
if (!str) return { hours: is12 ? "12" : "12", minutes: "00", period: "AM" };
|
|
3107
|
+
const trimmed = str.trim();
|
|
3108
|
+
let period2 = "AM";
|
|
3109
|
+
let cleanTime = trimmed;
|
|
3110
|
+
if (trimmed.toLowerCase().includes("pm")) {
|
|
3111
|
+
period2 = "PM";
|
|
3112
|
+
cleanTime = trimmed.replace(/pm/i, "").trim();
|
|
3113
|
+
} else if (trimmed.toLowerCase().includes("am")) {
|
|
3114
|
+
period2 = "AM";
|
|
3115
|
+
cleanTime = trimmed.replace(/am/i, "").trim();
|
|
3116
|
+
}
|
|
3117
|
+
const parts = cleanTime.split(":");
|
|
3118
|
+
let h = parts[0] ? parts[0].padStart(2, "0") : is12 ? "12" : "00";
|
|
3119
|
+
const m = parts[1] ? parts[1].padStart(2, "0") : "00";
|
|
3120
|
+
return { hours: h, minutes: m, period: period2 };
|
|
3121
|
+
};
|
|
3122
|
+
const { hours, minutes, period } = parseTime(time, is12h);
|
|
3123
|
+
const updateTime = (newH, newM, newPeriod) => {
|
|
3124
|
+
let formatted;
|
|
3125
|
+
if (is12h) {
|
|
3126
|
+
const p = newPeriod ?? period;
|
|
3127
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")} ${p}`;
|
|
3128
|
+
} else {
|
|
3129
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")}`;
|
|
3130
|
+
}
|
|
3131
|
+
setTime(formatted);
|
|
3132
|
+
};
|
|
3133
|
+
const hourOptions = is12h ? Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0")) : Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
3134
|
+
const minuteOptions = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"];
|
|
3135
|
+
const hourListRef = React66.useRef(null);
|
|
3136
|
+
const minuteListRef = React66.useRef(null);
|
|
3137
|
+
const periodListRef = React66.useRef(null);
|
|
3138
|
+
React66.useEffect(() => {
|
|
3139
|
+
if (isOpen) {
|
|
3140
|
+
const timer = setTimeout(() => {
|
|
3141
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
3142
|
+
activeHourEl?.focus();
|
|
3143
|
+
}, 10);
|
|
3144
|
+
return () => clearTimeout(timer);
|
|
3145
|
+
}
|
|
3146
|
+
}, [isOpen]);
|
|
3147
|
+
const handleHourKeyDown = (e) => {
|
|
3148
|
+
const idx = hourOptions.indexOf(hours);
|
|
3149
|
+
if (e.key === "ArrowDown") {
|
|
3150
|
+
e.preventDefault();
|
|
3151
|
+
const next = hourOptions[(idx + 1) % hourOptions.length];
|
|
3152
|
+
updateTime(next, minutes, period);
|
|
3153
|
+
} else if (e.key === "ArrowUp") {
|
|
3154
|
+
e.preventDefault();
|
|
3155
|
+
const prev = hourOptions[(idx - 1 + hourOptions.length) % hourOptions.length];
|
|
3156
|
+
updateTime(prev, minutes, period);
|
|
3157
|
+
} else if (e.key === "ArrowRight") {
|
|
3158
|
+
e.preventDefault();
|
|
3159
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]') || minuteListRef.current?.querySelector('[role="option"]');
|
|
3160
|
+
activeMinEl?.focus();
|
|
3161
|
+
} else if (e.key === "Escape") {
|
|
3162
|
+
e.preventDefault();
|
|
3163
|
+
setIsOpen(false);
|
|
3164
|
+
}
|
|
3165
|
+
};
|
|
3166
|
+
const handleMinuteKeyDown = (e) => {
|
|
3167
|
+
const idx = minuteOptions.indexOf(minutes);
|
|
3168
|
+
if (e.key === "ArrowDown") {
|
|
3169
|
+
e.preventDefault();
|
|
3170
|
+
const next = minuteOptions[(idx + 1) % minuteOptions.length];
|
|
3171
|
+
updateTime(hours, next, period);
|
|
3172
|
+
} else if (e.key === "ArrowUp") {
|
|
3173
|
+
e.preventDefault();
|
|
3174
|
+
const prev = minuteOptions[(idx - 1 + minuteOptions.length) % minuteOptions.length];
|
|
3175
|
+
updateTime(hours, prev, period);
|
|
3176
|
+
} else if (e.key === "ArrowLeft") {
|
|
3177
|
+
e.preventDefault();
|
|
3178
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
3179
|
+
activeHourEl?.focus();
|
|
3180
|
+
} else if (e.key === "ArrowRight" && is12h) {
|
|
3181
|
+
e.preventDefault();
|
|
3182
|
+
const activePeriodEl = periodListRef.current?.querySelector('[aria-selected="true"]');
|
|
3183
|
+
activePeriodEl?.focus();
|
|
3184
|
+
} else if (e.key === "Escape") {
|
|
3185
|
+
e.preventDefault();
|
|
3186
|
+
setIsOpen(false);
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
const handlePeriodKeyDown = (e) => {
|
|
3190
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
3191
|
+
e.preventDefault();
|
|
3192
|
+
const next = period === "AM" ? "PM" : "AM";
|
|
3193
|
+
updateTime(hours, minutes, next);
|
|
3194
|
+
} else if (e.key === "ArrowLeft") {
|
|
3195
|
+
e.preventDefault();
|
|
3196
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]');
|
|
3197
|
+
activeMinEl?.focus();
|
|
3198
|
+
} else if (e.key === "Escape") {
|
|
3199
|
+
e.preventDefault();
|
|
3200
|
+
setIsOpen(false);
|
|
3201
|
+
}
|
|
2599
3202
|
};
|
|
2600
3203
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2601
3204
|
Popover,
|
|
2602
3205
|
{
|
|
2603
|
-
isOpen,
|
|
2604
|
-
|
|
3206
|
+
open: isOpen,
|
|
3207
|
+
onOpenChange: setIsOpen,
|
|
2605
3208
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2606
3209
|
"button",
|
|
2607
3210
|
{
|
|
2608
3211
|
type: "button",
|
|
2609
3212
|
disabled,
|
|
2610
|
-
|
|
2611
|
-
|
|
3213
|
+
"aria-haspopup": "dialog",
|
|
3214
|
+
"aria-expanded": isOpen,
|
|
3215
|
+
"aria-label": `Selected time: ${time}`,
|
|
3216
|
+
className: cn(
|
|
3217
|
+
"w-full h-10 px-3.5 rounded-[var(--ui-radius-md)] border border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)] bg-[var(--ui-bg-surface)] text-left flex items-center justify-between text-sm shadow-inner transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
3218
|
+
disabled && "opacity-50 cursor-not-allowed"
|
|
3219
|
+
),
|
|
2612
3220
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
2613
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)]" }),
|
|
2614
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time })
|
|
3221
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
3222
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time || placeholder })
|
|
2615
3223
|
] })
|
|
2616
3224
|
}
|
|
2617
3225
|
),
|
|
2618
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
"
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
"
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3226
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3227
|
+
"div",
|
|
3228
|
+
{
|
|
3229
|
+
role: "dialog",
|
|
3230
|
+
"aria-label": "Choose time",
|
|
3231
|
+
className: "p-4 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-3",
|
|
3232
|
+
children: [
|
|
3233
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-[var(--ui-text-muted)] uppercase tracking-wider font-mono", children: [
|
|
3234
|
+
"Select Time (",
|
|
3235
|
+
format,
|
|
3236
|
+
")"
|
|
3237
|
+
] }),
|
|
3238
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
3239
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3240
|
+
"div",
|
|
3241
|
+
{
|
|
3242
|
+
ref: hourListRef,
|
|
3243
|
+
tabIndex: 0,
|
|
3244
|
+
onKeyDown: handleHourKeyDown,
|
|
3245
|
+
className: "h-40 overflow-y-auto border border-[var(--ui-border-subtle)] rounded-xl p-1 w-16 text-center space-y-1 focus:outline-none focus:ring-1 focus:ring-[var(--ui-primary)]",
|
|
3246
|
+
role: "listbox",
|
|
3247
|
+
"aria-label": "Hour",
|
|
3248
|
+
children: hourOptions.map((h) => {
|
|
3249
|
+
const isSelected = hours === h;
|
|
3250
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3251
|
+
"div",
|
|
3252
|
+
{
|
|
3253
|
+
tabIndex: isSelected ? 0 : -1,
|
|
3254
|
+
role: "option",
|
|
3255
|
+
"aria-selected": isSelected,
|
|
3256
|
+
onClick: () => updateTime(h, minutes, period),
|
|
3257
|
+
className: cn(
|
|
3258
|
+
"text-xs py-1 rounded-lg font-mono font-semibold cursor-pointer transition-colors select-none outline-none focus-visible:ring-1 focus-visible:ring-[var(--ui-primary)]",
|
|
3259
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
3260
|
+
),
|
|
3261
|
+
children: h
|
|
3262
|
+
},
|
|
3263
|
+
h
|
|
3264
|
+
);
|
|
3265
|
+
})
|
|
3266
|
+
}
|
|
2648
3267
|
),
|
|
2649
|
-
children:
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
3268
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold", children: ":" }),
|
|
3269
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3270
|
+
"div",
|
|
3271
|
+
{
|
|
3272
|
+
ref: minuteListRef,
|
|
3273
|
+
tabIndex: 0,
|
|
3274
|
+
onKeyDown: handleMinuteKeyDown,
|
|
3275
|
+
className: "h-40 overflow-y-auto border border-[var(--ui-border-subtle)] rounded-xl p-1 w-16 text-center space-y-1 focus:outline-none focus:ring-1 focus:ring-[var(--ui-primary)]",
|
|
3276
|
+
role: "listbox",
|
|
3277
|
+
"aria-label": "Minute",
|
|
3278
|
+
children: minuteOptions.map((m) => {
|
|
3279
|
+
const isSelected = minutes === m;
|
|
3280
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3281
|
+
"div",
|
|
3282
|
+
{
|
|
3283
|
+
tabIndex: isSelected ? 0 : -1,
|
|
3284
|
+
role: "option",
|
|
3285
|
+
"aria-selected": isSelected,
|
|
3286
|
+
onClick: () => updateTime(hours, m, period),
|
|
3287
|
+
className: cn(
|
|
3288
|
+
"text-xs py-1 rounded-lg font-mono font-semibold cursor-pointer transition-colors select-none outline-none focus-visible:ring-1 focus-visible:ring-[var(--ui-primary)]",
|
|
3289
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
3290
|
+
),
|
|
3291
|
+
children: m
|
|
3292
|
+
},
|
|
3293
|
+
m
|
|
3294
|
+
);
|
|
3295
|
+
})
|
|
3296
|
+
}
|
|
3297
|
+
),
|
|
3298
|
+
is12h && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3299
|
+
"div",
|
|
3300
|
+
{
|
|
3301
|
+
ref: periodListRef,
|
|
3302
|
+
tabIndex: 0,
|
|
3303
|
+
onKeyDown: handlePeriodKeyDown,
|
|
3304
|
+
className: "h-40 border border-[var(--ui-border-subtle)] rounded-xl p-1 w-14 text-center space-y-1 flex flex-col justify-center focus:outline-none focus:ring-1 focus:ring-[var(--ui-primary)]",
|
|
3305
|
+
role: "listbox",
|
|
3306
|
+
"aria-label": "Period",
|
|
3307
|
+
children: ["AM", "PM"].map((p) => {
|
|
3308
|
+
const isSelected = period === p;
|
|
3309
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3310
|
+
"div",
|
|
3311
|
+
{
|
|
3312
|
+
tabIndex: isSelected ? 0 : -1,
|
|
3313
|
+
role: "option",
|
|
3314
|
+
"aria-selected": isSelected,
|
|
3315
|
+
onClick: () => updateTime(hours, minutes, p),
|
|
3316
|
+
className: cn(
|
|
3317
|
+
"text-xs py-2 rounded-lg font-mono font-semibold cursor-pointer transition-colors select-none outline-none focus-visible:ring-1 focus-visible:ring-[var(--ui-primary)]",
|
|
3318
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
3319
|
+
),
|
|
3320
|
+
children: p
|
|
3321
|
+
},
|
|
3322
|
+
p
|
|
3323
|
+
);
|
|
3324
|
+
})
|
|
3325
|
+
}
|
|
3326
|
+
)
|
|
3327
|
+
] })
|
|
3328
|
+
]
|
|
3329
|
+
}
|
|
3330
|
+
)
|
|
2655
3331
|
}
|
|
2656
3332
|
) });
|
|
2657
3333
|
}
|
|
@@ -2729,135 +3405,205 @@ var Combobox = React66.forwardRef(
|
|
|
2729
3405
|
emptyText = "No items found.",
|
|
2730
3406
|
disabled = false,
|
|
2731
3407
|
clearable = true,
|
|
2732
|
-
className
|
|
3408
|
+
className,
|
|
3409
|
+
name,
|
|
3410
|
+
id: customId,
|
|
3411
|
+
"aria-label": ariaLabel = "Select option"
|
|
2733
3412
|
}, ref) => {
|
|
3413
|
+
const baseId = React66.useId();
|
|
3414
|
+
const comboId = customId || `combobox-${baseId}`;
|
|
3415
|
+
const listboxId = `listbox-${baseId}`;
|
|
2734
3416
|
const [internalVal, setInternalVal] = React66.useState(defaultValue);
|
|
2735
|
-
const
|
|
3417
|
+
const isControlled = controlledVal !== void 0;
|
|
3418
|
+
const selectedValue = isControlled ? controlledVal : internalVal;
|
|
2736
3419
|
const [search, setSearch] = React66.useState("");
|
|
2737
3420
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
2738
3421
|
const [highlightIndex, setHighlightIndex] = React66.useState(0);
|
|
3422
|
+
const searchInputRef = React66.useRef(null);
|
|
3423
|
+
const triggerRef = React66.useRef(null);
|
|
2739
3424
|
const filteredOptions = options.filter(
|
|
2740
3425
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase()) || opt.description && opt.description.toLowerCase().includes(search.toLowerCase())
|
|
2741
3426
|
);
|
|
2742
3427
|
const selectedOption = options.find((opt) => opt.value === selectedValue);
|
|
3428
|
+
React66.useEffect(() => {
|
|
3429
|
+
if (filteredOptions.length > 0) {
|
|
3430
|
+
setHighlightIndex((prev) => Math.min(prev, filteredOptions.length - 1));
|
|
3431
|
+
}
|
|
3432
|
+
}, [filteredOptions.length]);
|
|
2743
3433
|
const handleSelect = (val) => {
|
|
2744
|
-
|
|
3434
|
+
if (!isControlled) {
|
|
3435
|
+
setInternalVal(val);
|
|
3436
|
+
}
|
|
2745
3437
|
onChange?.(val);
|
|
2746
3438
|
setIsOpen(false);
|
|
2747
3439
|
setSearch("");
|
|
3440
|
+
triggerRef.current?.focus();
|
|
2748
3441
|
};
|
|
2749
3442
|
const handleClear = (e) => {
|
|
2750
3443
|
e.stopPropagation();
|
|
2751
|
-
|
|
3444
|
+
if (!isControlled) {
|
|
3445
|
+
setInternalVal("");
|
|
3446
|
+
}
|
|
2752
3447
|
onChange?.("");
|
|
2753
3448
|
};
|
|
2754
3449
|
const handleKeyDown = (e) => {
|
|
2755
3450
|
if (!isOpen) {
|
|
2756
|
-
if (e.key === "ArrowDown" || e.key === "Enter") {
|
|
3451
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
3452
|
+
e.preventDefault();
|
|
2757
3453
|
setIsOpen(true);
|
|
2758
3454
|
}
|
|
2759
3455
|
return;
|
|
2760
3456
|
}
|
|
2761
3457
|
if (e.key === "ArrowDown") {
|
|
2762
3458
|
e.preventDefault();
|
|
2763
|
-
|
|
3459
|
+
let next = highlightIndex + 1;
|
|
3460
|
+
while (next < filteredOptions.length && filteredOptions[next]?.disabled) {
|
|
3461
|
+
next++;
|
|
3462
|
+
}
|
|
3463
|
+
if (next < filteredOptions.length) {
|
|
3464
|
+
setHighlightIndex(next);
|
|
3465
|
+
}
|
|
2764
3466
|
} else if (e.key === "ArrowUp") {
|
|
2765
3467
|
e.preventDefault();
|
|
2766
|
-
|
|
3468
|
+
let prev = highlightIndex - 1;
|
|
3469
|
+
while (prev >= 0 && filteredOptions[prev]?.disabled) {
|
|
3470
|
+
prev--;
|
|
3471
|
+
}
|
|
3472
|
+
if (prev >= 0) {
|
|
3473
|
+
setHighlightIndex(prev);
|
|
3474
|
+
}
|
|
3475
|
+
} else if (e.key === "Home") {
|
|
3476
|
+
e.preventDefault();
|
|
3477
|
+
let first = 0;
|
|
3478
|
+
while (first < filteredOptions.length && filteredOptions[first]?.disabled) {
|
|
3479
|
+
first++;
|
|
3480
|
+
}
|
|
3481
|
+
setHighlightIndex(first);
|
|
3482
|
+
} else if (e.key === "End") {
|
|
3483
|
+
e.preventDefault();
|
|
3484
|
+
let last = filteredOptions.length - 1;
|
|
3485
|
+
while (last >= 0 && filteredOptions[last]?.disabled) {
|
|
3486
|
+
last--;
|
|
3487
|
+
}
|
|
3488
|
+
setHighlightIndex(last);
|
|
2767
3489
|
} else if (e.key === "Enter") {
|
|
2768
3490
|
e.preventDefault();
|
|
2769
|
-
|
|
2770
|
-
|
|
3491
|
+
const highlighted = filteredOptions[highlightIndex];
|
|
3492
|
+
if (highlighted && !highlighted.disabled) {
|
|
3493
|
+
handleSelect(highlighted.value);
|
|
2771
3494
|
}
|
|
2772
3495
|
} else if (e.key === "Escape") {
|
|
3496
|
+
e.preventDefault();
|
|
2773
3497
|
setIsOpen(false);
|
|
3498
|
+
triggerRef.current?.focus();
|
|
2774
3499
|
}
|
|
2775
3500
|
};
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
{
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
disabled
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
3501
|
+
const activeDescendantId = filteredOptions[highlightIndex] ? `combobox-opt-${baseId}-${filteredOptions[highlightIndex].value}` : void 0;
|
|
3502
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("relative w-full", className), children: [
|
|
3503
|
+
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selectedValue }),
|
|
3504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3505
|
+
Popover,
|
|
3506
|
+
{
|
|
3507
|
+
isOpen,
|
|
3508
|
+
onClose: () => setIsOpen(false),
|
|
3509
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3510
|
+
"button",
|
|
3511
|
+
{
|
|
3512
|
+
ref: triggerRef,
|
|
3513
|
+
id: comboId,
|
|
3514
|
+
type: "button",
|
|
3515
|
+
disabled,
|
|
3516
|
+
role: "combobox",
|
|
3517
|
+
"aria-expanded": isOpen,
|
|
3518
|
+
"aria-haspopup": "listbox",
|
|
3519
|
+
"aria-controls": isOpen ? listboxId : void 0,
|
|
3520
|
+
"aria-label": ariaLabel,
|
|
3521
|
+
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
3522
|
+
onKeyDown: handleKeyDown,
|
|
3523
|
+
className: cn(
|
|
3524
|
+
"w-full h-10 px-3.5 rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)] text-left flex items-center justify-between text-sm transition-all shadow-inner cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
3525
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
3526
|
+
),
|
|
3527
|
+
children: [
|
|
3528
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
3529
|
+
selectedOption?.icon,
|
|
3530
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: selectedOption ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: selectedOption ? selectedOption.label : placeholder })
|
|
3531
|
+
] }),
|
|
3532
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: [
|
|
3533
|
+
clearable && selectedValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3534
|
+
"span",
|
|
3535
|
+
{
|
|
3536
|
+
onClick: handleClear,
|
|
3537
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
3538
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
3539
|
+
}
|
|
3540
|
+
),
|
|
3541
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "w-4 h-4 opacity-50 shrink-0" })
|
|
3542
|
+
] })
|
|
3543
|
+
]
|
|
3544
|
+
}
|
|
3545
|
+
),
|
|
3546
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 w-72 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-2", children: [
|
|
3547
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center", children: [
|
|
3548
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 w-3.5 h-3.5 text-[var(--ui-text-muted)] pointer-events-none" }),
|
|
3549
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3550
|
+
"input",
|
|
3551
|
+
{
|
|
3552
|
+
ref: searchInputRef,
|
|
3553
|
+
type: "text",
|
|
3554
|
+
autoFocus: true,
|
|
3555
|
+
placeholder: searchPlaceholder,
|
|
3556
|
+
value: search,
|
|
3557
|
+
onChange: (e) => {
|
|
3558
|
+
setSearch(e.target.value);
|
|
3559
|
+
setHighlightIndex(0);
|
|
3560
|
+
},
|
|
3561
|
+
onKeyDown: handleKeyDown,
|
|
3562
|
+
role: "combobox",
|
|
3563
|
+
"aria-autocomplete": "list",
|
|
3564
|
+
"aria-expanded": "true",
|
|
3565
|
+
"aria-controls": listboxId,
|
|
3566
|
+
"aria-activedescendant": activeDescendantId,
|
|
3567
|
+
className: "w-full h-8 pl-8 pr-3 text-xs rounded-xl bg-[var(--ui-bg-secondary)] border border-[var(--ui-border-subtle)] focus:outline-none focus:border-[var(--ui-primary)] text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)]"
|
|
3568
|
+
}
|
|
3569
|
+
)
|
|
3570
|
+
] }),
|
|
3571
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { id: listboxId, className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: emptyText }) : filteredOptions.map((opt, idx) => {
|
|
3572
|
+
const isSelected = opt.value === selectedValue;
|
|
3573
|
+
const isHighlighted = idx === highlightIndex;
|
|
3574
|
+
const optId = `combobox-opt-${baseId}-${opt.value}`;
|
|
3575
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3576
|
+
"div",
|
|
3577
|
+
{
|
|
3578
|
+
id: optId,
|
|
3579
|
+
role: "option",
|
|
3580
|
+
"aria-selected": isSelected,
|
|
3581
|
+
"aria-disabled": opt.disabled,
|
|
3582
|
+
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
3583
|
+
onMouseEnter: () => !opt.disabled && setHighlightIndex(idx),
|
|
3584
|
+
className: cn(
|
|
3585
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none",
|
|
3586
|
+
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
3587
|
+
isSelected ? "bg-[var(--ui-primary)] text-white font-semibold shadow-xs" : isHighlighted ? "bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]" : "text-[var(--ui-text-primary)]"
|
|
3588
|
+
),
|
|
3589
|
+
children: [
|
|
3590
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
3591
|
+
opt.icon,
|
|
3592
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3593
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: opt.label }),
|
|
3594
|
+
opt.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
3595
|
+
] })
|
|
3596
|
+
] }),
|
|
3597
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
3598
|
+
]
|
|
2824
3599
|
},
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
)
|
|
2828
|
-
] })
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2833
|
-
"div",
|
|
2834
|
-
{
|
|
2835
|
-
role: "option",
|
|
2836
|
-
"aria-selected": isSelected,
|
|
2837
|
-
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
2838
|
-
onMouseEnter: () => setHighlightIndex(idx),
|
|
2839
|
-
className: cn(
|
|
2840
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors",
|
|
2841
|
-
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
2842
|
-
isSelected ? "bg-[var(--ui-primary)] text-white font-semibold shadow-xs" : isHighlighted ? "bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]" : "text-[var(--ui-text-primary)]"
|
|
2843
|
-
),
|
|
2844
|
-
children: [
|
|
2845
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
2846
|
-
opt.icon,
|
|
2847
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2848
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: opt.label }),
|
|
2849
|
-
opt.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
2850
|
-
] })
|
|
2851
|
-
] }),
|
|
2852
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
2853
|
-
]
|
|
2854
|
-
},
|
|
2855
|
-
opt.value
|
|
2856
|
-
);
|
|
2857
|
-
}) })
|
|
2858
|
-
] })
|
|
2859
|
-
}
|
|
2860
|
-
) });
|
|
3600
|
+
opt.value
|
|
3601
|
+
);
|
|
3602
|
+
}) })
|
|
3603
|
+
] })
|
|
3604
|
+
}
|
|
3605
|
+
)
|
|
3606
|
+
] });
|
|
2861
3607
|
}
|
|
2862
3608
|
);
|
|
2863
3609
|
Combobox.displayName = "Combobox";
|
|
@@ -2874,28 +3620,69 @@ var MultiSelect = React66.forwardRef(
|
|
|
2874
3620
|
className
|
|
2875
3621
|
}, ref) => {
|
|
2876
3622
|
const [internalVal, setInternalVal] = React66.useState(defaultValue);
|
|
2877
|
-
const
|
|
3623
|
+
const isControlled = controlledVal !== void 0;
|
|
3624
|
+
const selectedValues = isControlled ? controlledVal : internalVal;
|
|
2878
3625
|
const [search, setSearch] = React66.useState("");
|
|
2879
3626
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
3627
|
+
const [highlightedIndex, setHighlightedIndex] = React66.useState(0);
|
|
2880
3628
|
const filteredOptions = options.filter(
|
|
2881
3629
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase())
|
|
2882
3630
|
);
|
|
2883
3631
|
const toggleOption = (val) => {
|
|
3632
|
+
const opt = options.find((o) => o.value === val);
|
|
3633
|
+
if (opt?.disabled) return;
|
|
2884
3634
|
const next = selectedValues.includes(val) ? selectedValues.filter((v) => v !== val) : [...selectedValues, val];
|
|
2885
|
-
|
|
3635
|
+
if (!isControlled) {
|
|
3636
|
+
setInternalVal(next);
|
|
3637
|
+
}
|
|
2886
3638
|
onChange?.(next);
|
|
2887
3639
|
};
|
|
2888
3640
|
const removeTag = (val, e) => {
|
|
2889
3641
|
e?.stopPropagation?.();
|
|
2890
3642
|
const next = selectedValues.filter((v) => v !== val);
|
|
2891
|
-
|
|
3643
|
+
if (!isControlled) {
|
|
3644
|
+
setInternalVal(next);
|
|
3645
|
+
}
|
|
2892
3646
|
onChange?.(next);
|
|
2893
3647
|
};
|
|
2894
3648
|
const clearAll = (e) => {
|
|
2895
3649
|
e?.stopPropagation?.();
|
|
2896
|
-
|
|
3650
|
+
if (!isControlled) {
|
|
3651
|
+
setInternalVal([]);
|
|
3652
|
+
}
|
|
2897
3653
|
onChange?.([]);
|
|
2898
3654
|
};
|
|
3655
|
+
const handleSearchKeyDown = (e) => {
|
|
3656
|
+
if (filteredOptions.length === 0) return;
|
|
3657
|
+
if (e.key === "ArrowDown") {
|
|
3658
|
+
e.preventDefault();
|
|
3659
|
+
setHighlightedIndex((prev) => {
|
|
3660
|
+
let next = (prev + 1) % filteredOptions.length;
|
|
3661
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
3662
|
+
next = (next + 1) % filteredOptions.length;
|
|
3663
|
+
}
|
|
3664
|
+
return next;
|
|
3665
|
+
});
|
|
3666
|
+
} else if (e.key === "ArrowUp") {
|
|
3667
|
+
e.preventDefault();
|
|
3668
|
+
setHighlightedIndex((prev) => {
|
|
3669
|
+
let next = (prev - 1 + filteredOptions.length) % filteredOptions.length;
|
|
3670
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
3671
|
+
next = (next - 1 + filteredOptions.length) % filteredOptions.length;
|
|
3672
|
+
}
|
|
3673
|
+
return next;
|
|
3674
|
+
});
|
|
3675
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
3676
|
+
e.preventDefault();
|
|
3677
|
+
const current = filteredOptions[highlightedIndex];
|
|
3678
|
+
if (current && !current.disabled) {
|
|
3679
|
+
toggleOption(current.value);
|
|
3680
|
+
}
|
|
3681
|
+
} else if (e.key === "Escape") {
|
|
3682
|
+
e.preventDefault();
|
|
3683
|
+
setIsOpen(false);
|
|
3684
|
+
}
|
|
3685
|
+
};
|
|
2899
3686
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2900
3687
|
Popover,
|
|
2901
3688
|
{
|
|
@@ -2904,9 +3691,19 @@ var MultiSelect = React66.forwardRef(
|
|
|
2904
3691
|
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2905
3692
|
"div",
|
|
2906
3693
|
{
|
|
3694
|
+
tabIndex: disabled ? -1 : 0,
|
|
3695
|
+
role: "combobox",
|
|
3696
|
+
"aria-expanded": isOpen,
|
|
3697
|
+
"aria-haspopup": "listbox",
|
|
2907
3698
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
3699
|
+
onKeyDown: (e) => {
|
|
3700
|
+
if ((e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && !disabled) {
|
|
3701
|
+
e.preventDefault();
|
|
3702
|
+
setIsOpen(true);
|
|
3703
|
+
}
|
|
3704
|
+
},
|
|
2908
3705
|
className: cn(
|
|
2909
|
-
"min-h-10 py-1.5 px-3 rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)] flex flex-wrap items-center justify-between gap-1.5 text-sm transition-all shadow-inner cursor-pointer",
|
|
3706
|
+
"min-h-10 py-1.5 px-3 rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)] flex flex-wrap items-center justify-between gap-1.5 text-sm transition-all shadow-inner cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
2910
3707
|
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
2911
3708
|
),
|
|
2912
3709
|
children: [
|
|
@@ -2935,7 +3732,7 @@ var MultiSelect = React66.forwardRef(
|
|
|
2935
3732
|
"span",
|
|
2936
3733
|
{
|
|
2937
3734
|
onClick: clearAll,
|
|
2938
|
-
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
3735
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
2939
3736
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
2940
3737
|
}
|
|
2941
3738
|
),
|
|
@@ -2954,20 +3751,30 @@ var MultiSelect = React66.forwardRef(
|
|
|
2954
3751
|
autoFocus: true,
|
|
2955
3752
|
placeholder: searchPlaceholder,
|
|
2956
3753
|
value: search,
|
|
2957
|
-
onChange: (e) =>
|
|
3754
|
+
onChange: (e) => {
|
|
3755
|
+
setSearch(e.target.value);
|
|
3756
|
+
setHighlightedIndex(0);
|
|
3757
|
+
},
|
|
3758
|
+
onKeyDown: handleSearchKeyDown,
|
|
2958
3759
|
className: "w-full h-8 pl-8 pr-3 text-xs rounded-xl bg-[var(--ui-bg-secondary)] border border-[var(--ui-border-subtle)] focus:outline-none focus:border-[var(--ui-primary)] text-[var(--ui-text-primary)]"
|
|
2959
3760
|
}
|
|
2960
3761
|
)
|
|
2961
3762
|
] }),
|
|
2962
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt) => {
|
|
3763
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt, index) => {
|
|
2963
3764
|
const isChecked = selectedValues.includes(opt.value);
|
|
3765
|
+
const isHighlighted = highlightedIndex === index;
|
|
2964
3766
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2965
3767
|
"div",
|
|
2966
3768
|
{
|
|
2967
|
-
|
|
3769
|
+
role: "option",
|
|
3770
|
+
"aria-selected": isChecked,
|
|
3771
|
+
"aria-disabled": opt.disabled,
|
|
3772
|
+
onClick: () => !opt.disabled && toggleOption(opt.value),
|
|
2968
3773
|
className: cn(
|
|
2969
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium
|
|
2970
|
-
|
|
3774
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium transition-colors select-none",
|
|
3775
|
+
opt.disabled ? "opacity-40 cursor-not-allowed text-[var(--ui-text-muted)]" : "cursor-pointer",
|
|
3776
|
+
isChecked && "text-[var(--ui-primary)] font-semibold",
|
|
3777
|
+
isHighlighted && !opt.disabled && "bg-[var(--ui-primary)]/15"
|
|
2971
3778
|
),
|
|
2972
3779
|
children: [
|
|
2973
3780
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
@@ -3009,26 +3816,58 @@ var FileUpload = React66.forwardRef(
|
|
|
3009
3816
|
const [isDragOver, setIsDragOver] = React66.useState(false);
|
|
3010
3817
|
const [fileList, setFileList] = React66.useState([]);
|
|
3011
3818
|
const inputRef = React66.useRef(null);
|
|
3819
|
+
const fileListRef = React66.useRef(fileList);
|
|
3820
|
+
fileListRef.current = fileList;
|
|
3821
|
+
React66.useEffect(() => {
|
|
3822
|
+
return () => {
|
|
3823
|
+
fileListRef.current.forEach((item) => {
|
|
3824
|
+
if (item.previewUrl) {
|
|
3825
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
3826
|
+
}
|
|
3827
|
+
});
|
|
3828
|
+
};
|
|
3829
|
+
}, []);
|
|
3830
|
+
const matchesAccept = (file, acceptStr) => {
|
|
3831
|
+
if (!acceptStr) return true;
|
|
3832
|
+
const patterns = acceptStr.split(",").map((p) => p.trim().toLowerCase());
|
|
3833
|
+
const fileName = file.name.toLowerCase();
|
|
3834
|
+
const fileType = file.type.toLowerCase();
|
|
3835
|
+
return patterns.some((p) => {
|
|
3836
|
+
if (p.startsWith(".")) {
|
|
3837
|
+
return fileName.endsWith(p);
|
|
3838
|
+
}
|
|
3839
|
+
if (p.endsWith("/*")) {
|
|
3840
|
+
const baseType = p.replace("/*", "");
|
|
3841
|
+
return fileType.startsWith(baseType);
|
|
3842
|
+
}
|
|
3843
|
+
return fileType === p;
|
|
3844
|
+
});
|
|
3845
|
+
};
|
|
3012
3846
|
const processFiles = (incoming) => {
|
|
3013
3847
|
if (!incoming) return;
|
|
3014
3848
|
const validFiles = [];
|
|
3015
3849
|
const newUploaded = [];
|
|
3016
3850
|
Array.from(incoming).forEach((file) => {
|
|
3017
3851
|
if (fileList.length + newUploaded.length >= maxFiles) return;
|
|
3852
|
+
const isAccepted = matchesAccept(file, accept);
|
|
3018
3853
|
const isOversized = file.size > maxSizeMB * 1024 * 1024;
|
|
3854
|
+
const hasError = !isAccepted || isOversized;
|
|
3855
|
+
let errorMsg;
|
|
3856
|
+
if (!isAccepted) errorMsg = `File type not supported (${accept})`;
|
|
3857
|
+
else if (isOversized) errorMsg = `File exceeds ${maxSizeMB}MB limit`;
|
|
3019
3858
|
const fileObj = {
|
|
3020
3859
|
id: `${file.name}-${Date.now()}-${Math.random()}`,
|
|
3021
3860
|
file,
|
|
3022
3861
|
name: file.name,
|
|
3023
3862
|
size: file.size,
|
|
3024
3863
|
type: file.type,
|
|
3025
|
-
progress:
|
|
3026
|
-
status:
|
|
3027
|
-
errorMessage:
|
|
3028
|
-
previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
|
|
3864
|
+
progress: hasError ? 0 : 100,
|
|
3865
|
+
status: hasError ? "error" : "completed",
|
|
3866
|
+
errorMessage: errorMsg,
|
|
3867
|
+
previewUrl: file.type.startsWith("image/") && !hasError ? URL.createObjectURL(file) : void 0
|
|
3029
3868
|
};
|
|
3030
3869
|
newUploaded.push(fileObj);
|
|
3031
|
-
if (!
|
|
3870
|
+
if (!hasError) validFiles.push(file);
|
|
3032
3871
|
});
|
|
3033
3872
|
setFileList((prev) => [...prev, ...newUploaded]);
|
|
3034
3873
|
if (validFiles.length > 0) {
|
|
@@ -3054,7 +3893,13 @@ var FileUpload = React66.forwardRef(
|
|
|
3054
3893
|
if (inputRef.current) inputRef.current.value = "";
|
|
3055
3894
|
};
|
|
3056
3895
|
const removeFile = (id) => {
|
|
3057
|
-
setFileList((prev) =>
|
|
3896
|
+
setFileList((prev) => {
|
|
3897
|
+
const item = prev.find((f) => f.id === id);
|
|
3898
|
+
if (item?.previewUrl) {
|
|
3899
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
3900
|
+
}
|
|
3901
|
+
return prev.filter((f) => f.id !== id);
|
|
3902
|
+
});
|
|
3058
3903
|
onFileRemove?.(id);
|
|
3059
3904
|
};
|
|
3060
3905
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("w-full space-y-4", className), children: [
|
|
@@ -3162,8 +4007,12 @@ var NumberInput = React66.forwardRef(
|
|
|
3162
4007
|
className,
|
|
3163
4008
|
...props
|
|
3164
4009
|
}, ref) => {
|
|
3165
|
-
const [
|
|
3166
|
-
|
|
4010
|
+
const [value, setValue] = useControllableState({
|
|
4011
|
+
value: controlledVal,
|
|
4012
|
+
defaultValue,
|
|
4013
|
+
onChange,
|
|
4014
|
+
name: "NumberInput"
|
|
4015
|
+
});
|
|
3167
4016
|
const clamp = (num) => {
|
|
3168
4017
|
let clamped = Math.min(max, Math.max(min, num));
|
|
3169
4018
|
if (precision > 0) {
|
|
@@ -3174,14 +4023,12 @@ var NumberInput = React66.forwardRef(
|
|
|
3174
4023
|
const handleIncrement = () => {
|
|
3175
4024
|
if (disabled) return;
|
|
3176
4025
|
const next = clamp(value + step);
|
|
3177
|
-
|
|
3178
|
-
onChange?.(next);
|
|
4026
|
+
setValue(next);
|
|
3179
4027
|
};
|
|
3180
4028
|
const handleDecrement = () => {
|
|
3181
4029
|
if (disabled) return;
|
|
3182
4030
|
const next = clamp(value - step);
|
|
3183
|
-
|
|
3184
|
-
onChange?.(next);
|
|
4031
|
+
setValue(next);
|
|
3185
4032
|
};
|
|
3186
4033
|
const handleInputChange = (e) => {
|
|
3187
4034
|
const val = e.target.value;
|
|
@@ -3191,8 +4038,7 @@ var NumberInput = React66.forwardRef(
|
|
|
3191
4038
|
const parsed = parseFloat(val);
|
|
3192
4039
|
if (!isNaN(parsed)) {
|
|
3193
4040
|
const next = clamp(parsed);
|
|
3194
|
-
|
|
3195
|
-
onChange?.(next);
|
|
4041
|
+
setValue(next);
|
|
3196
4042
|
}
|
|
3197
4043
|
};
|
|
3198
4044
|
const handleKeyDown = (e) => {
|
|
@@ -3213,7 +4059,7 @@ var NumberInput = React66.forwardRef(
|
|
|
3213
4059
|
type: "button",
|
|
3214
4060
|
disabled: disabled || value <= min,
|
|
3215
4061
|
onClick: handleDecrement,
|
|
3216
|
-
className: "px-3 h-full flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] disabled:opacity-30 disabled:pointer-events-none text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors border-r border-[var(--ui-border-subtle)]",
|
|
4062
|
+
className: "px-3 h-full flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] disabled:opacity-30 disabled:pointer-events-none text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors border-r border-[var(--ui-border-subtle)] cursor-pointer",
|
|
3217
4063
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3.5 h-3.5" })
|
|
3218
4064
|
}
|
|
3219
4065
|
),
|
|
@@ -3236,7 +4082,7 @@ var NumberInput = React66.forwardRef(
|
|
|
3236
4082
|
type: "button",
|
|
3237
4083
|
disabled: disabled || value >= max,
|
|
3238
4084
|
onClick: handleIncrement,
|
|
3239
|
-
className: "px-3 h-full flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] disabled:opacity-30 disabled:pointer-events-none text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors border-l border-[var(--ui-border-subtle)]",
|
|
4085
|
+
className: "px-3 h-full flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] disabled:opacity-30 disabled:pointer-events-none text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors border-l border-[var(--ui-border-subtle)] cursor-pointer",
|
|
3240
4086
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3.5 h-3.5" })
|
|
3241
4087
|
}
|
|
3242
4088
|
)
|
|
@@ -3267,7 +4113,7 @@ var NumberInput = React66.forwardRef(
|
|
|
3267
4113
|
type: "button",
|
|
3268
4114
|
disabled: disabled || value >= max,
|
|
3269
4115
|
onClick: handleIncrement,
|
|
3270
|
-
className: "px-1.5 h-1/2 flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] rounded-t text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] disabled:opacity-30 transition-colors",
|
|
4116
|
+
className: "px-1.5 h-1/2 flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] rounded-t text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] disabled:opacity-30 transition-colors cursor-pointer",
|
|
3271
4117
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3 h-3" })
|
|
3272
4118
|
}
|
|
3273
4119
|
),
|
|
@@ -3277,7 +4123,7 @@ var NumberInput = React66.forwardRef(
|
|
|
3277
4123
|
type: "button",
|
|
3278
4124
|
disabled: disabled || value <= min,
|
|
3279
4125
|
onClick: handleDecrement,
|
|
3280
|
-
className: "px-1.5 h-1/2 flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] rounded-b text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] disabled:opacity-30 transition-colors",
|
|
4126
|
+
className: "px-1.5 h-1/2 flex items-center justify-center hover:bg-[var(--ui-bg-tertiary)] rounded-b text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] disabled:opacity-30 transition-colors cursor-pointer",
|
|
3281
4127
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3 h-3" })
|
|
3282
4128
|
}
|
|
3283
4129
|
)
|
|
@@ -3306,19 +4152,27 @@ var CurrencyInput = React66.forwardRef(
|
|
|
3306
4152
|
}, ref) => {
|
|
3307
4153
|
const symbol = currencySymbol || (currency === "VND" ? "\u20AB" : currency === "EUR" ? "\u20AC" : "$");
|
|
3308
4154
|
const isSuffix = position === "suffix" || currency === "VND";
|
|
3309
|
-
const [
|
|
3310
|
-
|
|
3311
|
-
|
|
4155
|
+
const [value, setValue, isControlled] = useControllableState({
|
|
4156
|
+
value: controlledVal,
|
|
4157
|
+
defaultValue,
|
|
4158
|
+
onChange,
|
|
4159
|
+
name: "CurrencyInput"
|
|
4160
|
+
});
|
|
4161
|
+
const [internalInputString, setInternalInputString] = React66.useState(formatCurrencyString(value));
|
|
3312
4162
|
React66.useEffect(() => {
|
|
3313
|
-
|
|
3314
|
-
|
|
4163
|
+
if (!isControlled) {
|
|
4164
|
+
setInternalInputString(formatCurrencyString(value));
|
|
4165
|
+
}
|
|
4166
|
+
}, [value, isControlled]);
|
|
4167
|
+
const displayVal = isControlled ? formatCurrencyString(value) : internalInputString;
|
|
3315
4168
|
const handleInputChange = (e) => {
|
|
3316
4169
|
const raw = e.target.value.replace(/[^0-9.]/g, "");
|
|
3317
4170
|
const parsed = parseFloat(raw);
|
|
3318
4171
|
const num = isNaN(parsed) ? 0 : parsed;
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
4172
|
+
if (!isControlled) {
|
|
4173
|
+
setInternalInputString(raw ? formatCurrencyString(num) : "");
|
|
4174
|
+
}
|
|
4175
|
+
setValue(num);
|
|
3322
4176
|
};
|
|
3323
4177
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative w-full flex items-center", className), children: [
|
|
3324
4178
|
!isSuffix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-3.5 text-sm font-bold text-[var(--ui-text-muted)] pointer-events-none font-mono", children: symbol }),
|
|
@@ -3440,6 +4294,7 @@ __export(overlays_exports, {
|
|
|
3440
4294
|
ModalTitle: () => ModalTitle,
|
|
3441
4295
|
Popover: () => Popover,
|
|
3442
4296
|
Portal: () => Portal,
|
|
4297
|
+
Toast: () => Toast,
|
|
3443
4298
|
ToasterProvider: () => ToasterProvider,
|
|
3444
4299
|
Tooltip: () => Tooltip,
|
|
3445
4300
|
drawerVariants: () => drawerVariants,
|
|
@@ -3450,37 +4305,112 @@ __export(overlays_exports, {
|
|
|
3450
4305
|
useModal: () => useModal,
|
|
3451
4306
|
useToast: () => useToast
|
|
3452
4307
|
});
|
|
3453
|
-
var FOCUSABLE_SELECTOR =
|
|
3454
|
-
|
|
4308
|
+
var FOCUSABLE_SELECTOR = [
|
|
4309
|
+
"a[href]",
|
|
4310
|
+
"button:not([disabled])",
|
|
4311
|
+
"textarea:not([disabled])",
|
|
4312
|
+
"input:not([disabled])",
|
|
4313
|
+
"select:not([disabled])",
|
|
4314
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
4315
|
+
'[contenteditable="true"]'
|
|
4316
|
+
].join(", ");
|
|
4317
|
+
var trapStack = [];
|
|
4318
|
+
function FocusTrap({
|
|
4319
|
+
children,
|
|
4320
|
+
active = true,
|
|
4321
|
+
autoFocus = true,
|
|
4322
|
+
restoreFocus = true
|
|
4323
|
+
}) {
|
|
3455
4324
|
const rootRef = React66.useRef(null);
|
|
4325
|
+
const previousActiveElementRef = React66.useRef(null);
|
|
3456
4326
|
React66.useEffect(() => {
|
|
3457
4327
|
if (!active) return;
|
|
4328
|
+
if (typeof document !== "undefined") {
|
|
4329
|
+
previousActiveElementRef.current = document.activeElement;
|
|
4330
|
+
}
|
|
3458
4331
|
const root = rootRef.current;
|
|
3459
4332
|
if (!root) return;
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
4333
|
+
trapStack.push(root);
|
|
4334
|
+
if (autoFocus) {
|
|
4335
|
+
const timer = setTimeout(() => {
|
|
4336
|
+
const focusable = root.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
4337
|
+
if (focusable.length > 0) {
|
|
4338
|
+
focusable[0].focus();
|
|
4339
|
+
} else if (typeof root.focus === "function") {
|
|
4340
|
+
root.focus();
|
|
4341
|
+
}
|
|
4342
|
+
}, 0);
|
|
4343
|
+
return () => clearTimeout(timer);
|
|
3465
4344
|
}
|
|
4345
|
+
return () => {
|
|
4346
|
+
const idx = trapStack.indexOf(root);
|
|
4347
|
+
if (idx !== -1) trapStack.splice(idx, 1);
|
|
4348
|
+
};
|
|
4349
|
+
}, [active, autoFocus]);
|
|
4350
|
+
React66.useEffect(() => {
|
|
4351
|
+
if (!active) return;
|
|
3466
4352
|
const handleKeyDown = (e) => {
|
|
3467
4353
|
if (e.key !== "Tab") return;
|
|
4354
|
+
const root = rootRef.current;
|
|
4355
|
+
if (!root) return;
|
|
4356
|
+
if (trapStack.length > 0 && trapStack[trapStack.length - 1] !== root) {
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
const focusable = Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
|
|
4360
|
+
(el) => el.offsetParent !== null || el.getClientRects().length > 0
|
|
4361
|
+
);
|
|
4362
|
+
if (focusable.length === 0) {
|
|
4363
|
+
e.preventDefault();
|
|
4364
|
+
root.focus();
|
|
4365
|
+
return;
|
|
4366
|
+
}
|
|
4367
|
+
const first = focusable[0];
|
|
4368
|
+
const last = focusable[focusable.length - 1];
|
|
3468
4369
|
if (e.shiftKey) {
|
|
3469
|
-
if (document.activeElement === first) {
|
|
4370
|
+
if (document.activeElement === first || !root.contains(document.activeElement)) {
|
|
3470
4371
|
e.preventDefault();
|
|
3471
|
-
last
|
|
4372
|
+
last.focus();
|
|
3472
4373
|
}
|
|
3473
4374
|
} else {
|
|
3474
|
-
if (document.activeElement === last) {
|
|
4375
|
+
if (document.activeElement === last || !root.contains(document.activeElement)) {
|
|
3475
4376
|
e.preventDefault();
|
|
3476
|
-
first
|
|
4377
|
+
first.focus();
|
|
3477
4378
|
}
|
|
3478
4379
|
}
|
|
3479
4380
|
};
|
|
3480
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
3481
|
-
return () =>
|
|
3482
|
-
|
|
3483
|
-
|
|
4381
|
+
window.addEventListener("keydown", handleKeyDown, true);
|
|
4382
|
+
return () => {
|
|
4383
|
+
window.removeEventListener("keydown", handleKeyDown, true);
|
|
4384
|
+
const root = rootRef.current;
|
|
4385
|
+
if (root) {
|
|
4386
|
+
const idx = trapStack.indexOf(root);
|
|
4387
|
+
if (idx !== -1) trapStack.splice(idx, 1);
|
|
4388
|
+
}
|
|
4389
|
+
if (restoreFocus && previousActiveElementRef.current && typeof previousActiveElementRef.current.focus === "function") {
|
|
4390
|
+
previousActiveElementRef.current.focus();
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
}, [active, restoreFocus]);
|
|
4394
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: rootRef, tabIndex: -1, className: "contents", style: { outline: "none" }, children });
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
// src/utils/scroll-lock.ts
|
|
4398
|
+
var lockCount = 0;
|
|
4399
|
+
var previousOverflow = "";
|
|
4400
|
+
function lockBodyScroll() {
|
|
4401
|
+
if (typeof document === "undefined") return;
|
|
4402
|
+
if (lockCount === 0) {
|
|
4403
|
+
previousOverflow = document.body.style.overflow;
|
|
4404
|
+
document.body.style.overflow = "hidden";
|
|
4405
|
+
}
|
|
4406
|
+
lockCount++;
|
|
4407
|
+
}
|
|
4408
|
+
function unlockBodyScroll() {
|
|
4409
|
+
if (typeof document === "undefined") return;
|
|
4410
|
+
lockCount = Math.max(0, lockCount - 1);
|
|
4411
|
+
if (lockCount === 0) {
|
|
4412
|
+
document.body.style.overflow = previousOverflow;
|
|
4413
|
+
}
|
|
3484
4414
|
}
|
|
3485
4415
|
var ModalContext = React66.createContext(null);
|
|
3486
4416
|
function useModal() {
|
|
@@ -3495,33 +4425,76 @@ function Modal({
|
|
|
3495
4425
|
onClose,
|
|
3496
4426
|
children,
|
|
3497
4427
|
closeOnEsc = true,
|
|
3498
|
-
closeOnOverlayClick = true
|
|
4428
|
+
closeOnOverlayClick = true,
|
|
4429
|
+
role = "dialog",
|
|
4430
|
+
"aria-label": ariaLabel,
|
|
4431
|
+
"aria-labelledby": customLabelledBy,
|
|
4432
|
+
"aria-describedby": customDescribedBy
|
|
3499
4433
|
}) {
|
|
4434
|
+
const generatedId = React66.useId();
|
|
4435
|
+
const titleId = customLabelledBy || `modal-title-${generatedId}`;
|
|
4436
|
+
const descriptionId = customDescribedBy || `modal-desc-${generatedId}`;
|
|
4437
|
+
const [mountedTitleId, setMountedTitleId] = React66.useState(null);
|
|
4438
|
+
const [mountedDescId, setMountedDescId] = React66.useState(null);
|
|
4439
|
+
const registerTitle = React66.useCallback(
|
|
4440
|
+
(id) => {
|
|
4441
|
+
const activeId = id || titleId;
|
|
4442
|
+
setMountedTitleId(activeId);
|
|
4443
|
+
return () => setMountedTitleId(null);
|
|
4444
|
+
},
|
|
4445
|
+
[titleId]
|
|
4446
|
+
);
|
|
4447
|
+
const registerDescription = React66.useCallback(
|
|
4448
|
+
(id) => {
|
|
4449
|
+
const activeId = id || descriptionId;
|
|
4450
|
+
setMountedDescId(activeId);
|
|
4451
|
+
return () => setMountedDescId(null);
|
|
4452
|
+
},
|
|
4453
|
+
[descriptionId]
|
|
4454
|
+
);
|
|
3500
4455
|
React66.useEffect(() => {
|
|
3501
4456
|
if (!isOpen) return;
|
|
4457
|
+
lockBodyScroll();
|
|
3502
4458
|
const handleKeyDown = (e) => {
|
|
3503
4459
|
if (closeOnEsc && e.key === "Escape") {
|
|
3504
4460
|
onClose();
|
|
3505
4461
|
}
|
|
3506
4462
|
};
|
|
3507
|
-
const originalOverflow = document.body.style.overflow;
|
|
3508
|
-
document.body.style.overflow = "hidden";
|
|
3509
4463
|
window.addEventListener("keydown", handleKeyDown);
|
|
3510
4464
|
return () => {
|
|
3511
4465
|
window.removeEventListener("keydown", handleKeyDown);
|
|
3512
|
-
|
|
4466
|
+
unlockBodyScroll();
|
|
3513
4467
|
};
|
|
3514
4468
|
}, [isOpen, closeOnEsc, onClose]);
|
|
3515
4469
|
if (!isOpen) return null;
|
|
3516
|
-
|
|
3517
|
-
|
|
4470
|
+
const resolvedLabelledBy = customLabelledBy || (ariaLabel ? void 0 : mountedTitleId || void 0);
|
|
4471
|
+
const resolvedDescribedBy = customDescribedBy || (mountedDescId || void 0);
|
|
4472
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4473
|
+
ModalContext.Provider,
|
|
3518
4474
|
{
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
4475
|
+
value: {
|
|
4476
|
+
isOpen,
|
|
4477
|
+
onClose,
|
|
4478
|
+
closeOnOverlayClick,
|
|
4479
|
+
titleId,
|
|
4480
|
+
descriptionId,
|
|
4481
|
+
registerTitle,
|
|
4482
|
+
registerDescription
|
|
4483
|
+
},
|
|
4484
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4485
|
+
"div",
|
|
4486
|
+
{
|
|
4487
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6",
|
|
4488
|
+
role,
|
|
4489
|
+
"aria-modal": "true",
|
|
4490
|
+
"aria-label": ariaLabel,
|
|
4491
|
+
"aria-labelledby": resolvedLabelledBy,
|
|
4492
|
+
"aria-describedby": resolvedDescribedBy,
|
|
4493
|
+
children
|
|
4494
|
+
}
|
|
4495
|
+
) })
|
|
3523
4496
|
}
|
|
3524
|
-
) })
|
|
4497
|
+
) });
|
|
3525
4498
|
}
|
|
3526
4499
|
var ModalOverlay = React66.forwardRef(
|
|
3527
4500
|
({ className, onClick, ...props }, ref) => {
|
|
@@ -3584,14 +4557,42 @@ var ModalHeader = React66.forwardRef(
|
|
|
3584
4557
|
);
|
|
3585
4558
|
ModalHeader.displayName = "ModalHeader";
|
|
3586
4559
|
var ModalTitle = React66.forwardRef(
|
|
3587
|
-
({ className, children, ...props }, ref) => {
|
|
3588
|
-
|
|
4560
|
+
({ className, children, id, ...props }, ref) => {
|
|
4561
|
+
const { titleId, registerTitle } = useModal();
|
|
4562
|
+
const activeId = id || titleId;
|
|
4563
|
+
React66.useEffect(() => {
|
|
4564
|
+
return registerTitle(activeId);
|
|
4565
|
+
}, [registerTitle, activeId]);
|
|
4566
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4567
|
+
"h2",
|
|
4568
|
+
{
|
|
4569
|
+
ref,
|
|
4570
|
+
id: activeId,
|
|
4571
|
+
className: cn("text-lg sm:text-xl font-bold tracking-tight text-[var(--ui-text-primary)]", className),
|
|
4572
|
+
...props,
|
|
4573
|
+
children
|
|
4574
|
+
}
|
|
4575
|
+
);
|
|
3589
4576
|
}
|
|
3590
4577
|
);
|
|
3591
4578
|
ModalTitle.displayName = "ModalTitle";
|
|
3592
4579
|
var ModalDescription = React66.forwardRef(
|
|
3593
|
-
({ className, children, ...props }, ref) => {
|
|
3594
|
-
|
|
4580
|
+
({ className, children, id, ...props }, ref) => {
|
|
4581
|
+
const { descriptionId, registerDescription } = useModal();
|
|
4582
|
+
const activeId = id || descriptionId;
|
|
4583
|
+
React66.useEffect(() => {
|
|
4584
|
+
return registerDescription(activeId);
|
|
4585
|
+
}, [registerDescription, activeId]);
|
|
4586
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4587
|
+
"p",
|
|
4588
|
+
{
|
|
4589
|
+
ref,
|
|
4590
|
+
id: activeId,
|
|
4591
|
+
className: cn("text-sm text-[var(--ui-text-muted)]", className),
|
|
4592
|
+
...props,
|
|
4593
|
+
children
|
|
4594
|
+
}
|
|
4595
|
+
);
|
|
3595
4596
|
}
|
|
3596
4597
|
);
|
|
3597
4598
|
ModalDescription.displayName = "ModalDescription";
|
|
@@ -3621,7 +4622,7 @@ var ModalCloseButton = React66.forwardRef(
|
|
|
3621
4622
|
},
|
|
3622
4623
|
"aria-label": "Close dialog",
|
|
3623
4624
|
className: cn(
|
|
3624
|
-
"absolute top-4 right-4 p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors",
|
|
4625
|
+
"absolute top-4 right-4 p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
3625
4626
|
className
|
|
3626
4627
|
),
|
|
3627
4628
|
...props,
|
|
@@ -3640,32 +4641,43 @@ function AlertDialog({
|
|
|
3640
4641
|
confirmText = "Confirm",
|
|
3641
4642
|
cancelText = "Cancel",
|
|
3642
4643
|
variant = "danger",
|
|
3643
|
-
isLoading = false
|
|
4644
|
+
isLoading = false,
|
|
4645
|
+
closeOnOverlayClick = false
|
|
3644
4646
|
}) {
|
|
3645
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
onClick:
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
4647
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4648
|
+
Modal,
|
|
4649
|
+
{
|
|
4650
|
+
isOpen,
|
|
4651
|
+
onClose,
|
|
4652
|
+
role: "alertdialog",
|
|
4653
|
+
closeOnOverlayClick,
|
|
4654
|
+
children: [
|
|
4655
|
+
/* @__PURE__ */ jsxRuntime.jsx(ModalOverlay, {}),
|
|
4656
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ModalContent, { size: "sm", children: [
|
|
4657
|
+
/* @__PURE__ */ jsxRuntime.jsx(ModalCloseButton, {}),
|
|
4658
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ModalHeader, { children: [
|
|
4659
|
+
/* @__PURE__ */ jsxRuntime.jsx(ModalTitle, { children: title }),
|
|
4660
|
+
/* @__PURE__ */ jsxRuntime.jsx(ModalDescription, { children: description })
|
|
4661
|
+
] }),
|
|
4662
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ModalFooter, { children: [
|
|
4663
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: isLoading, autoFocus: true, children: cancelText }),
|
|
4664
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4665
|
+
Button,
|
|
4666
|
+
{
|
|
4667
|
+
variant: variant === "danger" ? "danger" : "primary",
|
|
4668
|
+
size: "sm",
|
|
4669
|
+
loading: isLoading,
|
|
4670
|
+
onClick: onConfirm,
|
|
4671
|
+
children: confirmText
|
|
4672
|
+
}
|
|
4673
|
+
)
|
|
4674
|
+
] })
|
|
4675
|
+
] })
|
|
4676
|
+
]
|
|
4677
|
+
}
|
|
4678
|
+
);
|
|
3668
4679
|
}
|
|
4680
|
+
AlertDialog.displayName = "AlertDialog";
|
|
3669
4681
|
var DrawerContext = React66.createContext(null);
|
|
3670
4682
|
function useDrawer() {
|
|
3671
4683
|
const context = React66.useContext(DrawerContext);
|
|
@@ -3695,54 +4707,68 @@ function Drawer({
|
|
|
3695
4707
|
onClose,
|
|
3696
4708
|
placement = "right",
|
|
3697
4709
|
children,
|
|
3698
|
-
closeOnEsc = true
|
|
4710
|
+
closeOnEsc = true,
|
|
4711
|
+
"aria-label": ariaLabel,
|
|
4712
|
+
"aria-labelledby": customLabelledBy
|
|
3699
4713
|
}) {
|
|
4714
|
+
const generatedId = React66.useId();
|
|
4715
|
+
const titleId = customLabelledBy || `drawer-title-${generatedId}`;
|
|
3700
4716
|
React66.useEffect(() => {
|
|
3701
4717
|
if (!isOpen) return;
|
|
4718
|
+
lockBodyScroll();
|
|
3702
4719
|
const handleKeyDown = (e) => {
|
|
3703
4720
|
if (closeOnEsc && e.key === "Escape") {
|
|
3704
4721
|
onClose();
|
|
3705
4722
|
}
|
|
3706
4723
|
};
|
|
3707
|
-
const originalOverflow = document.body.style.overflow;
|
|
3708
|
-
document.body.style.overflow = "hidden";
|
|
3709
4724
|
window.addEventListener("keydown", handleKeyDown);
|
|
3710
4725
|
return () => {
|
|
3711
4726
|
window.removeEventListener("keydown", handleKeyDown);
|
|
3712
|
-
|
|
4727
|
+
unlockBodyScroll();
|
|
3713
4728
|
};
|
|
3714
4729
|
}, [isOpen, closeOnEsc, onClose]);
|
|
3715
4730
|
if (!isOpen) return null;
|
|
3716
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(DrawerContext.Provider, { value: { isOpen, onClose }, children: /* @__PURE__ */ jsxRuntime.jsx(FocusTrap, { active: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
4731
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(DrawerContext.Provider, { value: { isOpen, onClose, titleId }, children: /* @__PURE__ */ jsxRuntime.jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4732
|
+
"div",
|
|
4733
|
+
{
|
|
4734
|
+
className: "fixed inset-0 z-50 overflow-hidden",
|
|
4735
|
+
role: "dialog",
|
|
4736
|
+
"aria-modal": "true",
|
|
4737
|
+
"aria-label": ariaLabel,
|
|
4738
|
+
"aria-labelledby": ariaLabel ? void 0 : titleId,
|
|
4739
|
+
children: [
|
|
4740
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4741
|
+
"div",
|
|
4742
|
+
{
|
|
4743
|
+
"aria-hidden": "true",
|
|
4744
|
+
onClick: onClose,
|
|
4745
|
+
className: "fixed inset-0 bg-[var(--ui-bg-overlay)] backdrop-blur-sm transition-opacity animate-in fade-in duration-200"
|
|
4746
|
+
}
|
|
4747
|
+
),
|
|
4748
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(drawerVariants({ placement })), children })
|
|
4749
|
+
]
|
|
4750
|
+
}
|
|
4751
|
+
) }) }) });
|
|
3727
4752
|
}
|
|
3728
4753
|
var DrawerHeader = React66.forwardRef(
|
|
3729
|
-
({ className, children, ...props }, ref) => {
|
|
3730
|
-
const { onClose } = useDrawer();
|
|
4754
|
+
({ className, children, id, ...props }, ref) => {
|
|
4755
|
+
const { onClose, titleId } = useDrawer();
|
|
3731
4756
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3732
4757
|
"div",
|
|
3733
4758
|
{
|
|
3734
4759
|
ref,
|
|
4760
|
+
id: id || titleId,
|
|
3735
4761
|
className: cn("flex items-center justify-between p-5 border-b border-[var(--ui-border-subtle)]", className),
|
|
3736
4762
|
...props,
|
|
3737
4763
|
children: [
|
|
3738
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children }),
|
|
4764
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 font-semibold text-base text-[var(--ui-text-primary)]", children }),
|
|
3739
4765
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3740
4766
|
"button",
|
|
3741
4767
|
{
|
|
3742
4768
|
type: "button",
|
|
3743
4769
|
onClick: onClose,
|
|
3744
4770
|
"aria-label": "Close drawer",
|
|
3745
|
-
className: "p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors ml-4",
|
|
4771
|
+
className: "p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors ml-4 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
3746
4772
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
3747
4773
|
}
|
|
3748
4774
|
)
|
|
@@ -3793,40 +4819,57 @@ function Tooltip({
|
|
|
3793
4819
|
children,
|
|
3794
4820
|
placement = "top",
|
|
3795
4821
|
delayDuration = 200,
|
|
3796
|
-
className
|
|
4822
|
+
className,
|
|
4823
|
+
id: customId
|
|
3797
4824
|
}) {
|
|
4825
|
+
const generatedId = React66.useId();
|
|
4826
|
+
const tooltipId = customId || `tooltip-${generatedId}`;
|
|
3798
4827
|
const [isVisible, setIsVisible] = React66.useState(false);
|
|
3799
4828
|
const [coords, setCoords] = React66.useState({ top: 0, left: 0 });
|
|
3800
4829
|
const timeoutRef = React66.useRef(null);
|
|
3801
4830
|
const triggerRef = React66.useRef(null);
|
|
4831
|
+
const calculatePosition = () => {
|
|
4832
|
+
if (triggerRef.current) {
|
|
4833
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
4834
|
+
let top = 0;
|
|
4835
|
+
let left = 0;
|
|
4836
|
+
if (placement === "top") {
|
|
4837
|
+
top = rect.top + window.scrollY;
|
|
4838
|
+
left = rect.left + rect.width / 2 + window.scrollX;
|
|
4839
|
+
} else if (placement === "bottom") {
|
|
4840
|
+
top = rect.bottom + window.scrollY;
|
|
4841
|
+
left = rect.left + rect.width / 2 + window.scrollX;
|
|
4842
|
+
} else if (placement === "left") {
|
|
4843
|
+
top = rect.top + rect.height / 2 + window.scrollY;
|
|
4844
|
+
left = rect.left + window.scrollX;
|
|
4845
|
+
} else if (placement === "right") {
|
|
4846
|
+
top = rect.top + rect.height / 2 + window.scrollY;
|
|
4847
|
+
left = rect.right + window.scrollX;
|
|
4848
|
+
}
|
|
4849
|
+
setCoords({ top, left });
|
|
4850
|
+
}
|
|
4851
|
+
};
|
|
3802
4852
|
const handleMouseEnter = () => {
|
|
4853
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
3803
4854
|
timeoutRef.current = setTimeout(() => {
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
let top = 0;
|
|
3807
|
-
let left = 0;
|
|
3808
|
-
if (placement === "top") {
|
|
3809
|
-
top = rect.top + window.scrollY;
|
|
3810
|
-
left = rect.left + rect.width / 2 + window.scrollX;
|
|
3811
|
-
} else if (placement === "bottom") {
|
|
3812
|
-
top = rect.bottom + window.scrollY;
|
|
3813
|
-
left = rect.left + rect.width / 2 + window.scrollX;
|
|
3814
|
-
} else if (placement === "left") {
|
|
3815
|
-
top = rect.top + rect.height / 2 + window.scrollY;
|
|
3816
|
-
left = rect.left + window.scrollX;
|
|
3817
|
-
} else if (placement === "right") {
|
|
3818
|
-
top = rect.top + rect.height / 2 + window.scrollY;
|
|
3819
|
-
left = rect.right + window.scrollX;
|
|
3820
|
-
}
|
|
3821
|
-
setCoords({ top, left });
|
|
3822
|
-
setIsVisible(true);
|
|
3823
|
-
}
|
|
4855
|
+
calculatePosition();
|
|
4856
|
+
setIsVisible(true);
|
|
3824
4857
|
}, delayDuration);
|
|
3825
4858
|
};
|
|
3826
4859
|
const handleMouseLeave = () => {
|
|
3827
4860
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
3828
4861
|
setIsVisible(false);
|
|
3829
4862
|
};
|
|
4863
|
+
React66.useEffect(() => {
|
|
4864
|
+
if (!isVisible) return;
|
|
4865
|
+
const handleKeyDown = (e) => {
|
|
4866
|
+
if (e.key === "Escape") {
|
|
4867
|
+
setIsVisible(false);
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
4871
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
4872
|
+
}, [isVisible]);
|
|
3830
4873
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3831
4874
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3832
4875
|
"div",
|
|
@@ -3836,6 +4879,7 @@ function Tooltip({
|
|
|
3836
4879
|
onMouseLeave: handleMouseLeave,
|
|
3837
4880
|
onFocus: handleMouseEnter,
|
|
3838
4881
|
onBlur: handleMouseLeave,
|
|
4882
|
+
"aria-describedby": isVisible ? tooltipId : void 0,
|
|
3839
4883
|
className: "inline-flex cursor-pointer",
|
|
3840
4884
|
children
|
|
3841
4885
|
}
|
|
@@ -3843,8 +4887,9 @@ function Tooltip({
|
|
|
3843
4887
|
isVisible && /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3844
4888
|
"div",
|
|
3845
4889
|
{
|
|
3846
|
-
|
|
4890
|
+
id: tooltipId,
|
|
3847
4891
|
role: "tooltip",
|
|
4892
|
+
style: { top: `${coords.top}px`, left: `${coords.left}px`, position: "absolute" },
|
|
3848
4893
|
className: cn(tooltipVariants({ placement }), className),
|
|
3849
4894
|
children: content
|
|
3850
4895
|
}
|
|
@@ -3881,6 +4926,38 @@ var toastIcons = {
|
|
|
3881
4926
|
warning: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "w-5 h-5 text-[var(--ui-warning)] flex-shrink-0" }),
|
|
3882
4927
|
error: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-5 h-5 text-[var(--ui-error)] flex-shrink-0" })
|
|
3883
4928
|
};
|
|
4929
|
+
var Toast = React66__default.default.forwardRef(
|
|
4930
|
+
({ className, title, description, type = "info", onDismiss, ...props }, ref) => {
|
|
4931
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4932
|
+
"div",
|
|
4933
|
+
{
|
|
4934
|
+
ref,
|
|
4935
|
+
role: "status",
|
|
4936
|
+
"aria-live": "polite",
|
|
4937
|
+
className: cn(toastVariants({ type }), className),
|
|
4938
|
+
...props,
|
|
4939
|
+
children: [
|
|
4940
|
+
toastIcons[type || "info"],
|
|
4941
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-1", children: [
|
|
4942
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-[var(--ui-text-primary)]", children: title }),
|
|
4943
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-[var(--ui-text-muted)]", children: description })
|
|
4944
|
+
] }),
|
|
4945
|
+
onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4946
|
+
"button",
|
|
4947
|
+
{
|
|
4948
|
+
type: "button",
|
|
4949
|
+
onClick: onDismiss,
|
|
4950
|
+
"aria-label": "Dismiss toast",
|
|
4951
|
+
className: "p-1 rounded text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors cursor-pointer",
|
|
4952
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
4953
|
+
}
|
|
4954
|
+
)
|
|
4955
|
+
]
|
|
4956
|
+
}
|
|
4957
|
+
);
|
|
4958
|
+
}
|
|
4959
|
+
);
|
|
4960
|
+
Toast.displayName = "Toast";
|
|
3884
4961
|
function ToasterProvider({ children, position = "bottom-right" }) {
|
|
3885
4962
|
const [toasts, setToasts] = React66.useState([]);
|
|
3886
4963
|
const dismiss = React66.useCallback((id) => {
|
|
@@ -3953,66 +5030,86 @@ var BottomSheet = React66.forwardRef(
|
|
|
3953
5030
|
description,
|
|
3954
5031
|
className,
|
|
3955
5032
|
showHandle = true,
|
|
3956
|
-
maxHeight = "max-h-[85vh]"
|
|
5033
|
+
maxHeight = "max-h-[85vh]",
|
|
5034
|
+
"aria-label": ariaLabel
|
|
3957
5035
|
}, ref) => {
|
|
3958
|
-
React66.
|
|
5036
|
+
const generatedId = React66.useId();
|
|
5037
|
+
const titleId = `bottomsheet-title-${generatedId}`;
|
|
5038
|
+
const descId = `bottomsheet-desc-${generatedId}`;
|
|
3959
5039
|
React66.useEffect(() => {
|
|
5040
|
+
if (!isOpen) return;
|
|
5041
|
+
lockBodyScroll();
|
|
3960
5042
|
const handleKeyDown = (e) => {
|
|
3961
|
-
if (e.key === "Escape"
|
|
5043
|
+
if (e.key === "Escape") {
|
|
3962
5044
|
onClose();
|
|
3963
5045
|
}
|
|
3964
5046
|
};
|
|
3965
5047
|
window.addEventListener("keydown", handleKeyDown);
|
|
3966
|
-
return () =>
|
|
5048
|
+
return () => {
|
|
5049
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
5050
|
+
unlockBodyScroll();
|
|
5051
|
+
};
|
|
3967
5052
|
}, [isOpen, onClose]);
|
|
3968
5053
|
if (!isOpen) return null;
|
|
3969
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(FocusTrap, { active: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
5054
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5055
|
+
"div",
|
|
5056
|
+
{
|
|
5057
|
+
className: "fixed inset-0 z-50 flex flex-col justify-end",
|
|
5058
|
+
role: "dialog",
|
|
5059
|
+
"aria-modal": "true",
|
|
5060
|
+
"aria-label": ariaLabel,
|
|
5061
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
5062
|
+
"aria-describedby": description ? descId : void 0,
|
|
5063
|
+
children: [
|
|
5064
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5065
|
+
"div",
|
|
5066
|
+
{
|
|
5067
|
+
onClick: onClose,
|
|
5068
|
+
className: "fixed inset-0 bg-black/60 backdrop-blur-md transition-opacity animate-in fade-in duration-300",
|
|
5069
|
+
"aria-hidden": "true"
|
|
5070
|
+
}
|
|
3986
5071
|
),
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
5072
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5073
|
+
"div",
|
|
5074
|
+
{
|
|
5075
|
+
ref,
|
|
5076
|
+
className: cn(
|
|
5077
|
+
"relative z-50 w-full max-w-2xl mx-auto rounded-t-[2rem] border-t border-x border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] backdrop-blur-3xl shadow-2xl overflow-hidden flex flex-col transition-transform animate-in slide-in-from-bottom duration-300",
|
|
5078
|
+
maxHeight,
|
|
5079
|
+
className
|
|
5080
|
+
),
|
|
5081
|
+
children: [
|
|
5082
|
+
showHandle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5083
|
+
"div",
|
|
5084
|
+
{
|
|
5085
|
+
onClick: onClose,
|
|
5086
|
+
className: "w-full py-3 flex items-center justify-center cursor-grab active:cursor-grabbing",
|
|
5087
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-1.5 rounded-full bg-[var(--ui-text-muted)]/40 hover:bg-[var(--ui-text-muted)] transition-colors" })
|
|
5088
|
+
}
|
|
5089
|
+
),
|
|
5090
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 pb-3 flex items-center justify-between border-b border-[var(--ui-border-subtle)]", children: [
|
|
5091
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
5092
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h3", { id: titleId, className: "text-base font-bold tracking-tight text-[var(--ui-text-primary)]", children: title }),
|
|
5093
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { id: descId, className: "text-xs text-[var(--ui-text-muted)]", children: description })
|
|
5094
|
+
] }),
|
|
5095
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5096
|
+
"button",
|
|
5097
|
+
{
|
|
5098
|
+
type: "button",
|
|
5099
|
+
onClick: onClose,
|
|
5100
|
+
"aria-label": "Close sheet",
|
|
5101
|
+
className: "p-1.5 rounded-full hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
5102
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
5103
|
+
}
|
|
5104
|
+
)
|
|
5105
|
+
] }),
|
|
5106
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 overflow-y-auto flex-1 space-y-4", children })
|
|
5107
|
+
]
|
|
5108
|
+
}
|
|
5109
|
+
)
|
|
5110
|
+
]
|
|
5111
|
+
}
|
|
5112
|
+
) }) });
|
|
4016
5113
|
}
|
|
4017
5114
|
);
|
|
4018
5115
|
BottomSheet.displayName = "BottomSheet";
|
|
@@ -4033,14 +5130,21 @@ var ContextMenuItem = ({
|
|
|
4033
5130
|
"div",
|
|
4034
5131
|
{
|
|
4035
5132
|
role: "menuitem",
|
|
5133
|
+
tabIndex: disabled ? -1 : 0,
|
|
4036
5134
|
"aria-disabled": disabled,
|
|
4037
5135
|
onClick: () => {
|
|
4038
5136
|
if (!disabled) onSelect?.();
|
|
4039
5137
|
},
|
|
5138
|
+
onKeyDown: (e) => {
|
|
5139
|
+
if (!disabled && (e.key === "Enter" || e.key === " ")) {
|
|
5140
|
+
e.preventDefault();
|
|
5141
|
+
onSelect?.();
|
|
5142
|
+
}
|
|
5143
|
+
},
|
|
4040
5144
|
className: cn(
|
|
4041
|
-
"flex items-center justify-between px-3 py-1.5 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none",
|
|
5145
|
+
"flex items-center justify-between px-3 py-1.5 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none focus:outline-none focus:bg-[var(--ui-bg-tertiary)]",
|
|
4042
5146
|
disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
4043
|
-
destructive ? "text-rose-500 hover:bg-rose-500/10" : "text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)]",
|
|
5147
|
+
destructive ? "text-rose-500 hover:bg-rose-500/10 focus:bg-rose-500/10" : "text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)]",
|
|
4044
5148
|
className
|
|
4045
5149
|
),
|
|
4046
5150
|
...props,
|
|
@@ -4068,20 +5172,47 @@ var ContextMenu = React66.forwardRef(
|
|
|
4068
5172
|
setIsOpen(true);
|
|
4069
5173
|
};
|
|
4070
5174
|
React66.useEffect(() => {
|
|
5175
|
+
if (!isOpen) return;
|
|
5176
|
+
const timer = setTimeout(() => {
|
|
5177
|
+
const firstItem = menuRef.current?.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
|
|
5178
|
+
firstItem?.focus();
|
|
5179
|
+
}, 10);
|
|
4071
5180
|
const handleClickOutside = (e) => {
|
|
4072
5181
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
4073
5182
|
setIsOpen(false);
|
|
4074
5183
|
}
|
|
4075
5184
|
};
|
|
4076
5185
|
const handleKeyDown = (e) => {
|
|
4077
|
-
if (
|
|
5186
|
+
if (!menuRef.current) return;
|
|
5187
|
+
const items = Array.from(
|
|
5188
|
+
menuRef.current.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')
|
|
5189
|
+
);
|
|
5190
|
+
if (items.length === 0) return;
|
|
5191
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
5192
|
+
if (e.key === "ArrowDown") {
|
|
5193
|
+
e.preventDefault();
|
|
5194
|
+
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
|
|
5195
|
+
items[nextIndex]?.focus();
|
|
5196
|
+
} else if (e.key === "ArrowUp") {
|
|
5197
|
+
e.preventDefault();
|
|
5198
|
+
const prevIndex = currentIndex === -1 ? items.length - 1 : (currentIndex - 1 + items.length) % items.length;
|
|
5199
|
+
items[prevIndex]?.focus();
|
|
5200
|
+
} else if (e.key === "Home") {
|
|
5201
|
+
e.preventDefault();
|
|
5202
|
+
items[0]?.focus();
|
|
5203
|
+
} else if (e.key === "End") {
|
|
5204
|
+
e.preventDefault();
|
|
5205
|
+
items[items.length - 1]?.focus();
|
|
5206
|
+
} else if (e.key === "Escape") {
|
|
5207
|
+
e.preventDefault();
|
|
5208
|
+
setIsOpen(false);
|
|
5209
|
+
}
|
|
4078
5210
|
};
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
4083
|
-
}
|
|
5211
|
+
window.addEventListener("click", handleClickOutside);
|
|
5212
|
+
window.addEventListener("contextmenu", handleClickOutside);
|
|
5213
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
4084
5214
|
return () => {
|
|
5215
|
+
clearTimeout(timer);
|
|
4085
5216
|
window.removeEventListener("click", handleClickOutside);
|
|
4086
5217
|
window.removeEventListener("contextmenu", handleClickOutside);
|
|
4087
5218
|
window.removeEventListener("keydown", handleKeyDown);
|
|
@@ -4093,9 +5224,11 @@ var ContextMenu = React66.forwardRef(
|
|
|
4093
5224
|
"div",
|
|
4094
5225
|
{
|
|
4095
5226
|
ref: menuRef,
|
|
5227
|
+
role: "menu",
|
|
5228
|
+
tabIndex: -1,
|
|
4096
5229
|
style: { top: `${position.y}px`, left: `${position.x}px` },
|
|
4097
5230
|
onClick: () => setIsOpen(false),
|
|
4098
|
-
className: "fixed z-50 min-w-48 p-1.5 rounded-2xl bg-[var(--ui-bg-surface)]/90 backdrop-blur-3xl border border-[var(--ui-border-default)] shadow-2xl space-y-0.5 animate-in fade-in zoom-in-95 duration-150",
|
|
5231
|
+
className: "fixed z-50 min-w-48 p-1.5 rounded-2xl bg-[var(--ui-bg-surface)]/90 backdrop-blur-3xl border border-[var(--ui-border-default)] shadow-2xl space-y-0.5 animate-in fade-in zoom-in-95 duration-150 focus:outline-none",
|
|
4099
5232
|
children: menuContent
|
|
4100
5233
|
}
|
|
4101
5234
|
) })
|
|
@@ -4189,29 +5322,114 @@ function useTabs() {
|
|
|
4189
5322
|
return context;
|
|
4190
5323
|
}
|
|
4191
5324
|
var Tabs = React66.forwardRef(
|
|
4192
|
-
({
|
|
5325
|
+
({
|
|
5326
|
+
defaultValue = "",
|
|
5327
|
+
value: controlledValue,
|
|
5328
|
+
onValueChange,
|
|
5329
|
+
variant = "pills",
|
|
5330
|
+
orientation = "horizontal",
|
|
5331
|
+
className,
|
|
5332
|
+
children,
|
|
5333
|
+
...props
|
|
5334
|
+
}, ref) => {
|
|
5335
|
+
const baseId = React66.useId();
|
|
4193
5336
|
const [selectedTab, setSelectedTab] = React66.useState(defaultValue);
|
|
4194
5337
|
const activeTab = controlledValue !== void 0 ? controlledValue : selectedTab;
|
|
5338
|
+
const tabRegistryRef = React66.useRef(
|
|
5339
|
+
/* @__PURE__ */ new Map()
|
|
5340
|
+
);
|
|
5341
|
+
const registerTab = (value, disabled, element) => {
|
|
5342
|
+
tabRegistryRef.current.set(value, { disabled, element });
|
|
5343
|
+
};
|
|
5344
|
+
const unregisterTab = (value) => {
|
|
5345
|
+
tabRegistryRef.current.delete(value);
|
|
5346
|
+
};
|
|
4195
5347
|
const handleTabChange = (val) => {
|
|
4196
|
-
|
|
5348
|
+
if (!controlledValue) {
|
|
5349
|
+
setSelectedTab(val);
|
|
5350
|
+
}
|
|
4197
5351
|
onValueChange?.(val);
|
|
4198
5352
|
};
|
|
4199
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5353
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5354
|
+
TabsContext.Provider,
|
|
5355
|
+
{
|
|
5356
|
+
value: {
|
|
5357
|
+
activeTab,
|
|
5358
|
+
setActiveTab: handleTabChange,
|
|
5359
|
+
variant,
|
|
5360
|
+
orientation,
|
|
5361
|
+
baseId,
|
|
5362
|
+
registerTab,
|
|
5363
|
+
unregisterTab
|
|
5364
|
+
},
|
|
5365
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5366
|
+
"div",
|
|
5367
|
+
{
|
|
5368
|
+
ref,
|
|
5369
|
+
className: cn(
|
|
5370
|
+
"w-full flex space-y-4",
|
|
5371
|
+
orientation === "vertical" ? "flex-row space-y-0 space-x-6" : "flex-col",
|
|
5372
|
+
className
|
|
5373
|
+
),
|
|
5374
|
+
...props,
|
|
5375
|
+
children
|
|
5376
|
+
}
|
|
5377
|
+
)
|
|
5378
|
+
}
|
|
5379
|
+
);
|
|
4200
5380
|
}
|
|
4201
5381
|
);
|
|
4202
5382
|
Tabs.displayName = "Tabs";
|
|
4203
5383
|
var TabsList = React66.forwardRef(
|
|
4204
|
-
({ className, children, ...props }, ref) => {
|
|
4205
|
-
const { variant } = useTabs();
|
|
5384
|
+
({ className, children, onKeyDown, ...props }, ref) => {
|
|
5385
|
+
const { variant, orientation, activeTab, setActiveTab } = useTabs();
|
|
5386
|
+
const listRef = React66.useRef(null);
|
|
5387
|
+
const handleKeyDown = (e) => {
|
|
5388
|
+
onKeyDown?.(e);
|
|
5389
|
+
if (e.defaultPrevented) return;
|
|
5390
|
+
const tabs = Array.from(
|
|
5391
|
+
listRef.current?.querySelectorAll('[role="tab"]:not([disabled])') || []
|
|
5392
|
+
);
|
|
5393
|
+
if (tabs.length === 0) return;
|
|
5394
|
+
const currentIndex = tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true");
|
|
5395
|
+
let nextIndex = -1;
|
|
5396
|
+
const isPrev = orientation === "horizontal" && e.key === "ArrowLeft" || orientation === "vertical" && e.key === "ArrowUp";
|
|
5397
|
+
const isNext = orientation === "horizontal" && e.key === "ArrowRight" || orientation === "vertical" && e.key === "ArrowDown";
|
|
5398
|
+
if (isNext) {
|
|
5399
|
+
e.preventDefault();
|
|
5400
|
+
nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % tabs.length;
|
|
5401
|
+
} else if (isPrev) {
|
|
5402
|
+
e.preventDefault();
|
|
5403
|
+
nextIndex = currentIndex === -1 ? tabs.length - 1 : (currentIndex - 1 + tabs.length) % tabs.length;
|
|
5404
|
+
} else if (e.key === "Home") {
|
|
5405
|
+
e.preventDefault();
|
|
5406
|
+
nextIndex = 0;
|
|
5407
|
+
} else if (e.key === "End") {
|
|
5408
|
+
e.preventDefault();
|
|
5409
|
+
nextIndex = tabs.length - 1;
|
|
5410
|
+
}
|
|
5411
|
+
if (nextIndex !== -1 && tabs[nextIndex]) {
|
|
5412
|
+
const nextTab = tabs[nextIndex];
|
|
5413
|
+
nextTab.focus();
|
|
5414
|
+
nextTab.click();
|
|
5415
|
+
}
|
|
5416
|
+
};
|
|
4206
5417
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4207
5418
|
"div",
|
|
4208
5419
|
{
|
|
4209
|
-
ref
|
|
5420
|
+
ref: (node) => {
|
|
5421
|
+
listRef.current = node;
|
|
5422
|
+
if (typeof ref === "function") ref(node);
|
|
5423
|
+
else if (ref) ref.current = node;
|
|
5424
|
+
},
|
|
4210
5425
|
role: "tablist",
|
|
5426
|
+
"aria-orientation": orientation,
|
|
5427
|
+
onKeyDown: handleKeyDown,
|
|
4211
5428
|
className: cn(
|
|
4212
|
-
"inline-flex
|
|
5429
|
+
"inline-flex",
|
|
5430
|
+
orientation === "vertical" ? "flex-col items-stretch gap-1" : "items-center",
|
|
4213
5431
|
variant === "pills" && "p-1 rounded-[var(--ui-radius-lg)] bg-[var(--ui-bg-secondary)] border border-[var(--ui-border-subtle)] gap-1",
|
|
4214
|
-
variant === "line" && "border-b border-[var(--ui-border-default)] gap-6",
|
|
5432
|
+
variant === "line" && (orientation === "vertical" ? "border-r border-[var(--ui-border-default)] pr-2" : "border-b border-[var(--ui-border-default)] gap-6"),
|
|
4215
5433
|
variant === "enclosed" && "border-b border-[var(--ui-border-default)] gap-1",
|
|
4216
5434
|
className
|
|
4217
5435
|
),
|
|
@@ -4223,17 +5441,35 @@ var TabsList = React66.forwardRef(
|
|
|
4223
5441
|
);
|
|
4224
5442
|
TabsList.displayName = "TabsList";
|
|
4225
5443
|
var TabsTrigger = React66.forwardRef(
|
|
4226
|
-
({ className, value, children, ...props }, ref) => {
|
|
4227
|
-
const { activeTab, setActiveTab, variant } = useTabs();
|
|
5444
|
+
({ className, value, disabled = false, children, id, onClick, ...props }, ref) => {
|
|
5445
|
+
const { activeTab, setActiveTab, variant, baseId, registerTab, unregisterTab } = useTabs();
|
|
4228
5446
|
const isActive = activeTab === value;
|
|
5447
|
+
const triggerId = id || `tab-${baseId}-${value}`;
|
|
5448
|
+
const panelId = `panel-${baseId}-${value}`;
|
|
5449
|
+
const innerRef = React66.useRef(null);
|
|
5450
|
+
React66.useEffect(() => {
|
|
5451
|
+
registerTab(value, Boolean(disabled), innerRef.current);
|
|
5452
|
+
return () => unregisterTab(value);
|
|
5453
|
+
}, [value, disabled, registerTab, unregisterTab]);
|
|
4229
5454
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4230
5455
|
"button",
|
|
4231
5456
|
{
|
|
4232
|
-
ref
|
|
5457
|
+
ref: (node) => {
|
|
5458
|
+
innerRef.current = node;
|
|
5459
|
+
if (typeof ref === "function") ref(node);
|
|
5460
|
+
else if (ref) ref.current = node;
|
|
5461
|
+
},
|
|
5462
|
+
id: triggerId,
|
|
4233
5463
|
role: "tab",
|
|
4234
5464
|
type: "button",
|
|
5465
|
+
disabled,
|
|
4235
5466
|
"aria-selected": isActive,
|
|
4236
|
-
|
|
5467
|
+
"aria-controls": panelId,
|
|
5468
|
+
tabIndex: isActive ? 0 : -1,
|
|
5469
|
+
onClick: (e) => {
|
|
5470
|
+
onClick?.(e);
|
|
5471
|
+
if (!disabled) setActiveTab(value);
|
|
5472
|
+
},
|
|
4237
5473
|
className: cn(
|
|
4238
5474
|
"inline-flex items-center justify-center font-medium text-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] disabled:pointer-events-none disabled:opacity-50 select-none cursor-pointer",
|
|
4239
5475
|
variant === "pills" && "px-3.5 py-1.5 rounded-[var(--ui-radius-md)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)]",
|
|
@@ -4252,16 +5488,24 @@ var TabsTrigger = React66.forwardRef(
|
|
|
4252
5488
|
);
|
|
4253
5489
|
TabsTrigger.displayName = "TabsTrigger";
|
|
4254
5490
|
var TabsContent = React66.forwardRef(
|
|
4255
|
-
({ className, value, children, ...props }, ref) => {
|
|
4256
|
-
const { activeTab } = useTabs();
|
|
4257
|
-
|
|
5491
|
+
({ className, value, children, id, ...props }, ref) => {
|
|
5492
|
+
const { activeTab, baseId } = useTabs();
|
|
5493
|
+
const isActive = activeTab === value;
|
|
5494
|
+
if (!isActive) return null;
|
|
5495
|
+
const panelId = id || `panel-${baseId}-${value}`;
|
|
5496
|
+
const triggerId = `tab-${baseId}-${value}`;
|
|
4258
5497
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4259
5498
|
"div",
|
|
4260
5499
|
{
|
|
4261
5500
|
ref,
|
|
5501
|
+
id: panelId,
|
|
4262
5502
|
role: "tabpanel",
|
|
5503
|
+
"aria-labelledby": triggerId,
|
|
4263
5504
|
tabIndex: 0,
|
|
4264
|
-
className: cn(
|
|
5505
|
+
className: cn(
|
|
5506
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] animate-in fade-in duration-150",
|
|
5507
|
+
className
|
|
5508
|
+
),
|
|
4265
5509
|
...props,
|
|
4266
5510
|
children
|
|
4267
5511
|
}
|
|
@@ -4489,12 +5733,13 @@ function useDropdown() {
|
|
|
4489
5733
|
function DropdownMenu({ children }) {
|
|
4490
5734
|
const [isOpen, setIsOpen] = React66.useState(false);
|
|
4491
5735
|
const [triggerRect, setTriggerRect] = React66.useState(null);
|
|
4492
|
-
|
|
5736
|
+
const triggerRef = React66.useRef(null);
|
|
5737
|
+
const menuId = React66.useId();
|
|
5738
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value: { isOpen, setIsOpen, triggerRect, setTriggerRect, triggerRef, menuId }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative inline-block text-left", children }) });
|
|
4493
5739
|
}
|
|
4494
5740
|
var DropdownMenuTrigger = React66.forwardRef(
|
|
4495
|
-
({ children, onClick, ...props }, ref) => {
|
|
4496
|
-
const { isOpen, setIsOpen, setTriggerRect } = useDropdown();
|
|
4497
|
-
const triggerRef = React66.useRef(null);
|
|
5741
|
+
({ children, onClick, onKeyDown, ...props }, ref) => {
|
|
5742
|
+
const { isOpen, setIsOpen, setTriggerRect, triggerRef, menuId } = useDropdown();
|
|
4498
5743
|
const handleClick = (e) => {
|
|
4499
5744
|
if (triggerRef.current) {
|
|
4500
5745
|
setTriggerRect(triggerRef.current.getBoundingClientRect());
|
|
@@ -4502,39 +5747,110 @@ var DropdownMenuTrigger = React66.forwardRef(
|
|
|
4502
5747
|
setIsOpen(!isOpen);
|
|
4503
5748
|
onClick?.(e);
|
|
4504
5749
|
};
|
|
4505
|
-
|
|
5750
|
+
const handleKeyDown = (e) => {
|
|
5751
|
+
onKeyDown?.(e);
|
|
5752
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
5753
|
+
e.preventDefault();
|
|
5754
|
+
if (triggerRef.current) {
|
|
5755
|
+
setTriggerRect(triggerRef.current.getBoundingClientRect());
|
|
5756
|
+
}
|
|
5757
|
+
setIsOpen(true);
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5760
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5761
|
+
"div",
|
|
5762
|
+
{
|
|
5763
|
+
ref: (node) => {
|
|
5764
|
+
triggerRef.current = node;
|
|
5765
|
+
if (typeof ref === "function") ref(node);
|
|
5766
|
+
else if (ref) ref.current = node;
|
|
5767
|
+
},
|
|
5768
|
+
onClick: handleClick,
|
|
5769
|
+
onKeyDown: handleKeyDown,
|
|
5770
|
+
role: "button",
|
|
5771
|
+
tabIndex: 0,
|
|
5772
|
+
"aria-haspopup": "menu",
|
|
5773
|
+
"aria-expanded": isOpen,
|
|
5774
|
+
"aria-controls": isOpen ? `menu-${menuId}` : void 0,
|
|
5775
|
+
className: "inline-flex cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)] rounded-[var(--ui-radius-sm)]",
|
|
5776
|
+
...props,
|
|
5777
|
+
children
|
|
5778
|
+
}
|
|
5779
|
+
);
|
|
4506
5780
|
}
|
|
4507
5781
|
);
|
|
4508
5782
|
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
|
|
4509
5783
|
var DropdownMenuContent = React66.forwardRef(
|
|
4510
5784
|
({ className, align = "start", children, ...props }, ref) => {
|
|
4511
|
-
const { isOpen, setIsOpen, triggerRect } = useDropdown();
|
|
5785
|
+
const { isOpen, setIsOpen, triggerRect, triggerRef, menuId } = useDropdown();
|
|
4512
5786
|
const menuRef = React66.useRef(null);
|
|
4513
5787
|
React66.useEffect(() => {
|
|
5788
|
+
if (!isOpen) return;
|
|
5789
|
+
const timer = setTimeout(() => {
|
|
5790
|
+
const firstItem = menuRef.current?.querySelector('[role="menuitem"]:not([disabled])');
|
|
5791
|
+
firstItem?.focus();
|
|
5792
|
+
}, 10);
|
|
4514
5793
|
const handleOutside = (e) => {
|
|
4515
|
-
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
5794
|
+
if (menuRef.current && !menuRef.current.contains(e.target) && !triggerRef.current?.contains(e.target)) {
|
|
4516
5795
|
setIsOpen(false);
|
|
4517
5796
|
}
|
|
4518
5797
|
};
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
5798
|
+
const handleKeyDown = (e) => {
|
|
5799
|
+
if (!menuRef.current) return;
|
|
5800
|
+
const items = Array.from(
|
|
5801
|
+
menuRef.current.querySelectorAll('[role="menuitem"]:not([disabled])')
|
|
5802
|
+
);
|
|
5803
|
+
if (items.length === 0) return;
|
|
5804
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
5805
|
+
if (e.key === "ArrowDown") {
|
|
5806
|
+
e.preventDefault();
|
|
5807
|
+
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
|
|
5808
|
+
items[nextIndex]?.focus();
|
|
5809
|
+
} else if (e.key === "ArrowUp") {
|
|
5810
|
+
e.preventDefault();
|
|
5811
|
+
const prevIndex = currentIndex === -1 ? items.length - 1 : (currentIndex - 1 + items.length) % items.length;
|
|
5812
|
+
items[prevIndex]?.focus();
|
|
5813
|
+
} else if (e.key === "Home") {
|
|
5814
|
+
e.preventDefault();
|
|
5815
|
+
items[0]?.focus();
|
|
5816
|
+
} else if (e.key === "End") {
|
|
5817
|
+
e.preventDefault();
|
|
5818
|
+
items[items.length - 1]?.focus();
|
|
5819
|
+
} else if (e.key === "Escape") {
|
|
5820
|
+
e.preventDefault();
|
|
5821
|
+
setIsOpen(false);
|
|
5822
|
+
triggerRef.current?.focus();
|
|
5823
|
+
}
|
|
5824
|
+
};
|
|
5825
|
+
document.addEventListener("mousedown", handleOutside);
|
|
5826
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
5827
|
+
return () => {
|
|
5828
|
+
clearTimeout(timer);
|
|
5829
|
+
document.removeEventListener("mousedown", handleOutside);
|
|
5830
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
5831
|
+
};
|
|
5832
|
+
}, [isOpen, setIsOpen, triggerRef]);
|
|
4524
5833
|
if (!isOpen || !triggerRect) return null;
|
|
4525
5834
|
const top = triggerRect.bottom + window.scrollY + 6;
|
|
4526
5835
|
const left = align === "end" ? triggerRect.right + window.scrollX : triggerRect.left + window.scrollX;
|
|
4527
5836
|
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4528
5837
|
"div",
|
|
4529
5838
|
{
|
|
4530
|
-
ref:
|
|
5839
|
+
ref: (node) => {
|
|
5840
|
+
menuRef.current = node;
|
|
5841
|
+
if (typeof ref === "function") ref(node);
|
|
5842
|
+
else if (ref) ref.current = node;
|
|
5843
|
+
},
|
|
5844
|
+
id: `menu-${menuId}`,
|
|
5845
|
+
role: "menu",
|
|
5846
|
+
tabIndex: -1,
|
|
4531
5847
|
style: {
|
|
4532
5848
|
position: "absolute",
|
|
4533
5849
|
top: `${top}px`,
|
|
4534
5850
|
left: `${left}px`
|
|
4535
5851
|
},
|
|
4536
5852
|
className: cn(
|
|
4537
|
-
"z-50 min-w-[12rem] overflow-hidden rounded-[var(--ui-radius-lg)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] p-1 text-[var(--ui-text-primary)] shadow-xl animate-in fade-in zoom-in-95",
|
|
5853
|
+
"z-50 min-w-[12rem] overflow-hidden rounded-[var(--ui-radius-lg)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] p-1 text-[var(--ui-text-primary)] shadow-xl animate-in fade-in zoom-in-95 focus:outline-none",
|
|
4538
5854
|
align === "end" && "-translate-x-full",
|
|
4539
5855
|
className
|
|
4540
5856
|
),
|
|
@@ -4552,14 +5868,15 @@ var DropdownMenuItem = React66.forwardRef(
|
|
|
4552
5868
|
"button",
|
|
4553
5869
|
{
|
|
4554
5870
|
ref,
|
|
5871
|
+
role: "menuitem",
|
|
4555
5872
|
type: "button",
|
|
4556
5873
|
onClick: (e) => {
|
|
4557
5874
|
onClick?.(e);
|
|
4558
5875
|
setIsOpen(false);
|
|
4559
5876
|
},
|
|
4560
5877
|
className: cn(
|
|
4561
|
-
"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-[var(--ui-radius-sm)] px-2.5 py-1.5 text-xs font-medium outline-none transition-colors hover:bg-[var(--ui-bg-tertiary)] disabled:pointer-events-none disabled:opacity-50 text-left",
|
|
4562
|
-
destructive && "text-[var(--ui-error)] hover:bg-[var(--ui-error-subtle)]",
|
|
5878
|
+
"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-[var(--ui-radius-sm)] px-2.5 py-1.5 text-xs font-medium outline-none transition-colors hover:bg-[var(--ui-bg-tertiary)] focus:bg-[var(--ui-bg-tertiary)] disabled:pointer-events-none disabled:opacity-50 text-left",
|
|
5879
|
+
destructive && "text-[var(--ui-error)] hover:bg-[var(--ui-error-subtle)] focus:bg-[var(--ui-error-subtle)]",
|
|
4563
5880
|
className
|
|
4564
5881
|
),
|
|
4565
5882
|
...props,
|
|
@@ -4575,7 +5892,7 @@ var DropdownMenuItem = React66.forwardRef(
|
|
|
4575
5892
|
DropdownMenuItem.displayName = "DropdownMenuItem";
|
|
4576
5893
|
var DropdownMenuSeparator = React66.forwardRef(
|
|
4577
5894
|
({ className, ...props }, ref) => {
|
|
4578
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("-mx-1 my-1 h-px bg-[var(--ui-border-subtle)]", className), ...props });
|
|
5895
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, role: "separator", className: cn("-mx-1 my-1 h-px bg-[var(--ui-border-subtle)]", className), ...props });
|
|
4579
5896
|
}
|
|
4580
5897
|
);
|
|
4581
5898
|
DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
|
|
@@ -5110,79 +6427,183 @@ function DataTablePro({
|
|
|
5110
6427
|
searchable = true,
|
|
5111
6428
|
searchPlaceholder = "Search all records...",
|
|
5112
6429
|
selectable = true,
|
|
6430
|
+
selectedKeys: controlledSelectedKeys,
|
|
6431
|
+
defaultSelectedKeys = [],
|
|
5113
6432
|
onSelectionChange,
|
|
5114
6433
|
pageSize: initialPageSize = 10,
|
|
5115
6434
|
pageSizeOptions = [5, 10, 25, 50],
|
|
5116
6435
|
title,
|
|
5117
6436
|
description,
|
|
5118
6437
|
actions,
|
|
5119
|
-
className
|
|
6438
|
+
className,
|
|
6439
|
+
loading = false,
|
|
6440
|
+
// Multi-column sorting
|
|
6441
|
+
multiSort = true,
|
|
6442
|
+
sorting: controlledSorting,
|
|
6443
|
+
defaultSorting = [],
|
|
6444
|
+
onSortingChange,
|
|
6445
|
+
// Server-side / manual mode props
|
|
6446
|
+
manualPagination = false,
|
|
6447
|
+
manualSorting = false,
|
|
6448
|
+
manualFiltering = false,
|
|
6449
|
+
pageIndex: controlledPageIndex,
|
|
6450
|
+
pageCount: controlledPageCount,
|
|
6451
|
+
totalCount: controlledTotalCount,
|
|
6452
|
+
onPaginationChange,
|
|
6453
|
+
searchQuery: controlledSearchQuery,
|
|
6454
|
+
onSearchChange,
|
|
6455
|
+
onSelectAllDataset,
|
|
6456
|
+
emptyText = "No matching records found."
|
|
5120
6457
|
}) {
|
|
5121
|
-
const [
|
|
5122
|
-
const
|
|
5123
|
-
const [
|
|
5124
|
-
const [
|
|
5125
|
-
const [
|
|
5126
|
-
const
|
|
6458
|
+
const [internalSearch, setInternalSearch] = React66.useState("");
|
|
6459
|
+
const search = controlledSearchQuery !== void 0 ? controlledSearchQuery : internalSearch;
|
|
6460
|
+
const [internalSorting, setInternalSorting] = React66.useState(defaultSorting);
|
|
6461
|
+
const currentSorting = controlledSorting !== void 0 ? controlledSorting ?? [] : internalSorting;
|
|
6462
|
+
const [internalCurrentPage, setInternalCurrentPage] = React66.useState(1);
|
|
6463
|
+
const currentPage = controlledPageIndex !== void 0 ? controlledPageIndex : internalCurrentPage;
|
|
6464
|
+
const [internalPageSize, setInternalPageSize] = React66.useState(initialPageSize);
|
|
6465
|
+
const pageSize = internalPageSize;
|
|
6466
|
+
const [internalSelectedKeys, setInternalSelectedKeys] = React66.useState(defaultSelectedKeys);
|
|
6467
|
+
const isSelectedControlled = controlledSelectedKeys !== void 0;
|
|
6468
|
+
const selectedKeys = isSelectedControlled ? controlledSelectedKeys : internalSelectedKeys;
|
|
6469
|
+
const handleSelectionUpdate = (nextKeys) => {
|
|
6470
|
+
if (!isSelectedControlled) {
|
|
6471
|
+
setInternalSelectedKeys(nextKeys);
|
|
6472
|
+
}
|
|
6473
|
+
onSelectionChange?.(nextKeys);
|
|
6474
|
+
};
|
|
5127
6475
|
const filteredData = React66.useMemo(() => {
|
|
5128
|
-
if (!search.trim()) return data;
|
|
6476
|
+
if (manualFiltering || !search.trim()) return data;
|
|
5129
6477
|
const q = search.toLowerCase();
|
|
5130
6478
|
return data.filter((row) => {
|
|
5131
|
-
return
|
|
6479
|
+
return columns.some((col) => {
|
|
6480
|
+
const val = col.accessor ? col.accessor(row) : row[col.key];
|
|
5132
6481
|
if (val === null || val === void 0) return false;
|
|
5133
6482
|
return String(val).toLowerCase().includes(q);
|
|
5134
6483
|
});
|
|
5135
6484
|
});
|
|
5136
|
-
}, [data, search]);
|
|
6485
|
+
}, [data, search, columns, manualFiltering]);
|
|
5137
6486
|
const sortedData = React66.useMemo(() => {
|
|
5138
|
-
if (
|
|
5139
|
-
const col = columns.find((c) => c.key === sortKey);
|
|
6487
|
+
if (manualSorting || currentSorting.length === 0) return filteredData;
|
|
5140
6488
|
return [...filteredData].sort((a, b) => {
|
|
5141
|
-
const
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
6489
|
+
for (const { key, direction } of currentSorting) {
|
|
6490
|
+
const col = columns.find((c) => c.key === key);
|
|
6491
|
+
let comp = 0;
|
|
6492
|
+
if (col?.sortComparator) {
|
|
6493
|
+
comp = col.sortComparator(a, b);
|
|
6494
|
+
} else {
|
|
6495
|
+
const valA = col?.accessor ? col.accessor(a) : a[key];
|
|
6496
|
+
const valB = col?.accessor ? col.accessor(b) : b[key];
|
|
6497
|
+
if (valA === valB) continue;
|
|
6498
|
+
if (valA === null || valA === void 0) return 1;
|
|
6499
|
+
if (valB === null || valB === void 0) return -1;
|
|
6500
|
+
comp = valA < valB ? -1 : 1;
|
|
6501
|
+
}
|
|
6502
|
+
if (comp !== 0) {
|
|
6503
|
+
return direction === "asc" ? comp : -comp;
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
return 0;
|
|
5148
6507
|
});
|
|
5149
|
-
}, [filteredData,
|
|
5150
|
-
const
|
|
6508
|
+
}, [filteredData, currentSorting, columns, manualSorting]);
|
|
6509
|
+
const totalItems = controlledTotalCount !== void 0 ? controlledTotalCount : sortedData.length;
|
|
6510
|
+
const totalPages = controlledPageCount !== void 0 ? Math.max(1, controlledPageCount) : Math.max(1, Math.ceil(totalItems / pageSize));
|
|
6511
|
+
React66.useEffect(() => {
|
|
6512
|
+
if (!manualPagination && currentPage > totalPages && totalPages > 0) {
|
|
6513
|
+
setInternalCurrentPage(totalPages);
|
|
6514
|
+
onPaginationChange?.(totalPages, pageSize);
|
|
6515
|
+
}
|
|
6516
|
+
}, [totalPages, currentPage, manualPagination, pageSize, onPaginationChange]);
|
|
5151
6517
|
const paginatedData = React66.useMemo(() => {
|
|
6518
|
+
if (manualPagination) return data;
|
|
5152
6519
|
const start = (currentPage - 1) * pageSize;
|
|
5153
6520
|
return sortedData.slice(start, start + pageSize);
|
|
5154
|
-
}, [sortedData, currentPage, pageSize]);
|
|
5155
|
-
const handleSort = (key) => {
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
6521
|
+
}, [data, sortedData, currentPage, pageSize, manualPagination]);
|
|
6522
|
+
const handleSort = (key, e) => {
|
|
6523
|
+
const isShift = e?.shiftKey || false;
|
|
6524
|
+
let nextSorting = [...currentSorting];
|
|
6525
|
+
const existingIdx = nextSorting.findIndex((s) => s.key === key);
|
|
6526
|
+
if (isShift && multiSort) {
|
|
6527
|
+
if (existingIdx >= 0) {
|
|
6528
|
+
if (nextSorting[existingIdx].direction === "asc") {
|
|
6529
|
+
nextSorting[existingIdx] = { key, direction: "desc" };
|
|
6530
|
+
} else {
|
|
6531
|
+
nextSorting.splice(existingIdx, 1);
|
|
6532
|
+
}
|
|
5159
6533
|
} else {
|
|
5160
|
-
|
|
5161
|
-
setSortDirection("asc");
|
|
6534
|
+
nextSorting.push({ key, direction: "asc" });
|
|
5162
6535
|
}
|
|
5163
6536
|
} else {
|
|
5164
|
-
|
|
5165
|
-
|
|
6537
|
+
if (existingIdx >= 0 && nextSorting.length === 1) {
|
|
6538
|
+
if (nextSorting[0].direction === "asc") {
|
|
6539
|
+
nextSorting = [{ key, direction: "desc" }];
|
|
6540
|
+
} else {
|
|
6541
|
+
nextSorting = [];
|
|
6542
|
+
}
|
|
6543
|
+
} else {
|
|
6544
|
+
nextSorting = [{ key, direction: "asc" }];
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
if (controlledSorting === void 0) {
|
|
6548
|
+
setInternalSorting(nextSorting);
|
|
5166
6549
|
}
|
|
6550
|
+
onSortingChange?.(nextSorting);
|
|
5167
6551
|
};
|
|
5168
|
-
const
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
6552
|
+
const handlePageChange = (newPage) => {
|
|
6553
|
+
const clamped = Math.max(1, Math.min(totalPages, newPage));
|
|
6554
|
+
if (controlledPageIndex === void 0) {
|
|
6555
|
+
setInternalCurrentPage(clamped);
|
|
6556
|
+
}
|
|
6557
|
+
onPaginationChange?.(clamped, pageSize);
|
|
6558
|
+
};
|
|
6559
|
+
const handlePageSizeChange = (newSize) => {
|
|
6560
|
+
setInternalPageSize(newSize);
|
|
6561
|
+
if (controlledPageIndex === void 0) {
|
|
6562
|
+
setInternalCurrentPage(1);
|
|
6563
|
+
}
|
|
6564
|
+
onPaginationChange?.(1, newSize);
|
|
6565
|
+
};
|
|
6566
|
+
const handleSearchChange = (query) => {
|
|
6567
|
+
if (controlledSearchQuery === void 0) {
|
|
6568
|
+
setInternalSearch(query);
|
|
6569
|
+
if (controlledPageIndex === void 0) {
|
|
6570
|
+
setInternalCurrentPage(1);
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
onSearchChange?.(query);
|
|
6574
|
+
};
|
|
6575
|
+
const currentPageKeys = React66.useMemo(() => paginatedData.map(keyExtractor), [paginatedData, keyExtractor]);
|
|
6576
|
+
const isAllCurrentPageSelected = currentPageKeys.length > 0 && currentPageKeys.every((k) => selectedKeys.includes(k));
|
|
6577
|
+
const isSomeCurrentPageSelected = currentPageKeys.some((k) => selectedKeys.includes(k)) && !isAllCurrentPageSelected;
|
|
6578
|
+
const toggleSelectAllCurrentPage = () => {
|
|
6579
|
+
if (isAllCurrentPageSelected) {
|
|
6580
|
+
const next = selectedKeys.filter((k) => !currentPageKeys.includes(k));
|
|
6581
|
+
handleSelectionUpdate(next);
|
|
5172
6582
|
} else {
|
|
5173
|
-
const
|
|
5174
|
-
|
|
5175
|
-
onSelectionChange?.(allKeys);
|
|
6583
|
+
const combined = Array.from(/* @__PURE__ */ new Set([...selectedKeys, ...currentPageKeys]));
|
|
6584
|
+
handleSelectionUpdate(combined);
|
|
5176
6585
|
}
|
|
5177
6586
|
};
|
|
5178
6587
|
const toggleSelectRow = (key) => {
|
|
5179
6588
|
const next = selectedKeys.includes(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
|
|
5180
|
-
|
|
5181
|
-
|
|
6589
|
+
handleSelectionUpdate(next);
|
|
6590
|
+
};
|
|
6591
|
+
const canSelectAllDataset = !manualPagination || typeof onSelectAllDataset === "function";
|
|
6592
|
+
const selectAllDataset = () => {
|
|
6593
|
+
if (manualPagination) {
|
|
6594
|
+
onSelectAllDataset?.();
|
|
6595
|
+
return;
|
|
6596
|
+
}
|
|
6597
|
+
const allKeys = sortedData.map(keyExtractor);
|
|
6598
|
+
handleSelectionUpdate(allKeys);
|
|
6599
|
+
};
|
|
6600
|
+
const clearSelection = () => {
|
|
6601
|
+
handleSelectionUpdate([]);
|
|
5182
6602
|
};
|
|
5183
6603
|
const exportCSV = () => {
|
|
6604
|
+
const exportRows = manualPagination ? data : sortedData;
|
|
5184
6605
|
const headers = columns.map((c) => typeof c.header === "string" ? c.header : c.key);
|
|
5185
|
-
const rows =
|
|
6606
|
+
const rows = exportRows.map(
|
|
5186
6607
|
(row) => columns.map((c) => {
|
|
5187
6608
|
const val = c.accessor ? c.accessor(row) : row[c.key];
|
|
5188
6609
|
return `"${String(val ?? "").replace(/"/g, '""')}"`;
|
|
@@ -5197,7 +6618,6 @@ function DataTablePro({
|
|
|
5197
6618
|
link.click();
|
|
5198
6619
|
document.body.removeChild(link);
|
|
5199
6620
|
};
|
|
5200
|
-
const isAllSelected = paginatedData.length > 0 && selectedKeys.length === paginatedData.length;
|
|
5201
6621
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full space-y-4", className), children: [
|
|
5202
6622
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4", children: [
|
|
5203
6623
|
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -5213,10 +6633,7 @@ function DataTablePro({
|
|
|
5213
6633
|
type: "text",
|
|
5214
6634
|
placeholder: searchPlaceholder,
|
|
5215
6635
|
value: search,
|
|
5216
|
-
onChange: (e) =>
|
|
5217
|
-
setSearch(e.target.value);
|
|
5218
|
-
setCurrentPage(1);
|
|
5219
|
-
},
|
|
6636
|
+
onChange: (e) => handleSearchChange(e.target.value),
|
|
5220
6637
|
className: "w-full h-8 pl-8 pr-3 text-xs rounded-xl bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)] focus:outline-none focus:ring-2 focus:ring-[var(--ui-primary)]/40 text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] transition-all shadow-inner"
|
|
5221
6638
|
}
|
|
5222
6639
|
)
|
|
@@ -5225,83 +6642,110 @@ function DataTablePro({
|
|
|
5225
6642
|
actions
|
|
5226
6643
|
] })
|
|
5227
6644
|
] }),
|
|
5228
|
-
selectedKeys.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 rounded-xl bg-[var(--ui-primary)]/15 border border-[var(--ui-primary)]/30 flex items-center justify-between animate-in fade-in duration-200", children: [
|
|
5229
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
6645
|
+
selectedKeys.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 rounded-xl bg-[var(--ui-primary)]/15 border border-[var(--ui-primary)]/30 flex flex-wrap items-center justify-between gap-2 animate-in fade-in duration-200", children: [
|
|
6646
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs font-semibold text-[var(--ui-primary)]", children: [
|
|
6647
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
6648
|
+
"Selected ",
|
|
6649
|
+
selectedKeys.length,
|
|
6650
|
+
" records"
|
|
6651
|
+
] }),
|
|
6652
|
+
canSelectAllDataset && selectedKeys.length < totalItems && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6653
|
+
"button",
|
|
6654
|
+
{
|
|
6655
|
+
type: "button",
|
|
6656
|
+
onClick: selectAllDataset,
|
|
6657
|
+
className: "underline hover:text-[var(--ui-primary)]/80 cursor-pointer font-bold ml-1",
|
|
6658
|
+
children: [
|
|
6659
|
+
"Select all ",
|
|
6660
|
+
totalItems,
|
|
6661
|
+
" records"
|
|
6662
|
+
]
|
|
6663
|
+
}
|
|
6664
|
+
)
|
|
6665
|
+
] }),
|
|
5236
6666
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5237
6667
|
Button,
|
|
5238
6668
|
{
|
|
5239
6669
|
size: "xs",
|
|
5240
6670
|
variant: "ghost",
|
|
5241
6671
|
className: "text-xs text-rose-500 hover:text-rose-600 hover:bg-rose-500/10",
|
|
5242
|
-
onClick:
|
|
5243
|
-
setSelectedKeys([]);
|
|
5244
|
-
onSelectionChange?.([]);
|
|
5245
|
-
},
|
|
6672
|
+
onClick: clearSelection,
|
|
5246
6673
|
children: "Clear Selection"
|
|
5247
6674
|
}
|
|
5248
6675
|
)
|
|
5249
6676
|
] }),
|
|
5250
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5251
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5252
|
-
|
|
5253
|
-
|
|
6677
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full overflow-x-auto rounded-2xl border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] shadow-sm", children: [
|
|
6678
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-20 bg-[var(--ui-bg-surface)]/60 backdrop-blur-xs flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-6 h-6 animate-spin text-[var(--ui-primary)]" }) }),
|
|
6679
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
|
|
6680
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TableRow, { children: [
|
|
6681
|
+
selectable && /* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "w-10 px-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6682
|
+
Checkbox,
|
|
6683
|
+
{
|
|
6684
|
+
checked: isAllCurrentPageSelected,
|
|
6685
|
+
indeterminate: isSomeCurrentPageSelected,
|
|
6686
|
+
onCheckedChange: toggleSelectAllCurrentPage,
|
|
6687
|
+
"aria-label": "Select all on page"
|
|
6688
|
+
}
|
|
6689
|
+
) }),
|
|
6690
|
+
columns.map((col) => {
|
|
6691
|
+
const sortIndex = currentSorting.findIndex((s) => s.key === col.key);
|
|
6692
|
+
const sortRule = sortIndex >= 0 ? currentSorting[sortIndex] : null;
|
|
6693
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6694
|
+
TableHead,
|
|
6695
|
+
{
|
|
6696
|
+
style: { width: col.width },
|
|
6697
|
+
onClick: (e) => col.sortable !== false && handleSort(col.key, e),
|
|
6698
|
+
className: cn(
|
|
6699
|
+
col.sortable !== false && "cursor-pointer select-none hover:text-[var(--ui-text-primary)] transition-colors"
|
|
6700
|
+
),
|
|
6701
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
6702
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: col.header }),
|
|
6703
|
+
col.sortable !== false && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 text-[var(--ui-text-muted)]", children: [
|
|
6704
|
+
sortRule ? sortRule.direction === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "w-3.5 h-3.5 text-[var(--ui-primary)] font-bold" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3.5 h-3.5 text-[var(--ui-primary)] font-bold" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "w-3 h-3 opacity-40" }),
|
|
6705
|
+
currentSorting.length > 1 && sortIndex >= 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono font-bold text-[var(--ui-primary)]", children: sortIndex + 1 })
|
|
6706
|
+
] })
|
|
6707
|
+
] })
|
|
6708
|
+
},
|
|
6709
|
+
col.key
|
|
6710
|
+
);
|
|
6711
|
+
})
|
|
6712
|
+
] }) }),
|
|
6713
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: paginatedData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6714
|
+
TableCell,
|
|
5254
6715
|
{
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
className: "rounded border-[var(--ui-border-default)] text-[var(--ui-primary)] focus:ring-[var(--ui-primary)] cursor-pointer"
|
|
6716
|
+
colSpan: columns.length + (selectable ? 1 : 0),
|
|
6717
|
+
className: "h-32 text-center text-xs text-[var(--ui-text-muted)]",
|
|
6718
|
+
children: emptyText
|
|
5259
6719
|
}
|
|
5260
|
-
) }),
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
type: "checkbox",
|
|
5290
|
-
checked: isSelected,
|
|
5291
|
-
onChange: () => toggleSelectRow(key),
|
|
5292
|
-
className: "rounded border-[var(--ui-border-default)] text-[var(--ui-primary)] focus:ring-[var(--ui-primary)] cursor-pointer"
|
|
5293
|
-
}
|
|
5294
|
-
) }),
|
|
5295
|
-
columns.map((col) => {
|
|
5296
|
-
const cellContent = col.render ? col.render(row, rowIdx) : col.accessor ? col.accessor(row) : row[col.key];
|
|
5297
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: cellContent }, col.key);
|
|
5298
|
-
})
|
|
5299
|
-
]
|
|
5300
|
-
},
|
|
5301
|
-
key
|
|
5302
|
-
);
|
|
5303
|
-
}) })
|
|
5304
|
-
] }) }),
|
|
6720
|
+
) }) : paginatedData.map((row, rowIdx) => {
|
|
6721
|
+
const key = keyExtractor(row);
|
|
6722
|
+
const isSelected = selectedKeys.includes(key);
|
|
6723
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6724
|
+
TableRow,
|
|
6725
|
+
{
|
|
6726
|
+
"data-state": isSelected ? "selected" : void 0,
|
|
6727
|
+
className: cn(isSelected && "bg-[var(--ui-primary)]/10"),
|
|
6728
|
+
children: [
|
|
6729
|
+
selectable && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "w-10 px-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6730
|
+
Checkbox,
|
|
6731
|
+
{
|
|
6732
|
+
checked: isSelected,
|
|
6733
|
+
onCheckedChange: () => toggleSelectRow(key),
|
|
6734
|
+
"aria-label": `Select row ${key}`
|
|
6735
|
+
}
|
|
6736
|
+
) }),
|
|
6737
|
+
columns.map((col) => {
|
|
6738
|
+
const val = col.accessor ? col.accessor(row) : row[col.key];
|
|
6739
|
+
const cellContent = col.render ? col.render(row, rowIdx, val) : val;
|
|
6740
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: cellContent }, col.key);
|
|
6741
|
+
})
|
|
6742
|
+
]
|
|
6743
|
+
},
|
|
6744
|
+
key
|
|
6745
|
+
);
|
|
6746
|
+
}) })
|
|
6747
|
+
] })
|
|
6748
|
+
] }),
|
|
5305
6749
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 px-1 text-xs text-[var(--ui-text-muted)] font-mono", children: [
|
|
5306
6750
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5307
6751
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rows per page:" }),
|
|
@@ -5309,21 +6753,18 @@ function DataTablePro({
|
|
|
5309
6753
|
"select",
|
|
5310
6754
|
{
|
|
5311
6755
|
value: pageSize,
|
|
5312
|
-
onChange: (e) =>
|
|
5313
|
-
setPageSize(Number(e.target.value));
|
|
5314
|
-
setCurrentPage(1);
|
|
5315
|
-
},
|
|
6756
|
+
onChange: (e) => handlePageSizeChange(Number(e.target.value)),
|
|
5316
6757
|
className: "px-2 py-1 rounded-lg bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] text-[var(--ui-text-primary)] font-mono text-xs focus:outline-none",
|
|
5317
6758
|
children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt, children: opt }, opt))
|
|
5318
6759
|
}
|
|
5319
6760
|
),
|
|
5320
6761
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[var(--ui-text-muted)]", children: [
|
|
5321
6762
|
"Showing ",
|
|
5322
|
-
Math.min((currentPage - 1) * pageSize + 1,
|
|
6763
|
+
totalItems === 0 ? 0 : Math.min((currentPage - 1) * pageSize + 1, totalItems),
|
|
5323
6764
|
"\u2013",
|
|
5324
|
-
Math.min(currentPage * pageSize,
|
|
6765
|
+
Math.min(currentPage * pageSize, totalItems),
|
|
5325
6766
|
" of ",
|
|
5326
|
-
|
|
6767
|
+
totalItems
|
|
5327
6768
|
] })
|
|
5328
6769
|
] }),
|
|
5329
6770
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
@@ -5332,8 +6773,9 @@ function DataTablePro({
|
|
|
5332
6773
|
{
|
|
5333
6774
|
size: "xs",
|
|
5334
6775
|
variant: "outline",
|
|
5335
|
-
disabled: currentPage
|
|
5336
|
-
onClick: () =>
|
|
6776
|
+
disabled: currentPage <= 1 || loading,
|
|
6777
|
+
onClick: () => handlePageChange(currentPage - 1),
|
|
6778
|
+
"aria-label": "Previous page",
|
|
5337
6779
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-3.5 h-3.5" })
|
|
5338
6780
|
}
|
|
5339
6781
|
),
|
|
@@ -5347,8 +6789,9 @@ function DataTablePro({
|
|
|
5347
6789
|
{
|
|
5348
6790
|
size: "xs",
|
|
5349
6791
|
variant: "outline",
|
|
5350
|
-
disabled: currentPage
|
|
5351
|
-
onClick: () =>
|
|
6792
|
+
disabled: currentPage >= totalPages || loading,
|
|
6793
|
+
onClick: () => handlePageChange(currentPage + 1),
|
|
6794
|
+
"aria-label": "Next page",
|
|
5352
6795
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3.5 h-3.5" })
|
|
5353
6796
|
}
|
|
5354
6797
|
)
|
|
@@ -5356,100 +6799,218 @@ function DataTablePro({
|
|
|
5356
6799
|
] })
|
|
5357
6800
|
] });
|
|
5358
6801
|
}
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
return
|
|
5380
|
-
}
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
6802
|
+
var TreeView = React66.forwardRef(
|
|
6803
|
+
({
|
|
6804
|
+
data,
|
|
6805
|
+
selectedId,
|
|
6806
|
+
defaultExpandedIds,
|
|
6807
|
+
onSelect,
|
|
6808
|
+
onExpandedChange,
|
|
6809
|
+
className,
|
|
6810
|
+
"aria-label": ariaLabel = "Tree View"
|
|
6811
|
+
}, ref) => {
|
|
6812
|
+
const initialExpanded = React66.useMemo(() => {
|
|
6813
|
+
if (defaultExpandedIds) return new Set(defaultExpandedIds);
|
|
6814
|
+
const set = /* @__PURE__ */ new Set();
|
|
6815
|
+
const collect = (items) => {
|
|
6816
|
+
for (const it of items) {
|
|
6817
|
+
if (it.defaultExpanded) set.add(it.id);
|
|
6818
|
+
if (it.children) collect(it.children);
|
|
6819
|
+
}
|
|
6820
|
+
};
|
|
6821
|
+
collect(data);
|
|
6822
|
+
return set;
|
|
6823
|
+
}, [data, defaultExpandedIds]);
|
|
6824
|
+
const [expandedSet, setExpandedSet] = React66.useState(initialExpanded);
|
|
6825
|
+
const [focusedId, setFocusedId] = React66.useState(null);
|
|
6826
|
+
const itemRefs = React66.useRef(/* @__PURE__ */ new Map());
|
|
6827
|
+
const focusNode = (id) => {
|
|
6828
|
+
setFocusedId(id);
|
|
6829
|
+
const el = itemRefs.current.get(id);
|
|
6830
|
+
if (el) {
|
|
6831
|
+
el.focus();
|
|
6832
|
+
}
|
|
6833
|
+
};
|
|
6834
|
+
const toggleExpand = (id) => {
|
|
6835
|
+
setExpandedSet((prev) => {
|
|
6836
|
+
const next = new Set(prev);
|
|
6837
|
+
if (next.has(id)) next.delete(id);
|
|
6838
|
+
else next.add(id);
|
|
6839
|
+
onExpandedChange?.(Array.from(next));
|
|
6840
|
+
return next;
|
|
6841
|
+
});
|
|
6842
|
+
};
|
|
6843
|
+
const expandNode = (id) => {
|
|
6844
|
+
if (!expandedSet.has(id)) {
|
|
6845
|
+
setExpandedSet((prev) => {
|
|
6846
|
+
const next = new Set(prev).add(id);
|
|
6847
|
+
onExpandedChange?.(Array.from(next));
|
|
6848
|
+
return next;
|
|
6849
|
+
});
|
|
6850
|
+
}
|
|
6851
|
+
};
|
|
6852
|
+
const collapseNode = (id) => {
|
|
6853
|
+
if (expandedSet.has(id)) {
|
|
6854
|
+
setExpandedSet((prev) => {
|
|
6855
|
+
const next = new Set(prev);
|
|
6856
|
+
next.delete(id);
|
|
6857
|
+
onExpandedChange?.(Array.from(next));
|
|
6858
|
+
return next;
|
|
6859
|
+
});
|
|
6860
|
+
}
|
|
6861
|
+
};
|
|
6862
|
+
const visibleList = React66.useMemo(() => {
|
|
6863
|
+
const list = [];
|
|
6864
|
+
const traverse = (items, level, parentId) => {
|
|
6865
|
+
for (const item of items) {
|
|
6866
|
+
const hasChildren = Boolean(item.children && item.children.length > 0);
|
|
6867
|
+
list.push({ item, level, parentId, hasChildren });
|
|
6868
|
+
if (hasChildren && expandedSet.has(item.id)) {
|
|
6869
|
+
traverse(item.children, level + 1, item.id);
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
};
|
|
6873
|
+
traverse(data, 0, null);
|
|
6874
|
+
return list;
|
|
6875
|
+
}, [data, expandedSet]);
|
|
6876
|
+
const handleKeyDown = (e) => {
|
|
6877
|
+
if (visibleList.length === 0) return;
|
|
6878
|
+
const currentIdx = focusedId ? visibleList.findIndex((f) => f.item.id === focusedId) : 0;
|
|
6879
|
+
const currentFlat = visibleList[currentIdx] || visibleList[0];
|
|
6880
|
+
if (e.key === "ArrowDown") {
|
|
6881
|
+
e.preventDefault();
|
|
6882
|
+
const nextIdx = Math.min(visibleList.length - 1, currentIdx + 1);
|
|
6883
|
+
focusNode(visibleList[nextIdx].item.id);
|
|
6884
|
+
} else if (e.key === "ArrowUp") {
|
|
6885
|
+
e.preventDefault();
|
|
6886
|
+
const prevIdx = Math.max(0, currentIdx - 1);
|
|
6887
|
+
focusNode(visibleList[prevIdx].item.id);
|
|
6888
|
+
} else if (e.key === "ArrowRight") {
|
|
6889
|
+
e.preventDefault();
|
|
6890
|
+
if (currentFlat.hasChildren) {
|
|
6891
|
+
if (!expandedSet.has(currentFlat.item.id)) {
|
|
6892
|
+
expandNode(currentFlat.item.id);
|
|
6893
|
+
} else {
|
|
6894
|
+
const nextIdx = currentIdx + 1;
|
|
6895
|
+
if (nextIdx < visibleList.length) {
|
|
6896
|
+
focusNode(visibleList[nextIdx].item.id);
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
}
|
|
6900
|
+
} else if (e.key === "ArrowLeft") {
|
|
6901
|
+
e.preventDefault();
|
|
6902
|
+
if (currentFlat.hasChildren && expandedSet.has(currentFlat.item.id)) {
|
|
6903
|
+
collapseNode(currentFlat.item.id);
|
|
6904
|
+
} else if (currentFlat.parentId) {
|
|
6905
|
+
focusNode(currentFlat.parentId);
|
|
6906
|
+
}
|
|
6907
|
+
} else if (e.key === "Home") {
|
|
6908
|
+
e.preventDefault();
|
|
6909
|
+
focusNode(visibleList[0].item.id);
|
|
6910
|
+
} else if (e.key === "End") {
|
|
6911
|
+
e.preventDefault();
|
|
6912
|
+
focusNode(visibleList[visibleList.length - 1].item.id);
|
|
6913
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
6914
|
+
e.preventDefault();
|
|
6915
|
+
if (currentFlat) {
|
|
6916
|
+
if (currentFlat.hasChildren) {
|
|
6917
|
+
toggleExpand(currentFlat.item.id);
|
|
6918
|
+
}
|
|
6919
|
+
onSelect?.(currentFlat.item);
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
};
|
|
6923
|
+
const getIcon = (item, hasChildren, isExpanded) => {
|
|
6924
|
+
if (item.icon) return item.icon;
|
|
6925
|
+
if (hasChildren) {
|
|
6926
|
+
return isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: "w-4 h-4 text-amber-500 shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Folder, { className: "w-4 h-4 text-amber-500 shrink-0" });
|
|
6927
|
+
}
|
|
6928
|
+
if (item.label.endsWith(".tsx") || item.label.endsWith(".ts") || item.label.endsWith(".js")) {
|
|
6929
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { className: "w-4 h-4 text-blue-500 shrink-0" });
|
|
6930
|
+
}
|
|
6931
|
+
if (item.label.endsWith(".png") || item.label.endsWith(".jpg") || item.label.endsWith(".svg")) {
|
|
6932
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { className: "w-4 h-4 text-purple-500 shrink-0" });
|
|
6933
|
+
}
|
|
6934
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" });
|
|
6935
|
+
};
|
|
6936
|
+
const renderNodes = (items, level) => {
|
|
6937
|
+
return items.map((item, index) => {
|
|
6938
|
+
const hasChildren = Boolean(item.children && item.children.length > 0);
|
|
6939
|
+
const isExpanded = expandedSet.has(item.id);
|
|
6940
|
+
const isSelected = selectedId === item.id;
|
|
6941
|
+
const isFocused = focusedId === item.id || !focusedId && level === 0 && index === 0;
|
|
6942
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "none", className: "select-none text-xs", children: [
|
|
6943
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6944
|
+
"div",
|
|
5401
6945
|
{
|
|
6946
|
+
ref: (el) => {
|
|
6947
|
+
if (el) itemRefs.current.set(item.id, el);
|
|
6948
|
+
else itemRefs.current.delete(item.id);
|
|
6949
|
+
},
|
|
6950
|
+
role: "treeitem",
|
|
6951
|
+
tabIndex: isFocused ? 0 : -1,
|
|
6952
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
6953
|
+
"aria-selected": isSelected,
|
|
6954
|
+
"aria-level": level + 1,
|
|
6955
|
+
"aria-posinset": index + 1,
|
|
6956
|
+
"aria-setsize": items.length,
|
|
6957
|
+
onClick: (e) => {
|
|
6958
|
+
e.stopPropagation();
|
|
6959
|
+
focusNode(item.id);
|
|
6960
|
+
if (hasChildren) {
|
|
6961
|
+
toggleExpand(item.id);
|
|
6962
|
+
}
|
|
6963
|
+
onSelect?.(item);
|
|
6964
|
+
},
|
|
6965
|
+
style: { paddingLeft: `${level * 16 + 8}px` },
|
|
5402
6966
|
className: cn(
|
|
5403
|
-
"
|
|
5404
|
-
isSelected ? "bg-
|
|
6967
|
+
"flex items-center justify-between py-1.5 pr-2.5 rounded-xl cursor-pointer transition-colors group outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
|
|
6968
|
+
isSelected ? "bg-[var(--ui-primary)] text-white font-semibold shadow-xs" : "text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)]"
|
|
5405
6969
|
),
|
|
5406
|
-
children:
|
|
6970
|
+
children: [
|
|
6971
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
6972
|
+
hasChildren ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("p-0.5 rounded transition-transform duration-200", isExpanded && "rotate-90"), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: cn("w-3 h-3", isSelected ? "text-white" : "text-[var(--ui-text-muted)]") }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4" }),
|
|
6973
|
+
getIcon(item, hasChildren, isExpanded),
|
|
6974
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label })
|
|
6975
|
+
] }),
|
|
6976
|
+
item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6977
|
+
"span",
|
|
6978
|
+
{
|
|
6979
|
+
className: cn(
|
|
6980
|
+
"px-1.5 py-0.5 rounded-md text-[10px] font-mono",
|
|
6981
|
+
isSelected ? "bg-white/20 text-white" : "bg-[var(--ui-bg-secondary)] text-[var(--ui-text-muted)]"
|
|
6982
|
+
),
|
|
6983
|
+
children: item.badge
|
|
6984
|
+
}
|
|
6985
|
+
)
|
|
6986
|
+
]
|
|
5407
6987
|
}
|
|
5408
|
-
)
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
{
|
|
5423
|
-
item: child,
|
|
5424
|
-
level: level + 1,
|
|
5425
|
-
selectedId,
|
|
5426
|
-
onSelect
|
|
5427
|
-
},
|
|
5428
|
-
child.id
|
|
5429
|
-
))
|
|
5430
|
-
] })
|
|
5431
|
-
] });
|
|
5432
|
-
}
|
|
5433
|
-
var TreeView = React66.forwardRef(
|
|
5434
|
-
({ data, selectedId, onSelect, className }, ref) => {
|
|
6988
|
+
),
|
|
6989
|
+
hasChildren && isExpanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", className: "relative space-y-0.5 animate-in fade-in duration-150", children: [
|
|
6990
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6991
|
+
"div",
|
|
6992
|
+
{
|
|
6993
|
+
className: "absolute left-[15px] top-0 bottom-0 w-[1px] bg-[var(--ui-border-subtle)]",
|
|
6994
|
+
style: { left: `${level * 16 + 15}px` }
|
|
6995
|
+
}
|
|
6996
|
+
),
|
|
6997
|
+
renderNodes(item.children, level + 1)
|
|
6998
|
+
] })
|
|
6999
|
+
] }, item.id);
|
|
7000
|
+
});
|
|
7001
|
+
};
|
|
5435
7002
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5436
7003
|
"div",
|
|
5437
7004
|
{
|
|
5438
7005
|
ref,
|
|
7006
|
+
role: "tree",
|
|
7007
|
+
"aria-label": ariaLabel,
|
|
7008
|
+
onKeyDown: handleKeyDown,
|
|
5439
7009
|
className: cn(
|
|
5440
|
-
"w-full p-2 rounded-2xl bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] space-y-0.5 shadow-sm overflow-hidden",
|
|
7010
|
+
"w-full p-2 rounded-2xl bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] space-y-0.5 shadow-sm overflow-hidden outline-none",
|
|
5441
7011
|
className
|
|
5442
7012
|
),
|
|
5443
|
-
children: data
|
|
5444
|
-
TreeNode,
|
|
5445
|
-
{
|
|
5446
|
-
item,
|
|
5447
|
-
level: 0,
|
|
5448
|
-
selectedId,
|
|
5449
|
-
onSelect
|
|
5450
|
-
},
|
|
5451
|
-
item.id
|
|
5452
|
-
))
|
|
7013
|
+
children: renderNodes(data, 0)
|
|
5453
7014
|
}
|
|
5454
7015
|
);
|
|
5455
7016
|
}
|
|
@@ -6232,6 +7793,7 @@ exports.CardTitle = CardTitle;
|
|
|
6232
7793
|
exports.Carousel = Carousel;
|
|
6233
7794
|
exports.Checkbox = Checkbox;
|
|
6234
7795
|
exports.CheckboxGroup = CheckboxGroup;
|
|
7796
|
+
exports.CheckboxGroupContext = CheckboxGroupContext;
|
|
6235
7797
|
exports.Chip = Chip;
|
|
6236
7798
|
exports.CircularProgress = CircularProgress;
|
|
6237
7799
|
exports.Code = Code;
|
|
@@ -6342,6 +7904,7 @@ exports.Textarea = Textarea;
|
|
|
6342
7904
|
exports.ThemeProvider = ThemeProvider;
|
|
6343
7905
|
exports.TimePicker = TimePicker;
|
|
6344
7906
|
exports.Timeline = Timeline;
|
|
7907
|
+
exports.Toast = Toast;
|
|
6345
7908
|
exports.ToasterProvider = ToasterProvider;
|
|
6346
7909
|
exports.ToggleButton = ToggleButton;
|
|
6347
7910
|
exports.ToggleGroup = ToggleGroup;
|
|
@@ -6389,6 +7952,7 @@ exports.toastVariants = toastVariants;
|
|
|
6389
7952
|
exports.toggleVariants = toggleVariants;
|
|
6390
7953
|
exports.tooltipVariants = tooltipVariants;
|
|
6391
7954
|
exports.useAccordion = useAccordion;
|
|
7955
|
+
exports.useCheckboxGroup = useCheckboxGroup;
|
|
6392
7956
|
exports.useDrawer = useDrawer;
|
|
6393
7957
|
exports.useDropdown = useDropdown;
|
|
6394
7958
|
exports.useFormField = useFormField;
|