@thangnqdev/ui 0.1.1 → 0.2.0
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 +86 -65
- 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 -130
- 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 +160 -116
- 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 -130
- 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
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Chip } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import React2, { createContext, useContext, forwardRef, useId, useState, useRef, useEffect } from 'react';
|
|
8
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
1
|
+
import { useFormField, useCheckboxGroup, checkboxVariants, FormLabel, FormHelperText, FormField, FormErrorMessage, CheckboxGroupContext, CheckboxGroup, Checkbox, Chip } from './chunk-DSEL7ELZ.js';
|
|
2
|
+
import { useControllableState, Popover } from './chunk-YOCGBORV.js';
|
|
3
|
+
import { Kbd } from './chunk-I5O2WCGF.js';
|
|
4
|
+
import { Button } from './chunk-I2FQBMVW.js';
|
|
5
|
+
import { __export, cn } from './chunk-2YJBW4E2.js';
|
|
6
|
+
import React, { forwardRef, useState, useRef, useEffect, createContext, useContext, useCallback, useId } from 'react';
|
|
9
7
|
import { cva } from 'class-variance-authority';
|
|
10
|
-
import { X, Search, EyeOff, Eye, ChevronDown,
|
|
8
|
+
import { X, Search, EyeOff, Eye, ChevronDown, ChevronLeft, ChevronRight, Calendar, Clock, Check, ChevronsUpDown, UploadCloud, FileText, File, CheckCircle2, AlertCircle, Minus, Plus } from 'lucide-react';
|
|
9
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
10
|
|
|
12
11
|
// src/forms/index.ts
|
|
13
12
|
var forms_exports = {};
|
|
14
13
|
__export(forms_exports, {
|
|
15
14
|
Checkbox: () => Checkbox,
|
|
16
15
|
CheckboxGroup: () => CheckboxGroup,
|
|
16
|
+
CheckboxGroupContext: () => CheckboxGroupContext,
|
|
17
17
|
Combobox: () => Combobox,
|
|
18
18
|
CurrencyInput: () => CurrencyInput,
|
|
19
19
|
DatePicker: () => DatePicker,
|
|
@@ -50,93 +50,9 @@ __export(forms_exports, {
|
|
|
50
50
|
switchThumbVariants: () => switchThumbVariants,
|
|
51
51
|
switchVariants: () => switchVariants,
|
|
52
52
|
textareaVariants: () => textareaVariants,
|
|
53
|
+
useCheckboxGroup: () => useCheckboxGroup,
|
|
53
54
|
useFormField: () => useFormField
|
|
54
55
|
});
|
|
55
|
-
var FormFieldContext = createContext(null);
|
|
56
|
-
function useFormField() {
|
|
57
|
-
const context = useContext(FormFieldContext);
|
|
58
|
-
return context;
|
|
59
|
-
}
|
|
60
|
-
var FormField = forwardRef(
|
|
61
|
-
({ className, isRequired, isInvalid, isDisabled, children, ...props }, ref) => {
|
|
62
|
-
const generatedId = useId();
|
|
63
|
-
const id = props.id || generatedId;
|
|
64
|
-
const labelId = `${id}-label`;
|
|
65
|
-
const descriptionId = `${id}-description`;
|
|
66
|
-
const errorId = `${id}-error`;
|
|
67
|
-
return /* @__PURE__ */ jsx(
|
|
68
|
-
FormFieldContext.Provider,
|
|
69
|
-
{
|
|
70
|
-
value: {
|
|
71
|
-
id,
|
|
72
|
-
labelId,
|
|
73
|
-
descriptionId,
|
|
74
|
-
errorId,
|
|
75
|
-
isRequired,
|
|
76
|
-
isInvalid,
|
|
77
|
-
isDisabled
|
|
78
|
-
},
|
|
79
|
-
children: /* @__PURE__ */ jsx("div", { ref, className: cn("space-y-1.5 w-full", className), ...props, children })
|
|
80
|
-
}
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
FormField.displayName = "FormField";
|
|
85
|
-
var FormLabel = forwardRef(
|
|
86
|
-
({ className, required, children, ...props }, ref) => {
|
|
87
|
-
const context = useFormField();
|
|
88
|
-
const isRequired = required ?? context?.isRequired;
|
|
89
|
-
const isError = context?.isInvalid;
|
|
90
|
-
return /* @__PURE__ */ jsx(
|
|
91
|
-
Label,
|
|
92
|
-
{
|
|
93
|
-
ref,
|
|
94
|
-
id: context?.labelId,
|
|
95
|
-
htmlFor: context?.id,
|
|
96
|
-
required: isRequired,
|
|
97
|
-
error: isError,
|
|
98
|
-
className,
|
|
99
|
-
...props,
|
|
100
|
-
children
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
FormLabel.displayName = "FormLabel";
|
|
106
|
-
var FormHelperText = forwardRef(
|
|
107
|
-
({ className, children, ...props }, ref) => {
|
|
108
|
-
const context = useFormField();
|
|
109
|
-
return /* @__PURE__ */ jsx(
|
|
110
|
-
"p",
|
|
111
|
-
{
|
|
112
|
-
ref,
|
|
113
|
-
id: context?.descriptionId,
|
|
114
|
-
className: cn("text-xs text-[var(--ui-text-muted)]", className),
|
|
115
|
-
...props,
|
|
116
|
-
children
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
FormHelperText.displayName = "FormHelperText";
|
|
122
|
-
var FormErrorMessage = forwardRef(
|
|
123
|
-
({ className, children, ...props }, ref) => {
|
|
124
|
-
const context = useFormField();
|
|
125
|
-
if (!context?.isInvalid && !children) return null;
|
|
126
|
-
return /* @__PURE__ */ jsx(
|
|
127
|
-
"p",
|
|
128
|
-
{
|
|
129
|
-
ref,
|
|
130
|
-
id: context?.errorId,
|
|
131
|
-
role: "alert",
|
|
132
|
-
className: cn("text-xs font-medium text-[var(--ui-error)]", className),
|
|
133
|
-
...props,
|
|
134
|
-
children
|
|
135
|
-
}
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
);
|
|
139
|
-
FormErrorMessage.displayName = "FormErrorMessage";
|
|
140
56
|
var inputVariants = cva(
|
|
141
57
|
"w-full rounded-[var(--ui-radius-md)] border bg-[var(--ui-bg-surface)]/70 backdrop-blur-md text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]/70 focus-visible:border-[var(--ui-primary)] focus-visible:shadow-[0_0_16px_rgba(99,102,241,0.2)] disabled:cursor-not-allowed disabled:opacity-50 shadow-inner",
|
|
142
58
|
{
|
|
@@ -187,7 +103,7 @@ var Input = forwardRef(
|
|
|
187
103
|
const [uncontrolledVal, setUncontrolledVal] = useState(
|
|
188
104
|
props.defaultValue !== void 0 ? String(props.defaultValue) : ""
|
|
189
105
|
);
|
|
190
|
-
const innerRef =
|
|
106
|
+
const innerRef = React.useRef(null);
|
|
191
107
|
const handleRef = (node) => {
|
|
192
108
|
innerRef.current = node;
|
|
193
109
|
if (typeof ref === "function") {
|
|
@@ -448,77 +364,6 @@ var Select = forwardRef(
|
|
|
448
364
|
}
|
|
449
365
|
);
|
|
450
366
|
Select.displayName = "Select";
|
|
451
|
-
var checkboxVariants = cva(
|
|
452
|
-
"peer shrink-0 rounded-[var(--ui-radius-sm)] 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 text-[var(--ui-primary-fg)]",
|
|
453
|
-
{
|
|
454
|
-
variants: {
|
|
455
|
-
size: {
|
|
456
|
-
sm: "w-4 h-4 text-xs",
|
|
457
|
-
md: "w-5 h-5 text-sm",
|
|
458
|
-
lg: "w-6 h-6 text-base"
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
defaultVariants: {
|
|
462
|
-
size: "md"
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
);
|
|
466
|
-
var Checkbox = forwardRef(
|
|
467
|
-
({ className, size, indeterminate = false, label, checked, disabled, id, onChange, ...props }, ref) => {
|
|
468
|
-
const formField = useFormField();
|
|
469
|
-
const isDisabled = disabled ?? formField?.isDisabled ?? false;
|
|
470
|
-
const checkboxId = id || formField?.id;
|
|
471
|
-
return /* @__PURE__ */ jsxs("label", { className: cn("inline-flex items-center gap-2 select-none cursor-pointer", isDisabled && "cursor-not-allowed opacity-50"), children: [
|
|
472
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
473
|
-
/* @__PURE__ */ jsx(
|
|
474
|
-
"input",
|
|
475
|
-
{
|
|
476
|
-
ref,
|
|
477
|
-
type: "checkbox",
|
|
478
|
-
id: checkboxId,
|
|
479
|
-
disabled: isDisabled,
|
|
480
|
-
checked,
|
|
481
|
-
onChange,
|
|
482
|
-
className: "sr-only peer",
|
|
483
|
-
...props
|
|
484
|
-
}
|
|
485
|
-
),
|
|
486
|
-
/* @__PURE__ */ jsx(
|
|
487
|
-
"div",
|
|
488
|
-
{
|
|
489
|
-
className: cn(
|
|
490
|
-
checkboxVariants({ size }),
|
|
491
|
-
(checked || indeterminate) && "bg-[var(--ui-primary)] border-[var(--ui-primary)]",
|
|
492
|
-
className
|
|
493
|
-
),
|
|
494
|
-
children: indeterminate ? /* @__PURE__ */ jsx(Minus, { className: "w-3.5 h-3.5 stroke-[3]" }) : checked && /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 stroke-[3]" })
|
|
495
|
-
}
|
|
496
|
-
)
|
|
497
|
-
] }),
|
|
498
|
-
label && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ui-text-primary)]", children: label })
|
|
499
|
-
] });
|
|
500
|
-
}
|
|
501
|
-
);
|
|
502
|
-
Checkbox.displayName = "Checkbox";
|
|
503
|
-
var CheckboxGroup = forwardRef(
|
|
504
|
-
({ className, orientation = "vertical", children, value, onChange, ...props }, ref) => {
|
|
505
|
-
return /* @__PURE__ */ jsx(
|
|
506
|
-
"div",
|
|
507
|
-
{
|
|
508
|
-
ref,
|
|
509
|
-
role: "group",
|
|
510
|
-
className: cn(
|
|
511
|
-
"flex gap-2",
|
|
512
|
-
orientation === "vertical" ? "flex-col" : "flex-row flex-wrap items-center",
|
|
513
|
-
className
|
|
514
|
-
),
|
|
515
|
-
...props,
|
|
516
|
-
children
|
|
517
|
-
}
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
);
|
|
521
|
-
CheckboxGroup.displayName = "CheckboxGroup";
|
|
522
367
|
var RadioGroupContext = createContext(null);
|
|
523
368
|
var radioVariants = cva(
|
|
524
369
|
"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",
|
|
@@ -593,7 +438,7 @@ var RadioGroup = forwardRef(
|
|
|
593
438
|
children,
|
|
594
439
|
...props
|
|
595
440
|
}, ref) => {
|
|
596
|
-
const [val, setVal] =
|
|
441
|
+
const [val, setVal] = React.useState(defaultValue || "");
|
|
597
442
|
const currentValue = controlledValue !== void 0 ? controlledValue : val;
|
|
598
443
|
const handleChange = (newValue) => {
|
|
599
444
|
setVal(newValue);
|
|
@@ -722,7 +567,7 @@ var Slider = forwardRef(
|
|
|
722
567
|
disabled,
|
|
723
568
|
...props
|
|
724
569
|
}, ref) => {
|
|
725
|
-
const [val, setVal] =
|
|
570
|
+
const [val, setVal] = React.useState(defaultValue);
|
|
726
571
|
const currentValue = controlledValue !== void 0 ? controlledValue : val;
|
|
727
572
|
const percentage = Math.min(100, Math.max(0, (currentValue - min) / (max - min) * 100));
|
|
728
573
|
const handleChange = (e) => {
|
|
@@ -778,81 +623,192 @@ var RangeSlider = forwardRef(
|
|
|
778
623
|
showValue = false,
|
|
779
624
|
formatValue = (v) => `${v}`,
|
|
780
625
|
className,
|
|
781
|
-
disabled = false
|
|
626
|
+
disabled = false,
|
|
627
|
+
name,
|
|
628
|
+
"aria-label": ariaLabel = "Range slider"
|
|
782
629
|
}, ref) => {
|
|
783
|
-
const
|
|
784
|
-
const [
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
630
|
+
const isControlled = controlledValue !== void 0;
|
|
631
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
632
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
633
|
+
const [minVal, maxVal] = [
|
|
634
|
+
Math.max(min, Math.min(currentValue[0], currentValue[1])),
|
|
635
|
+
Math.min(max, Math.max(currentValue[0], currentValue[1]))
|
|
636
|
+
];
|
|
637
|
+
const trackRef = useRef(null);
|
|
638
|
+
const draggingThumbRef = useRef(null);
|
|
639
|
+
const updateValues = useCallback(
|
|
640
|
+
(newMin, newMax) => {
|
|
641
|
+
const clampedMin = Math.max(min, Math.min(newMin, newMax));
|
|
642
|
+
const clampedMax = Math.min(max, Math.max(clampedMin, newMax));
|
|
643
|
+
const next = [clampedMin, clampedMax];
|
|
644
|
+
if (!isControlled) {
|
|
645
|
+
setInternalValue(next);
|
|
646
|
+
}
|
|
647
|
+
onChange?.(next);
|
|
648
|
+
},
|
|
649
|
+
[min, max, isControlled, onChange]
|
|
650
|
+
);
|
|
651
|
+
const getValueFromPointer = useCallback(
|
|
652
|
+
(clientX) => {
|
|
653
|
+
if (!trackRef.current) return min;
|
|
654
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
655
|
+
const percent = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
|
|
656
|
+
const rawValue = min + percent * (max - min);
|
|
657
|
+
const steppedValue = Math.round((rawValue - min) / step) * step + min;
|
|
658
|
+
return Math.min(max, Math.max(min, Number(steppedValue.toFixed(4))));
|
|
659
|
+
},
|
|
660
|
+
[min, max, step]
|
|
661
|
+
);
|
|
662
|
+
const handlePointerDown = (e) => {
|
|
663
|
+
if (disabled) return;
|
|
664
|
+
const clickedVal = getValueFromPointer(e.clientX);
|
|
665
|
+
const distToMin = Math.abs(clickedVal - minVal);
|
|
666
|
+
const distToMax = Math.abs(clickedVal - maxVal);
|
|
667
|
+
const targetThumb = distToMin <= distToMax ? "min" : "max";
|
|
668
|
+
draggingThumbRef.current = targetThumb;
|
|
669
|
+
if (targetThumb === "min") {
|
|
670
|
+
updateValues(Math.min(clickedVal, maxVal), maxVal);
|
|
671
|
+
} else {
|
|
672
|
+
updateValues(minVal, Math.max(clickedVal, minVal));
|
|
673
|
+
}
|
|
674
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
791
675
|
};
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
676
|
+
const handlePointerMove = (e) => {
|
|
677
|
+
if (disabled || !draggingThumbRef.current) return;
|
|
678
|
+
const moveVal = getValueFromPointer(e.clientX);
|
|
679
|
+
if (draggingThumbRef.current === "min") {
|
|
680
|
+
updateValues(Math.min(moveVal, maxVal), maxVal);
|
|
681
|
+
} else {
|
|
682
|
+
updateValues(minVal, Math.max(moveVal, minVal));
|
|
683
|
+
}
|
|
796
684
|
};
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
685
|
+
const handlePointerUp = (e) => {
|
|
686
|
+
if (draggingThumbRef.current) {
|
|
687
|
+
draggingThumbRef.current = null;
|
|
688
|
+
try {
|
|
689
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
690
|
+
} catch {
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const handleKeyDownMin = (e) => {
|
|
695
|
+
if (disabled) return;
|
|
696
|
+
let nextMin = minVal;
|
|
697
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
698
|
+
e.preventDefault();
|
|
699
|
+
nextMin = Math.min(minVal + step, maxVal);
|
|
700
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
701
|
+
e.preventDefault();
|
|
702
|
+
nextMin = Math.max(minVal - step, min);
|
|
703
|
+
} else if (e.key === "Home") {
|
|
704
|
+
e.preventDefault();
|
|
705
|
+
nextMin = min;
|
|
706
|
+
} else if (e.key === "End") {
|
|
707
|
+
e.preventDefault();
|
|
708
|
+
nextMin = maxVal;
|
|
709
|
+
}
|
|
710
|
+
if (nextMin !== minVal) {
|
|
711
|
+
updateValues(nextMin, maxVal);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
const handleKeyDownMax = (e) => {
|
|
715
|
+
if (disabled) return;
|
|
716
|
+
let nextMax = maxVal;
|
|
717
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
718
|
+
e.preventDefault();
|
|
719
|
+
nextMax = Math.min(maxVal + step, max);
|
|
720
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
721
|
+
e.preventDefault();
|
|
722
|
+
nextMax = Math.max(maxVal - step, minVal);
|
|
723
|
+
} else if (e.key === "Home") {
|
|
724
|
+
e.preventDefault();
|
|
725
|
+
nextMax = minVal;
|
|
726
|
+
} else if (e.key === "End") {
|
|
727
|
+
e.preventDefault();
|
|
728
|
+
nextMax = max;
|
|
729
|
+
}
|
|
730
|
+
if (nextMax !== maxVal) {
|
|
731
|
+
updateValues(minVal, nextMax);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
const minPercentage = Math.min(100, Math.max(0, (minVal - min) / (max - min || 1) * 100));
|
|
735
|
+
const maxPercentage = Math.min(100, Math.max(0, (maxVal - min) / (max - min || 1) * 100));
|
|
736
|
+
return /* @__PURE__ */ jsxs(
|
|
737
|
+
"div",
|
|
738
|
+
{
|
|
739
|
+
ref,
|
|
740
|
+
className: cn("w-full flex items-center gap-4", disabled && "opacity-50 cursor-not-allowed", className),
|
|
741
|
+
children: [
|
|
742
|
+
/* @__PURE__ */ jsxs(
|
|
743
|
+
"div",
|
|
744
|
+
{
|
|
745
|
+
ref: trackRef,
|
|
746
|
+
onPointerDown: handlePointerDown,
|
|
747
|
+
onPointerMove: handlePointerMove,
|
|
748
|
+
onPointerUp: handlePointerUp,
|
|
749
|
+
className: "relative w-full flex items-center h-7 select-none touch-none cursor-pointer",
|
|
750
|
+
children: [
|
|
751
|
+
/* @__PURE__ */ jsx("div", { className: "w-full h-2 rounded-full bg-[var(--ui-border-default)] overflow-hidden relative", children: /* @__PURE__ */ jsx(
|
|
752
|
+
"div",
|
|
753
|
+
{
|
|
754
|
+
className: "absolute h-full bg-[var(--ui-primary)]",
|
|
755
|
+
style: {
|
|
756
|
+
left: `${minPercentage}%`,
|
|
757
|
+
width: `${Math.max(0, maxPercentage - minPercentage)}%`
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
) }),
|
|
761
|
+
name && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
762
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}_min`, value: minVal }),
|
|
763
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}_max`, value: maxVal })
|
|
764
|
+
] }),
|
|
765
|
+
/* @__PURE__ */ jsx(
|
|
766
|
+
"div",
|
|
767
|
+
{
|
|
768
|
+
role: "slider",
|
|
769
|
+
tabIndex: disabled ? -1 : 0,
|
|
770
|
+
"aria-label": `${ariaLabel} minimum`,
|
|
771
|
+
"aria-valuemin": min,
|
|
772
|
+
"aria-valuemax": maxVal,
|
|
773
|
+
"aria-valuenow": minVal,
|
|
774
|
+
"aria-disabled": disabled,
|
|
775
|
+
onKeyDown: handleKeyDownMin,
|
|
776
|
+
className: cn(
|
|
777
|
+
"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",
|
|
778
|
+
disabled && "pointer-events-none"
|
|
779
|
+
),
|
|
780
|
+
style: { left: `${minPercentage}%` }
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ jsx(
|
|
784
|
+
"div",
|
|
785
|
+
{
|
|
786
|
+
role: "slider",
|
|
787
|
+
tabIndex: disabled ? -1 : 0,
|
|
788
|
+
"aria-label": `${ariaLabel} maximum`,
|
|
789
|
+
"aria-valuemin": minVal,
|
|
790
|
+
"aria-valuemax": max,
|
|
791
|
+
"aria-valuenow": maxVal,
|
|
792
|
+
"aria-disabled": disabled,
|
|
793
|
+
onKeyDown: handleKeyDownMax,
|
|
794
|
+
className: cn(
|
|
795
|
+
"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",
|
|
796
|
+
disabled && "pointer-events-none"
|
|
797
|
+
),
|
|
798
|
+
style: { left: `${maxPercentage}%` }
|
|
799
|
+
}
|
|
800
|
+
)
|
|
801
|
+
]
|
|
806
802
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
value: minVal,
|
|
817
|
-
disabled,
|
|
818
|
-
onChange: handleMinChange,
|
|
819
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-20"
|
|
820
|
-
}
|
|
821
|
-
),
|
|
822
|
-
/* @__PURE__ */ jsx(
|
|
823
|
-
"input",
|
|
824
|
-
{
|
|
825
|
-
type: "range",
|
|
826
|
-
min,
|
|
827
|
-
max,
|
|
828
|
-
step,
|
|
829
|
-
value: maxVal,
|
|
830
|
-
disabled,
|
|
831
|
-
onChange: handleMaxChange,
|
|
832
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-30"
|
|
833
|
-
}
|
|
834
|
-
),
|
|
835
|
-
/* @__PURE__ */ jsx(
|
|
836
|
-
"div",
|
|
837
|
-
{
|
|
838
|
-
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",
|
|
839
|
-
style: { left: `${minPercentage}%` }
|
|
840
|
-
}
|
|
841
|
-
),
|
|
842
|
-
/* @__PURE__ */ jsx(
|
|
843
|
-
"div",
|
|
844
|
-
{
|
|
845
|
-
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",
|
|
846
|
-
style: { left: `${maxPercentage}%` }
|
|
847
|
-
}
|
|
848
|
-
)
|
|
849
|
-
] }),
|
|
850
|
-
showValue && /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
851
|
-
formatValue(minVal),
|
|
852
|
-
" - ",
|
|
853
|
-
formatValue(maxVal)
|
|
854
|
-
] })
|
|
855
|
-
] });
|
|
803
|
+
),
|
|
804
|
+
showValue && /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
805
|
+
formatValue(minVal),
|
|
806
|
+
" - ",
|
|
807
|
+
formatValue(maxVal)
|
|
808
|
+
] })
|
|
809
|
+
]
|
|
810
|
+
}
|
|
811
|
+
);
|
|
856
812
|
}
|
|
857
813
|
);
|
|
858
814
|
RangeSlider.displayName = "RangeSlider";
|
|
@@ -870,16 +826,13 @@ var PinInput = forwardRef(
|
|
|
870
826
|
autoFocus = false,
|
|
871
827
|
...props
|
|
872
828
|
}, ref) => {
|
|
873
|
-
const
|
|
829
|
+
const isControlled = controlledValue !== void 0;
|
|
830
|
+
const [internalPins, setInternalPins] = useState(() => {
|
|
874
831
|
const initial = (controlledValue !== void 0 ? controlledValue : defaultValue).slice(0, length);
|
|
875
832
|
return Array.from({ length }, (_, i) => initial[i] || "");
|
|
876
833
|
});
|
|
834
|
+
const pins = isControlled ? Array.from({ length }, (_, i) => controlledValue[i] || "") : internalPins;
|
|
877
835
|
const inputsRef = useRef([]);
|
|
878
|
-
useEffect(() => {
|
|
879
|
-
if (controlledValue !== void 0) {
|
|
880
|
-
setPins(Array.from({ length }, (_, i) => controlledValue[i] || ""));
|
|
881
|
-
}
|
|
882
|
-
}, [controlledValue, length]);
|
|
883
836
|
useEffect(() => {
|
|
884
837
|
if (autoFocus && inputsRef.current[0]) {
|
|
885
838
|
inputsRef.current[0].focus();
|
|
@@ -889,7 +842,9 @@ var PinInput = forwardRef(
|
|
|
889
842
|
const sanitized = val.replace(/[^0-9a-zA-Z]/g, "").slice(-1);
|
|
890
843
|
const newPins = [...pins];
|
|
891
844
|
newPins[index] = sanitized;
|
|
892
|
-
|
|
845
|
+
if (!isControlled) {
|
|
846
|
+
setInternalPins(newPins);
|
|
847
|
+
}
|
|
893
848
|
const combined = newPins.join("");
|
|
894
849
|
onChange?.(combined);
|
|
895
850
|
if (sanitized && index < length - 1) {
|
|
@@ -900,16 +855,28 @@ var PinInput = forwardRef(
|
|
|
900
855
|
}
|
|
901
856
|
};
|
|
902
857
|
const handleKeyDown = (index, e) => {
|
|
903
|
-
if (e.key === "Backspace"
|
|
858
|
+
if (e.key === "Backspace") {
|
|
859
|
+
if (!pins[index] && index > 0) {
|
|
860
|
+
const newPins = [...pins];
|
|
861
|
+
newPins[index - 1] = "";
|
|
862
|
+
if (!isControlled) setInternalPins(newPins);
|
|
863
|
+
onChange?.(newPins.join(""));
|
|
864
|
+
inputsRef.current[index - 1]?.focus();
|
|
865
|
+
}
|
|
866
|
+
} else if (e.key === "ArrowLeft" && index > 0) {
|
|
867
|
+
e.preventDefault();
|
|
904
868
|
inputsRef.current[index - 1]?.focus();
|
|
869
|
+
} else if (e.key === "ArrowRight" && index < length - 1) {
|
|
870
|
+
e.preventDefault();
|
|
871
|
+
inputsRef.current[index + 1]?.focus();
|
|
905
872
|
}
|
|
906
873
|
};
|
|
907
874
|
const handlePaste = (e) => {
|
|
908
875
|
e.preventDefault();
|
|
909
|
-
const pasted = e.clipboardData.getData("text").slice(0, length);
|
|
876
|
+
const pasted = e.clipboardData.getData("text").replace(/[^0-9a-zA-Z]/g, "").slice(0, length);
|
|
910
877
|
if (!pasted) return;
|
|
911
878
|
const newPins = Array.from({ length }, (_, i) => pasted[i] || "");
|
|
912
|
-
|
|
879
|
+
if (!isControlled) setInternalPins(newPins);
|
|
913
880
|
const combined = newPins.join("");
|
|
914
881
|
onChange?.(combined);
|
|
915
882
|
const nextEmptyIndex = newPins.findIndex((p) => !p);
|
|
@@ -929,6 +896,7 @@ var PinInput = forwardRef(
|
|
|
929
896
|
inputMode: "numeric",
|
|
930
897
|
maxLength: 1,
|
|
931
898
|
disabled,
|
|
899
|
+
"aria-label": `Digit ${index + 1} of ${length}`,
|
|
932
900
|
value: pins[index],
|
|
933
901
|
onChange: (e) => handleDigitChange(index, e.target.value),
|
|
934
902
|
onKeyDown: (e) => handleKeyDown(index, e),
|
|
@@ -957,7 +925,7 @@ function formatDisplayDate(d) {
|
|
|
957
925
|
var DatePicker = forwardRef(
|
|
958
926
|
({
|
|
959
927
|
value: controlledValue,
|
|
960
|
-
defaultValue,
|
|
928
|
+
defaultValue = null,
|
|
961
929
|
onChange,
|
|
962
930
|
placeholder = "Select date...",
|
|
963
931
|
minDate,
|
|
@@ -967,26 +935,28 @@ var DatePicker = forwardRef(
|
|
|
967
935
|
className,
|
|
968
936
|
presets
|
|
969
937
|
}, ref) => {
|
|
970
|
-
const [
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
938
|
+
const [selectedDate, setSelectedDate] = useControllableState({
|
|
939
|
+
value: controlledValue,
|
|
940
|
+
defaultValue,
|
|
941
|
+
onChange,
|
|
942
|
+
name: "DatePicker"
|
|
943
|
+
});
|
|
974
944
|
const [viewDate, setViewDate] = useState(selectedDate || /* @__PURE__ */ new Date());
|
|
975
945
|
const [isOpen, setIsOpen] = useState(false);
|
|
946
|
+
const [focusedDate, setFocusedDate] = useState(selectedDate || /* @__PURE__ */ new Date());
|
|
976
947
|
useEffect(() => {
|
|
977
948
|
if (selectedDate) {
|
|
978
949
|
setViewDate(selectedDate);
|
|
950
|
+
setFocusedDate(selectedDate);
|
|
979
951
|
}
|
|
980
952
|
}, [selectedDate]);
|
|
981
953
|
const handleSelectDate = (d) => {
|
|
982
|
-
|
|
983
|
-
onChange?.(d);
|
|
954
|
+
setSelectedDate(d);
|
|
984
955
|
setIsOpen(false);
|
|
985
956
|
};
|
|
986
957
|
const handleClear = (e) => {
|
|
987
958
|
e.stopPropagation();
|
|
988
|
-
|
|
989
|
-
onChange?.(null);
|
|
959
|
+
setSelectedDate(null);
|
|
990
960
|
};
|
|
991
961
|
const prevMonth = () => {
|
|
992
962
|
setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1));
|
|
@@ -1019,20 +989,68 @@ var DatePicker = forwardRef(
|
|
|
1019
989
|
}
|
|
1020
990
|
const todayStr = formatDate(/* @__PURE__ */ new Date());
|
|
1021
991
|
const selectedStr = selectedDate ? formatDate(selectedDate) : "";
|
|
992
|
+
const focusedStr = formatDate(focusedDate);
|
|
993
|
+
const handleGridKeyDown = (e) => {
|
|
994
|
+
let nextDate = new Date(focusedDate);
|
|
995
|
+
let handled = false;
|
|
996
|
+
if (e.key === "ArrowLeft") {
|
|
997
|
+
nextDate.setDate(nextDate.getDate() - 1);
|
|
998
|
+
handled = true;
|
|
999
|
+
} else if (e.key === "ArrowRight") {
|
|
1000
|
+
nextDate.setDate(nextDate.getDate() + 1);
|
|
1001
|
+
handled = true;
|
|
1002
|
+
} else if (e.key === "ArrowUp") {
|
|
1003
|
+
nextDate.setDate(nextDate.getDate() - 7);
|
|
1004
|
+
handled = true;
|
|
1005
|
+
} else if (e.key === "ArrowDown") {
|
|
1006
|
+
nextDate.setDate(nextDate.getDate() + 7);
|
|
1007
|
+
handled = true;
|
|
1008
|
+
} else if (e.key === "PageUp") {
|
|
1009
|
+
nextDate.setMonth(nextDate.getMonth() - 1);
|
|
1010
|
+
handled = true;
|
|
1011
|
+
} else if (e.key === "PageDown") {
|
|
1012
|
+
nextDate.setMonth(nextDate.getMonth() + 1);
|
|
1013
|
+
handled = true;
|
|
1014
|
+
} else if (e.key === "Home") {
|
|
1015
|
+
nextDate.setDate(1);
|
|
1016
|
+
handled = true;
|
|
1017
|
+
} else if (e.key === "End") {
|
|
1018
|
+
const lastDay = new Date(nextDate.getFullYear(), nextDate.getMonth() + 1, 0).getDate();
|
|
1019
|
+
nextDate.setDate(lastDay);
|
|
1020
|
+
handled = true;
|
|
1021
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
1022
|
+
e.preventDefault();
|
|
1023
|
+
handleSelectDate(focusedDate);
|
|
1024
|
+
return;
|
|
1025
|
+
} else if (e.key === "Escape") {
|
|
1026
|
+
e.preventDefault();
|
|
1027
|
+
setIsOpen(false);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (handled) {
|
|
1031
|
+
e.preventDefault();
|
|
1032
|
+
setFocusedDate(nextDate);
|
|
1033
|
+
if (nextDate.getMonth() !== viewDate.getMonth() || nextDate.getFullYear() !== viewDate.getFullYear()) {
|
|
1034
|
+
setViewDate(new Date(nextDate.getFullYear(), nextDate.getMonth(), 1));
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1022
1038
|
return /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsx(
|
|
1023
1039
|
Popover,
|
|
1024
1040
|
{
|
|
1025
|
-
isOpen,
|
|
1026
|
-
|
|
1041
|
+
open: isOpen,
|
|
1042
|
+
onOpenChange: setIsOpen,
|
|
1027
1043
|
trigger: /* @__PURE__ */ jsxs(
|
|
1028
1044
|
"button",
|
|
1029
1045
|
{
|
|
1030
1046
|
type: "button",
|
|
1031
1047
|
disabled,
|
|
1032
|
-
|
|
1048
|
+
"aria-haspopup": "dialog",
|
|
1049
|
+
"aria-expanded": isOpen,
|
|
1050
|
+
"aria-label": selectedDate ? `Selected date: ${formatDisplayDate(selectedDate)}` : placeholder,
|
|
1033
1051
|
className: cn(
|
|
1034
|
-
"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",
|
|
1035
|
-
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]
|
|
1052
|
+
"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)]",
|
|
1053
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
1036
1054
|
),
|
|
1037
1055
|
children: [
|
|
1038
1056
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
@@ -1042,7 +1060,11 @@ var DatePicker = forwardRef(
|
|
|
1042
1060
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: clearable && selectedDate && !disabled && /* @__PURE__ */ jsx(
|
|
1043
1061
|
"span",
|
|
1044
1062
|
{
|
|
1063
|
+
role: "button",
|
|
1064
|
+
tabIndex: 0,
|
|
1065
|
+
"aria-label": "Clear date",
|
|
1045
1066
|
onClick: handleClear,
|
|
1067
|
+
onKeyDown: (e) => e.key === "Enter" && handleClear(e),
|
|
1046
1068
|
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
1047
1069
|
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
|
|
1048
1070
|
}
|
|
@@ -1050,74 +1072,131 @@ var DatePicker = forwardRef(
|
|
|
1050
1072
|
]
|
|
1051
1073
|
}
|
|
1052
1074
|
),
|
|
1053
|
-
children: /* @__PURE__ */ jsxs(
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1075
|
+
children: /* @__PURE__ */ jsxs(
|
|
1076
|
+
"div",
|
|
1077
|
+
{
|
|
1078
|
+
role: "dialog",
|
|
1079
|
+
"aria-label": "Choose date",
|
|
1080
|
+
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",
|
|
1081
|
+
onKeyDown: handleGridKeyDown,
|
|
1082
|
+
children: [
|
|
1083
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1084
|
+
/* @__PURE__ */ jsx(
|
|
1085
|
+
"span",
|
|
1086
|
+
{
|
|
1087
|
+
id: "calendar-month-heading",
|
|
1088
|
+
className: "text-xs font-bold text-[var(--ui-text-primary)] font-mono uppercase tracking-wider",
|
|
1089
|
+
"aria-live": "polite",
|
|
1090
|
+
children: viewDate.toLocaleString("default", { month: "long", year: "numeric" })
|
|
1091
|
+
}
|
|
1092
|
+
),
|
|
1093
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1094
|
+
/* @__PURE__ */ jsx(
|
|
1095
|
+
"button",
|
|
1096
|
+
{
|
|
1097
|
+
type: "button",
|
|
1098
|
+
onClick: prevMonth,
|
|
1099
|
+
"aria-label": "Previous month",
|
|
1100
|
+
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",
|
|
1101
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
|
|
1102
|
+
}
|
|
1103
|
+
),
|
|
1104
|
+
/* @__PURE__ */ jsx(
|
|
1105
|
+
"button",
|
|
1106
|
+
{
|
|
1107
|
+
type: "button",
|
|
1108
|
+
onClick: nextMonth,
|
|
1109
|
+
"aria-label": "Next month",
|
|
1110
|
+
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",
|
|
1111
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
|
|
1112
|
+
}
|
|
1113
|
+
)
|
|
1114
|
+
] })
|
|
1115
|
+
] }),
|
|
1116
|
+
presets && presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 pb-2 border-b border-[var(--ui-border-subtle)]", children: presets.map((preset, idx) => /* @__PURE__ */ jsx(
|
|
1058
1117
|
"button",
|
|
1059
1118
|
{
|
|
1060
1119
|
type: "button",
|
|
1061
|
-
onClick:
|
|
1062
|
-
className: "
|
|
1063
|
-
children:
|
|
1120
|
+
onClick: () => handleSelectDate(preset.date),
|
|
1121
|
+
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",
|
|
1122
|
+
children: preset.label
|
|
1123
|
+
},
|
|
1124
|
+
idx
|
|
1125
|
+
)) }),
|
|
1126
|
+
/* @__PURE__ */ jsx(
|
|
1127
|
+
"div",
|
|
1128
|
+
{
|
|
1129
|
+
role: "row",
|
|
1130
|
+
className: "grid grid-cols-7 text-center text-[10px] font-bold text-[var(--ui-text-muted)] font-mono",
|
|
1131
|
+
children: [
|
|
1132
|
+
{ short: "Su", full: "Sunday" },
|
|
1133
|
+
{ short: "Mo", full: "Monday" },
|
|
1134
|
+
{ short: "Tu", full: "Tuesday" },
|
|
1135
|
+
{ short: "We", full: "Wednesday" },
|
|
1136
|
+
{ short: "Th", full: "Thursday" },
|
|
1137
|
+
{ short: "Fr", full: "Friday" },
|
|
1138
|
+
{ short: "Sa", full: "Saturday" }
|
|
1139
|
+
].map((d) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": d.full, className: "py-1", children: d.short }, d.short))
|
|
1064
1140
|
}
|
|
1065
1141
|
),
|
|
1066
1142
|
/* @__PURE__ */ jsx(
|
|
1067
|
-
"
|
|
1143
|
+
"div",
|
|
1068
1144
|
{
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
className: "
|
|
1072
|
-
children:
|
|
1145
|
+
role: "grid",
|
|
1146
|
+
"aria-labelledby": "calendar-month-heading",
|
|
1147
|
+
className: "grid grid-cols-7 gap-1",
|
|
1148
|
+
children: days.map((item, idx) => {
|
|
1149
|
+
const itemStr = formatDate(item.date);
|
|
1150
|
+
const isSelected = itemStr === selectedStr;
|
|
1151
|
+
const isToday = itemStr === todayStr;
|
|
1152
|
+
const isFocused = itemStr === focusedStr;
|
|
1153
|
+
const fullDateLabel = item.date.toLocaleDateString("en-US", {
|
|
1154
|
+
weekday: "long",
|
|
1155
|
+
year: "numeric",
|
|
1156
|
+
month: "long",
|
|
1157
|
+
day: "numeric"
|
|
1158
|
+
});
|
|
1159
|
+
return /* @__PURE__ */ jsx(
|
|
1160
|
+
"button",
|
|
1161
|
+
{
|
|
1162
|
+
type: "button",
|
|
1163
|
+
role: "gridcell",
|
|
1164
|
+
disabled: item.disabled,
|
|
1165
|
+
"aria-selected": isSelected,
|
|
1166
|
+
"aria-current": isToday ? "date" : void 0,
|
|
1167
|
+
"aria-label": fullDateLabel,
|
|
1168
|
+
tabIndex: isFocused ? 0 : -1,
|
|
1169
|
+
onClick: () => handleSelectDate(item.date),
|
|
1170
|
+
className: cn(
|
|
1171
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer outline-none",
|
|
1172
|
+
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
1173
|
+
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
1174
|
+
isFocused && "ring-2 ring-[var(--ui-primary)]",
|
|
1175
|
+
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)]"
|
|
1176
|
+
),
|
|
1177
|
+
children: item.date.getDate()
|
|
1178
|
+
},
|
|
1179
|
+
idx
|
|
1180
|
+
);
|
|
1181
|
+
})
|
|
1073
1182
|
}
|
|
1074
1183
|
)
|
|
1075
|
-
]
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
"button",
|
|
1079
|
-
{
|
|
1080
|
-
type: "button",
|
|
1081
|
-
onClick: () => handleSelectDate(preset.date),
|
|
1082
|
-
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",
|
|
1083
|
-
children: preset.label
|
|
1084
|
-
},
|
|
1085
|
-
idx
|
|
1086
|
-
)) }),
|
|
1087
|
-
/* @__PURE__ */ 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__ */ jsx("div", { className: "py-1", children: d }, d)) }),
|
|
1088
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((item, idx) => {
|
|
1089
|
-
const itemStr = formatDate(item.date);
|
|
1090
|
-
const isSelected = itemStr === selectedStr;
|
|
1091
|
-
const isToday = itemStr === todayStr;
|
|
1092
|
-
return /* @__PURE__ */ jsx(
|
|
1093
|
-
"button",
|
|
1094
|
-
{
|
|
1095
|
-
type: "button",
|
|
1096
|
-
disabled: item.disabled,
|
|
1097
|
-
onClick: () => handleSelectDate(item.date),
|
|
1098
|
-
className: cn(
|
|
1099
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1100
|
-
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
1101
|
-
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
1102
|
-
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)]"
|
|
1103
|
-
),
|
|
1104
|
-
children: item.date.getDate()
|
|
1105
|
-
},
|
|
1106
|
-
idx
|
|
1107
|
-
);
|
|
1108
|
-
}) })
|
|
1109
|
-
] })
|
|
1184
|
+
]
|
|
1185
|
+
}
|
|
1186
|
+
)
|
|
1110
1187
|
}
|
|
1111
1188
|
) });
|
|
1112
1189
|
}
|
|
1113
1190
|
);
|
|
1114
1191
|
DatePicker.displayName = "DatePicker";
|
|
1115
1192
|
var DateRangePicker = forwardRef(
|
|
1116
|
-
({ value: controlledVal, defaultValue, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
1117
|
-
const [
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1193
|
+
({ value: controlledVal, defaultValue = { from: null, to: null }, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
1194
|
+
const [range, setRange] = useControllableState({
|
|
1195
|
+
value: controlledVal,
|
|
1196
|
+
defaultValue,
|
|
1197
|
+
onChange,
|
|
1198
|
+
name: "DateRangePicker"
|
|
1199
|
+
});
|
|
1121
1200
|
const [viewDate, setViewDate] = useState(range.from || /* @__PURE__ */ new Date());
|
|
1122
1201
|
const [isOpen, setIsOpen] = useState(false);
|
|
1123
1202
|
const handleDayClick = (d) => {
|
|
@@ -1132,8 +1211,7 @@ var DateRangePicker = forwardRef(
|
|
|
1132
1211
|
setIsOpen(false);
|
|
1133
1212
|
}
|
|
1134
1213
|
}
|
|
1135
|
-
|
|
1136
|
-
onChange?.(next);
|
|
1214
|
+
setRange(next);
|
|
1137
1215
|
};
|
|
1138
1216
|
const displayText = range.from && range.to ? `${formatDisplayDate(range.from)} \u2013 ${formatDisplayDate(range.to)}` : range.from ? `${formatDisplayDate(range.from)} \u2013 ...` : placeholder;
|
|
1139
1217
|
const year = viewDate.getFullYear();
|
|
@@ -1147,146 +1225,345 @@ var DateRangePicker = forwardRef(
|
|
|
1147
1225
|
return /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsx(
|
|
1148
1226
|
Popover,
|
|
1149
1227
|
{
|
|
1150
|
-
isOpen,
|
|
1151
|
-
|
|
1228
|
+
open: isOpen,
|
|
1229
|
+
onOpenChange: setIsOpen,
|
|
1152
1230
|
trigger: /* @__PURE__ */ jsx(
|
|
1153
1231
|
"button",
|
|
1154
1232
|
{
|
|
1155
1233
|
type: "button",
|
|
1156
1234
|
disabled,
|
|
1157
|
-
|
|
1158
|
-
|
|
1235
|
+
"aria-haspopup": "dialog",
|
|
1236
|
+
"aria-expanded": isOpen,
|
|
1237
|
+
"aria-label": displayText,
|
|
1238
|
+
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)]",
|
|
1159
1239
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
1160
1240
|
/* @__PURE__ */ jsx(Calendar, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
1161
1241
|
/* @__PURE__ */ jsx("span", { className: range.from ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: displayText })
|
|
1162
1242
|
] })
|
|
1163
1243
|
}
|
|
1164
1244
|
),
|
|
1165
|
-
children: /* @__PURE__ */ jsxs(
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1245
|
+
children: /* @__PURE__ */ jsxs(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
role: "dialog",
|
|
1249
|
+
"aria-label": "Choose date range",
|
|
1250
|
+
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",
|
|
1251
|
+
children: [
|
|
1252
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1253
|
+
/* @__PURE__ */ 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" }) }),
|
|
1254
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx(
|
|
1256
|
+
"button",
|
|
1257
|
+
{
|
|
1258
|
+
type: "button",
|
|
1259
|
+
onClick: () => setViewDate(new Date(year, month - 1, 1)),
|
|
1260
|
+
"aria-label": "Previous month",
|
|
1261
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
1262
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
|
|
1263
|
+
}
|
|
1264
|
+
),
|
|
1265
|
+
/* @__PURE__ */ jsx(
|
|
1266
|
+
"button",
|
|
1267
|
+
{
|
|
1268
|
+
type: "button",
|
|
1269
|
+
onClick: () => setViewDate(new Date(year, month + 1, 1)),
|
|
1270
|
+
"aria-label": "Next month",
|
|
1271
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
1272
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
|
|
1273
|
+
}
|
|
1274
|
+
)
|
|
1275
|
+
] })
|
|
1276
|
+
] }),
|
|
1277
|
+
/* @__PURE__ */ jsxs(
|
|
1278
|
+
"div",
|
|
1180
1279
|
{
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
className: "
|
|
1184
|
-
children:
|
|
1280
|
+
role: "grid",
|
|
1281
|
+
"aria-labelledby": "range-month-heading",
|
|
1282
|
+
className: "grid grid-cols-7 gap-1 text-center",
|
|
1283
|
+
children: [
|
|
1284
|
+
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsx("div", {}, `empty-${i}`)),
|
|
1285
|
+
days.map((d, idx) => {
|
|
1286
|
+
const dStr = formatDate(d);
|
|
1287
|
+
const fromStr = range.from ? formatDate(range.from) : "";
|
|
1288
|
+
const toStr = range.to ? formatDate(range.to) : "";
|
|
1289
|
+
const isFrom = dStr === fromStr;
|
|
1290
|
+
const isTo = dStr === toStr;
|
|
1291
|
+
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
1292
|
+
return /* @__PURE__ */ jsx(
|
|
1293
|
+
"button",
|
|
1294
|
+
{
|
|
1295
|
+
type: "button",
|
|
1296
|
+
role: "gridcell",
|
|
1297
|
+
"aria-selected": Boolean(isFrom || isTo || isInRange),
|
|
1298
|
+
"aria-label": d.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" }),
|
|
1299
|
+
onClick: () => handleDayClick(d),
|
|
1300
|
+
className: cn(
|
|
1301
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1302
|
+
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
1303
|
+
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
1304
|
+
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1305
|
+
),
|
|
1306
|
+
children: d.getDate()
|
|
1307
|
+
},
|
|
1308
|
+
idx
|
|
1309
|
+
);
|
|
1310
|
+
})
|
|
1311
|
+
]
|
|
1185
1312
|
}
|
|
1186
1313
|
)
|
|
1187
|
-
]
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsx("div", {}, `empty-${i}`)),
|
|
1191
|
-
days.map((d, idx) => {
|
|
1192
|
-
const dStr = formatDate(d);
|
|
1193
|
-
const fromStr = range.from ? formatDate(range.from) : "";
|
|
1194
|
-
const toStr = range.to ? formatDate(range.to) : "";
|
|
1195
|
-
const isFrom = dStr === fromStr;
|
|
1196
|
-
const isTo = dStr === toStr;
|
|
1197
|
-
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
1198
|
-
return /* @__PURE__ */ jsx(
|
|
1199
|
-
"button",
|
|
1200
|
-
{
|
|
1201
|
-
type: "button",
|
|
1202
|
-
onClick: () => handleDayClick(d),
|
|
1203
|
-
className: cn(
|
|
1204
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1205
|
-
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
1206
|
-
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
1207
|
-
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1208
|
-
),
|
|
1209
|
-
children: d.getDate()
|
|
1210
|
-
},
|
|
1211
|
-
idx
|
|
1212
|
-
);
|
|
1213
|
-
})
|
|
1214
|
-
] })
|
|
1215
|
-
] })
|
|
1314
|
+
]
|
|
1315
|
+
}
|
|
1316
|
+
)
|
|
1216
1317
|
}
|
|
1217
1318
|
) });
|
|
1218
1319
|
}
|
|
1219
1320
|
);
|
|
1220
1321
|
DateRangePicker.displayName = "DateRangePicker";
|
|
1221
1322
|
var TimePicker = forwardRef(
|
|
1222
|
-
({
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1323
|
+
({
|
|
1324
|
+
value: controlledVal,
|
|
1325
|
+
defaultValue,
|
|
1326
|
+
onChange,
|
|
1327
|
+
format = "24h",
|
|
1328
|
+
className,
|
|
1329
|
+
disabled = false,
|
|
1330
|
+
placeholder = "Select time..."
|
|
1331
|
+
}, ref) => {
|
|
1332
|
+
const is12h = format === "12h";
|
|
1333
|
+
const fallbackDefault = defaultValue ?? (is12h ? "12:00 PM" : "12:00");
|
|
1334
|
+
const [time, setTime] = useControllableState({
|
|
1335
|
+
value: controlledVal,
|
|
1336
|
+
defaultValue: fallbackDefault,
|
|
1337
|
+
onChange,
|
|
1338
|
+
name: "TimePicker"
|
|
1339
|
+
});
|
|
1227
1340
|
const [isOpen, setIsOpen] = useState(false);
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1341
|
+
const parseTime = (str, is12) => {
|
|
1342
|
+
if (!str) return { hours: is12 ? "12" : "12", minutes: "00", period: "AM" };
|
|
1343
|
+
const trimmed = str.trim();
|
|
1344
|
+
let period2 = "AM";
|
|
1345
|
+
let cleanTime = trimmed;
|
|
1346
|
+
if (trimmed.toLowerCase().includes("pm")) {
|
|
1347
|
+
period2 = "PM";
|
|
1348
|
+
cleanTime = trimmed.replace(/pm/i, "").trim();
|
|
1349
|
+
} else if (trimmed.toLowerCase().includes("am")) {
|
|
1350
|
+
period2 = "AM";
|
|
1351
|
+
cleanTime = trimmed.replace(/am/i, "").trim();
|
|
1352
|
+
}
|
|
1353
|
+
const parts = cleanTime.split(":");
|
|
1354
|
+
let h = parts[0] ? parts[0].padStart(2, "0") : is12 ? "12" : "00";
|
|
1355
|
+
const m = parts[1] ? parts[1].padStart(2, "0") : "00";
|
|
1356
|
+
return { hours: h, minutes: m, period: period2 };
|
|
1357
|
+
};
|
|
1358
|
+
const { hours, minutes, period } = parseTime(time, is12h);
|
|
1359
|
+
const updateTime = (newH, newM, newPeriod) => {
|
|
1360
|
+
let formatted;
|
|
1361
|
+
if (is12h) {
|
|
1362
|
+
const p = newPeriod ?? period;
|
|
1363
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")} ${p}`;
|
|
1364
|
+
} else {
|
|
1365
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")}`;
|
|
1366
|
+
}
|
|
1367
|
+
setTime(formatted);
|
|
1368
|
+
};
|
|
1369
|
+
const hourOptions = is12h ? Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0")) : Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
1370
|
+
const minuteOptions = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"];
|
|
1371
|
+
const hourListRef = useRef(null);
|
|
1372
|
+
const minuteListRef = useRef(null);
|
|
1373
|
+
const periodListRef = useRef(null);
|
|
1374
|
+
useEffect(() => {
|
|
1375
|
+
if (isOpen) {
|
|
1376
|
+
const timer = setTimeout(() => {
|
|
1377
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
1378
|
+
activeHourEl?.focus();
|
|
1379
|
+
}, 10);
|
|
1380
|
+
return () => clearTimeout(timer);
|
|
1381
|
+
}
|
|
1382
|
+
}, [isOpen]);
|
|
1383
|
+
const handleHourKeyDown = (e) => {
|
|
1384
|
+
const idx = hourOptions.indexOf(hours);
|
|
1385
|
+
if (e.key === "ArrowDown") {
|
|
1386
|
+
e.preventDefault();
|
|
1387
|
+
const next = hourOptions[(idx + 1) % hourOptions.length];
|
|
1388
|
+
updateTime(next, minutes, period);
|
|
1389
|
+
} else if (e.key === "ArrowUp") {
|
|
1390
|
+
e.preventDefault();
|
|
1391
|
+
const prev = hourOptions[(idx - 1 + hourOptions.length) % hourOptions.length];
|
|
1392
|
+
updateTime(prev, minutes, period);
|
|
1393
|
+
} else if (e.key === "ArrowRight") {
|
|
1394
|
+
e.preventDefault();
|
|
1395
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]') || minuteListRef.current?.querySelector('[role="option"]');
|
|
1396
|
+
activeMinEl?.focus();
|
|
1397
|
+
} else if (e.key === "Escape") {
|
|
1398
|
+
e.preventDefault();
|
|
1399
|
+
setIsOpen(false);
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
const handleMinuteKeyDown = (e) => {
|
|
1403
|
+
const idx = minuteOptions.indexOf(minutes);
|
|
1404
|
+
if (e.key === "ArrowDown") {
|
|
1405
|
+
e.preventDefault();
|
|
1406
|
+
const next = minuteOptions[(idx + 1) % minuteOptions.length];
|
|
1407
|
+
updateTime(hours, next, period);
|
|
1408
|
+
} else if (e.key === "ArrowUp") {
|
|
1409
|
+
e.preventDefault();
|
|
1410
|
+
const prev = minuteOptions[(idx - 1 + minuteOptions.length) % minuteOptions.length];
|
|
1411
|
+
updateTime(hours, prev, period);
|
|
1412
|
+
} else if (e.key === "ArrowLeft") {
|
|
1413
|
+
e.preventDefault();
|
|
1414
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
1415
|
+
activeHourEl?.focus();
|
|
1416
|
+
} else if (e.key === "ArrowRight" && is12h) {
|
|
1417
|
+
e.preventDefault();
|
|
1418
|
+
const activePeriodEl = periodListRef.current?.querySelector('[aria-selected="true"]');
|
|
1419
|
+
activePeriodEl?.focus();
|
|
1420
|
+
} else if (e.key === "Escape") {
|
|
1421
|
+
e.preventDefault();
|
|
1422
|
+
setIsOpen(false);
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
const handlePeriodKeyDown = (e) => {
|
|
1426
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1427
|
+
e.preventDefault();
|
|
1428
|
+
const next = period === "AM" ? "PM" : "AM";
|
|
1429
|
+
updateTime(hours, minutes, next);
|
|
1430
|
+
} else if (e.key === "ArrowLeft") {
|
|
1431
|
+
e.preventDefault();
|
|
1432
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]');
|
|
1433
|
+
activeMinEl?.focus();
|
|
1434
|
+
} else if (e.key === "Escape") {
|
|
1435
|
+
e.preventDefault();
|
|
1436
|
+
setIsOpen(false);
|
|
1437
|
+
}
|
|
1234
1438
|
};
|
|
1235
1439
|
return /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsx(
|
|
1236
1440
|
Popover,
|
|
1237
1441
|
{
|
|
1238
|
-
isOpen,
|
|
1239
|
-
|
|
1442
|
+
open: isOpen,
|
|
1443
|
+
onOpenChange: setIsOpen,
|
|
1240
1444
|
trigger: /* @__PURE__ */ jsx(
|
|
1241
1445
|
"button",
|
|
1242
1446
|
{
|
|
1243
1447
|
type: "button",
|
|
1244
1448
|
disabled,
|
|
1245
|
-
|
|
1246
|
-
|
|
1449
|
+
"aria-haspopup": "dialog",
|
|
1450
|
+
"aria-expanded": isOpen,
|
|
1451
|
+
"aria-label": `Selected time: ${time}`,
|
|
1452
|
+
className: cn(
|
|
1453
|
+
"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)]",
|
|
1454
|
+
disabled && "opacity-50 cursor-not-allowed"
|
|
1455
|
+
),
|
|
1247
1456
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
1248
|
-
/* @__PURE__ */ jsx(Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)]" }),
|
|
1249
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time })
|
|
1457
|
+
/* @__PURE__ */ jsx(Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
1458
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time || placeholder })
|
|
1250
1459
|
] })
|
|
1251
1460
|
}
|
|
1252
1461
|
),
|
|
1253
|
-
children: /* @__PURE__ */ jsxs(
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
"
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
"
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1462
|
+
children: /* @__PURE__ */ jsxs(
|
|
1463
|
+
"div",
|
|
1464
|
+
{
|
|
1465
|
+
role: "dialog",
|
|
1466
|
+
"aria-label": "Choose time",
|
|
1467
|
+
className: "p-4 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-3",
|
|
1468
|
+
children: [
|
|
1469
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-bold text-[var(--ui-text-muted)] uppercase tracking-wider font-mono", children: [
|
|
1470
|
+
"Select Time (",
|
|
1471
|
+
format,
|
|
1472
|
+
")"
|
|
1473
|
+
] }),
|
|
1474
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
1475
|
+
/* @__PURE__ */ jsx(
|
|
1476
|
+
"div",
|
|
1477
|
+
{
|
|
1478
|
+
ref: hourListRef,
|
|
1479
|
+
tabIndex: 0,
|
|
1480
|
+
onKeyDown: handleHourKeyDown,
|
|
1481
|
+
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)]",
|
|
1482
|
+
role: "listbox",
|
|
1483
|
+
"aria-label": "Hour",
|
|
1484
|
+
children: hourOptions.map((h) => {
|
|
1485
|
+
const isSelected = hours === h;
|
|
1486
|
+
return /* @__PURE__ */ jsx(
|
|
1487
|
+
"div",
|
|
1488
|
+
{
|
|
1489
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1490
|
+
role: "option",
|
|
1491
|
+
"aria-selected": isSelected,
|
|
1492
|
+
onClick: () => updateTime(h, minutes, period),
|
|
1493
|
+
className: cn(
|
|
1494
|
+
"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)]",
|
|
1495
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1496
|
+
),
|
|
1497
|
+
children: h
|
|
1498
|
+
},
|
|
1499
|
+
h
|
|
1500
|
+
);
|
|
1501
|
+
})
|
|
1502
|
+
}
|
|
1283
1503
|
),
|
|
1284
|
-
children:
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1504
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-bold", children: ":" }),
|
|
1505
|
+
/* @__PURE__ */ jsx(
|
|
1506
|
+
"div",
|
|
1507
|
+
{
|
|
1508
|
+
ref: minuteListRef,
|
|
1509
|
+
tabIndex: 0,
|
|
1510
|
+
onKeyDown: handleMinuteKeyDown,
|
|
1511
|
+
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)]",
|
|
1512
|
+
role: "listbox",
|
|
1513
|
+
"aria-label": "Minute",
|
|
1514
|
+
children: minuteOptions.map((m) => {
|
|
1515
|
+
const isSelected = minutes === m;
|
|
1516
|
+
return /* @__PURE__ */ jsx(
|
|
1517
|
+
"div",
|
|
1518
|
+
{
|
|
1519
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1520
|
+
role: "option",
|
|
1521
|
+
"aria-selected": isSelected,
|
|
1522
|
+
onClick: () => updateTime(hours, m, period),
|
|
1523
|
+
className: cn(
|
|
1524
|
+
"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)]",
|
|
1525
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1526
|
+
),
|
|
1527
|
+
children: m
|
|
1528
|
+
},
|
|
1529
|
+
m
|
|
1530
|
+
);
|
|
1531
|
+
})
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1534
|
+
is12h && /* @__PURE__ */ jsx(
|
|
1535
|
+
"div",
|
|
1536
|
+
{
|
|
1537
|
+
ref: periodListRef,
|
|
1538
|
+
tabIndex: 0,
|
|
1539
|
+
onKeyDown: handlePeriodKeyDown,
|
|
1540
|
+
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)]",
|
|
1541
|
+
role: "listbox",
|
|
1542
|
+
"aria-label": "Period",
|
|
1543
|
+
children: ["AM", "PM"].map((p) => {
|
|
1544
|
+
const isSelected = period === p;
|
|
1545
|
+
return /* @__PURE__ */ jsx(
|
|
1546
|
+
"div",
|
|
1547
|
+
{
|
|
1548
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1549
|
+
role: "option",
|
|
1550
|
+
"aria-selected": isSelected,
|
|
1551
|
+
onClick: () => updateTime(hours, minutes, p),
|
|
1552
|
+
className: cn(
|
|
1553
|
+
"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)]",
|
|
1554
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1555
|
+
),
|
|
1556
|
+
children: p
|
|
1557
|
+
},
|
|
1558
|
+
p
|
|
1559
|
+
);
|
|
1560
|
+
})
|
|
1561
|
+
}
|
|
1562
|
+
)
|
|
1563
|
+
] })
|
|
1564
|
+
]
|
|
1565
|
+
}
|
|
1566
|
+
)
|
|
1290
1567
|
}
|
|
1291
1568
|
) });
|
|
1292
1569
|
}
|
|
@@ -1303,135 +1580,205 @@ var Combobox = forwardRef(
|
|
|
1303
1580
|
emptyText = "No items found.",
|
|
1304
1581
|
disabled = false,
|
|
1305
1582
|
clearable = true,
|
|
1306
|
-
className
|
|
1583
|
+
className,
|
|
1584
|
+
name,
|
|
1585
|
+
id: customId,
|
|
1586
|
+
"aria-label": ariaLabel = "Select option"
|
|
1307
1587
|
}, ref) => {
|
|
1588
|
+
const baseId = useId();
|
|
1589
|
+
const comboId = customId || `combobox-${baseId}`;
|
|
1590
|
+
const listboxId = `listbox-${baseId}`;
|
|
1308
1591
|
const [internalVal, setInternalVal] = useState(defaultValue);
|
|
1309
|
-
const
|
|
1592
|
+
const isControlled = controlledVal !== void 0;
|
|
1593
|
+
const selectedValue = isControlled ? controlledVal : internalVal;
|
|
1310
1594
|
const [search, setSearch] = useState("");
|
|
1311
1595
|
const [isOpen, setIsOpen] = useState(false);
|
|
1312
1596
|
const [highlightIndex, setHighlightIndex] = useState(0);
|
|
1597
|
+
const searchInputRef = useRef(null);
|
|
1598
|
+
const triggerRef = useRef(null);
|
|
1313
1599
|
const filteredOptions = options.filter(
|
|
1314
1600
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase()) || opt.description && opt.description.toLowerCase().includes(search.toLowerCase())
|
|
1315
1601
|
);
|
|
1316
1602
|
const selectedOption = options.find((opt) => opt.value === selectedValue);
|
|
1603
|
+
useEffect(() => {
|
|
1604
|
+
if (filteredOptions.length > 0) {
|
|
1605
|
+
setHighlightIndex((prev) => Math.min(prev, filteredOptions.length - 1));
|
|
1606
|
+
}
|
|
1607
|
+
}, [filteredOptions.length]);
|
|
1317
1608
|
const handleSelect = (val) => {
|
|
1318
|
-
|
|
1609
|
+
if (!isControlled) {
|
|
1610
|
+
setInternalVal(val);
|
|
1611
|
+
}
|
|
1319
1612
|
onChange?.(val);
|
|
1320
1613
|
setIsOpen(false);
|
|
1321
1614
|
setSearch("");
|
|
1615
|
+
triggerRef.current?.focus();
|
|
1322
1616
|
};
|
|
1323
1617
|
const handleClear = (e) => {
|
|
1324
1618
|
e.stopPropagation();
|
|
1325
|
-
|
|
1619
|
+
if (!isControlled) {
|
|
1620
|
+
setInternalVal("");
|
|
1621
|
+
}
|
|
1326
1622
|
onChange?.("");
|
|
1327
1623
|
};
|
|
1328
1624
|
const handleKeyDown = (e) => {
|
|
1329
1625
|
if (!isOpen) {
|
|
1330
|
-
if (e.key === "ArrowDown" || e.key === "Enter") {
|
|
1626
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
1627
|
+
e.preventDefault();
|
|
1331
1628
|
setIsOpen(true);
|
|
1332
1629
|
}
|
|
1333
1630
|
return;
|
|
1334
1631
|
}
|
|
1335
1632
|
if (e.key === "ArrowDown") {
|
|
1336
1633
|
e.preventDefault();
|
|
1337
|
-
|
|
1634
|
+
let next = highlightIndex + 1;
|
|
1635
|
+
while (next < filteredOptions.length && filteredOptions[next]?.disabled) {
|
|
1636
|
+
next++;
|
|
1637
|
+
}
|
|
1638
|
+
if (next < filteredOptions.length) {
|
|
1639
|
+
setHighlightIndex(next);
|
|
1640
|
+
}
|
|
1338
1641
|
} else if (e.key === "ArrowUp") {
|
|
1339
1642
|
e.preventDefault();
|
|
1340
|
-
|
|
1643
|
+
let prev = highlightIndex - 1;
|
|
1644
|
+
while (prev >= 0 && filteredOptions[prev]?.disabled) {
|
|
1645
|
+
prev--;
|
|
1646
|
+
}
|
|
1647
|
+
if (prev >= 0) {
|
|
1648
|
+
setHighlightIndex(prev);
|
|
1649
|
+
}
|
|
1650
|
+
} else if (e.key === "Home") {
|
|
1651
|
+
e.preventDefault();
|
|
1652
|
+
let first = 0;
|
|
1653
|
+
while (first < filteredOptions.length && filteredOptions[first]?.disabled) {
|
|
1654
|
+
first++;
|
|
1655
|
+
}
|
|
1656
|
+
setHighlightIndex(first);
|
|
1657
|
+
} else if (e.key === "End") {
|
|
1658
|
+
e.preventDefault();
|
|
1659
|
+
let last = filteredOptions.length - 1;
|
|
1660
|
+
while (last >= 0 && filteredOptions[last]?.disabled) {
|
|
1661
|
+
last--;
|
|
1662
|
+
}
|
|
1663
|
+
setHighlightIndex(last);
|
|
1341
1664
|
} else if (e.key === "Enter") {
|
|
1342
1665
|
e.preventDefault();
|
|
1343
|
-
|
|
1344
|
-
|
|
1666
|
+
const highlighted = filteredOptions[highlightIndex];
|
|
1667
|
+
if (highlighted && !highlighted.disabled) {
|
|
1668
|
+
handleSelect(highlighted.value);
|
|
1345
1669
|
}
|
|
1346
1670
|
} else if (e.key === "Escape") {
|
|
1671
|
+
e.preventDefault();
|
|
1347
1672
|
setIsOpen(false);
|
|
1673
|
+
triggerRef.current?.focus();
|
|
1348
1674
|
}
|
|
1349
1675
|
};
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
{
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
disabled
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1676
|
+
const activeDescendantId = filteredOptions[highlightIndex] ? `combobox-opt-${baseId}-${filteredOptions[highlightIndex].value}` : void 0;
|
|
1677
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative w-full", className), children: [
|
|
1678
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selectedValue }),
|
|
1679
|
+
/* @__PURE__ */ jsx(
|
|
1680
|
+
Popover,
|
|
1681
|
+
{
|
|
1682
|
+
isOpen,
|
|
1683
|
+
onClose: () => setIsOpen(false),
|
|
1684
|
+
trigger: /* @__PURE__ */ jsxs(
|
|
1685
|
+
"button",
|
|
1686
|
+
{
|
|
1687
|
+
ref: triggerRef,
|
|
1688
|
+
id: comboId,
|
|
1689
|
+
type: "button",
|
|
1690
|
+
disabled,
|
|
1691
|
+
role: "combobox",
|
|
1692
|
+
"aria-expanded": isOpen,
|
|
1693
|
+
"aria-haspopup": "listbox",
|
|
1694
|
+
"aria-controls": isOpen ? listboxId : void 0,
|
|
1695
|
+
"aria-label": ariaLabel,
|
|
1696
|
+
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
1697
|
+
onKeyDown: handleKeyDown,
|
|
1698
|
+
className: cn(
|
|
1699
|
+
"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)]",
|
|
1700
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
1701
|
+
),
|
|
1702
|
+
children: [
|
|
1703
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
1704
|
+
selectedOption?.icon,
|
|
1705
|
+
/* @__PURE__ */ jsx("span", { className: selectedOption ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: selectedOption ? selectedOption.label : placeholder })
|
|
1706
|
+
] }),
|
|
1707
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: [
|
|
1708
|
+
clearable && selectedValue && !disabled && /* @__PURE__ */ jsx(
|
|
1709
|
+
"span",
|
|
1710
|
+
{
|
|
1711
|
+
onClick: handleClear,
|
|
1712
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
1713
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
|
|
1714
|
+
}
|
|
1715
|
+
),
|
|
1716
|
+
/* @__PURE__ */ jsx(ChevronsUpDown, { className: "w-4 h-4 opacity-50 shrink-0" })
|
|
1717
|
+
] })
|
|
1718
|
+
]
|
|
1719
|
+
}
|
|
1720
|
+
),
|
|
1721
|
+
children: /* @__PURE__ */ 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: [
|
|
1722
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
|
|
1723
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 w-3.5 h-3.5 text-[var(--ui-text-muted)] pointer-events-none" }),
|
|
1724
|
+
/* @__PURE__ */ jsx(
|
|
1725
|
+
"input",
|
|
1726
|
+
{
|
|
1727
|
+
ref: searchInputRef,
|
|
1728
|
+
type: "text",
|
|
1729
|
+
autoFocus: true,
|
|
1730
|
+
placeholder: searchPlaceholder,
|
|
1731
|
+
value: search,
|
|
1732
|
+
onChange: (e) => {
|
|
1733
|
+
setSearch(e.target.value);
|
|
1734
|
+
setHighlightIndex(0);
|
|
1735
|
+
},
|
|
1736
|
+
onKeyDown: handleKeyDown,
|
|
1737
|
+
role: "combobox",
|
|
1738
|
+
"aria-autocomplete": "list",
|
|
1739
|
+
"aria-expanded": "true",
|
|
1740
|
+
"aria-controls": listboxId,
|
|
1741
|
+
"aria-activedescendant": activeDescendantId,
|
|
1742
|
+
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)]"
|
|
1743
|
+
}
|
|
1744
|
+
)
|
|
1745
|
+
] }),
|
|
1746
|
+
/* @__PURE__ */ jsx("div", { id: listboxId, className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: emptyText }) : filteredOptions.map((opt, idx) => {
|
|
1747
|
+
const isSelected = opt.value === selectedValue;
|
|
1748
|
+
const isHighlighted = idx === highlightIndex;
|
|
1749
|
+
const optId = `combobox-opt-${baseId}-${opt.value}`;
|
|
1750
|
+
return /* @__PURE__ */ jsxs(
|
|
1751
|
+
"div",
|
|
1752
|
+
{
|
|
1753
|
+
id: optId,
|
|
1754
|
+
role: "option",
|
|
1755
|
+
"aria-selected": isSelected,
|
|
1756
|
+
"aria-disabled": opt.disabled,
|
|
1757
|
+
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
1758
|
+
onMouseEnter: () => !opt.disabled && setHighlightIndex(idx),
|
|
1759
|
+
className: cn(
|
|
1760
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none",
|
|
1761
|
+
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
1762
|
+
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)]"
|
|
1763
|
+
),
|
|
1764
|
+
children: [
|
|
1765
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
1766
|
+
opt.icon,
|
|
1767
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1768
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate", children: opt.label }),
|
|
1769
|
+
opt.description && /* @__PURE__ */ jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
1770
|
+
] })
|
|
1771
|
+
] }),
|
|
1772
|
+
isSelected && /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
1773
|
+
]
|
|
1398
1774
|
},
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
)
|
|
1402
|
-
] })
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
return /* @__PURE__ */ jsxs(
|
|
1407
|
-
"div",
|
|
1408
|
-
{
|
|
1409
|
-
role: "option",
|
|
1410
|
-
"aria-selected": isSelected,
|
|
1411
|
-
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
1412
|
-
onMouseEnter: () => setHighlightIndex(idx),
|
|
1413
|
-
className: cn(
|
|
1414
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors",
|
|
1415
|
-
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
1416
|
-
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)]"
|
|
1417
|
-
),
|
|
1418
|
-
children: [
|
|
1419
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
1420
|
-
opt.icon,
|
|
1421
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1422
|
-
/* @__PURE__ */ jsx("span", { className: "block truncate", children: opt.label }),
|
|
1423
|
-
opt.description && /* @__PURE__ */ jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
1424
|
-
] })
|
|
1425
|
-
] }),
|
|
1426
|
-
isSelected && /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
1427
|
-
]
|
|
1428
|
-
},
|
|
1429
|
-
opt.value
|
|
1430
|
-
);
|
|
1431
|
-
}) })
|
|
1432
|
-
] })
|
|
1433
|
-
}
|
|
1434
|
-
) });
|
|
1775
|
+
opt.value
|
|
1776
|
+
);
|
|
1777
|
+
}) })
|
|
1778
|
+
] })
|
|
1779
|
+
}
|
|
1780
|
+
)
|
|
1781
|
+
] });
|
|
1435
1782
|
}
|
|
1436
1783
|
);
|
|
1437
1784
|
Combobox.displayName = "Combobox";
|
|
@@ -1448,28 +1795,69 @@ var MultiSelect = forwardRef(
|
|
|
1448
1795
|
className
|
|
1449
1796
|
}, ref) => {
|
|
1450
1797
|
const [internalVal, setInternalVal] = useState(defaultValue);
|
|
1451
|
-
const
|
|
1798
|
+
const isControlled = controlledVal !== void 0;
|
|
1799
|
+
const selectedValues = isControlled ? controlledVal : internalVal;
|
|
1452
1800
|
const [search, setSearch] = useState("");
|
|
1453
1801
|
const [isOpen, setIsOpen] = useState(false);
|
|
1802
|
+
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
|
1454
1803
|
const filteredOptions = options.filter(
|
|
1455
1804
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase())
|
|
1456
1805
|
);
|
|
1457
1806
|
const toggleOption = (val) => {
|
|
1807
|
+
const opt = options.find((o) => o.value === val);
|
|
1808
|
+
if (opt?.disabled) return;
|
|
1458
1809
|
const next = selectedValues.includes(val) ? selectedValues.filter((v) => v !== val) : [...selectedValues, val];
|
|
1459
|
-
|
|
1810
|
+
if (!isControlled) {
|
|
1811
|
+
setInternalVal(next);
|
|
1812
|
+
}
|
|
1460
1813
|
onChange?.(next);
|
|
1461
1814
|
};
|
|
1462
1815
|
const removeTag = (val, e) => {
|
|
1463
1816
|
e?.stopPropagation?.();
|
|
1464
1817
|
const next = selectedValues.filter((v) => v !== val);
|
|
1465
|
-
|
|
1818
|
+
if (!isControlled) {
|
|
1819
|
+
setInternalVal(next);
|
|
1820
|
+
}
|
|
1466
1821
|
onChange?.(next);
|
|
1467
1822
|
};
|
|
1468
1823
|
const clearAll = (e) => {
|
|
1469
1824
|
e?.stopPropagation?.();
|
|
1470
|
-
|
|
1825
|
+
if (!isControlled) {
|
|
1826
|
+
setInternalVal([]);
|
|
1827
|
+
}
|
|
1471
1828
|
onChange?.([]);
|
|
1472
1829
|
};
|
|
1830
|
+
const handleSearchKeyDown = (e) => {
|
|
1831
|
+
if (filteredOptions.length === 0) return;
|
|
1832
|
+
if (e.key === "ArrowDown") {
|
|
1833
|
+
e.preventDefault();
|
|
1834
|
+
setHighlightedIndex((prev) => {
|
|
1835
|
+
let next = (prev + 1) % filteredOptions.length;
|
|
1836
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
1837
|
+
next = (next + 1) % filteredOptions.length;
|
|
1838
|
+
}
|
|
1839
|
+
return next;
|
|
1840
|
+
});
|
|
1841
|
+
} else if (e.key === "ArrowUp") {
|
|
1842
|
+
e.preventDefault();
|
|
1843
|
+
setHighlightedIndex((prev) => {
|
|
1844
|
+
let next = (prev - 1 + filteredOptions.length) % filteredOptions.length;
|
|
1845
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
1846
|
+
next = (next - 1 + filteredOptions.length) % filteredOptions.length;
|
|
1847
|
+
}
|
|
1848
|
+
return next;
|
|
1849
|
+
});
|
|
1850
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
1851
|
+
e.preventDefault();
|
|
1852
|
+
const current = filteredOptions[highlightedIndex];
|
|
1853
|
+
if (current && !current.disabled) {
|
|
1854
|
+
toggleOption(current.value);
|
|
1855
|
+
}
|
|
1856
|
+
} else if (e.key === "Escape") {
|
|
1857
|
+
e.preventDefault();
|
|
1858
|
+
setIsOpen(false);
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1473
1861
|
return /* @__PURE__ */ jsx("div", { ref, className: cn("relative w-full", className), children: /* @__PURE__ */ jsx(
|
|
1474
1862
|
Popover,
|
|
1475
1863
|
{
|
|
@@ -1478,9 +1866,19 @@ var MultiSelect = forwardRef(
|
|
|
1478
1866
|
trigger: /* @__PURE__ */ jsxs(
|
|
1479
1867
|
"div",
|
|
1480
1868
|
{
|
|
1869
|
+
tabIndex: disabled ? -1 : 0,
|
|
1870
|
+
role: "combobox",
|
|
1871
|
+
"aria-expanded": isOpen,
|
|
1872
|
+
"aria-haspopup": "listbox",
|
|
1481
1873
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
1874
|
+
onKeyDown: (e) => {
|
|
1875
|
+
if ((e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && !disabled) {
|
|
1876
|
+
e.preventDefault();
|
|
1877
|
+
setIsOpen(true);
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1482
1880
|
className: cn(
|
|
1483
|
-
"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",
|
|
1881
|
+
"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)]",
|
|
1484
1882
|
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
1485
1883
|
),
|
|
1486
1884
|
children: [
|
|
@@ -1509,7 +1907,7 @@ var MultiSelect = forwardRef(
|
|
|
1509
1907
|
"span",
|
|
1510
1908
|
{
|
|
1511
1909
|
onClick: clearAll,
|
|
1512
|
-
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
1910
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
1513
1911
|
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
|
|
1514
1912
|
}
|
|
1515
1913
|
),
|
|
@@ -1528,20 +1926,30 @@ var MultiSelect = forwardRef(
|
|
|
1528
1926
|
autoFocus: true,
|
|
1529
1927
|
placeholder: searchPlaceholder,
|
|
1530
1928
|
value: search,
|
|
1531
|
-
onChange: (e) =>
|
|
1929
|
+
onChange: (e) => {
|
|
1930
|
+
setSearch(e.target.value);
|
|
1931
|
+
setHighlightedIndex(0);
|
|
1932
|
+
},
|
|
1933
|
+
onKeyDown: handleSearchKeyDown,
|
|
1532
1934
|
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)]"
|
|
1533
1935
|
}
|
|
1534
1936
|
)
|
|
1535
1937
|
] }),
|
|
1536
|
-
/* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt) => {
|
|
1938
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt, index) => {
|
|
1537
1939
|
const isChecked = selectedValues.includes(opt.value);
|
|
1940
|
+
const isHighlighted = highlightedIndex === index;
|
|
1538
1941
|
return /* @__PURE__ */ jsxs(
|
|
1539
1942
|
"div",
|
|
1540
1943
|
{
|
|
1541
|
-
|
|
1944
|
+
role: "option",
|
|
1945
|
+
"aria-selected": isChecked,
|
|
1946
|
+
"aria-disabled": opt.disabled,
|
|
1947
|
+
onClick: () => !opt.disabled && toggleOption(opt.value),
|
|
1542
1948
|
className: cn(
|
|
1543
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium
|
|
1544
|
-
|
|
1949
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium transition-colors select-none",
|
|
1950
|
+
opt.disabled ? "opacity-40 cursor-not-allowed text-[var(--ui-text-muted)]" : "cursor-pointer",
|
|
1951
|
+
isChecked && "text-[var(--ui-primary)] font-semibold",
|
|
1952
|
+
isHighlighted && !opt.disabled && "bg-[var(--ui-primary)]/15"
|
|
1545
1953
|
),
|
|
1546
1954
|
children: [
|
|
1547
1955
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
@@ -1583,26 +1991,58 @@ var FileUpload = forwardRef(
|
|
|
1583
1991
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
1584
1992
|
const [fileList, setFileList] = useState([]);
|
|
1585
1993
|
const inputRef = useRef(null);
|
|
1994
|
+
const fileListRef = useRef(fileList);
|
|
1995
|
+
fileListRef.current = fileList;
|
|
1996
|
+
useEffect(() => {
|
|
1997
|
+
return () => {
|
|
1998
|
+
fileListRef.current.forEach((item) => {
|
|
1999
|
+
if (item.previewUrl) {
|
|
2000
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
2001
|
+
}
|
|
2002
|
+
});
|
|
2003
|
+
};
|
|
2004
|
+
}, []);
|
|
2005
|
+
const matchesAccept = (file, acceptStr) => {
|
|
2006
|
+
if (!acceptStr) return true;
|
|
2007
|
+
const patterns = acceptStr.split(",").map((p) => p.trim().toLowerCase());
|
|
2008
|
+
const fileName = file.name.toLowerCase();
|
|
2009
|
+
const fileType = file.type.toLowerCase();
|
|
2010
|
+
return patterns.some((p) => {
|
|
2011
|
+
if (p.startsWith(".")) {
|
|
2012
|
+
return fileName.endsWith(p);
|
|
2013
|
+
}
|
|
2014
|
+
if (p.endsWith("/*")) {
|
|
2015
|
+
const baseType = p.replace("/*", "");
|
|
2016
|
+
return fileType.startsWith(baseType);
|
|
2017
|
+
}
|
|
2018
|
+
return fileType === p;
|
|
2019
|
+
});
|
|
2020
|
+
};
|
|
1586
2021
|
const processFiles = (incoming) => {
|
|
1587
2022
|
if (!incoming) return;
|
|
1588
2023
|
const validFiles = [];
|
|
1589
2024
|
const newUploaded = [];
|
|
1590
2025
|
Array.from(incoming).forEach((file) => {
|
|
1591
2026
|
if (fileList.length + newUploaded.length >= maxFiles) return;
|
|
2027
|
+
const isAccepted = matchesAccept(file, accept);
|
|
1592
2028
|
const isOversized = file.size > maxSizeMB * 1024 * 1024;
|
|
2029
|
+
const hasError = !isAccepted || isOversized;
|
|
2030
|
+
let errorMsg;
|
|
2031
|
+
if (!isAccepted) errorMsg = `File type not supported (${accept})`;
|
|
2032
|
+
else if (isOversized) errorMsg = `File exceeds ${maxSizeMB}MB limit`;
|
|
1593
2033
|
const fileObj = {
|
|
1594
2034
|
id: `${file.name}-${Date.now()}-${Math.random()}`,
|
|
1595
2035
|
file,
|
|
1596
2036
|
name: file.name,
|
|
1597
2037
|
size: file.size,
|
|
1598
2038
|
type: file.type,
|
|
1599
|
-
progress:
|
|
1600
|
-
status:
|
|
1601
|
-
errorMessage:
|
|
1602
|
-
previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
|
|
2039
|
+
progress: hasError ? 0 : 100,
|
|
2040
|
+
status: hasError ? "error" : "completed",
|
|
2041
|
+
errorMessage: errorMsg,
|
|
2042
|
+
previewUrl: file.type.startsWith("image/") && !hasError ? URL.createObjectURL(file) : void 0
|
|
1603
2043
|
};
|
|
1604
2044
|
newUploaded.push(fileObj);
|
|
1605
|
-
if (!
|
|
2045
|
+
if (!hasError) validFiles.push(file);
|
|
1606
2046
|
});
|
|
1607
2047
|
setFileList((prev) => [...prev, ...newUploaded]);
|
|
1608
2048
|
if (validFiles.length > 0) {
|
|
@@ -1628,7 +2068,13 @@ var FileUpload = forwardRef(
|
|
|
1628
2068
|
if (inputRef.current) inputRef.current.value = "";
|
|
1629
2069
|
};
|
|
1630
2070
|
const removeFile = (id) => {
|
|
1631
|
-
setFileList((prev) =>
|
|
2071
|
+
setFileList((prev) => {
|
|
2072
|
+
const item = prev.find((f) => f.id === id);
|
|
2073
|
+
if (item?.previewUrl) {
|
|
2074
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
2075
|
+
}
|
|
2076
|
+
return prev.filter((f) => f.id !== id);
|
|
2077
|
+
});
|
|
1632
2078
|
onFileRemove?.(id);
|
|
1633
2079
|
};
|
|
1634
2080
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full space-y-4", className), children: [
|
|
@@ -1736,8 +2182,12 @@ var NumberInput = forwardRef(
|
|
|
1736
2182
|
className,
|
|
1737
2183
|
...props
|
|
1738
2184
|
}, ref) => {
|
|
1739
|
-
const [
|
|
1740
|
-
|
|
2185
|
+
const [value, setValue] = useControllableState({
|
|
2186
|
+
value: controlledVal,
|
|
2187
|
+
defaultValue,
|
|
2188
|
+
onChange,
|
|
2189
|
+
name: "NumberInput"
|
|
2190
|
+
});
|
|
1741
2191
|
const clamp = (num) => {
|
|
1742
2192
|
let clamped = Math.min(max, Math.max(min, num));
|
|
1743
2193
|
if (precision > 0) {
|
|
@@ -1748,14 +2198,12 @@ var NumberInput = forwardRef(
|
|
|
1748
2198
|
const handleIncrement = () => {
|
|
1749
2199
|
if (disabled) return;
|
|
1750
2200
|
const next = clamp(value + step);
|
|
1751
|
-
|
|
1752
|
-
onChange?.(next);
|
|
2201
|
+
setValue(next);
|
|
1753
2202
|
};
|
|
1754
2203
|
const handleDecrement = () => {
|
|
1755
2204
|
if (disabled) return;
|
|
1756
2205
|
const next = clamp(value - step);
|
|
1757
|
-
|
|
1758
|
-
onChange?.(next);
|
|
2206
|
+
setValue(next);
|
|
1759
2207
|
};
|
|
1760
2208
|
const handleInputChange = (e) => {
|
|
1761
2209
|
const val = e.target.value;
|
|
@@ -1765,8 +2213,7 @@ var NumberInput = forwardRef(
|
|
|
1765
2213
|
const parsed = parseFloat(val);
|
|
1766
2214
|
if (!isNaN(parsed)) {
|
|
1767
2215
|
const next = clamp(parsed);
|
|
1768
|
-
|
|
1769
|
-
onChange?.(next);
|
|
2216
|
+
setValue(next);
|
|
1770
2217
|
}
|
|
1771
2218
|
};
|
|
1772
2219
|
const handleKeyDown = (e) => {
|
|
@@ -1787,7 +2234,7 @@ var NumberInput = forwardRef(
|
|
|
1787
2234
|
type: "button",
|
|
1788
2235
|
disabled: disabled || value <= min,
|
|
1789
2236
|
onClick: handleDecrement,
|
|
1790
|
-
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)]",
|
|
2237
|
+
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",
|
|
1791
2238
|
children: /* @__PURE__ */ jsx(Minus, { className: "w-3.5 h-3.5" })
|
|
1792
2239
|
}
|
|
1793
2240
|
),
|
|
@@ -1810,7 +2257,7 @@ var NumberInput = forwardRef(
|
|
|
1810
2257
|
type: "button",
|
|
1811
2258
|
disabled: disabled || value >= max,
|
|
1812
2259
|
onClick: handleIncrement,
|
|
1813
|
-
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)]",
|
|
2260
|
+
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",
|
|
1814
2261
|
children: /* @__PURE__ */ jsx(Plus, { className: "w-3.5 h-3.5" })
|
|
1815
2262
|
}
|
|
1816
2263
|
)
|
|
@@ -1841,7 +2288,7 @@ var NumberInput = forwardRef(
|
|
|
1841
2288
|
type: "button",
|
|
1842
2289
|
disabled: disabled || value >= max,
|
|
1843
2290
|
onClick: handleIncrement,
|
|
1844
|
-
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",
|
|
2291
|
+
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",
|
|
1845
2292
|
children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3" })
|
|
1846
2293
|
}
|
|
1847
2294
|
),
|
|
@@ -1851,7 +2298,7 @@ var NumberInput = forwardRef(
|
|
|
1851
2298
|
type: "button",
|
|
1852
2299
|
disabled: disabled || value <= min,
|
|
1853
2300
|
onClick: handleDecrement,
|
|
1854
|
-
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",
|
|
2301
|
+
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",
|
|
1855
2302
|
children: /* @__PURE__ */ jsx(Minus, { className: "w-3 h-3" })
|
|
1856
2303
|
}
|
|
1857
2304
|
)
|
|
@@ -1880,19 +2327,27 @@ var CurrencyInput = forwardRef(
|
|
|
1880
2327
|
}, ref) => {
|
|
1881
2328
|
const symbol = currencySymbol || (currency === "VND" ? "\u20AB" : currency === "EUR" ? "\u20AC" : "$");
|
|
1882
2329
|
const isSuffix = position === "suffix" || currency === "VND";
|
|
1883
|
-
const [
|
|
1884
|
-
|
|
1885
|
-
|
|
2330
|
+
const [value, setValue, isControlled] = useControllableState({
|
|
2331
|
+
value: controlledVal,
|
|
2332
|
+
defaultValue,
|
|
2333
|
+
onChange,
|
|
2334
|
+
name: "CurrencyInput"
|
|
2335
|
+
});
|
|
2336
|
+
const [internalInputString, setInternalInputString] = useState(formatCurrencyString(value));
|
|
1886
2337
|
useEffect(() => {
|
|
1887
|
-
|
|
1888
|
-
|
|
2338
|
+
if (!isControlled) {
|
|
2339
|
+
setInternalInputString(formatCurrencyString(value));
|
|
2340
|
+
}
|
|
2341
|
+
}, [value, isControlled]);
|
|
2342
|
+
const displayVal = isControlled ? formatCurrencyString(value) : internalInputString;
|
|
1889
2343
|
const handleInputChange = (e) => {
|
|
1890
2344
|
const raw = e.target.value.replace(/[^0-9.]/g, "");
|
|
1891
2345
|
const parsed = parseFloat(raw);
|
|
1892
2346
|
const num = isNaN(parsed) ? 0 : parsed;
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
2347
|
+
if (!isControlled) {
|
|
2348
|
+
setInternalInputString(raw ? formatCurrencyString(num) : "");
|
|
2349
|
+
}
|
|
2350
|
+
setValue(num);
|
|
1896
2351
|
};
|
|
1897
2352
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative w-full flex items-center", className), children: [
|
|
1898
2353
|
!isSuffix && /* @__PURE__ */ jsx("span", { className: "absolute left-3.5 text-sm font-bold text-[var(--ui-text-muted)] pointer-events-none font-mono", children: symbol }),
|
|
@@ -1986,6 +2441,6 @@ var MaskedInput = forwardRef(
|
|
|
1986
2441
|
);
|
|
1987
2442
|
MaskedInput.displayName = "MaskedInput";
|
|
1988
2443
|
|
|
1989
|
-
export {
|
|
1990
|
-
//# sourceMappingURL=chunk-
|
|
1991
|
-
//# sourceMappingURL=chunk-
|
|
2444
|
+
export { Combobox, CurrencyInput, DatePicker, DateRangePicker, FileUpload, Input, MaskedInput, MultiSelect, NumberInput, PasswordInput, PinInput, Radio, RadioGroup, RangeSlider, SearchInput, Select, Slider, Switch, Textarea, TimePicker, applyMask, formatCurrencyString, formatDate, formatDisplayDate, formatFileSize, forms_exports, inputVariants, radioVariants, selectVariants, switchThumbVariants, switchVariants, textareaVariants };
|
|
2445
|
+
//# sourceMappingURL=chunk-4CRFNALF.js.map
|
|
2446
|
+
//# sourceMappingURL=chunk-4CRFNALF.js.map
|