@thangnqdev/ui 0.1.2 → 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 +54 -206
- package/dist/bento/index.cjs.map +1 -1
- package/dist/bento/index.js +2 -2
- package/dist/bento/styles.css +2 -46
- package/dist/buttons/index.cjs +111 -18
- package/dist/buttons/index.cjs.map +1 -1
- package/dist/buttons/index.d.cts +1 -1
- package/dist/buttons/index.d.ts +1 -1
- package/dist/buttons/index.js +3 -3
- package/dist/buttons/styles.css +2 -6
- package/dist/{chunk-4S6L5V7X.js → chunk-2YJBW4E2.js} +2 -2
- package/dist/chunk-2YJBW4E2.js.map +1 -0
- package/dist/{chunk-F5Q7BTMH.js → chunk-4CRFNALF.js} +1011 -556
- package/dist/chunk-4CRFNALF.js.map +1 -0
- package/dist/{chunk-LWFRUJRU.js → chunk-4MS6ZHFQ.js} +225 -41
- package/dist/chunk-4MS6ZHFQ.js.map +1 -0
- package/dist/{chunk-2OXZK6IP.js → chunk-77KTYOMQ.js} +3 -3
- package/dist/chunk-77KTYOMQ.js.map +1 -0
- package/dist/{chunk-IABPUCAQ.js → chunk-ADO3SVUA.js} +2 -2
- package/dist/chunk-ADO3SVUA.js.map +1 -0
- package/dist/{chunk-GXI2H5XA.js → chunk-D7FB3TCA.js} +3 -3
- package/dist/chunk-D7FB3TCA.js.map +1 -0
- package/dist/chunk-DSEL7ELZ.js +314 -0
- package/dist/chunk-DSEL7ELZ.js.map +1 -0
- package/dist/{chunk-KJRBRTQE.js → chunk-EXFR2BFJ.js} +37 -28
- package/dist/chunk-EXFR2BFJ.js.map +1 -0
- package/dist/{chunk-PF4ZHGVW.js → chunk-GXWB6BSY.js} +116 -23
- package/dist/chunk-GXWB6BSY.js.map +1 -0
- package/dist/{chunk-66VWUJEI.js → chunk-I2FQBMVW.js} +3 -3
- package/dist/chunk-I2FQBMVW.js.map +1 -0
- package/dist/{chunk-EU2LPUYP.js → chunk-I5O2WCGF.js} +3 -3
- package/dist/chunk-I5O2WCGF.js.map +1 -0
- package/dist/{chunk-ATYGDTMK.js → chunk-ME4XILG5.js} +5 -5
- package/dist/chunk-ME4XILG5.js.map +1 -0
- package/dist/{chunk-5FZ6ZFII.js → chunk-PC5SZFZC.js} +373 -150
- package/dist/chunk-PC5SZFZC.js.map +1 -0
- package/dist/{chunk-C4SUXMI3.js → chunk-S6XJMYME.js} +3 -3
- package/dist/chunk-S6XJMYME.js.map +1 -0
- package/dist/{chunk-A7P3O5Z5.js → chunk-V52JOSDF.js} +3 -3
- package/dist/chunk-V52JOSDF.js.map +1 -0
- package/dist/{chunk-NFVONJUK.js → chunk-W5WNWTQC.js} +455 -211
- package/dist/chunk-W5WNWTQC.js.map +1 -0
- package/dist/{chunk-WAFHC4DQ.js → chunk-YJ64RI7J.js} +3 -3
- package/dist/chunk-YJ64RI7J.js.map +1 -0
- package/dist/chunk-YOCGBORV.js +155 -0
- package/dist/chunk-YOCGBORV.js.map +1 -0
- package/dist/chunk-ZEDXARXW.js +96 -0
- package/dist/chunk-ZEDXARXW.js.map +1 -0
- package/dist/components.css +2 -0
- package/dist/core/index.cjs +33 -24
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/styles.css +2 -134
- package/dist/data-display/index.cjs +770 -247
- package/dist/data-display/index.cjs.map +1 -1
- package/dist/data-display/index.d.cts +1 -1
- package/dist/data-display/index.d.ts +1 -1
- package/dist/data-display/index.js +5 -4
- package/dist/data-display/styles.css +2 -4
- package/dist/feedback/index.cjs.map +1 -1
- package/dist/feedback/index.js +2 -2
- package/dist/feedback/styles.css +2 -4
- package/dist/forms/index.cjs +1211 -459
- package/dist/forms/index.cjs.map +1 -1
- package/dist/forms/index.d.cts +1 -1
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.js +8 -8
- package/dist/forms/styles.css +2 -6
- package/dist/glass/index.cjs.map +1 -1
- package/dist/glass/index.js +2 -2
- package/dist/glass/styles.css +2 -81
- package/dist/{index-D6fE7w-b.d.cts → index-BGt_hecO.d.cts} +1 -0
- package/dist/{index-D6fE7w-b.d.ts → index-BGt_hecO.d.ts} +1 -0
- package/dist/{index-DzcWh1Hs.d.cts → index-BtDvpZCf.d.cts} +7 -0
- package/dist/{index-DzcWh1Hs.d.ts → index-BtDvpZCf.d.ts} +7 -0
- package/dist/{index-Cle6QzQl.d.ts → index-COX1sBUN.d.ts} +27 -3
- package/dist/{index-BkRqGNG1.d.ts → index-Cg8MuP7u.d.cts} +40 -13
- package/dist/{index-BkRqGNG1.d.cts → index-Cg8MuP7u.d.ts} +40 -13
- package/dist/{index-CenuF_P2.d.ts → index-DHkPRTvE.d.cts} +39 -8
- package/dist/{index-CenuF_P2.d.cts → index-DHkPRTvE.d.ts} +39 -8
- package/dist/{index-TyK-Ldpy.d.cts → index-Du_oht9k.d.cts} +27 -3
- package/dist/index.cjs +2481 -917
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +19 -19
- package/dist/layout/index.cjs.map +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/styles.css +2 -8
- package/dist/navigation/index.cjs +288 -48
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +1 -1
- package/dist/navigation/index.d.ts +1 -1
- package/dist/navigation/index.js +6 -6
- package/dist/navigation/styles.css +2 -4
- package/dist/overlays/index.cjs +566 -229
- package/dist/overlays/index.cjs.map +1 -1
- package/dist/overlays/index.d.cts +1 -1
- package/dist/overlays/index.d.ts +1 -1
- package/dist/overlays/index.js +7 -7
- package/dist/overlays/styles.css +2 -8
- package/dist/reset.css +45 -0
- package/dist/styles.css +1 -1
- package/dist/tokens.css +11 -0
- package/dist/typography/index.cjs.map +1 -1
- package/dist/typography/index.js +4 -4
- package/dist/typography/styles.css +2 -5
- package/llms.txt +211 -113
- package/package.json +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 -134
- package/src/data-display/Accordion.tsx +147 -147
- package/src/data-display/Avatar.tsx +134 -134
- package/src/data-display/Card.tsx +112 -112
- package/src/data-display/Chip.tsx +74 -74
- package/src/data-display/DataTablePro.tsx +299 -121
- package/src/data-display/StatCard.tsx +60 -60
- package/src/data-display/Table.tsx +74 -74
- package/src/data-display/Timeline.tsx +62 -62
- package/src/data-display/TreeView.tsx +243 -104
- package/src/data-display/tokens.css +4 -4
- package/src/feedback/Alert.tsx +80 -80
- package/src/feedback/EmptyState.tsx +45 -45
- package/src/feedback/Skeleton.tsx +66 -66
- package/src/feedback/Spinner.tsx +65 -65
- package/src/feedback/index.ts +5 -5
- package/src/feedback/tokens.css +4 -4
- package/src/forms/Checkbox.tsx +149 -69
- package/src/forms/CheckboxGroup.tsx +76 -29
- package/src/forms/Combobox.tsx +172 -28
- package/src/forms/DatePicker.tsx +385 -81
- package/src/forms/FileUpload.tsx +51 -7
- package/src/forms/FormField.tsx +115 -115
- package/src/forms/Input.tsx +151 -151
- package/src/forms/NumberInput.tsx +34 -20
- package/src/forms/PasswordInput.tsx +35 -35
- package/src/forms/PinInput.tsx +139 -125
- package/src/forms/Radio.tsx +142 -142
- package/src/forms/RangeSlider.tsx +237 -111
- package/src/forms/SearchInput.tsx +34 -34
- package/src/forms/Select.tsx +103 -103
- package/src/forms/Slider.tsx +85 -85
- package/src/forms/Textarea.tsx +114 -114
- package/src/forms/tokens.css +6 -6
- package/src/glass/GlassButton.tsx +59 -59
- package/src/glass/GlassCard.tsx +71 -71
- package/src/glass/index.ts +3 -3
- package/src/glass/tokens.css +81 -81
- package/src/index.ts +26 -26
- package/src/layout/AspectRatio.tsx +39 -39
- package/src/layout/Container.tsx +53 -53
- package/src/layout/Divider.tsx +61 -61
- package/src/layout/Flex.tsx +79 -79
- package/src/layout/Grid.tsx +124 -124
- package/src/layout/PageLayout.tsx +59 -59
- package/src/layout/Section.tsx +47 -47
- package/src/layout/Spacer.tsx +30 -30
- package/src/layout/Stack.tsx +88 -88
- package/src/layout/tokens.css +8 -8
- package/src/navigation/Breadcrumb.tsx +73 -73
- package/src/navigation/CommandPalette.tsx +142 -142
- package/src/navigation/DropdownMenu.tsx +244 -160
- package/src/navigation/Pagination.tsx +114 -114
- package/src/navigation/Stepper.tsx +93 -93
- package/src/navigation/Tabs.tsx +265 -133
- package/src/navigation/index.ts +6 -6
- package/src/navigation/tokens.css +4 -4
- package/src/overlays/AlertDialog.tsx +63 -54
- package/src/overlays/BottomSheet.tsx +30 -10
- package/src/overlays/ContextMenu.tsx +51 -9
- package/src/overlays/Drawer.tsx +160 -145
- package/src/overlays/FocusTrap.tsx +122 -49
- package/src/overlays/Modal.tsx +296 -219
- package/src/overlays/Popover.tsx +55 -25
- package/src/overlays/Portal.tsx +23 -23
- package/src/overlays/Toast.tsx +175 -137
- package/src/overlays/Tooltip.tsx +125 -100
- package/src/overlays/tokens.css +8 -8
- package/src/registry/components.ts +1368 -0
- package/src/styles/components.css +39 -0
- package/src/styles/index.css +2 -93
- package/src/styles/reset.css +45 -0
- package/src/styles/tokens.css +11 -2
- package/src/typography/Blockquote.tsx +32 -32
- package/src/typography/Code.tsx +89 -89
- package/src/typography/Heading.tsx +56 -56
- package/src/typography/Kbd.tsx +25 -25
- package/src/typography/Label.tsx +47 -47
- package/src/typography/Link.tsx +56 -56
- package/src/typography/Text.tsx +62 -62
- package/src/typography/index.ts +7 -7
- package/src/typography/tokens.css +5 -5
- package/src/utils/cn.ts +6 -6
- package/src/utils/scroll-lock.ts +19 -0
- package/src/utils/useControllableState.ts +54 -0
- package/ui-contract.json +192 -35
- package/dist/chunk-2OXZK6IP.js.map +0 -1
- package/dist/chunk-4S6L5V7X.js.map +0 -1
- package/dist/chunk-5FZ6ZFII.js.map +0 -1
- package/dist/chunk-66VWUJEI.js.map +0 -1
- package/dist/chunk-A7P3O5Z5.js.map +0 -1
- package/dist/chunk-ATYGDTMK.js.map +0 -1
- package/dist/chunk-C4SUXMI3.js.map +0 -1
- package/dist/chunk-EU2LPUYP.js.map +0 -1
- package/dist/chunk-F5Q7BTMH.js.map +0 -1
- package/dist/chunk-GXI2H5XA.js.map +0 -1
- package/dist/chunk-H3LCNPMC.js +0 -101
- package/dist/chunk-H3LCNPMC.js.map +0 -1
- package/dist/chunk-IABPUCAQ.js.map +0 -1
- package/dist/chunk-KJRBRTQE.js.map +0 -1
- package/dist/chunk-LBUBXUYQ.js +0 -40
- package/dist/chunk-LBUBXUYQ.js.map +0 -1
- package/dist/chunk-LWFRUJRU.js.map +0 -1
- package/dist/chunk-NFVONJUK.js.map +0 -1
- package/dist/chunk-PF4ZHGVW.js.map +0 -1
- package/dist/chunk-WAFHC4DQ.js.map +0 -1
- package/dist/chunk-X76KBH4Y.js +0 -71
- package/dist/chunk-X76KBH4Y.js.map +0 -1
package/dist/forms/index.cjs
CHANGED
|
@@ -466,8 +466,59 @@ var Select = React3.forwardRef(
|
|
|
466
466
|
}
|
|
467
467
|
);
|
|
468
468
|
Select.displayName = "Select";
|
|
469
|
+
var CheckboxGroupContext = React3.createContext(null);
|
|
470
|
+
var useCheckboxGroup = () => React3.useContext(CheckboxGroupContext);
|
|
471
|
+
var CheckboxGroup = React3.forwardRef(
|
|
472
|
+
({
|
|
473
|
+
className,
|
|
474
|
+
orientation = "vertical",
|
|
475
|
+
children,
|
|
476
|
+
value: controlledValue,
|
|
477
|
+
defaultValue = [],
|
|
478
|
+
onChange,
|
|
479
|
+
disabled,
|
|
480
|
+
name,
|
|
481
|
+
...props
|
|
482
|
+
}, ref) => {
|
|
483
|
+
const [internalValue, setInternalValue] = React3.useState(defaultValue);
|
|
484
|
+
const isControlled = controlledValue !== void 0;
|
|
485
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
486
|
+
const handleValueChange = (nextValue) => {
|
|
487
|
+
if (!isControlled) {
|
|
488
|
+
setInternalValue(nextValue);
|
|
489
|
+
}
|
|
490
|
+
onChange?.(nextValue);
|
|
491
|
+
};
|
|
492
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
493
|
+
CheckboxGroupContext.Provider,
|
|
494
|
+
{
|
|
495
|
+
value: {
|
|
496
|
+
value,
|
|
497
|
+
onChange: handleValueChange,
|
|
498
|
+
disabled,
|
|
499
|
+
name
|
|
500
|
+
},
|
|
501
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
502
|
+
"div",
|
|
503
|
+
{
|
|
504
|
+
ref,
|
|
505
|
+
role: "group",
|
|
506
|
+
className: cn(
|
|
507
|
+
"flex gap-2",
|
|
508
|
+
orientation === "vertical" ? "flex-col" : "flex-row flex-wrap items-center",
|
|
509
|
+
className
|
|
510
|
+
),
|
|
511
|
+
...props,
|
|
512
|
+
children
|
|
513
|
+
}
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
CheckboxGroup.displayName = "CheckboxGroup";
|
|
469
520
|
var checkboxVariants = classVarianceAuthority.cva(
|
|
470
|
-
"peer shrink-0 rounded-[var(--ui-radius-sm)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)]
|
|
521
|
+
"peer shrink-0 rounded-[var(--ui-radius-sm)] border border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] transition-all flex items-center justify-center text-[var(--ui-primary-fg)] peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--ui-primary)] peer-focus-visible:ring-offset-2",
|
|
471
522
|
{
|
|
472
523
|
variants: {
|
|
473
524
|
size: {
|
|
@@ -482,61 +533,96 @@ var checkboxVariants = classVarianceAuthority.cva(
|
|
|
482
533
|
}
|
|
483
534
|
);
|
|
484
535
|
var Checkbox = React3.forwardRef(
|
|
485
|
-
({
|
|
536
|
+
({
|
|
537
|
+
className,
|
|
538
|
+
size,
|
|
539
|
+
indeterminate = false,
|
|
540
|
+
label,
|
|
541
|
+
checked,
|
|
542
|
+
defaultChecked = false,
|
|
543
|
+
disabled,
|
|
544
|
+
id,
|
|
545
|
+
name,
|
|
546
|
+
value: propValue,
|
|
547
|
+
onChange,
|
|
548
|
+
onCheckedChange,
|
|
549
|
+
...props
|
|
550
|
+
}, ref) => {
|
|
486
551
|
const formField = useFormField();
|
|
487
|
-
const
|
|
552
|
+
const group = useCheckboxGroup();
|
|
553
|
+
const isGroupMember = group !== null && propValue !== void 0;
|
|
554
|
+
const isDisabled = disabled ?? group?.disabled ?? formField?.isDisabled ?? false;
|
|
488
555
|
const checkboxId = id || formField?.id;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
label
|
|
517
|
-
] });
|
|
518
|
-
}
|
|
519
|
-
);
|
|
520
|
-
Checkbox.displayName = "Checkbox";
|
|
521
|
-
var CheckboxGroup = React3.forwardRef(
|
|
522
|
-
({ className, orientation = "vertical", children, value, onChange, ...props }, ref) => {
|
|
523
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
524
|
-
"div",
|
|
556
|
+
const checkboxName = name || group?.name;
|
|
557
|
+
const isControlled = checked !== void 0;
|
|
558
|
+
const [internalChecked, setInternalChecked] = React3.useState(defaultChecked);
|
|
559
|
+
const isChecked = isGroupMember ? group.value.includes(String(propValue)) : isControlled ? Boolean(checked) : internalChecked;
|
|
560
|
+
const innerRef = React3.useRef(null);
|
|
561
|
+
React3.useImperativeHandle(ref, () => innerRef.current);
|
|
562
|
+
React3.useEffect(() => {
|
|
563
|
+
if (innerRef.current) {
|
|
564
|
+
innerRef.current.indeterminate = Boolean(indeterminate);
|
|
565
|
+
}
|
|
566
|
+
}, [indeterminate]);
|
|
567
|
+
const handleChange = (e) => {
|
|
568
|
+
if (isDisabled) return;
|
|
569
|
+
const newChecked = e.target.checked;
|
|
570
|
+
if (!isControlled && !isGroupMember) {
|
|
571
|
+
setInternalChecked(newChecked);
|
|
572
|
+
}
|
|
573
|
+
if (isGroupMember && group) {
|
|
574
|
+
const val = String(propValue);
|
|
575
|
+
const next = newChecked ? [...group.value, val] : group.value.filter((v) => v !== val);
|
|
576
|
+
group.onChange(next);
|
|
577
|
+
}
|
|
578
|
+
onChange?.(e);
|
|
579
|
+
onCheckedChange?.(newChecked);
|
|
580
|
+
};
|
|
581
|
+
const isVisualActive = isChecked || indeterminate;
|
|
582
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
583
|
+
"label",
|
|
525
584
|
{
|
|
526
|
-
ref,
|
|
527
|
-
role: "group",
|
|
528
585
|
className: cn(
|
|
529
|
-
"flex gap-2",
|
|
530
|
-
|
|
531
|
-
className
|
|
586
|
+
"inline-flex items-center gap-2 select-none cursor-pointer group",
|
|
587
|
+
isDisabled && "cursor-not-allowed opacity-50"
|
|
532
588
|
),
|
|
533
|
-
|
|
534
|
-
|
|
589
|
+
children: [
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
591
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
592
|
+
"input",
|
|
593
|
+
{
|
|
594
|
+
ref: innerRef,
|
|
595
|
+
type: "checkbox",
|
|
596
|
+
id: checkboxId,
|
|
597
|
+
name: checkboxName,
|
|
598
|
+
value: propValue,
|
|
599
|
+
disabled: isDisabled,
|
|
600
|
+
checked: isChecked,
|
|
601
|
+
onChange: handleChange,
|
|
602
|
+
"aria-checked": indeterminate ? "mixed" : isChecked,
|
|
603
|
+
className: "sr-only peer",
|
|
604
|
+
...props
|
|
605
|
+
}
|
|
606
|
+
),
|
|
607
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
608
|
+
"div",
|
|
609
|
+
{
|
|
610
|
+
className: cn(
|
|
611
|
+
checkboxVariants({ size }),
|
|
612
|
+
isVisualActive ? "bg-[var(--ui-primary)] border-[var(--ui-primary)] text-white" : "hover:border-[var(--ui-border-strong)]",
|
|
613
|
+
className
|
|
614
|
+
),
|
|
615
|
+
children: indeterminate ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3.5 h-3.5 stroke-[3]" }) : isChecked && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 stroke-[3]" })
|
|
616
|
+
}
|
|
617
|
+
)
|
|
618
|
+
] }),
|
|
619
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-[var(--ui-text-primary)]", children: label })
|
|
620
|
+
]
|
|
535
621
|
}
|
|
536
622
|
);
|
|
537
623
|
}
|
|
538
624
|
);
|
|
539
|
-
|
|
625
|
+
Checkbox.displayName = "Checkbox";
|
|
540
626
|
var RadioGroupContext = React3.createContext(null);
|
|
541
627
|
var radioVariants = classVarianceAuthority.cva(
|
|
542
628
|
"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",
|
|
@@ -796,81 +882,192 @@ var RangeSlider = React3.forwardRef(
|
|
|
796
882
|
showValue = false,
|
|
797
883
|
formatValue = (v) => `${v}`,
|
|
798
884
|
className,
|
|
799
|
-
disabled = false
|
|
885
|
+
disabled = false,
|
|
886
|
+
name,
|
|
887
|
+
"aria-label": ariaLabel = "Range slider"
|
|
800
888
|
}, ref) => {
|
|
801
|
-
const
|
|
802
|
-
const [
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
889
|
+
const isControlled = controlledValue !== void 0;
|
|
890
|
+
const [internalValue, setInternalValue] = React3.useState(defaultValue);
|
|
891
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
892
|
+
const [minVal, maxVal] = [
|
|
893
|
+
Math.max(min, Math.min(currentValue[0], currentValue[1])),
|
|
894
|
+
Math.min(max, Math.max(currentValue[0], currentValue[1]))
|
|
895
|
+
];
|
|
896
|
+
const trackRef = React3.useRef(null);
|
|
897
|
+
const draggingThumbRef = React3.useRef(null);
|
|
898
|
+
const updateValues = React3.useCallback(
|
|
899
|
+
(newMin, newMax) => {
|
|
900
|
+
const clampedMin = Math.max(min, Math.min(newMin, newMax));
|
|
901
|
+
const clampedMax = Math.min(max, Math.max(clampedMin, newMax));
|
|
902
|
+
const next = [clampedMin, clampedMax];
|
|
903
|
+
if (!isControlled) {
|
|
904
|
+
setInternalValue(next);
|
|
905
|
+
}
|
|
906
|
+
onChange?.(next);
|
|
907
|
+
},
|
|
908
|
+
[min, max, isControlled, onChange]
|
|
909
|
+
);
|
|
910
|
+
const getValueFromPointer = React3.useCallback(
|
|
911
|
+
(clientX) => {
|
|
912
|
+
if (!trackRef.current) return min;
|
|
913
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
914
|
+
const percent = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
|
|
915
|
+
const rawValue = min + percent * (max - min);
|
|
916
|
+
const steppedValue = Math.round((rawValue - min) / step) * step + min;
|
|
917
|
+
return Math.min(max, Math.max(min, Number(steppedValue.toFixed(4))));
|
|
918
|
+
},
|
|
919
|
+
[min, max, step]
|
|
920
|
+
);
|
|
921
|
+
const handlePointerDown = (e) => {
|
|
922
|
+
if (disabled) return;
|
|
923
|
+
const clickedVal = getValueFromPointer(e.clientX);
|
|
924
|
+
const distToMin = Math.abs(clickedVal - minVal);
|
|
925
|
+
const distToMax = Math.abs(clickedVal - maxVal);
|
|
926
|
+
const targetThumb = distToMin <= distToMax ? "min" : "max";
|
|
927
|
+
draggingThumbRef.current = targetThumb;
|
|
928
|
+
if (targetThumb === "min") {
|
|
929
|
+
updateValues(Math.min(clickedVal, maxVal), maxVal);
|
|
930
|
+
} else {
|
|
931
|
+
updateValues(minVal, Math.max(clickedVal, minVal));
|
|
932
|
+
}
|
|
933
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
809
934
|
};
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
935
|
+
const handlePointerMove = (e) => {
|
|
936
|
+
if (disabled || !draggingThumbRef.current) return;
|
|
937
|
+
const moveVal = getValueFromPointer(e.clientX);
|
|
938
|
+
if (draggingThumbRef.current === "min") {
|
|
939
|
+
updateValues(Math.min(moveVal, maxVal), maxVal);
|
|
940
|
+
} else {
|
|
941
|
+
updateValues(minVal, Math.max(moveVal, minVal));
|
|
942
|
+
}
|
|
814
943
|
};
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
944
|
+
const handlePointerUp = (e) => {
|
|
945
|
+
if (draggingThumbRef.current) {
|
|
946
|
+
draggingThumbRef.current = null;
|
|
947
|
+
try {
|
|
948
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
949
|
+
} catch {
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
const handleKeyDownMin = (e) => {
|
|
954
|
+
if (disabled) return;
|
|
955
|
+
let nextMin = minVal;
|
|
956
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
957
|
+
e.preventDefault();
|
|
958
|
+
nextMin = Math.min(minVal + step, maxVal);
|
|
959
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
960
|
+
e.preventDefault();
|
|
961
|
+
nextMin = Math.max(minVal - step, min);
|
|
962
|
+
} else if (e.key === "Home") {
|
|
963
|
+
e.preventDefault();
|
|
964
|
+
nextMin = min;
|
|
965
|
+
} else if (e.key === "End") {
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
nextMin = maxVal;
|
|
968
|
+
}
|
|
969
|
+
if (nextMin !== minVal) {
|
|
970
|
+
updateValues(nextMin, maxVal);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
const handleKeyDownMax = (e) => {
|
|
974
|
+
if (disabled) return;
|
|
975
|
+
let nextMax = maxVal;
|
|
976
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
977
|
+
e.preventDefault();
|
|
978
|
+
nextMax = Math.min(maxVal + step, max);
|
|
979
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
980
|
+
e.preventDefault();
|
|
981
|
+
nextMax = Math.max(maxVal - step, minVal);
|
|
982
|
+
} else if (e.key === "Home") {
|
|
983
|
+
e.preventDefault();
|
|
984
|
+
nextMax = minVal;
|
|
985
|
+
} else if (e.key === "End") {
|
|
986
|
+
e.preventDefault();
|
|
987
|
+
nextMax = max;
|
|
988
|
+
}
|
|
989
|
+
if (nextMax !== maxVal) {
|
|
990
|
+
updateValues(minVal, nextMax);
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
const minPercentage = Math.min(100, Math.max(0, (minVal - min) / (max - min || 1) * 100));
|
|
994
|
+
const maxPercentage = Math.min(100, Math.max(0, (maxVal - min) / (max - min || 1) * 100));
|
|
995
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
996
|
+
"div",
|
|
997
|
+
{
|
|
998
|
+
ref,
|
|
999
|
+
className: cn("w-full flex items-center gap-4", disabled && "opacity-50 cursor-not-allowed", className),
|
|
1000
|
+
children: [
|
|
1001
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1002
|
+
"div",
|
|
1003
|
+
{
|
|
1004
|
+
ref: trackRef,
|
|
1005
|
+
onPointerDown: handlePointerDown,
|
|
1006
|
+
onPointerMove: handlePointerMove,
|
|
1007
|
+
onPointerUp: handlePointerUp,
|
|
1008
|
+
className: "relative w-full flex items-center h-7 select-none touch-none cursor-pointer",
|
|
1009
|
+
children: [
|
|
1010
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-2 rounded-full bg-[var(--ui-border-default)] overflow-hidden relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1011
|
+
"div",
|
|
1012
|
+
{
|
|
1013
|
+
className: "absolute h-full bg-[var(--ui-primary)]",
|
|
1014
|
+
style: {
|
|
1015
|
+
left: `${minPercentage}%`,
|
|
1016
|
+
width: `${Math.max(0, maxPercentage - minPercentage)}%`
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
) }),
|
|
1020
|
+
name && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1021
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${name}_min`, value: minVal }),
|
|
1022
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${name}_max`, value: maxVal })
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1025
|
+
"div",
|
|
1026
|
+
{
|
|
1027
|
+
role: "slider",
|
|
1028
|
+
tabIndex: disabled ? -1 : 0,
|
|
1029
|
+
"aria-label": `${ariaLabel} minimum`,
|
|
1030
|
+
"aria-valuemin": min,
|
|
1031
|
+
"aria-valuemax": maxVal,
|
|
1032
|
+
"aria-valuenow": minVal,
|
|
1033
|
+
"aria-disabled": disabled,
|
|
1034
|
+
onKeyDown: handleKeyDownMin,
|
|
1035
|
+
className: cn(
|
|
1036
|
+
"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",
|
|
1037
|
+
disabled && "pointer-events-none"
|
|
1038
|
+
),
|
|
1039
|
+
style: { left: `${minPercentage}%` }
|
|
1040
|
+
}
|
|
1041
|
+
),
|
|
1042
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1043
|
+
"div",
|
|
1044
|
+
{
|
|
1045
|
+
role: "slider",
|
|
1046
|
+
tabIndex: disabled ? -1 : 0,
|
|
1047
|
+
"aria-label": `${ariaLabel} maximum`,
|
|
1048
|
+
"aria-valuemin": minVal,
|
|
1049
|
+
"aria-valuemax": max,
|
|
1050
|
+
"aria-valuenow": maxVal,
|
|
1051
|
+
"aria-disabled": disabled,
|
|
1052
|
+
onKeyDown: handleKeyDownMax,
|
|
1053
|
+
className: cn(
|
|
1054
|
+
"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",
|
|
1055
|
+
disabled && "pointer-events-none"
|
|
1056
|
+
),
|
|
1057
|
+
style: { left: `${maxPercentage}%` }
|
|
1058
|
+
}
|
|
1059
|
+
)
|
|
1060
|
+
]
|
|
824
1061
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
value: minVal,
|
|
835
|
-
disabled,
|
|
836
|
-
onChange: handleMinChange,
|
|
837
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-20"
|
|
838
|
-
}
|
|
839
|
-
),
|
|
840
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
841
|
-
"input",
|
|
842
|
-
{
|
|
843
|
-
type: "range",
|
|
844
|
-
min,
|
|
845
|
-
max,
|
|
846
|
-
step,
|
|
847
|
-
value: maxVal,
|
|
848
|
-
disabled,
|
|
849
|
-
onChange: handleMaxChange,
|
|
850
|
-
className: "absolute inset-0 w-full h-full opacity-0 pointer-events-auto cursor-pointer z-30"
|
|
851
|
-
}
|
|
852
|
-
),
|
|
853
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
854
|
-
"div",
|
|
855
|
-
{
|
|
856
|
-
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",
|
|
857
|
-
style: { left: `${minPercentage}%` }
|
|
858
|
-
}
|
|
859
|
-
),
|
|
860
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
861
|
-
"div",
|
|
862
|
-
{
|
|
863
|
-
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",
|
|
864
|
-
style: { left: `${maxPercentage}%` }
|
|
865
|
-
}
|
|
866
|
-
)
|
|
867
|
-
] }),
|
|
868
|
-
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
869
|
-
formatValue(minVal),
|
|
870
|
-
" - ",
|
|
871
|
-
formatValue(maxVal)
|
|
872
|
-
] })
|
|
873
|
-
] });
|
|
1062
|
+
),
|
|
1063
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold font-mono text-[var(--ui-text-secondary)] min-w-[5rem] text-right", children: [
|
|
1064
|
+
formatValue(minVal),
|
|
1065
|
+
" - ",
|
|
1066
|
+
formatValue(maxVal)
|
|
1067
|
+
] })
|
|
1068
|
+
]
|
|
1069
|
+
}
|
|
1070
|
+
);
|
|
874
1071
|
}
|
|
875
1072
|
);
|
|
876
1073
|
RangeSlider.displayName = "RangeSlider";
|
|
@@ -888,16 +1085,13 @@ var PinInput = React3.forwardRef(
|
|
|
888
1085
|
autoFocus = false,
|
|
889
1086
|
...props
|
|
890
1087
|
}, ref) => {
|
|
891
|
-
const
|
|
1088
|
+
const isControlled = controlledValue !== void 0;
|
|
1089
|
+
const [internalPins, setInternalPins] = React3.useState(() => {
|
|
892
1090
|
const initial = (controlledValue !== void 0 ? controlledValue : defaultValue).slice(0, length);
|
|
893
1091
|
return Array.from({ length }, (_, i) => initial[i] || "");
|
|
894
1092
|
});
|
|
1093
|
+
const pins = isControlled ? Array.from({ length }, (_, i) => controlledValue[i] || "") : internalPins;
|
|
895
1094
|
const inputsRef = React3.useRef([]);
|
|
896
|
-
React3.useEffect(() => {
|
|
897
|
-
if (controlledValue !== void 0) {
|
|
898
|
-
setPins(Array.from({ length }, (_, i) => controlledValue[i] || ""));
|
|
899
|
-
}
|
|
900
|
-
}, [controlledValue, length]);
|
|
901
1095
|
React3.useEffect(() => {
|
|
902
1096
|
if (autoFocus && inputsRef.current[0]) {
|
|
903
1097
|
inputsRef.current[0].focus();
|
|
@@ -907,7 +1101,9 @@ var PinInput = React3.forwardRef(
|
|
|
907
1101
|
const sanitized = val.replace(/[^0-9a-zA-Z]/g, "").slice(-1);
|
|
908
1102
|
const newPins = [...pins];
|
|
909
1103
|
newPins[index] = sanitized;
|
|
910
|
-
|
|
1104
|
+
if (!isControlled) {
|
|
1105
|
+
setInternalPins(newPins);
|
|
1106
|
+
}
|
|
911
1107
|
const combined = newPins.join("");
|
|
912
1108
|
onChange?.(combined);
|
|
913
1109
|
if (sanitized && index < length - 1) {
|
|
@@ -918,16 +1114,28 @@ var PinInput = React3.forwardRef(
|
|
|
918
1114
|
}
|
|
919
1115
|
};
|
|
920
1116
|
const handleKeyDown = (index, e) => {
|
|
921
|
-
if (e.key === "Backspace"
|
|
1117
|
+
if (e.key === "Backspace") {
|
|
1118
|
+
if (!pins[index] && index > 0) {
|
|
1119
|
+
const newPins = [...pins];
|
|
1120
|
+
newPins[index - 1] = "";
|
|
1121
|
+
if (!isControlled) setInternalPins(newPins);
|
|
1122
|
+
onChange?.(newPins.join(""));
|
|
1123
|
+
inputsRef.current[index - 1]?.focus();
|
|
1124
|
+
}
|
|
1125
|
+
} else if (e.key === "ArrowLeft" && index > 0) {
|
|
1126
|
+
e.preventDefault();
|
|
922
1127
|
inputsRef.current[index - 1]?.focus();
|
|
1128
|
+
} else if (e.key === "ArrowRight" && index < length - 1) {
|
|
1129
|
+
e.preventDefault();
|
|
1130
|
+
inputsRef.current[index + 1]?.focus();
|
|
923
1131
|
}
|
|
924
1132
|
};
|
|
925
1133
|
const handlePaste = (e) => {
|
|
926
1134
|
e.preventDefault();
|
|
927
|
-
const pasted = e.clipboardData.getData("text").slice(0, length);
|
|
1135
|
+
const pasted = e.clipboardData.getData("text").replace(/[^0-9a-zA-Z]/g, "").slice(0, length);
|
|
928
1136
|
if (!pasted) return;
|
|
929
1137
|
const newPins = Array.from({ length }, (_, i) => pasted[i] || "");
|
|
930
|
-
|
|
1138
|
+
if (!isControlled) setInternalPins(newPins);
|
|
931
1139
|
const combined = newPins.join("");
|
|
932
1140
|
onChange?.(combined);
|
|
933
1141
|
const nextEmptyIndex = newPins.findIndex((p) => !p);
|
|
@@ -947,6 +1155,7 @@ var PinInput = React3.forwardRef(
|
|
|
947
1155
|
inputMode: "numeric",
|
|
948
1156
|
maxLength: 1,
|
|
949
1157
|
disabled,
|
|
1158
|
+
"aria-label": `Digit ${index + 1} of ${length}`,
|
|
950
1159
|
value: pins[index],
|
|
951
1160
|
onChange: (e) => handleDigitChange(index, e.target.value),
|
|
952
1161
|
onKeyDown: (e) => handleKeyDown(index, e),
|
|
@@ -972,20 +1181,67 @@ function Portal({ children, container }) {
|
|
|
972
1181
|
if (!target) return null;
|
|
973
1182
|
return reactDom.createPortal(children, target);
|
|
974
1183
|
}
|
|
1184
|
+
function useControllableState({
|
|
1185
|
+
value: controlledValue,
|
|
1186
|
+
defaultValue,
|
|
1187
|
+
onChange,
|
|
1188
|
+
name
|
|
1189
|
+
}) {
|
|
1190
|
+
const isControlled = controlledValue !== void 0;
|
|
1191
|
+
const isControlledRef = React3.useRef(isControlled);
|
|
1192
|
+
React3.useEffect(() => {
|
|
1193
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production" && isControlledRef.current !== isControlled) {
|
|
1194
|
+
console.warn(
|
|
1195
|
+
`[${name || "Component"}] is changing from ${isControlledRef.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}. Check the value/defaultValue props.`
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
isControlledRef.current = isControlled;
|
|
1199
|
+
}, [isControlled, name]);
|
|
1200
|
+
const [internalState, setInternalState] = React3.useState(defaultValue);
|
|
1201
|
+
const value = isControlled ? controlledValue : internalState;
|
|
1202
|
+
const setValue = React3.useCallback(
|
|
1203
|
+
(next) => {
|
|
1204
|
+
const nextValue = typeof next === "function" ? next(value) : next;
|
|
1205
|
+
if (!isControlled) {
|
|
1206
|
+
setInternalState(nextValue);
|
|
1207
|
+
}
|
|
1208
|
+
onChange?.(nextValue);
|
|
1209
|
+
},
|
|
1210
|
+
[isControlled, value, onChange]
|
|
1211
|
+
);
|
|
1212
|
+
return [value, setValue, isControlled];
|
|
1213
|
+
}
|
|
975
1214
|
var Popover = ({
|
|
976
1215
|
trigger,
|
|
977
1216
|
children,
|
|
978
|
-
|
|
1217
|
+
open: controlledOpen,
|
|
1218
|
+
isOpen: legacyIsOpen,
|
|
1219
|
+
defaultOpen = false,
|
|
1220
|
+
onOpenChange,
|
|
979
1221
|
onClose,
|
|
980
1222
|
placement = "bottom",
|
|
981
1223
|
className
|
|
982
1224
|
}) => {
|
|
983
|
-
const
|
|
984
|
-
const
|
|
1225
|
+
const activeControlled = controlledOpen !== void 0 ? controlledOpen : legacyIsOpen;
|
|
1226
|
+
const handleStateChange = React3.useCallback(
|
|
1227
|
+
(nextOpen) => {
|
|
1228
|
+
onOpenChange?.(nextOpen);
|
|
1229
|
+
if (!nextOpen) {
|
|
1230
|
+
onClose?.();
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
[onOpenChange, onClose]
|
|
1234
|
+
);
|
|
1235
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
1236
|
+
value: activeControlled,
|
|
1237
|
+
defaultValue: defaultOpen,
|
|
1238
|
+
onChange: handleStateChange,
|
|
1239
|
+
name: "Popover"
|
|
1240
|
+
});
|
|
985
1241
|
const [coords, setCoords] = React3.useState({ top: 0, left: 0 });
|
|
986
1242
|
const triggerRef = React3.useRef(null);
|
|
987
1243
|
const popoverRef = React3.useRef(null);
|
|
988
|
-
const calculatePosition = () => {
|
|
1244
|
+
const calculatePosition = React3.useCallback(() => {
|
|
989
1245
|
if (triggerRef.current) {
|
|
990
1246
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
991
1247
|
let top = 0;
|
|
@@ -1005,45 +1261,52 @@ var Popover = ({
|
|
|
1005
1261
|
}
|
|
1006
1262
|
setCoords({ top, left });
|
|
1007
1263
|
}
|
|
1008
|
-
};
|
|
1264
|
+
}, [placement]);
|
|
1009
1265
|
const handleToggle = () => {
|
|
1010
1266
|
if (!isOpen) {
|
|
1011
1267
|
calculatePosition();
|
|
1012
1268
|
}
|
|
1013
|
-
|
|
1014
|
-
if (isOpen) onClose?.();
|
|
1015
|
-
} else {
|
|
1016
|
-
setInternalIsOpen(!internalIsOpen);
|
|
1017
|
-
}
|
|
1269
|
+
setIsOpen(!isOpen);
|
|
1018
1270
|
};
|
|
1019
1271
|
React3.useEffect(() => {
|
|
1020
1272
|
if (isOpen) {
|
|
1021
1273
|
calculatePosition();
|
|
1022
1274
|
}
|
|
1023
|
-
}, [isOpen]);
|
|
1275
|
+
}, [isOpen, calculatePosition]);
|
|
1024
1276
|
React3.useEffect(() => {
|
|
1277
|
+
if (!isOpen) return;
|
|
1025
1278
|
const handleOutside = (e) => {
|
|
1026
1279
|
if (popoverRef.current && !popoverRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
1027
|
-
|
|
1028
|
-
onClose?.();
|
|
1029
|
-
} else {
|
|
1030
|
-
setInternalIsOpen(false);
|
|
1031
|
-
}
|
|
1280
|
+
setIsOpen(false);
|
|
1032
1281
|
}
|
|
1033
1282
|
};
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
}
|
|
1283
|
+
const handleKeyDown = (e) => {
|
|
1284
|
+
if (e.key === "Escape") {
|
|
1285
|
+
setIsOpen(false);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
document.addEventListener("mousedown", handleOutside);
|
|
1289
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1290
|
+
window.addEventListener("resize", calculatePosition);
|
|
1291
|
+
window.addEventListener("scroll", calculatePosition);
|
|
1039
1292
|
return () => {
|
|
1040
1293
|
document.removeEventListener("mousedown", handleOutside);
|
|
1294
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1041
1295
|
window.removeEventListener("resize", calculatePosition);
|
|
1042
1296
|
window.removeEventListener("scroll", calculatePosition);
|
|
1043
1297
|
};
|
|
1044
|
-
}, [isOpen,
|
|
1298
|
+
}, [isOpen, setIsOpen, calculatePosition]);
|
|
1045
1299
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1046
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1300
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1301
|
+
"div",
|
|
1302
|
+
{
|
|
1303
|
+
ref: triggerRef,
|
|
1304
|
+
onClick: handleToggle,
|
|
1305
|
+
className: "w-full inline-block cursor-pointer",
|
|
1306
|
+
"aria-expanded": isOpen,
|
|
1307
|
+
children: trigger
|
|
1308
|
+
}
|
|
1309
|
+
),
|
|
1047
1310
|
isOpen && /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1048
1311
|
"div",
|
|
1049
1312
|
{
|
|
@@ -1078,7 +1341,7 @@ function formatDisplayDate(d) {
|
|
|
1078
1341
|
var DatePicker = React3.forwardRef(
|
|
1079
1342
|
({
|
|
1080
1343
|
value: controlledValue,
|
|
1081
|
-
defaultValue,
|
|
1344
|
+
defaultValue = null,
|
|
1082
1345
|
onChange,
|
|
1083
1346
|
placeholder = "Select date...",
|
|
1084
1347
|
minDate,
|
|
@@ -1088,26 +1351,28 @@ var DatePicker = React3.forwardRef(
|
|
|
1088
1351
|
className,
|
|
1089
1352
|
presets
|
|
1090
1353
|
}, ref) => {
|
|
1091
|
-
const [
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1354
|
+
const [selectedDate, setSelectedDate] = useControllableState({
|
|
1355
|
+
value: controlledValue,
|
|
1356
|
+
defaultValue,
|
|
1357
|
+
onChange,
|
|
1358
|
+
name: "DatePicker"
|
|
1359
|
+
});
|
|
1095
1360
|
const [viewDate, setViewDate] = React3.useState(selectedDate || /* @__PURE__ */ new Date());
|
|
1096
1361
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
1362
|
+
const [focusedDate, setFocusedDate] = React3.useState(selectedDate || /* @__PURE__ */ new Date());
|
|
1097
1363
|
React3.useEffect(() => {
|
|
1098
1364
|
if (selectedDate) {
|
|
1099
1365
|
setViewDate(selectedDate);
|
|
1366
|
+
setFocusedDate(selectedDate);
|
|
1100
1367
|
}
|
|
1101
1368
|
}, [selectedDate]);
|
|
1102
1369
|
const handleSelectDate = (d) => {
|
|
1103
|
-
|
|
1104
|
-
onChange?.(d);
|
|
1370
|
+
setSelectedDate(d);
|
|
1105
1371
|
setIsOpen(false);
|
|
1106
1372
|
};
|
|
1107
1373
|
const handleClear = (e) => {
|
|
1108
1374
|
e.stopPropagation();
|
|
1109
|
-
|
|
1110
|
-
onChange?.(null);
|
|
1375
|
+
setSelectedDate(null);
|
|
1111
1376
|
};
|
|
1112
1377
|
const prevMonth = () => {
|
|
1113
1378
|
setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1));
|
|
@@ -1140,20 +1405,68 @@ var DatePicker = React3.forwardRef(
|
|
|
1140
1405
|
}
|
|
1141
1406
|
const todayStr = formatDate(/* @__PURE__ */ new Date());
|
|
1142
1407
|
const selectedStr = selectedDate ? formatDate(selectedDate) : "";
|
|
1408
|
+
const focusedStr = formatDate(focusedDate);
|
|
1409
|
+
const handleGridKeyDown = (e) => {
|
|
1410
|
+
let nextDate = new Date(focusedDate);
|
|
1411
|
+
let handled = false;
|
|
1412
|
+
if (e.key === "ArrowLeft") {
|
|
1413
|
+
nextDate.setDate(nextDate.getDate() - 1);
|
|
1414
|
+
handled = true;
|
|
1415
|
+
} else if (e.key === "ArrowRight") {
|
|
1416
|
+
nextDate.setDate(nextDate.getDate() + 1);
|
|
1417
|
+
handled = true;
|
|
1418
|
+
} else if (e.key === "ArrowUp") {
|
|
1419
|
+
nextDate.setDate(nextDate.getDate() - 7);
|
|
1420
|
+
handled = true;
|
|
1421
|
+
} else if (e.key === "ArrowDown") {
|
|
1422
|
+
nextDate.setDate(nextDate.getDate() + 7);
|
|
1423
|
+
handled = true;
|
|
1424
|
+
} else if (e.key === "PageUp") {
|
|
1425
|
+
nextDate.setMonth(nextDate.getMonth() - 1);
|
|
1426
|
+
handled = true;
|
|
1427
|
+
} else if (e.key === "PageDown") {
|
|
1428
|
+
nextDate.setMonth(nextDate.getMonth() + 1);
|
|
1429
|
+
handled = true;
|
|
1430
|
+
} else if (e.key === "Home") {
|
|
1431
|
+
nextDate.setDate(1);
|
|
1432
|
+
handled = true;
|
|
1433
|
+
} else if (e.key === "End") {
|
|
1434
|
+
const lastDay = new Date(nextDate.getFullYear(), nextDate.getMonth() + 1, 0).getDate();
|
|
1435
|
+
nextDate.setDate(lastDay);
|
|
1436
|
+
handled = true;
|
|
1437
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
1438
|
+
e.preventDefault();
|
|
1439
|
+
handleSelectDate(focusedDate);
|
|
1440
|
+
return;
|
|
1441
|
+
} else if (e.key === "Escape") {
|
|
1442
|
+
e.preventDefault();
|
|
1443
|
+
setIsOpen(false);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
if (handled) {
|
|
1447
|
+
e.preventDefault();
|
|
1448
|
+
setFocusedDate(nextDate);
|
|
1449
|
+
if (nextDate.getMonth() !== viewDate.getMonth() || nextDate.getFullYear() !== viewDate.getFullYear()) {
|
|
1450
|
+
setViewDate(new Date(nextDate.getFullYear(), nextDate.getMonth(), 1));
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1143
1454
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1144
1455
|
Popover,
|
|
1145
1456
|
{
|
|
1146
|
-
isOpen,
|
|
1147
|
-
|
|
1457
|
+
open: isOpen,
|
|
1458
|
+
onOpenChange: setIsOpen,
|
|
1148
1459
|
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1149
1460
|
"button",
|
|
1150
1461
|
{
|
|
1151
1462
|
type: "button",
|
|
1152
1463
|
disabled,
|
|
1153
|
-
|
|
1464
|
+
"aria-haspopup": "dialog",
|
|
1465
|
+
"aria-expanded": isOpen,
|
|
1466
|
+
"aria-label": selectedDate ? `Selected date: ${formatDisplayDate(selectedDate)}` : placeholder,
|
|
1154
1467
|
className: cn(
|
|
1155
|
-
"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",
|
|
1156
|
-
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]
|
|
1468
|
+
"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)]",
|
|
1469
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
1157
1470
|
),
|
|
1158
1471
|
children: [
|
|
1159
1472
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
@@ -1163,7 +1476,11 @@ var DatePicker = React3.forwardRef(
|
|
|
1163
1476
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: clearable && selectedDate && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1164
1477
|
"span",
|
|
1165
1478
|
{
|
|
1479
|
+
role: "button",
|
|
1480
|
+
tabIndex: 0,
|
|
1481
|
+
"aria-label": "Clear date",
|
|
1166
1482
|
onClick: handleClear,
|
|
1483
|
+
onKeyDown: (e) => e.key === "Enter" && handleClear(e),
|
|
1167
1484
|
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
1168
1485
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
1169
1486
|
}
|
|
@@ -1171,74 +1488,131 @@ var DatePicker = React3.forwardRef(
|
|
|
1171
1488
|
]
|
|
1172
1489
|
}
|
|
1173
1490
|
),
|
|
1174
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1491
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1492
|
+
"div",
|
|
1493
|
+
{
|
|
1494
|
+
role: "dialog",
|
|
1495
|
+
"aria-label": "Choose date",
|
|
1496
|
+
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",
|
|
1497
|
+
onKeyDown: handleGridKeyDown,
|
|
1498
|
+
children: [
|
|
1499
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1500
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1501
|
+
"span",
|
|
1502
|
+
{
|
|
1503
|
+
id: "calendar-month-heading",
|
|
1504
|
+
className: "text-xs font-bold text-[var(--ui-text-primary)] font-mono uppercase tracking-wider",
|
|
1505
|
+
"aria-live": "polite",
|
|
1506
|
+
children: viewDate.toLocaleString("default", { month: "long", year: "numeric" })
|
|
1507
|
+
}
|
|
1508
|
+
),
|
|
1509
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1510
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1511
|
+
"button",
|
|
1512
|
+
{
|
|
1513
|
+
type: "button",
|
|
1514
|
+
onClick: prevMonth,
|
|
1515
|
+
"aria-label": "Previous month",
|
|
1516
|
+
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",
|
|
1517
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
1518
|
+
}
|
|
1519
|
+
),
|
|
1520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1521
|
+
"button",
|
|
1522
|
+
{
|
|
1523
|
+
type: "button",
|
|
1524
|
+
onClick: nextMonth,
|
|
1525
|
+
"aria-label": "Next month",
|
|
1526
|
+
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",
|
|
1527
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
|
|
1528
|
+
}
|
|
1529
|
+
)
|
|
1530
|
+
] })
|
|
1531
|
+
] }),
|
|
1532
|
+
presets && presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 pb-2 border-b border-[var(--ui-border-subtle)]", children: presets.map((preset, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1179
1533
|
"button",
|
|
1180
1534
|
{
|
|
1181
1535
|
type: "button",
|
|
1182
|
-
onClick:
|
|
1183
|
-
className: "
|
|
1184
|
-
children:
|
|
1536
|
+
onClick: () => handleSelectDate(preset.date),
|
|
1537
|
+
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",
|
|
1538
|
+
children: preset.label
|
|
1539
|
+
},
|
|
1540
|
+
idx
|
|
1541
|
+
)) }),
|
|
1542
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1543
|
+
"div",
|
|
1544
|
+
{
|
|
1545
|
+
role: "row",
|
|
1546
|
+
className: "grid grid-cols-7 text-center text-[10px] font-bold text-[var(--ui-text-muted)] font-mono",
|
|
1547
|
+
children: [
|
|
1548
|
+
{ short: "Su", full: "Sunday" },
|
|
1549
|
+
{ short: "Mo", full: "Monday" },
|
|
1550
|
+
{ short: "Tu", full: "Tuesday" },
|
|
1551
|
+
{ short: "We", full: "Wednesday" },
|
|
1552
|
+
{ short: "Th", full: "Thursday" },
|
|
1553
|
+
{ short: "Fr", full: "Friday" },
|
|
1554
|
+
{ short: "Sa", full: "Saturday" }
|
|
1555
|
+
].map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "columnheader", "aria-label": d.full, className: "py-1", children: d.short }, d.short))
|
|
1185
1556
|
}
|
|
1186
1557
|
),
|
|
1187
1558
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1188
|
-
"
|
|
1559
|
+
"div",
|
|
1189
1560
|
{
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
className: "
|
|
1193
|
-
children:
|
|
1561
|
+
role: "grid",
|
|
1562
|
+
"aria-labelledby": "calendar-month-heading",
|
|
1563
|
+
className: "grid grid-cols-7 gap-1",
|
|
1564
|
+
children: days.map((item, idx) => {
|
|
1565
|
+
const itemStr = formatDate(item.date);
|
|
1566
|
+
const isSelected = itemStr === selectedStr;
|
|
1567
|
+
const isToday = itemStr === todayStr;
|
|
1568
|
+
const isFocused = itemStr === focusedStr;
|
|
1569
|
+
const fullDateLabel = item.date.toLocaleDateString("en-US", {
|
|
1570
|
+
weekday: "long",
|
|
1571
|
+
year: "numeric",
|
|
1572
|
+
month: "long",
|
|
1573
|
+
day: "numeric"
|
|
1574
|
+
});
|
|
1575
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1576
|
+
"button",
|
|
1577
|
+
{
|
|
1578
|
+
type: "button",
|
|
1579
|
+
role: "gridcell",
|
|
1580
|
+
disabled: item.disabled,
|
|
1581
|
+
"aria-selected": isSelected,
|
|
1582
|
+
"aria-current": isToday ? "date" : void 0,
|
|
1583
|
+
"aria-label": fullDateLabel,
|
|
1584
|
+
tabIndex: isFocused ? 0 : -1,
|
|
1585
|
+
onClick: () => handleSelectDate(item.date),
|
|
1586
|
+
className: cn(
|
|
1587
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer outline-none",
|
|
1588
|
+
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
1589
|
+
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
1590
|
+
isFocused && "ring-2 ring-[var(--ui-primary)]",
|
|
1591
|
+
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)]"
|
|
1592
|
+
),
|
|
1593
|
+
children: item.date.getDate()
|
|
1594
|
+
},
|
|
1595
|
+
idx
|
|
1596
|
+
);
|
|
1597
|
+
})
|
|
1194
1598
|
}
|
|
1195
1599
|
)
|
|
1196
|
-
]
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
"button",
|
|
1200
|
-
{
|
|
1201
|
-
type: "button",
|
|
1202
|
-
onClick: () => handleSelectDate(preset.date),
|
|
1203
|
-
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",
|
|
1204
|
-
children: preset.label
|
|
1205
|
-
},
|
|
1206
|
-
idx
|
|
1207
|
-
)) }),
|
|
1208
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 text-center text-[10px] font-bold text-[var(--ui-text-muted)] font-mono", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: d }, d)) }),
|
|
1209
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((item, idx) => {
|
|
1210
|
-
const itemStr = formatDate(item.date);
|
|
1211
|
-
const isSelected = itemStr === selectedStr;
|
|
1212
|
-
const isToday = itemStr === todayStr;
|
|
1213
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1214
|
-
"button",
|
|
1215
|
-
{
|
|
1216
|
-
type: "button",
|
|
1217
|
-
disabled: item.disabled,
|
|
1218
|
-
onClick: () => handleSelectDate(item.date),
|
|
1219
|
-
className: cn(
|
|
1220
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1221
|
-
item.disabled && "opacity-25 cursor-not-allowed pointer-events-none",
|
|
1222
|
-
!item.currentMonth && "text-[var(--ui-text-muted)] opacity-50",
|
|
1223
|
-
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)]"
|
|
1224
|
-
),
|
|
1225
|
-
children: item.date.getDate()
|
|
1226
|
-
},
|
|
1227
|
-
idx
|
|
1228
|
-
);
|
|
1229
|
-
}) })
|
|
1230
|
-
] })
|
|
1600
|
+
]
|
|
1601
|
+
}
|
|
1602
|
+
)
|
|
1231
1603
|
}
|
|
1232
1604
|
) });
|
|
1233
1605
|
}
|
|
1234
1606
|
);
|
|
1235
1607
|
DatePicker.displayName = "DatePicker";
|
|
1236
1608
|
var DateRangePicker = React3.forwardRef(
|
|
1237
|
-
({ value: controlledVal, defaultValue, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
1238
|
-
const [
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1609
|
+
({ value: controlledVal, defaultValue = { from: null, to: null }, onChange, placeholder = "Select date range...", className, disabled = false }, ref) => {
|
|
1610
|
+
const [range, setRange] = useControllableState({
|
|
1611
|
+
value: controlledVal,
|
|
1612
|
+
defaultValue,
|
|
1613
|
+
onChange,
|
|
1614
|
+
name: "DateRangePicker"
|
|
1615
|
+
});
|
|
1242
1616
|
const [viewDate, setViewDate] = React3.useState(range.from || /* @__PURE__ */ new Date());
|
|
1243
1617
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
1244
1618
|
const handleDayClick = (d) => {
|
|
@@ -1253,8 +1627,7 @@ var DateRangePicker = React3.forwardRef(
|
|
|
1253
1627
|
setIsOpen(false);
|
|
1254
1628
|
}
|
|
1255
1629
|
}
|
|
1256
|
-
|
|
1257
|
-
onChange?.(next);
|
|
1630
|
+
setRange(next);
|
|
1258
1631
|
};
|
|
1259
1632
|
const displayText = range.from && range.to ? `${formatDisplayDate(range.from)} \u2013 ${formatDisplayDate(range.to)}` : range.from ? `${formatDisplayDate(range.from)} \u2013 ...` : placeholder;
|
|
1260
1633
|
const year = viewDate.getFullYear();
|
|
@@ -1268,146 +1641,345 @@ var DateRangePicker = React3.forwardRef(
|
|
|
1268
1641
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1269
1642
|
Popover,
|
|
1270
1643
|
{
|
|
1271
|
-
isOpen,
|
|
1272
|
-
|
|
1644
|
+
open: isOpen,
|
|
1645
|
+
onOpenChange: setIsOpen,
|
|
1273
1646
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1274
1647
|
"button",
|
|
1275
1648
|
{
|
|
1276
1649
|
type: "button",
|
|
1277
1650
|
disabled,
|
|
1278
|
-
|
|
1279
|
-
|
|
1651
|
+
"aria-haspopup": "dialog",
|
|
1652
|
+
"aria-expanded": isOpen,
|
|
1653
|
+
"aria-label": displayText,
|
|
1654
|
+
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)]",
|
|
1280
1655
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
1281
1656
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
1282
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: range.from ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: displayText })
|
|
1283
|
-
] })
|
|
1284
|
-
}
|
|
1285
|
-
),
|
|
1286
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1657
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: range.from ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: displayText })
|
|
1658
|
+
] })
|
|
1659
|
+
}
|
|
1660
|
+
),
|
|
1661
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1662
|
+
"div",
|
|
1663
|
+
{
|
|
1664
|
+
role: "dialog",
|
|
1665
|
+
"aria-label": "Choose date range",
|
|
1666
|
+
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",
|
|
1667
|
+
children: [
|
|
1668
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1669
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { id: "range-month-heading", className: "text-xs font-bold text-[var(--ui-text-primary)] font-mono uppercase tracking-wider", children: viewDate.toLocaleString("default", { month: "long", year: "numeric" }) }),
|
|
1670
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1671
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1672
|
+
"button",
|
|
1673
|
+
{
|
|
1674
|
+
type: "button",
|
|
1675
|
+
onClick: () => setViewDate(new Date(year, month - 1, 1)),
|
|
1676
|
+
"aria-label": "Previous month",
|
|
1677
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
1678
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
1679
|
+
}
|
|
1680
|
+
),
|
|
1681
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1682
|
+
"button",
|
|
1683
|
+
{
|
|
1684
|
+
type: "button",
|
|
1685
|
+
onClick: () => setViewDate(new Date(year, month + 1, 1)),
|
|
1686
|
+
"aria-label": "Next month",
|
|
1687
|
+
className: "p-1 rounded-lg hover:bg-[var(--ui-bg-tertiary)] cursor-pointer",
|
|
1688
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
|
|
1689
|
+
}
|
|
1690
|
+
)
|
|
1691
|
+
] })
|
|
1692
|
+
] }),
|
|
1693
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1694
|
+
"div",
|
|
1301
1695
|
{
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
className: "
|
|
1305
|
-
children:
|
|
1696
|
+
role: "grid",
|
|
1697
|
+
"aria-labelledby": "range-month-heading",
|
|
1698
|
+
className: "grid grid-cols-7 gap-1 text-center",
|
|
1699
|
+
children: [
|
|
1700
|
+
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", {}, `empty-${i}`)),
|
|
1701
|
+
days.map((d, idx) => {
|
|
1702
|
+
const dStr = formatDate(d);
|
|
1703
|
+
const fromStr = range.from ? formatDate(range.from) : "";
|
|
1704
|
+
const toStr = range.to ? formatDate(range.to) : "";
|
|
1705
|
+
const isFrom = dStr === fromStr;
|
|
1706
|
+
const isTo = dStr === toStr;
|
|
1707
|
+
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
1708
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1709
|
+
"button",
|
|
1710
|
+
{
|
|
1711
|
+
type: "button",
|
|
1712
|
+
role: "gridcell",
|
|
1713
|
+
"aria-selected": Boolean(isFrom || isTo || isInRange),
|
|
1714
|
+
"aria-label": d.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" }),
|
|
1715
|
+
onClick: () => handleDayClick(d),
|
|
1716
|
+
className: cn(
|
|
1717
|
+
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1718
|
+
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
1719
|
+
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
1720
|
+
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1721
|
+
),
|
|
1722
|
+
children: d.getDate()
|
|
1723
|
+
},
|
|
1724
|
+
idx
|
|
1725
|
+
);
|
|
1726
|
+
})
|
|
1727
|
+
]
|
|
1306
1728
|
}
|
|
1307
1729
|
)
|
|
1308
|
-
]
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
Array.from({ length: firstDayIndex }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", {}, `empty-${i}`)),
|
|
1312
|
-
days.map((d, idx) => {
|
|
1313
|
-
const dStr = formatDate(d);
|
|
1314
|
-
const fromStr = range.from ? formatDate(range.from) : "";
|
|
1315
|
-
const toStr = range.to ? formatDate(range.to) : "";
|
|
1316
|
-
const isFrom = dStr === fromStr;
|
|
1317
|
-
const isTo = dStr === toStr;
|
|
1318
|
-
const isInRange = range.from && range.to && d > range.from && d < range.to;
|
|
1319
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1320
|
-
"button",
|
|
1321
|
-
{
|
|
1322
|
-
type: "button",
|
|
1323
|
-
onClick: () => handleDayClick(d),
|
|
1324
|
-
className: cn(
|
|
1325
|
-
"h-8 w-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all cursor-pointer",
|
|
1326
|
-
(isFrom || isTo) && "bg-[var(--ui-primary)] text-white shadow-md",
|
|
1327
|
-
isInRange && "bg-[var(--ui-primary)]/20 text-[var(--ui-primary)] rounded-none",
|
|
1328
|
-
!isFrom && !isTo && !isInRange && "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1329
|
-
),
|
|
1330
|
-
children: d.getDate()
|
|
1331
|
-
},
|
|
1332
|
-
idx
|
|
1333
|
-
);
|
|
1334
|
-
})
|
|
1335
|
-
] })
|
|
1336
|
-
] })
|
|
1730
|
+
]
|
|
1731
|
+
}
|
|
1732
|
+
)
|
|
1337
1733
|
}
|
|
1338
1734
|
) });
|
|
1339
1735
|
}
|
|
1340
1736
|
);
|
|
1341
1737
|
DateRangePicker.displayName = "DateRangePicker";
|
|
1342
1738
|
var TimePicker = React3.forwardRef(
|
|
1343
|
-
({
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1739
|
+
({
|
|
1740
|
+
value: controlledVal,
|
|
1741
|
+
defaultValue,
|
|
1742
|
+
onChange,
|
|
1743
|
+
format = "24h",
|
|
1744
|
+
className,
|
|
1745
|
+
disabled = false,
|
|
1746
|
+
placeholder = "Select time..."
|
|
1747
|
+
}, ref) => {
|
|
1748
|
+
const is12h = format === "12h";
|
|
1749
|
+
const fallbackDefault = defaultValue ?? (is12h ? "12:00 PM" : "12:00");
|
|
1750
|
+
const [time, setTime] = useControllableState({
|
|
1751
|
+
value: controlledVal,
|
|
1752
|
+
defaultValue: fallbackDefault,
|
|
1753
|
+
onChange,
|
|
1754
|
+
name: "TimePicker"
|
|
1755
|
+
});
|
|
1348
1756
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1757
|
+
const parseTime = (str, is12) => {
|
|
1758
|
+
if (!str) return { hours: is12 ? "12" : "12", minutes: "00", period: "AM" };
|
|
1759
|
+
const trimmed = str.trim();
|
|
1760
|
+
let period2 = "AM";
|
|
1761
|
+
let cleanTime = trimmed;
|
|
1762
|
+
if (trimmed.toLowerCase().includes("pm")) {
|
|
1763
|
+
period2 = "PM";
|
|
1764
|
+
cleanTime = trimmed.replace(/pm/i, "").trim();
|
|
1765
|
+
} else if (trimmed.toLowerCase().includes("am")) {
|
|
1766
|
+
period2 = "AM";
|
|
1767
|
+
cleanTime = trimmed.replace(/am/i, "").trim();
|
|
1768
|
+
}
|
|
1769
|
+
const parts = cleanTime.split(":");
|
|
1770
|
+
let h = parts[0] ? parts[0].padStart(2, "0") : is12 ? "12" : "00";
|
|
1771
|
+
const m = parts[1] ? parts[1].padStart(2, "0") : "00";
|
|
1772
|
+
return { hours: h, minutes: m, period: period2 };
|
|
1773
|
+
};
|
|
1774
|
+
const { hours, minutes, period } = parseTime(time, is12h);
|
|
1775
|
+
const updateTime = (newH, newM, newPeriod) => {
|
|
1776
|
+
let formatted;
|
|
1777
|
+
if (is12h) {
|
|
1778
|
+
const p = newPeriod ?? period;
|
|
1779
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")} ${p}`;
|
|
1780
|
+
} else {
|
|
1781
|
+
formatted = `${newH.padStart(2, "0")}:${newM.padStart(2, "0")}`;
|
|
1782
|
+
}
|
|
1783
|
+
setTime(formatted);
|
|
1784
|
+
};
|
|
1785
|
+
const hourOptions = is12h ? Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0")) : Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
1786
|
+
const minuteOptions = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"];
|
|
1787
|
+
const hourListRef = React3.useRef(null);
|
|
1788
|
+
const minuteListRef = React3.useRef(null);
|
|
1789
|
+
const periodListRef = React3.useRef(null);
|
|
1790
|
+
React3.useEffect(() => {
|
|
1791
|
+
if (isOpen) {
|
|
1792
|
+
const timer = setTimeout(() => {
|
|
1793
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
1794
|
+
activeHourEl?.focus();
|
|
1795
|
+
}, 10);
|
|
1796
|
+
return () => clearTimeout(timer);
|
|
1797
|
+
}
|
|
1798
|
+
}, [isOpen]);
|
|
1799
|
+
const handleHourKeyDown = (e) => {
|
|
1800
|
+
const idx = hourOptions.indexOf(hours);
|
|
1801
|
+
if (e.key === "ArrowDown") {
|
|
1802
|
+
e.preventDefault();
|
|
1803
|
+
const next = hourOptions[(idx + 1) % hourOptions.length];
|
|
1804
|
+
updateTime(next, minutes, period);
|
|
1805
|
+
} else if (e.key === "ArrowUp") {
|
|
1806
|
+
e.preventDefault();
|
|
1807
|
+
const prev = hourOptions[(idx - 1 + hourOptions.length) % hourOptions.length];
|
|
1808
|
+
updateTime(prev, minutes, period);
|
|
1809
|
+
} else if (e.key === "ArrowRight") {
|
|
1810
|
+
e.preventDefault();
|
|
1811
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]') || minuteListRef.current?.querySelector('[role="option"]');
|
|
1812
|
+
activeMinEl?.focus();
|
|
1813
|
+
} else if (e.key === "Escape") {
|
|
1814
|
+
e.preventDefault();
|
|
1815
|
+
setIsOpen(false);
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
const handleMinuteKeyDown = (e) => {
|
|
1819
|
+
const idx = minuteOptions.indexOf(minutes);
|
|
1820
|
+
if (e.key === "ArrowDown") {
|
|
1821
|
+
e.preventDefault();
|
|
1822
|
+
const next = minuteOptions[(idx + 1) % minuteOptions.length];
|
|
1823
|
+
updateTime(hours, next, period);
|
|
1824
|
+
} else if (e.key === "ArrowUp") {
|
|
1825
|
+
e.preventDefault();
|
|
1826
|
+
const prev = minuteOptions[(idx - 1 + minuteOptions.length) % minuteOptions.length];
|
|
1827
|
+
updateTime(hours, prev, period);
|
|
1828
|
+
} else if (e.key === "ArrowLeft") {
|
|
1829
|
+
e.preventDefault();
|
|
1830
|
+
const activeHourEl = hourListRef.current?.querySelector('[aria-selected="true"]');
|
|
1831
|
+
activeHourEl?.focus();
|
|
1832
|
+
} else if (e.key === "ArrowRight" && is12h) {
|
|
1833
|
+
e.preventDefault();
|
|
1834
|
+
const activePeriodEl = periodListRef.current?.querySelector('[aria-selected="true"]');
|
|
1835
|
+
activePeriodEl?.focus();
|
|
1836
|
+
} else if (e.key === "Escape") {
|
|
1837
|
+
e.preventDefault();
|
|
1838
|
+
setIsOpen(false);
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
const handlePeriodKeyDown = (e) => {
|
|
1842
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1843
|
+
e.preventDefault();
|
|
1844
|
+
const next = period === "AM" ? "PM" : "AM";
|
|
1845
|
+
updateTime(hours, minutes, next);
|
|
1846
|
+
} else if (e.key === "ArrowLeft") {
|
|
1847
|
+
e.preventDefault();
|
|
1848
|
+
const activeMinEl = minuteListRef.current?.querySelector('[aria-selected="true"]');
|
|
1849
|
+
activeMinEl?.focus();
|
|
1850
|
+
} else if (e.key === "Escape") {
|
|
1851
|
+
e.preventDefault();
|
|
1852
|
+
setIsOpen(false);
|
|
1853
|
+
}
|
|
1355
1854
|
};
|
|
1356
1855
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1357
1856
|
Popover,
|
|
1358
1857
|
{
|
|
1359
|
-
isOpen,
|
|
1360
|
-
|
|
1858
|
+
open: isOpen,
|
|
1859
|
+
onOpenChange: setIsOpen,
|
|
1361
1860
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1362
1861
|
"button",
|
|
1363
1862
|
{
|
|
1364
1863
|
type: "button",
|
|
1365
1864
|
disabled,
|
|
1366
|
-
|
|
1367
|
-
|
|
1865
|
+
"aria-haspopup": "dialog",
|
|
1866
|
+
"aria-expanded": isOpen,
|
|
1867
|
+
"aria-label": `Selected time: ${time}`,
|
|
1868
|
+
className: cn(
|
|
1869
|
+
"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)]",
|
|
1870
|
+
disabled && "opacity-50 cursor-not-allowed"
|
|
1871
|
+
),
|
|
1368
1872
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
1369
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)]" }),
|
|
1370
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time })
|
|
1873
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-[var(--ui-text-muted)] shrink-0" }),
|
|
1874
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm font-semibold text-[var(--ui-text-primary)]", children: time || placeholder })
|
|
1371
1875
|
] })
|
|
1372
1876
|
}
|
|
1373
1877
|
),
|
|
1374
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
"
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
"
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1878
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1879
|
+
"div",
|
|
1880
|
+
{
|
|
1881
|
+
role: "dialog",
|
|
1882
|
+
"aria-label": "Choose time",
|
|
1883
|
+
className: "p-4 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-3",
|
|
1884
|
+
children: [
|
|
1885
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-[var(--ui-text-muted)] uppercase tracking-wider font-mono", children: [
|
|
1886
|
+
"Select Time (",
|
|
1887
|
+
format,
|
|
1888
|
+
")"
|
|
1889
|
+
] }),
|
|
1890
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
1891
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1892
|
+
"div",
|
|
1893
|
+
{
|
|
1894
|
+
ref: hourListRef,
|
|
1895
|
+
tabIndex: 0,
|
|
1896
|
+
onKeyDown: handleHourKeyDown,
|
|
1897
|
+
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)]",
|
|
1898
|
+
role: "listbox",
|
|
1899
|
+
"aria-label": "Hour",
|
|
1900
|
+
children: hourOptions.map((h) => {
|
|
1901
|
+
const isSelected = hours === h;
|
|
1902
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1903
|
+
"div",
|
|
1904
|
+
{
|
|
1905
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1906
|
+
role: "option",
|
|
1907
|
+
"aria-selected": isSelected,
|
|
1908
|
+
onClick: () => updateTime(h, minutes, period),
|
|
1909
|
+
className: cn(
|
|
1910
|
+
"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)]",
|
|
1911
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1912
|
+
),
|
|
1913
|
+
children: h
|
|
1914
|
+
},
|
|
1915
|
+
h
|
|
1916
|
+
);
|
|
1917
|
+
})
|
|
1918
|
+
}
|
|
1404
1919
|
),
|
|
1405
|
-
children:
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1920
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold", children: ":" }),
|
|
1921
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1922
|
+
"div",
|
|
1923
|
+
{
|
|
1924
|
+
ref: minuteListRef,
|
|
1925
|
+
tabIndex: 0,
|
|
1926
|
+
onKeyDown: handleMinuteKeyDown,
|
|
1927
|
+
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)]",
|
|
1928
|
+
role: "listbox",
|
|
1929
|
+
"aria-label": "Minute",
|
|
1930
|
+
children: minuteOptions.map((m) => {
|
|
1931
|
+
const isSelected = minutes === m;
|
|
1932
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1933
|
+
"div",
|
|
1934
|
+
{
|
|
1935
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1936
|
+
role: "option",
|
|
1937
|
+
"aria-selected": isSelected,
|
|
1938
|
+
onClick: () => updateTime(hours, m, period),
|
|
1939
|
+
className: cn(
|
|
1940
|
+
"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)]",
|
|
1941
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1942
|
+
),
|
|
1943
|
+
children: m
|
|
1944
|
+
},
|
|
1945
|
+
m
|
|
1946
|
+
);
|
|
1947
|
+
})
|
|
1948
|
+
}
|
|
1949
|
+
),
|
|
1950
|
+
is12h && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1951
|
+
"div",
|
|
1952
|
+
{
|
|
1953
|
+
ref: periodListRef,
|
|
1954
|
+
tabIndex: 0,
|
|
1955
|
+
onKeyDown: handlePeriodKeyDown,
|
|
1956
|
+
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)]",
|
|
1957
|
+
role: "listbox",
|
|
1958
|
+
"aria-label": "Period",
|
|
1959
|
+
children: ["AM", "PM"].map((p) => {
|
|
1960
|
+
const isSelected = period === p;
|
|
1961
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1962
|
+
"div",
|
|
1963
|
+
{
|
|
1964
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1965
|
+
role: "option",
|
|
1966
|
+
"aria-selected": isSelected,
|
|
1967
|
+
onClick: () => updateTime(hours, minutes, p),
|
|
1968
|
+
className: cn(
|
|
1969
|
+
"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)]",
|
|
1970
|
+
isSelected ? "bg-[var(--ui-primary)] text-white" : "hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-primary)]"
|
|
1971
|
+
),
|
|
1972
|
+
children: p
|
|
1973
|
+
},
|
|
1974
|
+
p
|
|
1975
|
+
);
|
|
1976
|
+
})
|
|
1977
|
+
}
|
|
1978
|
+
)
|
|
1979
|
+
] })
|
|
1980
|
+
]
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1411
1983
|
}
|
|
1412
1984
|
) });
|
|
1413
1985
|
}
|
|
@@ -1485,135 +2057,205 @@ var Combobox = React3.forwardRef(
|
|
|
1485
2057
|
emptyText = "No items found.",
|
|
1486
2058
|
disabled = false,
|
|
1487
2059
|
clearable = true,
|
|
1488
|
-
className
|
|
2060
|
+
className,
|
|
2061
|
+
name,
|
|
2062
|
+
id: customId,
|
|
2063
|
+
"aria-label": ariaLabel = "Select option"
|
|
1489
2064
|
}, ref) => {
|
|
2065
|
+
const baseId = React3.useId();
|
|
2066
|
+
const comboId = customId || `combobox-${baseId}`;
|
|
2067
|
+
const listboxId = `listbox-${baseId}`;
|
|
1490
2068
|
const [internalVal, setInternalVal] = React3.useState(defaultValue);
|
|
1491
|
-
const
|
|
2069
|
+
const isControlled = controlledVal !== void 0;
|
|
2070
|
+
const selectedValue = isControlled ? controlledVal : internalVal;
|
|
1492
2071
|
const [search, setSearch] = React3.useState("");
|
|
1493
2072
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
1494
2073
|
const [highlightIndex, setHighlightIndex] = React3.useState(0);
|
|
2074
|
+
const searchInputRef = React3.useRef(null);
|
|
2075
|
+
const triggerRef = React3.useRef(null);
|
|
1495
2076
|
const filteredOptions = options.filter(
|
|
1496
2077
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase()) || opt.description && opt.description.toLowerCase().includes(search.toLowerCase())
|
|
1497
2078
|
);
|
|
1498
2079
|
const selectedOption = options.find((opt) => opt.value === selectedValue);
|
|
2080
|
+
React3.useEffect(() => {
|
|
2081
|
+
if (filteredOptions.length > 0) {
|
|
2082
|
+
setHighlightIndex((prev) => Math.min(prev, filteredOptions.length - 1));
|
|
2083
|
+
}
|
|
2084
|
+
}, [filteredOptions.length]);
|
|
1499
2085
|
const handleSelect = (val) => {
|
|
1500
|
-
|
|
2086
|
+
if (!isControlled) {
|
|
2087
|
+
setInternalVal(val);
|
|
2088
|
+
}
|
|
1501
2089
|
onChange?.(val);
|
|
1502
2090
|
setIsOpen(false);
|
|
1503
2091
|
setSearch("");
|
|
2092
|
+
triggerRef.current?.focus();
|
|
1504
2093
|
};
|
|
1505
2094
|
const handleClear = (e) => {
|
|
1506
2095
|
e.stopPropagation();
|
|
1507
|
-
|
|
2096
|
+
if (!isControlled) {
|
|
2097
|
+
setInternalVal("");
|
|
2098
|
+
}
|
|
1508
2099
|
onChange?.("");
|
|
1509
2100
|
};
|
|
1510
2101
|
const handleKeyDown = (e) => {
|
|
1511
2102
|
if (!isOpen) {
|
|
1512
|
-
if (e.key === "ArrowDown" || e.key === "Enter") {
|
|
2103
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
2104
|
+
e.preventDefault();
|
|
1513
2105
|
setIsOpen(true);
|
|
1514
2106
|
}
|
|
1515
2107
|
return;
|
|
1516
2108
|
}
|
|
1517
2109
|
if (e.key === "ArrowDown") {
|
|
1518
2110
|
e.preventDefault();
|
|
1519
|
-
|
|
2111
|
+
let next = highlightIndex + 1;
|
|
2112
|
+
while (next < filteredOptions.length && filteredOptions[next]?.disabled) {
|
|
2113
|
+
next++;
|
|
2114
|
+
}
|
|
2115
|
+
if (next < filteredOptions.length) {
|
|
2116
|
+
setHighlightIndex(next);
|
|
2117
|
+
}
|
|
1520
2118
|
} else if (e.key === "ArrowUp") {
|
|
1521
2119
|
e.preventDefault();
|
|
1522
|
-
|
|
2120
|
+
let prev = highlightIndex - 1;
|
|
2121
|
+
while (prev >= 0 && filteredOptions[prev]?.disabled) {
|
|
2122
|
+
prev--;
|
|
2123
|
+
}
|
|
2124
|
+
if (prev >= 0) {
|
|
2125
|
+
setHighlightIndex(prev);
|
|
2126
|
+
}
|
|
2127
|
+
} else if (e.key === "Home") {
|
|
2128
|
+
e.preventDefault();
|
|
2129
|
+
let first = 0;
|
|
2130
|
+
while (first < filteredOptions.length && filteredOptions[first]?.disabled) {
|
|
2131
|
+
first++;
|
|
2132
|
+
}
|
|
2133
|
+
setHighlightIndex(first);
|
|
2134
|
+
} else if (e.key === "End") {
|
|
2135
|
+
e.preventDefault();
|
|
2136
|
+
let last = filteredOptions.length - 1;
|
|
2137
|
+
while (last >= 0 && filteredOptions[last]?.disabled) {
|
|
2138
|
+
last--;
|
|
2139
|
+
}
|
|
2140
|
+
setHighlightIndex(last);
|
|
1523
2141
|
} else if (e.key === "Enter") {
|
|
1524
2142
|
e.preventDefault();
|
|
1525
|
-
|
|
1526
|
-
|
|
2143
|
+
const highlighted = filteredOptions[highlightIndex];
|
|
2144
|
+
if (highlighted && !highlighted.disabled) {
|
|
2145
|
+
handleSelect(highlighted.value);
|
|
1527
2146
|
}
|
|
1528
2147
|
} else if (e.key === "Escape") {
|
|
2148
|
+
e.preventDefault();
|
|
1529
2149
|
setIsOpen(false);
|
|
2150
|
+
triggerRef.current?.focus();
|
|
1530
2151
|
}
|
|
1531
2152
|
};
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
{
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
disabled
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
2153
|
+
const activeDescendantId = filteredOptions[highlightIndex] ? `combobox-opt-${baseId}-${filteredOptions[highlightIndex].value}` : void 0;
|
|
2154
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("relative w-full", className), children: [
|
|
2155
|
+
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selectedValue }),
|
|
2156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2157
|
+
Popover,
|
|
2158
|
+
{
|
|
2159
|
+
isOpen,
|
|
2160
|
+
onClose: () => setIsOpen(false),
|
|
2161
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2162
|
+
"button",
|
|
2163
|
+
{
|
|
2164
|
+
ref: triggerRef,
|
|
2165
|
+
id: comboId,
|
|
2166
|
+
type: "button",
|
|
2167
|
+
disabled,
|
|
2168
|
+
role: "combobox",
|
|
2169
|
+
"aria-expanded": isOpen,
|
|
2170
|
+
"aria-haspopup": "listbox",
|
|
2171
|
+
"aria-controls": isOpen ? listboxId : void 0,
|
|
2172
|
+
"aria-label": ariaLabel,
|
|
2173
|
+
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
2174
|
+
onKeyDown: handleKeyDown,
|
|
2175
|
+
className: cn(
|
|
2176
|
+
"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)]",
|
|
2177
|
+
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
2178
|
+
),
|
|
2179
|
+
children: [
|
|
2180
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
|
|
2181
|
+
selectedOption?.icon,
|
|
2182
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: selectedOption ? "text-[var(--ui-text-primary)] font-medium" : "text-[var(--ui-text-muted)]", children: selectedOption ? selectedOption.label : placeholder })
|
|
2183
|
+
] }),
|
|
2184
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-[var(--ui-text-muted)]", children: [
|
|
2185
|
+
clearable && selectedValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2186
|
+
"span",
|
|
2187
|
+
{
|
|
2188
|
+
onClick: handleClear,
|
|
2189
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
2190
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
2191
|
+
}
|
|
2192
|
+
),
|
|
2193
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "w-4 h-4 opacity-50 shrink-0" })
|
|
2194
|
+
] })
|
|
2195
|
+
]
|
|
2196
|
+
}
|
|
2197
|
+
),
|
|
2198
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 w-72 rounded-2xl bg-[var(--ui-bg-surface)] backdrop-blur-2xl border border-[var(--ui-border-default)] shadow-2xl space-y-2", children: [
|
|
2199
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center", children: [
|
|
2200
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 w-3.5 h-3.5 text-[var(--ui-text-muted)] pointer-events-none" }),
|
|
2201
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2202
|
+
"input",
|
|
2203
|
+
{
|
|
2204
|
+
ref: searchInputRef,
|
|
2205
|
+
type: "text",
|
|
2206
|
+
autoFocus: true,
|
|
2207
|
+
placeholder: searchPlaceholder,
|
|
2208
|
+
value: search,
|
|
2209
|
+
onChange: (e) => {
|
|
2210
|
+
setSearch(e.target.value);
|
|
2211
|
+
setHighlightIndex(0);
|
|
2212
|
+
},
|
|
2213
|
+
onKeyDown: handleKeyDown,
|
|
2214
|
+
role: "combobox",
|
|
2215
|
+
"aria-autocomplete": "list",
|
|
2216
|
+
"aria-expanded": "true",
|
|
2217
|
+
"aria-controls": listboxId,
|
|
2218
|
+
"aria-activedescendant": activeDescendantId,
|
|
2219
|
+
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)]"
|
|
2220
|
+
}
|
|
2221
|
+
)
|
|
2222
|
+
] }),
|
|
2223
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { id: listboxId, className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: emptyText }) : filteredOptions.map((opt, idx) => {
|
|
2224
|
+
const isSelected = opt.value === selectedValue;
|
|
2225
|
+
const isHighlighted = idx === highlightIndex;
|
|
2226
|
+
const optId = `combobox-opt-${baseId}-${opt.value}`;
|
|
2227
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2228
|
+
"div",
|
|
2229
|
+
{
|
|
2230
|
+
id: optId,
|
|
2231
|
+
role: "option",
|
|
2232
|
+
"aria-selected": isSelected,
|
|
2233
|
+
"aria-disabled": opt.disabled,
|
|
2234
|
+
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
2235
|
+
onMouseEnter: () => !opt.disabled && setHighlightIndex(idx),
|
|
2236
|
+
className: cn(
|
|
2237
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none",
|
|
2238
|
+
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
2239
|
+
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)]"
|
|
2240
|
+
),
|
|
2241
|
+
children: [
|
|
2242
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
2243
|
+
opt.icon,
|
|
2244
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2245
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: opt.label }),
|
|
2246
|
+
opt.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
2247
|
+
] })
|
|
2248
|
+
] }),
|
|
2249
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
2250
|
+
]
|
|
1580
2251
|
},
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
)
|
|
1584
|
-
] })
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1589
|
-
"div",
|
|
1590
|
-
{
|
|
1591
|
-
role: "option",
|
|
1592
|
-
"aria-selected": isSelected,
|
|
1593
|
-
onClick: () => !opt.disabled && handleSelect(opt.value),
|
|
1594
|
-
onMouseEnter: () => setHighlightIndex(idx),
|
|
1595
|
-
className: cn(
|
|
1596
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium cursor-pointer transition-colors",
|
|
1597
|
-
opt.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
|
|
1598
|
-
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)]"
|
|
1599
|
-
),
|
|
1600
|
-
children: [
|
|
1601
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
1602
|
-
opt.icon,
|
|
1603
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1604
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: opt.label }),
|
|
1605
|
-
opt.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[10px] block truncate", isSelected ? "text-white/80" : "text-[var(--ui-text-muted)]"), children: opt.description })
|
|
1606
|
-
] })
|
|
1607
|
-
] }),
|
|
1608
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 shrink-0" })
|
|
1609
|
-
]
|
|
1610
|
-
},
|
|
1611
|
-
opt.value
|
|
1612
|
-
);
|
|
1613
|
-
}) })
|
|
1614
|
-
] })
|
|
1615
|
-
}
|
|
1616
|
-
) });
|
|
2252
|
+
opt.value
|
|
2253
|
+
);
|
|
2254
|
+
}) })
|
|
2255
|
+
] })
|
|
2256
|
+
}
|
|
2257
|
+
)
|
|
2258
|
+
] });
|
|
1617
2259
|
}
|
|
1618
2260
|
);
|
|
1619
2261
|
Combobox.displayName = "Combobox";
|
|
@@ -1630,28 +2272,69 @@ var MultiSelect = React3.forwardRef(
|
|
|
1630
2272
|
className
|
|
1631
2273
|
}, ref) => {
|
|
1632
2274
|
const [internalVal, setInternalVal] = React3.useState(defaultValue);
|
|
1633
|
-
const
|
|
2275
|
+
const isControlled = controlledVal !== void 0;
|
|
2276
|
+
const selectedValues = isControlled ? controlledVal : internalVal;
|
|
1634
2277
|
const [search, setSearch] = React3.useState("");
|
|
1635
2278
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
2279
|
+
const [highlightedIndex, setHighlightedIndex] = React3.useState(0);
|
|
1636
2280
|
const filteredOptions = options.filter(
|
|
1637
2281
|
(opt) => opt.label.toLowerCase().includes(search.toLowerCase()) || opt.value.toLowerCase().includes(search.toLowerCase())
|
|
1638
2282
|
);
|
|
1639
2283
|
const toggleOption = (val) => {
|
|
2284
|
+
const opt = options.find((o) => o.value === val);
|
|
2285
|
+
if (opt?.disabled) return;
|
|
1640
2286
|
const next = selectedValues.includes(val) ? selectedValues.filter((v) => v !== val) : [...selectedValues, val];
|
|
1641
|
-
|
|
2287
|
+
if (!isControlled) {
|
|
2288
|
+
setInternalVal(next);
|
|
2289
|
+
}
|
|
1642
2290
|
onChange?.(next);
|
|
1643
2291
|
};
|
|
1644
2292
|
const removeTag = (val, e) => {
|
|
1645
2293
|
e?.stopPropagation?.();
|
|
1646
2294
|
const next = selectedValues.filter((v) => v !== val);
|
|
1647
|
-
|
|
2295
|
+
if (!isControlled) {
|
|
2296
|
+
setInternalVal(next);
|
|
2297
|
+
}
|
|
1648
2298
|
onChange?.(next);
|
|
1649
2299
|
};
|
|
1650
2300
|
const clearAll = (e) => {
|
|
1651
2301
|
e?.stopPropagation?.();
|
|
1652
|
-
|
|
2302
|
+
if (!isControlled) {
|
|
2303
|
+
setInternalVal([]);
|
|
2304
|
+
}
|
|
1653
2305
|
onChange?.([]);
|
|
1654
2306
|
};
|
|
2307
|
+
const handleSearchKeyDown = (e) => {
|
|
2308
|
+
if (filteredOptions.length === 0) return;
|
|
2309
|
+
if (e.key === "ArrowDown") {
|
|
2310
|
+
e.preventDefault();
|
|
2311
|
+
setHighlightedIndex((prev) => {
|
|
2312
|
+
let next = (prev + 1) % filteredOptions.length;
|
|
2313
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
2314
|
+
next = (next + 1) % filteredOptions.length;
|
|
2315
|
+
}
|
|
2316
|
+
return next;
|
|
2317
|
+
});
|
|
2318
|
+
} else if (e.key === "ArrowUp") {
|
|
2319
|
+
e.preventDefault();
|
|
2320
|
+
setHighlightedIndex((prev) => {
|
|
2321
|
+
let next = (prev - 1 + filteredOptions.length) % filteredOptions.length;
|
|
2322
|
+
while (filteredOptions[next]?.disabled && next !== prev) {
|
|
2323
|
+
next = (next - 1 + filteredOptions.length) % filteredOptions.length;
|
|
2324
|
+
}
|
|
2325
|
+
return next;
|
|
2326
|
+
});
|
|
2327
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
2328
|
+
e.preventDefault();
|
|
2329
|
+
const current = filteredOptions[highlightedIndex];
|
|
2330
|
+
if (current && !current.disabled) {
|
|
2331
|
+
toggleOption(current.value);
|
|
2332
|
+
}
|
|
2333
|
+
} else if (e.key === "Escape") {
|
|
2334
|
+
e.preventDefault();
|
|
2335
|
+
setIsOpen(false);
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
1655
2338
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1656
2339
|
Popover,
|
|
1657
2340
|
{
|
|
@@ -1660,9 +2343,19 @@ var MultiSelect = React3.forwardRef(
|
|
|
1660
2343
|
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1661
2344
|
"div",
|
|
1662
2345
|
{
|
|
2346
|
+
tabIndex: disabled ? -1 : 0,
|
|
2347
|
+
role: "combobox",
|
|
2348
|
+
"aria-expanded": isOpen,
|
|
2349
|
+
"aria-haspopup": "listbox",
|
|
1663
2350
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
2351
|
+
onKeyDown: (e) => {
|
|
2352
|
+
if ((e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && !disabled) {
|
|
2353
|
+
e.preventDefault();
|
|
2354
|
+
setIsOpen(true);
|
|
2355
|
+
}
|
|
2356
|
+
},
|
|
1664
2357
|
className: cn(
|
|
1665
|
-
"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",
|
|
2358
|
+
"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)]",
|
|
1666
2359
|
disabled ? "opacity-50 cursor-not-allowed border-[var(--ui-border-subtle)]" : "border-[var(--ui-border-default)] hover:border-[var(--ui-border-strong)]"
|
|
1667
2360
|
),
|
|
1668
2361
|
children: [
|
|
@@ -1691,7 +2384,7 @@ var MultiSelect = React3.forwardRef(
|
|
|
1691
2384
|
"span",
|
|
1692
2385
|
{
|
|
1693
2386
|
onClick: clearAll,
|
|
1694
|
-
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors",
|
|
2387
|
+
className: "p-1 rounded-full hover:bg-[var(--ui-bg-tertiary)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer",
|
|
1695
2388
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
|
|
1696
2389
|
}
|
|
1697
2390
|
),
|
|
@@ -1710,20 +2403,30 @@ var MultiSelect = React3.forwardRef(
|
|
|
1710
2403
|
autoFocus: true,
|
|
1711
2404
|
placeholder: searchPlaceholder,
|
|
1712
2405
|
value: search,
|
|
1713
|
-
onChange: (e) =>
|
|
2406
|
+
onChange: (e) => {
|
|
2407
|
+
setSearch(e.target.value);
|
|
2408
|
+
setHighlightedIndex(0);
|
|
2409
|
+
},
|
|
2410
|
+
onKeyDown: handleSearchKeyDown,
|
|
1714
2411
|
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)]"
|
|
1715
2412
|
}
|
|
1716
2413
|
)
|
|
1717
2414
|
] }),
|
|
1718
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt) => {
|
|
2415
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto space-y-0.5", role: "listbox", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 text-center text-xs text-[var(--ui-text-muted)]", children: "No options found." }) : filteredOptions.map((opt, index) => {
|
|
1719
2416
|
const isChecked = selectedValues.includes(opt.value);
|
|
2417
|
+
const isHighlighted = highlightedIndex === index;
|
|
1720
2418
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1721
2419
|
"div",
|
|
1722
2420
|
{
|
|
1723
|
-
|
|
2421
|
+
role: "option",
|
|
2422
|
+
"aria-selected": isChecked,
|
|
2423
|
+
"aria-disabled": opt.disabled,
|
|
2424
|
+
onClick: () => !opt.disabled && toggleOption(opt.value),
|
|
1724
2425
|
className: cn(
|
|
1725
|
-
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium
|
|
1726
|
-
|
|
2426
|
+
"flex items-center justify-between px-3 py-2 rounded-xl text-xs font-medium transition-colors select-none",
|
|
2427
|
+
opt.disabled ? "opacity-40 cursor-not-allowed text-[var(--ui-text-muted)]" : "cursor-pointer",
|
|
2428
|
+
isChecked && "text-[var(--ui-primary)] font-semibold",
|
|
2429
|
+
isHighlighted && !opt.disabled && "bg-[var(--ui-primary)]/15"
|
|
1727
2430
|
),
|
|
1728
2431
|
children: [
|
|
1729
2432
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
|
|
@@ -1841,26 +2544,58 @@ var FileUpload = React3.forwardRef(
|
|
|
1841
2544
|
const [isDragOver, setIsDragOver] = React3.useState(false);
|
|
1842
2545
|
const [fileList, setFileList] = React3.useState([]);
|
|
1843
2546
|
const inputRef = React3.useRef(null);
|
|
2547
|
+
const fileListRef = React3.useRef(fileList);
|
|
2548
|
+
fileListRef.current = fileList;
|
|
2549
|
+
React3.useEffect(() => {
|
|
2550
|
+
return () => {
|
|
2551
|
+
fileListRef.current.forEach((item) => {
|
|
2552
|
+
if (item.previewUrl) {
|
|
2553
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
};
|
|
2557
|
+
}, []);
|
|
2558
|
+
const matchesAccept = (file, acceptStr) => {
|
|
2559
|
+
if (!acceptStr) return true;
|
|
2560
|
+
const patterns = acceptStr.split(",").map((p) => p.trim().toLowerCase());
|
|
2561
|
+
const fileName = file.name.toLowerCase();
|
|
2562
|
+
const fileType = file.type.toLowerCase();
|
|
2563
|
+
return patterns.some((p) => {
|
|
2564
|
+
if (p.startsWith(".")) {
|
|
2565
|
+
return fileName.endsWith(p);
|
|
2566
|
+
}
|
|
2567
|
+
if (p.endsWith("/*")) {
|
|
2568
|
+
const baseType = p.replace("/*", "");
|
|
2569
|
+
return fileType.startsWith(baseType);
|
|
2570
|
+
}
|
|
2571
|
+
return fileType === p;
|
|
2572
|
+
});
|
|
2573
|
+
};
|
|
1844
2574
|
const processFiles = (incoming) => {
|
|
1845
2575
|
if (!incoming) return;
|
|
1846
2576
|
const validFiles = [];
|
|
1847
2577
|
const newUploaded = [];
|
|
1848
2578
|
Array.from(incoming).forEach((file) => {
|
|
1849
2579
|
if (fileList.length + newUploaded.length >= maxFiles) return;
|
|
2580
|
+
const isAccepted = matchesAccept(file, accept);
|
|
1850
2581
|
const isOversized = file.size > maxSizeMB * 1024 * 1024;
|
|
2582
|
+
const hasError = !isAccepted || isOversized;
|
|
2583
|
+
let errorMsg;
|
|
2584
|
+
if (!isAccepted) errorMsg = `File type not supported (${accept})`;
|
|
2585
|
+
else if (isOversized) errorMsg = `File exceeds ${maxSizeMB}MB limit`;
|
|
1851
2586
|
const fileObj = {
|
|
1852
2587
|
id: `${file.name}-${Date.now()}-${Math.random()}`,
|
|
1853
2588
|
file,
|
|
1854
2589
|
name: file.name,
|
|
1855
2590
|
size: file.size,
|
|
1856
2591
|
type: file.type,
|
|
1857
|
-
progress:
|
|
1858
|
-
status:
|
|
1859
|
-
errorMessage:
|
|
1860
|
-
previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
|
|
2592
|
+
progress: hasError ? 0 : 100,
|
|
2593
|
+
status: hasError ? "error" : "completed",
|
|
2594
|
+
errorMessage: errorMsg,
|
|
2595
|
+
previewUrl: file.type.startsWith("image/") && !hasError ? URL.createObjectURL(file) : void 0
|
|
1861
2596
|
};
|
|
1862
2597
|
newUploaded.push(fileObj);
|
|
1863
|
-
if (!
|
|
2598
|
+
if (!hasError) validFiles.push(file);
|
|
1864
2599
|
});
|
|
1865
2600
|
setFileList((prev) => [...prev, ...newUploaded]);
|
|
1866
2601
|
if (validFiles.length > 0) {
|
|
@@ -1886,7 +2621,13 @@ var FileUpload = React3.forwardRef(
|
|
|
1886
2621
|
if (inputRef.current) inputRef.current.value = "";
|
|
1887
2622
|
};
|
|
1888
2623
|
const removeFile = (id) => {
|
|
1889
|
-
setFileList((prev) =>
|
|
2624
|
+
setFileList((prev) => {
|
|
2625
|
+
const item = prev.find((f) => f.id === id);
|
|
2626
|
+
if (item?.previewUrl) {
|
|
2627
|
+
URL.revokeObjectURL(item.previewUrl);
|
|
2628
|
+
}
|
|
2629
|
+
return prev.filter((f) => f.id !== id);
|
|
2630
|
+
});
|
|
1890
2631
|
onFileRemove?.(id);
|
|
1891
2632
|
};
|
|
1892
2633
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("w-full space-y-4", className), children: [
|
|
@@ -1994,8 +2735,12 @@ var NumberInput = React3.forwardRef(
|
|
|
1994
2735
|
className,
|
|
1995
2736
|
...props
|
|
1996
2737
|
}, ref) => {
|
|
1997
|
-
const [
|
|
1998
|
-
|
|
2738
|
+
const [value, setValue] = useControllableState({
|
|
2739
|
+
value: controlledVal,
|
|
2740
|
+
defaultValue,
|
|
2741
|
+
onChange,
|
|
2742
|
+
name: "NumberInput"
|
|
2743
|
+
});
|
|
1999
2744
|
const clamp = (num) => {
|
|
2000
2745
|
let clamped = Math.min(max, Math.max(min, num));
|
|
2001
2746
|
if (precision > 0) {
|
|
@@ -2006,14 +2751,12 @@ var NumberInput = React3.forwardRef(
|
|
|
2006
2751
|
const handleIncrement = () => {
|
|
2007
2752
|
if (disabled) return;
|
|
2008
2753
|
const next = clamp(value + step);
|
|
2009
|
-
|
|
2010
|
-
onChange?.(next);
|
|
2754
|
+
setValue(next);
|
|
2011
2755
|
};
|
|
2012
2756
|
const handleDecrement = () => {
|
|
2013
2757
|
if (disabled) return;
|
|
2014
2758
|
const next = clamp(value - step);
|
|
2015
|
-
|
|
2016
|
-
onChange?.(next);
|
|
2759
|
+
setValue(next);
|
|
2017
2760
|
};
|
|
2018
2761
|
const handleInputChange = (e) => {
|
|
2019
2762
|
const val = e.target.value;
|
|
@@ -2023,8 +2766,7 @@ var NumberInput = React3.forwardRef(
|
|
|
2023
2766
|
const parsed = parseFloat(val);
|
|
2024
2767
|
if (!isNaN(parsed)) {
|
|
2025
2768
|
const next = clamp(parsed);
|
|
2026
|
-
|
|
2027
|
-
onChange?.(next);
|
|
2769
|
+
setValue(next);
|
|
2028
2770
|
}
|
|
2029
2771
|
};
|
|
2030
2772
|
const handleKeyDown = (e) => {
|
|
@@ -2045,7 +2787,7 @@ var NumberInput = React3.forwardRef(
|
|
|
2045
2787
|
type: "button",
|
|
2046
2788
|
disabled: disabled || value <= min,
|
|
2047
2789
|
onClick: handleDecrement,
|
|
2048
|
-
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)]",
|
|
2790
|
+
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",
|
|
2049
2791
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3.5 h-3.5" })
|
|
2050
2792
|
}
|
|
2051
2793
|
),
|
|
@@ -2068,7 +2810,7 @@ var NumberInput = React3.forwardRef(
|
|
|
2068
2810
|
type: "button",
|
|
2069
2811
|
disabled: disabled || value >= max,
|
|
2070
2812
|
onClick: handleIncrement,
|
|
2071
|
-
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)]",
|
|
2813
|
+
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",
|
|
2072
2814
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3.5 h-3.5" })
|
|
2073
2815
|
}
|
|
2074
2816
|
)
|
|
@@ -2099,7 +2841,7 @@ var NumberInput = React3.forwardRef(
|
|
|
2099
2841
|
type: "button",
|
|
2100
2842
|
disabled: disabled || value >= max,
|
|
2101
2843
|
onClick: handleIncrement,
|
|
2102
|
-
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",
|
|
2844
|
+
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",
|
|
2103
2845
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3 h-3" })
|
|
2104
2846
|
}
|
|
2105
2847
|
),
|
|
@@ -2109,7 +2851,7 @@ var NumberInput = React3.forwardRef(
|
|
|
2109
2851
|
type: "button",
|
|
2110
2852
|
disabled: disabled || value <= min,
|
|
2111
2853
|
onClick: handleDecrement,
|
|
2112
|
-
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",
|
|
2854
|
+
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",
|
|
2113
2855
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "w-3 h-3" })
|
|
2114
2856
|
}
|
|
2115
2857
|
)
|
|
@@ -2138,19 +2880,27 @@ var CurrencyInput = React3.forwardRef(
|
|
|
2138
2880
|
}, ref) => {
|
|
2139
2881
|
const symbol = currencySymbol || (currency === "VND" ? "\u20AB" : currency === "EUR" ? "\u20AC" : "$");
|
|
2140
2882
|
const isSuffix = position === "suffix" || currency === "VND";
|
|
2141
|
-
const [
|
|
2142
|
-
|
|
2143
|
-
|
|
2883
|
+
const [value, setValue, isControlled] = useControllableState({
|
|
2884
|
+
value: controlledVal,
|
|
2885
|
+
defaultValue,
|
|
2886
|
+
onChange,
|
|
2887
|
+
name: "CurrencyInput"
|
|
2888
|
+
});
|
|
2889
|
+
const [internalInputString, setInternalInputString] = React3.useState(formatCurrencyString(value));
|
|
2144
2890
|
React3.useEffect(() => {
|
|
2145
|
-
|
|
2146
|
-
|
|
2891
|
+
if (!isControlled) {
|
|
2892
|
+
setInternalInputString(formatCurrencyString(value));
|
|
2893
|
+
}
|
|
2894
|
+
}, [value, isControlled]);
|
|
2895
|
+
const displayVal = isControlled ? formatCurrencyString(value) : internalInputString;
|
|
2147
2896
|
const handleInputChange = (e) => {
|
|
2148
2897
|
const raw = e.target.value.replace(/[^0-9.]/g, "");
|
|
2149
2898
|
const parsed = parseFloat(raw);
|
|
2150
2899
|
const num = isNaN(parsed) ? 0 : parsed;
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2900
|
+
if (!isControlled) {
|
|
2901
|
+
setInternalInputString(raw ? formatCurrencyString(num) : "");
|
|
2902
|
+
}
|
|
2903
|
+
setValue(num);
|
|
2154
2904
|
};
|
|
2155
2905
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative w-full flex items-center", className), children: [
|
|
2156
2906
|
!isSuffix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-3.5 text-sm font-bold text-[var(--ui-text-muted)] pointer-events-none font-mono", children: symbol }),
|
|
@@ -2246,6 +2996,7 @@ MaskedInput.displayName = "MaskedInput";
|
|
|
2246
2996
|
|
|
2247
2997
|
exports.Checkbox = Checkbox;
|
|
2248
2998
|
exports.CheckboxGroup = CheckboxGroup;
|
|
2999
|
+
exports.CheckboxGroupContext = CheckboxGroupContext;
|
|
2249
3000
|
exports.Combobox = Combobox;
|
|
2250
3001
|
exports.CurrencyInput = CurrencyInput;
|
|
2251
3002
|
exports.DatePicker = DatePicker;
|
|
@@ -2282,6 +3033,7 @@ exports.selectVariants = selectVariants;
|
|
|
2282
3033
|
exports.switchThumbVariants = switchThumbVariants;
|
|
2283
3034
|
exports.switchVariants = switchVariants;
|
|
2284
3035
|
exports.textareaVariants = textareaVariants;
|
|
3036
|
+
exports.useCheckboxGroup = useCheckboxGroup;
|
|
2285
3037
|
exports.useFormField = useFormField;
|
|
2286
3038
|
//# sourceMappingURL=index.cjs.map
|
|
2287
3039
|
//# sourceMappingURL=index.cjs.map
|