@dashforge/tw 0.9.0-beta → 0.10.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/dist/index.esm.js +1634 -256
- package/dist/src/components/Calendar/Calendar.d.ts +14 -0
- package/dist/src/components/Calendar/Calendar.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.types.d.ts +72 -0
- package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts +99 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts.map +1 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts +14 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts +88 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts +97 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +13 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +85 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts +183 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +7 -43
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +50 -55
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts +36 -94
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts +12 -0
- package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts +80 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts +103 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts.map +1 -0
- package/dist/src/index.d.ts +15 -3
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/components/Calendar/Calendar.test.tsx +75 -0
- package/src/components/Calendar/Calendar.tsx +264 -0
- package/src/components/Calendar/calendar.types.ts +65 -0
- package/src/components/Calendar/calendar.variants.ts +64 -0
- package/src/components/DatePicker/DatePicker.test.tsx +76 -0
- package/src/components/DatePicker/DatePicker.tsx +305 -0
- package/src/components/DatePicker/datePicker.types.ts +77 -0
- package/src/components/DatePicker/datePicker.variants.ts +50 -0
- package/src/components/DateRangePicker/DateRangePicker.test.tsx +92 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +510 -0
- package/src/components/DateRangePicker/dateRangePicker.types.ts +74 -0
- package/src/components/DateRangePicker/dateRangePicker.variants.ts +101 -0
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +60 -186
- package/src/components/DateTimePicker/DateTimePicker.tsx +209 -181
- package/src/components/DateTimePicker/dateTimePicker.types.ts +50 -54
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +22 -51
- package/src/components/TimePicker/TimePicker.test.tsx +65 -0
- package/src/components/TimePicker/TimePicker.tsx +274 -0
- package/src/components/TimePicker/timePicker.types.ts +69 -0
- package/src/components/TimePicker/timePicker.variants.ts +53 -0
- package/src/index.ts +38 -6
- package/tsconfig.lib.json +7 -4
- package/src/components/DateTimePicker/DateTimePicker.perf.test.tsx +0 -153
package/dist/index.esm.js
CHANGED
|
@@ -11,10 +11,11 @@ import { useDashFieldMeta } from '@dashforge/forms';
|
|
|
11
11
|
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
12
12
|
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
13
13
|
import * as RadixRadioGroup from '@radix-ui/react-radio-group';
|
|
14
|
+
import { useCalendar, resolveCalendarKey, formatLongDate, parseISODate, formatTime, generateTimeOptions, timeStringToMinutes, getTodayISODate, useDateRange } from '@dashforge/calendar-core';
|
|
15
|
+
import * as RadixPopover from '@radix-ui/react-popover';
|
|
14
16
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
15
17
|
import * as RadixTabs from '@radix-ui/react-tabs';
|
|
16
18
|
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
17
|
-
import * as RadixPopover from '@radix-ui/react-popover';
|
|
18
19
|
import * as RadixAccordion from '@radix-ui/react-accordion';
|
|
19
20
|
|
|
20
21
|
function _extends() {
|
|
@@ -3427,193 +3428,1610 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3427
3428
|
}
|
|
3428
3429
|
|
|
3429
3430
|
/**
|
|
3430
|
-
*
|
|
3431
|
+
* Structural slots for `<Calendar>`.
|
|
3432
|
+
*
|
|
3433
|
+
* Theme identity: every class is a bare neutral / brand token-backed
|
|
3434
|
+
* Tailwind class. The `dashforgePreset()` CSS-variable swap auto-inverts
|
|
3435
|
+
* the neutral palette, so there are NO `dark:` variants here (a `dark:` on
|
|
3436
|
+
* a neutral class would double-invert — see `_shared/themeIdentity.test.ts`).
|
|
3437
|
+
*/ const calendarVariants = tv({
|
|
3438
|
+
slots: {
|
|
3439
|
+
root: 'inline-flex w-fit flex-col gap-2 rounded-lg border border-neutral-200 bg-neutral-50 p-3 select-none',
|
|
3440
|
+
header: 'flex items-center justify-between gap-1',
|
|
3441
|
+
navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[1rem] text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:pointer-events-none disabled:opacity-40',
|
|
3442
|
+
monthLabel: 'flex-1 text-center text-sm font-semibold text-neutral-900',
|
|
3443
|
+
grid: 'flex flex-col gap-1 focus:outline-none',
|
|
3444
|
+
weekdayRow: 'grid grid-cols-7 gap-1',
|
|
3445
|
+
weekday: 'flex h-8 items-center justify-center text-xs font-semibold text-neutral-500',
|
|
3446
|
+
weekRow: 'grid grid-cols-7 gap-1'
|
|
3447
|
+
},
|
|
3448
|
+
variants: {
|
|
3449
|
+
disabled: {
|
|
3450
|
+
true: {
|
|
3451
|
+
root: 'opacity-60'
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
},
|
|
3455
|
+
defaultVariants: {
|
|
3456
|
+
disabled: false
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3459
|
+
/**
|
|
3460
|
+
* Per-day-cell recipe. Each of the 42 grid cells is rendered with its own
|
|
3461
|
+
* state (`siblingMonth` / `today` / `selected` / `disabled`).
|
|
3462
|
+
*/ const calendarDayVariants = tv({
|
|
3463
|
+
base: 'flex h-9 w-9 items-center justify-center rounded-md text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500',
|
|
3464
|
+
variants: {
|
|
3465
|
+
siblingMonth: {
|
|
3466
|
+
true: 'text-neutral-400'
|
|
3467
|
+
},
|
|
3468
|
+
today: {
|
|
3469
|
+
true: 'font-bold ring-1 ring-inset ring-primary-400'
|
|
3470
|
+
},
|
|
3471
|
+
selected: {
|
|
3472
|
+
true: 'bg-primary-600 text-white hover:bg-primary-600'
|
|
3473
|
+
},
|
|
3474
|
+
disabled: {
|
|
3475
|
+
true: 'cursor-default text-neutral-300 opacity-60 hover:bg-transparent'
|
|
3476
|
+
}
|
|
3477
|
+
},
|
|
3478
|
+
compoundVariants: [
|
|
3479
|
+
// A selected cell drops the "today" ring — the solid fill is enough.
|
|
3480
|
+
{
|
|
3481
|
+
selected: true,
|
|
3482
|
+
today: true,
|
|
3483
|
+
class: 'ring-0'
|
|
3484
|
+
}
|
|
3485
|
+
],
|
|
3486
|
+
defaultVariants: {
|
|
3487
|
+
siblingMonth: false,
|
|
3488
|
+
today: false,
|
|
3489
|
+
selected: false,
|
|
3490
|
+
disabled: false
|
|
3491
|
+
}
|
|
3492
|
+
});
|
|
3493
|
+
|
|
3494
|
+
// Inline 16×16 stroke chevrons — no icon dependency (tw convention).
|
|
3495
|
+
function ChevronLeftIcon$1() {
|
|
3496
|
+
return jsx("svg", {
|
|
3497
|
+
width: "1em",
|
|
3498
|
+
height: "1em",
|
|
3499
|
+
viewBox: "0 0 16 16",
|
|
3500
|
+
fill: "none",
|
|
3501
|
+
"aria-hidden": "true",
|
|
3502
|
+
children: jsx("path", {
|
|
3503
|
+
d: "M10 4 6 8l4 4",
|
|
3504
|
+
stroke: "currentColor",
|
|
3505
|
+
strokeWidth: "1.5",
|
|
3506
|
+
strokeLinecap: "round",
|
|
3507
|
+
strokeLinejoin: "round"
|
|
3508
|
+
})
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
function ChevronRightIcon$2() {
|
|
3512
|
+
return jsx("svg", {
|
|
3513
|
+
width: "1em",
|
|
3514
|
+
height: "1em",
|
|
3515
|
+
viewBox: "0 0 16 16",
|
|
3516
|
+
fill: "none",
|
|
3517
|
+
"aria-hidden": "true",
|
|
3518
|
+
children: jsx("path", {
|
|
3519
|
+
d: "m6 4 4 4-4 4",
|
|
3520
|
+
stroke: "currentColor",
|
|
3521
|
+
strokeWidth: "1.5",
|
|
3522
|
+
strokeLinecap: "round",
|
|
3523
|
+
strokeLinejoin: "round"
|
|
3524
|
+
})
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
/**
|
|
3528
|
+
* `<Calendar>` — a standalone, inline month-grid date picker.
|
|
3529
|
+
*
|
|
3530
|
+
* The Tailwind presentation layer over the headless `useCalendar` engine
|
|
3531
|
+
* from `@dashforge/calendar-core` (the same engine the MUI `@dashforge/ui`
|
|
3532
|
+
* calendar consumes — only the skin differs). Implements the WCAG grid
|
|
3533
|
+
* pattern with roving tab-index focus.
|
|
3534
|
+
*
|
|
3535
|
+
* It is a primitive: no form-bridge / RBAC integration — use `<DatePicker>`
|
|
3536
|
+
* for a form-bound date field.
|
|
3537
|
+
*/ function Calendar(props) {
|
|
3538
|
+
var _slotProps_root, _slotProps_header, _slotProps_grid;
|
|
3539
|
+
const { value, defaultValue, onChange, month, year, defaultMonth, defaultYear, onMonthChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, today, disabled = false, autoFocus = false, sx, slotProps, testId, 'aria-label': ariaLabel = 'Calendar' } = props;
|
|
3540
|
+
const isValueControlled = value !== undefined;
|
|
3541
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
3542
|
+
const selectedValue = isValueControlled ? value : internalValue;
|
|
3543
|
+
const handleSelect = useCallback((iso)=>{
|
|
3544
|
+
if (!isValueControlled) {
|
|
3545
|
+
setInternalValue(iso);
|
|
3546
|
+
}
|
|
3547
|
+
onChange == null ? void 0 : onChange(iso);
|
|
3548
|
+
}, [
|
|
3549
|
+
isValueControlled,
|
|
3550
|
+
onChange
|
|
3551
|
+
]);
|
|
3552
|
+
const calendar = useCalendar({
|
|
3553
|
+
selected: selectedValue,
|
|
3554
|
+
month,
|
|
3555
|
+
year,
|
|
3556
|
+
defaultMonth,
|
|
3557
|
+
defaultYear,
|
|
3558
|
+
onMonthChange,
|
|
3559
|
+
minDate,
|
|
3560
|
+
maxDate,
|
|
3561
|
+
disabledDates,
|
|
3562
|
+
isDateDisabled,
|
|
3563
|
+
weekStartDay,
|
|
3564
|
+
locale,
|
|
3565
|
+
today,
|
|
3566
|
+
onSelect: handleSelect
|
|
3567
|
+
});
|
|
3568
|
+
// Roving focus: the active day cell holds DOM focus; it is moved only
|
|
3569
|
+
// after a keyboard interaction (or on mount when `autoFocus`). `focus`
|
|
3570
|
+
// uses `preventScroll` so opening the calendar inside a popup never
|
|
3571
|
+
// scroll-jumps the page before the popup is positioned.
|
|
3572
|
+
const focusedCellRef = useRef(null);
|
|
3573
|
+
const shouldFocusRef = useRef(autoFocus);
|
|
3574
|
+
useEffect(()=>{
|
|
3575
|
+
if (shouldFocusRef.current && focusedCellRef.current) {
|
|
3576
|
+
focusedCellRef.current.focus({
|
|
3577
|
+
preventScroll: true
|
|
3578
|
+
});
|
|
3579
|
+
shouldFocusRef.current = false;
|
|
3580
|
+
}
|
|
3581
|
+
}, [
|
|
3582
|
+
calendar.focusedDate
|
|
3583
|
+
]);
|
|
3584
|
+
const handleKeyDown = useCallback((event)=>{
|
|
3585
|
+
if (disabled) {
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
const action = resolveCalendarKey(event.key);
|
|
3589
|
+
if (action === null) {
|
|
3590
|
+
return;
|
|
3591
|
+
}
|
|
3592
|
+
event.preventDefault();
|
|
3593
|
+
shouldFocusRef.current = true;
|
|
3594
|
+
switch(action){
|
|
3595
|
+
case 'select':
|
|
3596
|
+
calendar.selectDate(calendar.focusedDate);
|
|
3597
|
+
break;
|
|
3598
|
+
case 'monthForward':
|
|
3599
|
+
calendar.goToNextMonth();
|
|
3600
|
+
break;
|
|
3601
|
+
case 'monthBackward':
|
|
3602
|
+
calendar.goToPreviousMonth();
|
|
3603
|
+
break;
|
|
3604
|
+
default:
|
|
3605
|
+
calendar.navigate(action);
|
|
3606
|
+
}
|
|
3607
|
+
}, [
|
|
3608
|
+
disabled,
|
|
3609
|
+
calendar
|
|
3610
|
+
]);
|
|
3611
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
3612
|
+
const v = calendarVariants({
|
|
3613
|
+
disabled
|
|
3614
|
+
});
|
|
3615
|
+
return jsxs("div", {
|
|
3616
|
+
"data-testid": testId,
|
|
3617
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
3618
|
+
children: [
|
|
3619
|
+
jsxs("div", {
|
|
3620
|
+
className: cn(v.header(), slotProps == null ? void 0 : (_slotProps_header = slotProps.header) == null ? void 0 : _slotProps_header.className),
|
|
3621
|
+
children: [
|
|
3622
|
+
jsx("button", {
|
|
3623
|
+
type: "button",
|
|
3624
|
+
className: v.navButton(),
|
|
3625
|
+
disabled: disabled,
|
|
3626
|
+
"aria-label": "Previous month",
|
|
3627
|
+
onClick: ()=>{
|
|
3628
|
+
calendar.goToPreviousMonth();
|
|
3629
|
+
},
|
|
3630
|
+
children: jsx(ChevronLeftIcon$1, {})
|
|
3631
|
+
}),
|
|
3632
|
+
jsx("span", {
|
|
3633
|
+
"aria-live": "polite",
|
|
3634
|
+
className: v.monthLabel(),
|
|
3635
|
+
children: calendar.monthLabel
|
|
3636
|
+
}),
|
|
3637
|
+
jsx("button", {
|
|
3638
|
+
type: "button",
|
|
3639
|
+
className: v.navButton(),
|
|
3640
|
+
disabled: disabled,
|
|
3641
|
+
"aria-label": "Next month",
|
|
3642
|
+
onClick: ()=>{
|
|
3643
|
+
calendar.goToNextMonth();
|
|
3644
|
+
},
|
|
3645
|
+
children: jsx(ChevronRightIcon$2, {})
|
|
3646
|
+
})
|
|
3647
|
+
]
|
|
3648
|
+
}),
|
|
3649
|
+
jsxs("div", {
|
|
3650
|
+
role: "grid",
|
|
3651
|
+
"aria-label": ariaLabel,
|
|
3652
|
+
onKeyDown: handleKeyDown,
|
|
3653
|
+
className: cn(v.grid(), slotProps == null ? void 0 : (_slotProps_grid = slotProps.grid) == null ? void 0 : _slotProps_grid.className),
|
|
3654
|
+
children: [
|
|
3655
|
+
jsx("div", {
|
|
3656
|
+
role: "row",
|
|
3657
|
+
className: v.weekdayRow(),
|
|
3658
|
+
children: calendar.weekdayNames.map((dayName, index)=>jsx("span", {
|
|
3659
|
+
role: "columnheader",
|
|
3660
|
+
className: v.weekday(),
|
|
3661
|
+
children: dayName
|
|
3662
|
+
}, `weekday-${String(index)}`))
|
|
3663
|
+
}),
|
|
3664
|
+
calendar.weeks.map((week)=>jsx("div", {
|
|
3665
|
+
role: "row",
|
|
3666
|
+
className: v.weekRow(),
|
|
3667
|
+
children: week.days.map((day)=>{
|
|
3668
|
+
var _slotProps_day;
|
|
3669
|
+
const isActiveCell = day.iso === calendar.focusedDate;
|
|
3670
|
+
return jsx("span", {
|
|
3671
|
+
role: "gridcell",
|
|
3672
|
+
className: "flex",
|
|
3673
|
+
children: jsx("button", {
|
|
3674
|
+
type: "button",
|
|
3675
|
+
ref: isActiveCell ? focusedCellRef : null,
|
|
3676
|
+
tabIndex: isActiveCell ? 0 : -1,
|
|
3677
|
+
disabled: disabled,
|
|
3678
|
+
"aria-label": formatLongDate({
|
|
3679
|
+
year: day.year,
|
|
3680
|
+
month: day.month,
|
|
3681
|
+
day: day.day
|
|
3682
|
+
}, resolvedLocale),
|
|
3683
|
+
"aria-current": day.isToday ? 'date' : undefined,
|
|
3684
|
+
"aria-pressed": day.isSelected,
|
|
3685
|
+
"aria-disabled": day.isDisabled,
|
|
3686
|
+
onClick: ()=>{
|
|
3687
|
+
calendar.selectDate(day.iso);
|
|
3688
|
+
},
|
|
3689
|
+
className: cn(calendarDayVariants({
|
|
3690
|
+
siblingMonth: day.isSiblingMonth,
|
|
3691
|
+
today: day.isToday,
|
|
3692
|
+
selected: day.isSelected,
|
|
3693
|
+
disabled: day.isDisabled
|
|
3694
|
+
}), slotProps == null ? void 0 : (_slotProps_day = slotProps.day) == null ? void 0 : _slotProps_day.className),
|
|
3695
|
+
children: day.day
|
|
3696
|
+
})
|
|
3697
|
+
}, day.iso);
|
|
3698
|
+
})
|
|
3699
|
+
}, week.key))
|
|
3700
|
+
]
|
|
3701
|
+
})
|
|
3702
|
+
]
|
|
3703
|
+
});
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
const popoverVariants = tv({
|
|
3707
|
+
slots: {
|
|
3708
|
+
content: [
|
|
3709
|
+
// Neutral border auto-inverts via CSS-var swap.
|
|
3710
|
+
'z-50 rounded-md border border-neutral-200',
|
|
3711
|
+
// bg-white hardcoded (no auto-invert) so `dark:` is required.
|
|
3712
|
+
// Target `dark:bg-neutral-100` resolves to #171717 in dark mode
|
|
3713
|
+
// (one elevation tier above the page surface).
|
|
3714
|
+
'bg-white dark:bg-neutral-100 p-4 shadow-lg',
|
|
3715
|
+
// Text auto-inverts.
|
|
3716
|
+
'text-sm text-neutral-900',
|
|
3717
|
+
'focus:outline-none',
|
|
3718
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
3719
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
3720
|
+
'motion-reduce:transition-none motion-reduce:duration-0'
|
|
3721
|
+
],
|
|
3722
|
+
// Arrow follows content bg: `fill-white` in light + matching dark target.
|
|
3723
|
+
arrow: 'fill-white dark:fill-neutral-100'
|
|
3724
|
+
}
|
|
3725
|
+
});
|
|
3726
|
+
|
|
3727
|
+
/**
|
|
3728
|
+
* Dashforge TW `<Popover>` — clickable floating content panel.
|
|
3431
3729
|
*
|
|
3432
|
-
*
|
|
3433
|
-
*
|
|
3730
|
+
* Built on `@radix-ui/react-popover`:
|
|
3731
|
+
* - Focus trap inside the popover (Tab cycles within).
|
|
3732
|
+
* - Outside-click + Escape dismiss.
|
|
3733
|
+
* - APG dialog-like semantics (`role="dialog"` on content).
|
|
3734
|
+
* - Portal-rendered.
|
|
3434
3735
|
*
|
|
3435
|
-
*
|
|
3736
|
+
* Use `<Popover>` for menus / pickers / settings panels with
|
|
3737
|
+
* interactive children. Use `<Tooltip>` for read-only hints.
|
|
3738
|
+
*/ function Popover(props) {
|
|
3739
|
+
var _slotProps_content, _slotProps_arrow;
|
|
3740
|
+
const { children, content, side = 'bottom', align = 'center', showArrow = false, open, onOpenChange, sideOffset = 8, width, slotProps } = props;
|
|
3741
|
+
const v = popoverVariants();
|
|
3742
|
+
return jsxs(RadixPopover.Root, {
|
|
3743
|
+
open: open,
|
|
3744
|
+
onOpenChange: onOpenChange,
|
|
3745
|
+
children: [
|
|
3746
|
+
jsx(RadixPopover.Trigger, {
|
|
3747
|
+
asChild: true,
|
|
3748
|
+
children: children
|
|
3749
|
+
}),
|
|
3750
|
+
jsx(RadixPopover.Portal, {
|
|
3751
|
+
children: jsxs(RadixPopover.Content, {
|
|
3752
|
+
side: side,
|
|
3753
|
+
align: align,
|
|
3754
|
+
sideOffset: sideOffset,
|
|
3755
|
+
style: width != null ? {
|
|
3756
|
+
width
|
|
3757
|
+
} : undefined,
|
|
3758
|
+
className: cn(v.content(), slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
|
|
3759
|
+
children: [
|
|
3760
|
+
content,
|
|
3761
|
+
showArrow && jsx(RadixPopover.Arrow, {
|
|
3762
|
+
className: cn(v.arrow(), slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
|
|
3763
|
+
})
|
|
3764
|
+
]
|
|
3765
|
+
})
|
|
3766
|
+
})
|
|
3767
|
+
]
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
/**
|
|
3772
|
+
* Slots for `<DatePicker>` — a bridge-integrated date field whose trigger
|
|
3773
|
+
* is a `<button>` styled as an input, opening a `<Calendar>` popover.
|
|
3436
3774
|
*
|
|
3437
|
-
*
|
|
3438
|
-
*
|
|
3439
|
-
*
|
|
3440
|
-
|
|
3441
|
-
* on these input types).
|
|
3442
|
-
* - The native picker indicator (calendar / clock icon) is
|
|
3443
|
-
* browser-supplied; we ensure it stays visible in dark mode via
|
|
3444
|
-
* `color-scheme: light dark` on the input (set inline below).
|
|
3445
|
-
*/ const dateTimePickerVariants = tv({
|
|
3775
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
3776
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
3777
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
3778
|
+
*/ const datePickerVariants = tv({
|
|
3446
3779
|
slots: {
|
|
3447
|
-
root: 'flex',
|
|
3780
|
+
root: 'flex flex-col',
|
|
3448
3781
|
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
3449
3782
|
requiredMark: 'text-danger-500 ml-0.5',
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3783
|
+
trigger: 'flex items-center gap-2 h-10 min-w-[12rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
|
|
3784
|
+
value: 'flex-1 truncate text-left',
|
|
3785
|
+
placeholder: 'flex-1 truncate text-left text-neutral-400',
|
|
3786
|
+
icon: 'shrink-0 text-[1rem] text-neutral-500',
|
|
3787
|
+
helperText: 'mt-1 text-sm text-neutral-600',
|
|
3788
|
+
errorText: 'mt-1 text-sm text-danger-600'
|
|
3789
|
+
},
|
|
3790
|
+
variants: {
|
|
3791
|
+
layout: {
|
|
3792
|
+
stacked: {},
|
|
3793
|
+
inline: {
|
|
3794
|
+
root: 'flex-row items-start gap-3',
|
|
3795
|
+
label: 'mb-0 pt-2.5 whitespace-nowrap shrink-0'
|
|
3796
|
+
}
|
|
3797
|
+
},
|
|
3798
|
+
error: {
|
|
3799
|
+
true: {
|
|
3800
|
+
trigger: 'border-danger-500 hover:border-danger-500 focus-visible:border-danger-500 focus-visible:ring-danger-500/30'
|
|
3801
|
+
}
|
|
3802
|
+
},
|
|
3803
|
+
fullWidth: {
|
|
3804
|
+
true: {
|
|
3805
|
+
root: 'w-full',
|
|
3806
|
+
trigger: 'w-full min-w-0'
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
},
|
|
3810
|
+
defaultVariants: {
|
|
3811
|
+
layout: 'stacked',
|
|
3812
|
+
error: false,
|
|
3813
|
+
fullWidth: false
|
|
3814
|
+
}
|
|
3815
|
+
});
|
|
3816
|
+
|
|
3817
|
+
// Inline 16×16 stroke calendar glyph — no icon dependency (tw convention).
|
|
3818
|
+
function CalendarIcon$2() {
|
|
3819
|
+
return jsxs("svg", {
|
|
3820
|
+
width: "1em",
|
|
3821
|
+
height: "1em",
|
|
3822
|
+
viewBox: "0 0 16 16",
|
|
3823
|
+
fill: "none",
|
|
3824
|
+
"aria-hidden": "true",
|
|
3825
|
+
children: [
|
|
3826
|
+
jsx("rect", {
|
|
3827
|
+
x: "2.5",
|
|
3828
|
+
y: "3.5",
|
|
3829
|
+
width: "11",
|
|
3830
|
+
height: "10",
|
|
3831
|
+
rx: "1.5",
|
|
3832
|
+
stroke: "currentColor",
|
|
3833
|
+
strokeWidth: "1.5"
|
|
3834
|
+
}),
|
|
3835
|
+
jsx("path", {
|
|
3836
|
+
d: "M2.5 6.5h11M5.5 2v3M10.5 2v3",
|
|
3837
|
+
stroke: "currentColor",
|
|
3838
|
+
strokeWidth: "1.5",
|
|
3839
|
+
strokeLinecap: "round"
|
|
3840
|
+
})
|
|
3841
|
+
]
|
|
3842
|
+
});
|
|
3843
|
+
}
|
|
3844
|
+
/**
|
|
3845
|
+
* Formats a stored ISO date for the trigger display, localized to a medium
|
|
3846
|
+
* date style (e.g. `"May 20, 2026"`). Formatting is done in UTC so the
|
|
3847
|
+
* displayed day always matches the stored `YYYY-MM-DD`.
|
|
3848
|
+
*/ function formatDisplayDate(iso, locale) {
|
|
3849
|
+
if (iso == null || iso === '') {
|
|
3850
|
+
return '';
|
|
3851
|
+
}
|
|
3852
|
+
const parts = parseISODate(iso);
|
|
3853
|
+
if (parts === null) {
|
|
3854
|
+
return iso;
|
|
3855
|
+
}
|
|
3856
|
+
return new Intl.DateTimeFormat(locale, {
|
|
3857
|
+
dateStyle: 'medium',
|
|
3858
|
+
timeZone: 'UTC'
|
|
3859
|
+
}).format(Date.UTC(parts.year, parts.month - 1, parts.day));
|
|
3860
|
+
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Dashforge TW `<DatePicker>` — a form-bound single-date field.
|
|
3863
|
+
*
|
|
3864
|
+
* A read-only trigger button paired with a `<Calendar>` popover (Radix
|
|
3865
|
+
* Popover — toggle / Escape / outside-click / focus-trap are Radix-managed).
|
|
3866
|
+
* Integrates with the Dashforge form bridge + RBAC, mirroring the other
|
|
3867
|
+
* `@dashforge/tw` form fields.
|
|
3868
|
+
*
|
|
3869
|
+
* Storage contract: an ISO `YYYY-MM-DD` string, or `null` — shared with the
|
|
3870
|
+
* MUI `@dashforge/ui` `DatePicker`.
|
|
3871
|
+
*/ function DatePicker(props) {
|
|
3872
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_trigger, _slotProps_errorText, _slotProps_helperText;
|
|
3873
|
+
const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, fullWidth, sx, slotProps, testId } = props;
|
|
3874
|
+
// ───── Hooks (unconditional, before any early return) ─────
|
|
3875
|
+
const bridge = useContext(DashFormContext);
|
|
3876
|
+
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
3877
|
+
const fieldMeta = useDashFieldMeta(name);
|
|
3878
|
+
const accessState = useAccessState(access);
|
|
3879
|
+
const fieldId = useId();
|
|
3880
|
+
const helperId = `${fieldId}-help`;
|
|
3881
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
3882
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
3883
|
+
// StrictMode-safe unregister-on-unmount.
|
|
3884
|
+
const unregisterRef = useRef({
|
|
3885
|
+
bridge,
|
|
3886
|
+
name
|
|
3887
|
+
});
|
|
3888
|
+
unregisterRef.current = {
|
|
3889
|
+
bridge,
|
|
3890
|
+
name
|
|
3891
|
+
};
|
|
3892
|
+
const isMountedRef = useRef(false);
|
|
3893
|
+
useEffect(()=>{
|
|
3894
|
+
isMountedRef.current = true;
|
|
3895
|
+
return ()=>{
|
|
3896
|
+
isMountedRef.current = false;
|
|
3897
|
+
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
3898
|
+
queueMicrotask(()=>{
|
|
3899
|
+
var _cap_unregister;
|
|
3900
|
+
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
3901
|
+
});
|
|
3902
|
+
};
|
|
3903
|
+
}, []);
|
|
3904
|
+
// ───── Derived ─────
|
|
3905
|
+
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
3906
|
+
const effectiveReadOnly = accessState.readonly;
|
|
3907
|
+
const isInteractive = !effectiveDisabled && !effectiveReadOnly;
|
|
3908
|
+
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
3909
|
+
let resolvedError = error;
|
|
3910
|
+
let resolvedHelperText = helperText;
|
|
3911
|
+
let registration = null;
|
|
3912
|
+
let resolvedValue;
|
|
3913
|
+
if (isFormMode && bridge) {
|
|
3914
|
+
registration = bridge.register(name, rules);
|
|
3915
|
+
const validation = resolveValidationState(name, bridge, error, helperText);
|
|
3916
|
+
resolvedError = validation.error;
|
|
3917
|
+
resolvedHelperText = validation.helperText;
|
|
3918
|
+
if (explicitValue !== undefined) {
|
|
3919
|
+
resolvedValue = explicitValue;
|
|
3920
|
+
} else {
|
|
3921
|
+
const bv = fieldMeta.value;
|
|
3922
|
+
resolvedValue = bv == null ? null : String(bv);
|
|
3923
|
+
}
|
|
3924
|
+
} else if (explicitValue !== undefined) {
|
|
3925
|
+
resolvedValue = explicitValue;
|
|
3926
|
+
} else {
|
|
3927
|
+
resolvedValue = internalValue;
|
|
3928
|
+
}
|
|
3929
|
+
// RHF threads its own callback ref through here in form mode.
|
|
3930
|
+
const registrationRefFn = registration == null ? void 0 : registration.ref;
|
|
3931
|
+
const triggerRef = useCallback((instance)=>{
|
|
3932
|
+
if (typeof registrationRefFn === 'function') {
|
|
3933
|
+
registrationRefFn(instance);
|
|
3934
|
+
}
|
|
3935
|
+
}, [
|
|
3936
|
+
registrationRefFn
|
|
3937
|
+
]);
|
|
3938
|
+
// ───── Render-time guards (after all hooks) ─────
|
|
3939
|
+
if (!isVisible) return null;
|
|
3940
|
+
if (!accessState.visible) return null;
|
|
3941
|
+
const v = datePickerVariants({
|
|
3942
|
+
layout,
|
|
3943
|
+
error: resolvedError,
|
|
3944
|
+
fullWidth
|
|
3945
|
+
});
|
|
3946
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
3947
|
+
const displayValue = formatDisplayDate(resolvedValue, resolvedLocale);
|
|
3948
|
+
const commitValue = (next)=>{
|
|
3949
|
+
if (isFormMode && bridge) {
|
|
3950
|
+
var _registration_onChange;
|
|
3951
|
+
bridge.setValue == null ? void 0 : bridge.setValue.call(bridge, name, next);
|
|
3952
|
+
void (registration == null ? void 0 : (_registration_onChange = registration.onChange) == null ? void 0 : _registration_onChange.call(registration, {
|
|
3953
|
+
target: {
|
|
3954
|
+
name,
|
|
3955
|
+
value: next != null ? next : ''
|
|
3956
|
+
},
|
|
3957
|
+
type: 'change'
|
|
3958
|
+
}));
|
|
3959
|
+
} else if (explicitValue === undefined) {
|
|
3960
|
+
setInternalValue(next);
|
|
3961
|
+
}
|
|
3962
|
+
onChange == null ? void 0 : onChange(next);
|
|
3963
|
+
};
|
|
3964
|
+
// Mark the field touched on popover close. Reads the freshest committed
|
|
3965
|
+
// value straight from the bridge so the touched-blur never reverts a
|
|
3966
|
+
// value just chosen in the calendar.
|
|
3967
|
+
const markTouched = ()=>{
|
|
3968
|
+
if (isFormMode && bridge) {
|
|
3969
|
+
var _registration_onBlur;
|
|
3970
|
+
const committed = bridge.getValue(name);
|
|
3971
|
+
void (registration == null ? void 0 : (_registration_onBlur = registration.onBlur) == null ? void 0 : _registration_onBlur.call(registration, {
|
|
3972
|
+
target: {
|
|
3973
|
+
name,
|
|
3974
|
+
value: committed == null ? '' : String(committed)
|
|
3975
|
+
},
|
|
3976
|
+
type: 'blur'
|
|
3977
|
+
}));
|
|
3978
|
+
}
|
|
3979
|
+
};
|
|
3980
|
+
const handleOpenChange = (next)=>{
|
|
3981
|
+
if (next && !isInteractive) {
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
setIsOpen(next);
|
|
3985
|
+
if (!next) {
|
|
3986
|
+
markTouched();
|
|
3987
|
+
}
|
|
3988
|
+
};
|
|
3989
|
+
const handleCalendarChange = (next)=>{
|
|
3990
|
+
commitValue(next);
|
|
3991
|
+
setIsOpen(false);
|
|
3992
|
+
markTouched();
|
|
3993
|
+
};
|
|
3994
|
+
return jsxs("div", {
|
|
3995
|
+
"data-testid": testId,
|
|
3996
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
3997
|
+
children: [
|
|
3998
|
+
label && jsxs("label", {
|
|
3999
|
+
htmlFor: fieldId,
|
|
4000
|
+
className: cn(v.label(), slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
4001
|
+
children: [
|
|
4002
|
+
label,
|
|
4003
|
+
required && jsx("span", {
|
|
4004
|
+
"aria-hidden": "true",
|
|
4005
|
+
className: cn(v.requiredMark(), slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
4006
|
+
children: "*"
|
|
4007
|
+
})
|
|
4008
|
+
]
|
|
4009
|
+
}),
|
|
4010
|
+
jsx(Popover, {
|
|
4011
|
+
open: isOpen,
|
|
4012
|
+
onOpenChange: handleOpenChange,
|
|
4013
|
+
side: "bottom",
|
|
4014
|
+
align: "start",
|
|
4015
|
+
content: jsx(Calendar, {
|
|
4016
|
+
value: resolvedValue,
|
|
4017
|
+
onChange: handleCalendarChange,
|
|
4018
|
+
minDate: minDate,
|
|
4019
|
+
maxDate: maxDate,
|
|
4020
|
+
disabledDates: disabledDates,
|
|
4021
|
+
isDateDisabled: isDateDisabled,
|
|
4022
|
+
weekStartDay: weekStartDay,
|
|
4023
|
+
locale: locale,
|
|
4024
|
+
"aria-label": "Choose date",
|
|
4025
|
+
sx: "border-0 bg-transparent p-0"
|
|
4026
|
+
}),
|
|
4027
|
+
children: jsxs("button", {
|
|
4028
|
+
type: "button",
|
|
4029
|
+
id: fieldId,
|
|
4030
|
+
ref: triggerRef,
|
|
4031
|
+
disabled: effectiveDisabled,
|
|
4032
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4033
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4034
|
+
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
4035
|
+
children: [
|
|
4036
|
+
jsx("span", {
|
|
4037
|
+
className: displayValue ? v.value() : v.placeholder(),
|
|
4038
|
+
children: displayValue || placeholder || 'Select a date'
|
|
4039
|
+
}),
|
|
4040
|
+
jsx("span", {
|
|
4041
|
+
className: v.icon(),
|
|
4042
|
+
children: jsx(CalendarIcon$2, {})
|
|
4043
|
+
})
|
|
4044
|
+
]
|
|
4045
|
+
})
|
|
4046
|
+
}),
|
|
4047
|
+
resolvedHelperText && jsx("p", {
|
|
4048
|
+
id: helperId,
|
|
4049
|
+
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? slotProps == null ? void 0 : (_slotProps_errorText = slotProps.errorText) == null ? void 0 : _slotProps_errorText.className : slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className),
|
|
4050
|
+
children: resolvedHelperText
|
|
4051
|
+
})
|
|
4052
|
+
]
|
|
4053
|
+
});
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
/**
|
|
4057
|
+
* Slots for `<TimePicker>` — a bridge-integrated time field whose trigger
|
|
4058
|
+
* is a `<button>` styled as an input, opening a time-list popover.
|
|
4059
|
+
*
|
|
4060
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
4061
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
4062
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
4063
|
+
*/ const timePickerVariants = tv({
|
|
4064
|
+
slots: {
|
|
4065
|
+
root: 'flex flex-col',
|
|
4066
|
+
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
4067
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
4068
|
+
trigger: 'flex items-center gap-2 h-10 min-w-[10rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
|
|
4069
|
+
value: 'flex-1 truncate text-left',
|
|
4070
|
+
placeholder: 'flex-1 truncate text-left text-neutral-400',
|
|
4071
|
+
icon: 'shrink-0 text-[1rem] text-neutral-500',
|
|
4072
|
+
list: 'flex max-h-64 min-w-[8rem] flex-col gap-0.5 overflow-y-auto p-1',
|
|
4073
|
+
option: 'flex w-full cursor-pointer items-center rounded px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600 aria-disabled:cursor-default aria-disabled:text-neutral-300 aria-disabled:opacity-60 aria-disabled:hover:bg-transparent',
|
|
4074
|
+
helperText: 'mt-1 text-sm text-neutral-600',
|
|
4075
|
+
errorText: 'mt-1 text-sm text-danger-600'
|
|
4076
|
+
},
|
|
4077
|
+
variants: {
|
|
4078
|
+
layout: {
|
|
4079
|
+
stacked: {},
|
|
4080
|
+
inline: {
|
|
4081
|
+
root: 'flex-row items-start gap-3',
|
|
4082
|
+
label: 'mb-0 pt-2.5 whitespace-nowrap shrink-0'
|
|
4083
|
+
}
|
|
4084
|
+
},
|
|
4085
|
+
error: {
|
|
4086
|
+
true: {
|
|
4087
|
+
trigger: 'border-danger-500 hover:border-danger-500 focus-visible:border-danger-500 focus-visible:ring-danger-500/30'
|
|
4088
|
+
}
|
|
4089
|
+
},
|
|
4090
|
+
fullWidth: {
|
|
4091
|
+
true: {
|
|
4092
|
+
root: 'w-full',
|
|
4093
|
+
trigger: 'w-full min-w-0'
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
},
|
|
4097
|
+
defaultVariants: {
|
|
4098
|
+
layout: 'stacked',
|
|
4099
|
+
error: false,
|
|
4100
|
+
fullWidth: false
|
|
4101
|
+
}
|
|
4102
|
+
});
|
|
4103
|
+
|
|
4104
|
+
// Inline 16×16 stroke clock glyph — no icon dependency (tw convention).
|
|
4105
|
+
function ClockIcon() {
|
|
4106
|
+
return jsxs("svg", {
|
|
4107
|
+
width: "1em",
|
|
4108
|
+
height: "1em",
|
|
4109
|
+
viewBox: "0 0 16 16",
|
|
4110
|
+
fill: "none",
|
|
4111
|
+
"aria-hidden": "true",
|
|
4112
|
+
children: [
|
|
4113
|
+
jsx("circle", {
|
|
4114
|
+
cx: "8",
|
|
4115
|
+
cy: "8",
|
|
4116
|
+
r: "6",
|
|
4117
|
+
stroke: "currentColor",
|
|
4118
|
+
strokeWidth: "1.5"
|
|
4119
|
+
}),
|
|
4120
|
+
jsx("path", {
|
|
4121
|
+
d: "M8 4.5V8l2.4 1.4",
|
|
4122
|
+
stroke: "currentColor",
|
|
4123
|
+
strokeWidth: "1.5",
|
|
4124
|
+
strokeLinecap: "round",
|
|
4125
|
+
strokeLinejoin: "round"
|
|
4126
|
+
})
|
|
4127
|
+
]
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
/**
|
|
4131
|
+
* Dashforge TW `<TimePicker>` — a form-bound time-of-day field.
|
|
4132
|
+
*
|
|
4133
|
+
* A read-only trigger button paired with a time-list popover (Radix
|
|
4134
|
+
* Popover). Integrates with the Dashforge form bridge + RBAC.
|
|
4135
|
+
*
|
|
4136
|
+
* Storage contract: a canonical 24-hour `"HH:mm"` string, or `null` —
|
|
4137
|
+
* shared with the MUI `@dashforge/ui` `TimePicker`. `hour12` is display-only.
|
|
4138
|
+
*/ function TimePicker(props) {
|
|
4139
|
+
var _timeStringToMinutes, _timeStringToMinutes1;
|
|
4140
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_trigger, _slotProps_errorText, _slotProps_helperText;
|
|
4141
|
+
const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minTime, maxTime, stepMinutes, hour12 = false, fullWidth, sx, slotProps, testId } = props;
|
|
4142
|
+
const bridge = useContext(DashFormContext);
|
|
4143
|
+
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
4144
|
+
const fieldMeta = useDashFieldMeta(name);
|
|
4145
|
+
const accessState = useAccessState(access);
|
|
4146
|
+
const fieldId = useId();
|
|
4147
|
+
const helperId = `${fieldId}-help`;
|
|
4148
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
4149
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
4150
|
+
const unregisterRef = useRef({
|
|
4151
|
+
bridge,
|
|
4152
|
+
name
|
|
4153
|
+
});
|
|
4154
|
+
unregisterRef.current = {
|
|
4155
|
+
bridge,
|
|
4156
|
+
name
|
|
4157
|
+
};
|
|
4158
|
+
const isMountedRef = useRef(false);
|
|
4159
|
+
useEffect(()=>{
|
|
4160
|
+
isMountedRef.current = true;
|
|
4161
|
+
return ()=>{
|
|
4162
|
+
isMountedRef.current = false;
|
|
4163
|
+
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
4164
|
+
queueMicrotask(()=>{
|
|
4165
|
+
var _cap_unregister;
|
|
4166
|
+
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
4167
|
+
});
|
|
4168
|
+
};
|
|
4169
|
+
}, []);
|
|
4170
|
+
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
4171
|
+
const isInteractive = !effectiveDisabled && !accessState.readonly;
|
|
4172
|
+
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
4173
|
+
let resolvedError = error;
|
|
4174
|
+
let resolvedHelperText = helperText;
|
|
4175
|
+
let registration = null;
|
|
4176
|
+
let resolvedValue;
|
|
4177
|
+
if (isFormMode && bridge) {
|
|
4178
|
+
registration = bridge.register(name, rules);
|
|
4179
|
+
const validation = resolveValidationState(name, bridge, error, helperText);
|
|
4180
|
+
resolvedError = validation.error;
|
|
4181
|
+
resolvedHelperText = validation.helperText;
|
|
4182
|
+
if (explicitValue !== undefined) {
|
|
4183
|
+
resolvedValue = explicitValue;
|
|
4184
|
+
} else {
|
|
4185
|
+
const bv = fieldMeta.value;
|
|
4186
|
+
resolvedValue = bv == null ? null : String(bv);
|
|
4187
|
+
}
|
|
4188
|
+
} else if (explicitValue !== undefined) {
|
|
4189
|
+
resolvedValue = explicitValue;
|
|
4190
|
+
} else {
|
|
4191
|
+
resolvedValue = internalValue;
|
|
4192
|
+
}
|
|
4193
|
+
const registrationRefFn = registration == null ? void 0 : registration.ref;
|
|
4194
|
+
const triggerRef = useCallback((instance)=>{
|
|
4195
|
+
if (typeof registrationRefFn === 'function') {
|
|
4196
|
+
registrationRefFn(instance);
|
|
4197
|
+
}
|
|
4198
|
+
}, [
|
|
4199
|
+
registrationRefFn
|
|
4200
|
+
]);
|
|
4201
|
+
if (!isVisible) return null;
|
|
4202
|
+
if (!accessState.visible) return null;
|
|
4203
|
+
const v = timePickerVariants({
|
|
4204
|
+
layout,
|
|
4205
|
+
error: resolvedError,
|
|
4206
|
+
fullWidth
|
|
4207
|
+
});
|
|
4208
|
+
const displayValue = resolvedValue != null && resolvedValue !== '' ? formatTime(resolvedValue, {
|
|
4209
|
+
hour12
|
|
4210
|
+
}) : '';
|
|
4211
|
+
const options = generateTimeOptions(_extends({}, minTime !== undefined && {
|
|
4212
|
+
start: minTime
|
|
4213
|
+
}, maxTime !== undefined && {
|
|
4214
|
+
end: maxTime
|
|
4215
|
+
}, stepMinutes !== undefined && {
|
|
4216
|
+
stepMinutes
|
|
4217
|
+
}));
|
|
4218
|
+
const minMinutes = (_timeStringToMinutes = timeStringToMinutes(minTime != null ? minTime : '00:00')) != null ? _timeStringToMinutes : 0;
|
|
4219
|
+
const maxMinutes = (_timeStringToMinutes1 = timeStringToMinutes(maxTime != null ? maxTime : '23:59')) != null ? _timeStringToMinutes1 : 1439;
|
|
4220
|
+
const isTimeDisabled = (time)=>{
|
|
4221
|
+
const minutes = timeStringToMinutes(time);
|
|
4222
|
+
return minutes === null || minutes < minMinutes || minutes > maxMinutes;
|
|
4223
|
+
};
|
|
4224
|
+
const commitValue = (next)=>{
|
|
4225
|
+
if (isFormMode && bridge) {
|
|
4226
|
+
var _registration_onChange;
|
|
4227
|
+
bridge.setValue == null ? void 0 : bridge.setValue.call(bridge, name, next);
|
|
4228
|
+
void (registration == null ? void 0 : (_registration_onChange = registration.onChange) == null ? void 0 : _registration_onChange.call(registration, {
|
|
4229
|
+
target: {
|
|
4230
|
+
name,
|
|
4231
|
+
value: next != null ? next : ''
|
|
4232
|
+
},
|
|
4233
|
+
type: 'change'
|
|
4234
|
+
}));
|
|
4235
|
+
} else if (explicitValue === undefined) {
|
|
4236
|
+
setInternalValue(next);
|
|
4237
|
+
}
|
|
4238
|
+
onChange == null ? void 0 : onChange(next);
|
|
4239
|
+
};
|
|
4240
|
+
const markTouched = ()=>{
|
|
4241
|
+
if (isFormMode && bridge) {
|
|
4242
|
+
var _registration_onBlur;
|
|
4243
|
+
const committed = bridge.getValue(name);
|
|
4244
|
+
void (registration == null ? void 0 : (_registration_onBlur = registration.onBlur) == null ? void 0 : _registration_onBlur.call(registration, {
|
|
4245
|
+
target: {
|
|
4246
|
+
name,
|
|
4247
|
+
value: committed == null ? '' : String(committed)
|
|
4248
|
+
},
|
|
4249
|
+
type: 'blur'
|
|
4250
|
+
}));
|
|
4251
|
+
}
|
|
4252
|
+
};
|
|
4253
|
+
const handleOpenChange = (next)=>{
|
|
4254
|
+
if (next && !isInteractive) {
|
|
4255
|
+
return;
|
|
4256
|
+
}
|
|
4257
|
+
setIsOpen(next);
|
|
4258
|
+
if (!next) {
|
|
4259
|
+
markTouched();
|
|
4260
|
+
}
|
|
4261
|
+
};
|
|
4262
|
+
const handleSelect = (time)=>{
|
|
4263
|
+
if (isTimeDisabled(time)) {
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
commitValue(time);
|
|
4267
|
+
setIsOpen(false);
|
|
4268
|
+
markTouched();
|
|
4269
|
+
};
|
|
4270
|
+
return jsxs("div", {
|
|
4271
|
+
"data-testid": testId,
|
|
4272
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
4273
|
+
children: [
|
|
4274
|
+
label && jsxs("label", {
|
|
4275
|
+
htmlFor: fieldId,
|
|
4276
|
+
className: cn(v.label(), slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
4277
|
+
children: [
|
|
4278
|
+
label,
|
|
4279
|
+
required && jsx("span", {
|
|
4280
|
+
"aria-hidden": "true",
|
|
4281
|
+
className: cn(v.requiredMark(), slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
4282
|
+
children: "*"
|
|
4283
|
+
})
|
|
4284
|
+
]
|
|
4285
|
+
}),
|
|
4286
|
+
jsx(Popover, {
|
|
4287
|
+
open: isOpen,
|
|
4288
|
+
onOpenChange: handleOpenChange,
|
|
4289
|
+
side: "bottom",
|
|
4290
|
+
align: "start",
|
|
4291
|
+
content: jsx("div", {
|
|
4292
|
+
role: "listbox",
|
|
4293
|
+
"aria-label": "Time options",
|
|
4294
|
+
className: v.list(),
|
|
4295
|
+
children: options.map((time)=>jsx("button", {
|
|
4296
|
+
type: "button",
|
|
4297
|
+
role: "option",
|
|
4298
|
+
"aria-selected": time === resolvedValue,
|
|
4299
|
+
"aria-disabled": isTimeDisabled(time),
|
|
4300
|
+
className: v.option(),
|
|
4301
|
+
onClick: ()=>{
|
|
4302
|
+
handleSelect(time);
|
|
4303
|
+
},
|
|
4304
|
+
children: formatTime(time, {
|
|
4305
|
+
hour12
|
|
4306
|
+
})
|
|
4307
|
+
}, time))
|
|
4308
|
+
}),
|
|
4309
|
+
children: jsxs("button", {
|
|
4310
|
+
type: "button",
|
|
4311
|
+
id: fieldId,
|
|
4312
|
+
ref: triggerRef,
|
|
4313
|
+
disabled: effectiveDisabled,
|
|
4314
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4315
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4316
|
+
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
4317
|
+
children: [
|
|
4318
|
+
jsx("span", {
|
|
4319
|
+
className: displayValue ? v.value() : v.placeholder(),
|
|
4320
|
+
children: displayValue || placeholder || 'Select a time'
|
|
4321
|
+
}),
|
|
4322
|
+
jsx("span", {
|
|
4323
|
+
className: v.icon(),
|
|
4324
|
+
children: jsx(ClockIcon, {})
|
|
4325
|
+
})
|
|
4326
|
+
]
|
|
4327
|
+
})
|
|
4328
|
+
}),
|
|
4329
|
+
resolvedHelperText && jsx("p", {
|
|
4330
|
+
id: helperId,
|
|
4331
|
+
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? slotProps == null ? void 0 : (_slotProps_errorText = slotProps.errorText) == null ? void 0 : _slotProps_errorText.className : slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className),
|
|
4332
|
+
children: resolvedHelperText
|
|
4333
|
+
})
|
|
4334
|
+
]
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
/**
|
|
4339
|
+
* Slots for `<DateTimePicker>` — a bridge-integrated date+time field whose
|
|
4340
|
+
* trigger is a `<button>` styled as an input, opening a popover that
|
|
4341
|
+
* combines a `<Calendar>` with a time list.
|
|
4342
|
+
*
|
|
4343
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
4344
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
4345
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
4346
|
+
*/ const dateTimePickerVariants = tv({
|
|
4347
|
+
slots: {
|
|
4348
|
+
root: 'flex flex-col',
|
|
4349
|
+
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
4350
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
4351
|
+
trigger: 'flex items-center gap-2 h-10 min-w-[13rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
|
|
4352
|
+
value: 'flex-1 truncate text-left',
|
|
4353
|
+
placeholder: 'flex-1 truncate text-left text-neutral-400',
|
|
4354
|
+
icon: 'shrink-0 text-[1rem] text-neutral-500',
|
|
4355
|
+
panel: 'flex items-stretch',
|
|
4356
|
+
list: 'flex max-h-[19rem] min-w-[6.5rem] flex-col gap-0.5 overflow-y-auto border-l border-neutral-200 p-1',
|
|
4357
|
+
option: 'flex w-full cursor-pointer items-center rounded px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600',
|
|
4358
|
+
helperText: 'mt-1 text-sm text-neutral-600',
|
|
4359
|
+
errorText: 'mt-1 text-sm text-danger-600'
|
|
4360
|
+
},
|
|
4361
|
+
variants: {
|
|
4362
|
+
layout: {
|
|
4363
|
+
stacked: {},
|
|
4364
|
+
inline: {
|
|
4365
|
+
root: 'flex-row items-start gap-3',
|
|
4366
|
+
label: 'mb-0 pt-2.5 whitespace-nowrap shrink-0'
|
|
4367
|
+
}
|
|
4368
|
+
},
|
|
4369
|
+
error: {
|
|
4370
|
+
true: {
|
|
4371
|
+
trigger: 'border-danger-500 hover:border-danger-500 focus-visible:border-danger-500 focus-visible:ring-danger-500/30'
|
|
4372
|
+
}
|
|
4373
|
+
},
|
|
4374
|
+
fullWidth: {
|
|
4375
|
+
true: {
|
|
4376
|
+
root: 'w-full',
|
|
4377
|
+
trigger: 'w-full min-w-0'
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
},
|
|
4381
|
+
defaultVariants: {
|
|
4382
|
+
layout: 'stacked',
|
|
4383
|
+
error: false,
|
|
4384
|
+
fullWidth: false
|
|
4385
|
+
}
|
|
4386
|
+
});
|
|
4387
|
+
|
|
4388
|
+
// Inline 16×16 stroke calendar glyph — no icon dependency (tw convention).
|
|
4389
|
+
function CalendarIcon$1() {
|
|
4390
|
+
return jsxs("svg", {
|
|
4391
|
+
width: "1em",
|
|
4392
|
+
height: "1em",
|
|
4393
|
+
viewBox: "0 0 16 16",
|
|
4394
|
+
fill: "none",
|
|
4395
|
+
"aria-hidden": "true",
|
|
4396
|
+
children: [
|
|
4397
|
+
jsx("rect", {
|
|
4398
|
+
x: "2.5",
|
|
4399
|
+
y: "3.5",
|
|
4400
|
+
width: "11",
|
|
4401
|
+
height: "10",
|
|
4402
|
+
rx: "1.5",
|
|
4403
|
+
stroke: "currentColor",
|
|
4404
|
+
strokeWidth: "1.5"
|
|
4405
|
+
}),
|
|
4406
|
+
jsx("path", {
|
|
4407
|
+
d: "M2.5 6.5h11M5.5 2v3M10.5 2v3",
|
|
4408
|
+
stroke: "currentColor",
|
|
4409
|
+
strokeWidth: "1.5",
|
|
4410
|
+
strokeLinecap: "round"
|
|
4411
|
+
})
|
|
4412
|
+
]
|
|
4413
|
+
});
|
|
4414
|
+
}
|
|
4415
|
+
const DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})/;
|
|
4416
|
+
/** Splits a stored `YYYY-MM-DDTHH:mm` value into its date and time parts. */ function splitDateTime(value) {
|
|
4417
|
+
if (value == null || value === '') {
|
|
4418
|
+
return {
|
|
4419
|
+
date: null,
|
|
4420
|
+
time: null
|
|
4421
|
+
};
|
|
4422
|
+
}
|
|
4423
|
+
const match = DATETIME_PATTERN.exec(value);
|
|
4424
|
+
if (match !== null) {
|
|
4425
|
+
var _match_, _match_1, _match_2;
|
|
4426
|
+
return {
|
|
4427
|
+
date: (_match_ = match[1]) != null ? _match_ : null,
|
|
4428
|
+
time: `${(_match_1 = match[2]) != null ? _match_1 : '00'}:${(_match_2 = match[3]) != null ? _match_2 : '00'}`
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4431
|
+
return {
|
|
4432
|
+
date: parseISODate(value) !== null ? value : null,
|
|
4433
|
+
time: null
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
/** Combines a date and a time into a `YYYY-MM-DDTHH:mm` value (or `null`). */ function joinDateTime(date, time) {
|
|
4437
|
+
if (date === null && time === null) {
|
|
4438
|
+
return null;
|
|
4439
|
+
}
|
|
4440
|
+
return `${date != null ? date : getTodayISODate()}T${time != null ? time : '00:00'}`;
|
|
4441
|
+
}
|
|
4442
|
+
/** Formats a stored datetime for the trigger display. */ function formatDateTime(value, locale, hour12) {
|
|
4443
|
+
const { date, time } = splitDateTime(value);
|
|
4444
|
+
const parts = [];
|
|
4445
|
+
if (date !== null) {
|
|
4446
|
+
const parsed = parseISODate(date);
|
|
4447
|
+
parts.push(parsed === null ? date : new Intl.DateTimeFormat(locale, {
|
|
4448
|
+
dateStyle: 'medium',
|
|
4449
|
+
timeZone: 'UTC'
|
|
4450
|
+
}).format(Date.UTC(parsed.year, parsed.month - 1, parsed.day)));
|
|
4451
|
+
}
|
|
4452
|
+
if (time !== null) {
|
|
4453
|
+
parts.push(formatTime(time, {
|
|
4454
|
+
hour12
|
|
4455
|
+
}));
|
|
4456
|
+
}
|
|
4457
|
+
return parts.join(', ');
|
|
4458
|
+
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Dashforge TW `<DateTimePicker>` — a form-bound date + time field.
|
|
4461
|
+
*
|
|
4462
|
+
* A read-only trigger button paired with a popover combining a `<Calendar>`
|
|
4463
|
+
* and a time list (Radix Popover). Integrates with the Dashforge form
|
|
4464
|
+
* bridge + RBAC.
|
|
4465
|
+
*
|
|
4466
|
+
* Storage contract: a naive ISO datetime `"YYYY-MM-DDTHH:mm"` — no seconds,
|
|
4467
|
+
* no timezone. Shared with the MUI `@dashforge/ui` `DateTimePicker`.
|
|
4468
|
+
*/ function DateTimePicker(props) {
|
|
4469
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_trigger, _slotProps_errorText, _slotProps_helperText;
|
|
4470
|
+
const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, stepMinutes, hour12 = false, fullWidth, sx, slotProps, testId } = props;
|
|
4471
|
+
const bridge = useContext(DashFormContext);
|
|
4472
|
+
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
4473
|
+
const fieldMeta = useDashFieldMeta(name);
|
|
4474
|
+
const accessState = useAccessState(access);
|
|
4475
|
+
const fieldId = useId();
|
|
4476
|
+
const helperId = `${fieldId}-help`;
|
|
4477
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
4478
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
4479
|
+
const unregisterRef = useRef({
|
|
4480
|
+
bridge,
|
|
4481
|
+
name
|
|
4482
|
+
});
|
|
4483
|
+
unregisterRef.current = {
|
|
4484
|
+
bridge,
|
|
4485
|
+
name
|
|
4486
|
+
};
|
|
4487
|
+
const isMountedRef = useRef(false);
|
|
4488
|
+
useEffect(()=>{
|
|
4489
|
+
isMountedRef.current = true;
|
|
4490
|
+
return ()=>{
|
|
4491
|
+
isMountedRef.current = false;
|
|
4492
|
+
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
4493
|
+
queueMicrotask(()=>{
|
|
4494
|
+
var _cap_unregister;
|
|
4495
|
+
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
4496
|
+
});
|
|
4497
|
+
};
|
|
4498
|
+
}, []);
|
|
4499
|
+
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
4500
|
+
const isInteractive = !effectiveDisabled && !accessState.readonly;
|
|
4501
|
+
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
4502
|
+
let resolvedError = error;
|
|
4503
|
+
let resolvedHelperText = helperText;
|
|
4504
|
+
let registration = null;
|
|
4505
|
+
let resolvedValue;
|
|
4506
|
+
if (isFormMode && bridge) {
|
|
4507
|
+
registration = bridge.register(name, rules);
|
|
4508
|
+
const validation = resolveValidationState(name, bridge, error, helperText);
|
|
4509
|
+
resolvedError = validation.error;
|
|
4510
|
+
resolvedHelperText = validation.helperText;
|
|
4511
|
+
if (explicitValue !== undefined) {
|
|
4512
|
+
resolvedValue = explicitValue;
|
|
4513
|
+
} else {
|
|
4514
|
+
const bv = fieldMeta.value;
|
|
4515
|
+
resolvedValue = bv == null ? null : String(bv);
|
|
4516
|
+
}
|
|
4517
|
+
} else if (explicitValue !== undefined) {
|
|
4518
|
+
resolvedValue = explicitValue;
|
|
4519
|
+
} else {
|
|
4520
|
+
resolvedValue = internalValue;
|
|
4521
|
+
}
|
|
4522
|
+
const registrationRefFn = registration == null ? void 0 : registration.ref;
|
|
4523
|
+
const triggerRef = useCallback((instance)=>{
|
|
4524
|
+
if (typeof registrationRefFn === 'function') {
|
|
4525
|
+
registrationRefFn(instance);
|
|
4526
|
+
}
|
|
4527
|
+
}, [
|
|
4528
|
+
registrationRefFn
|
|
4529
|
+
]);
|
|
4530
|
+
if (!isVisible) return null;
|
|
4531
|
+
if (!accessState.visible) return null;
|
|
4532
|
+
const v = dateTimePickerVariants({
|
|
4533
|
+
layout,
|
|
4534
|
+
error: resolvedError,
|
|
4535
|
+
fullWidth
|
|
4536
|
+
});
|
|
4537
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
4538
|
+
const { date, time } = splitDateTime(resolvedValue);
|
|
4539
|
+
const displayValue = formatDateTime(resolvedValue, resolvedLocale, hour12);
|
|
4540
|
+
const options = generateTimeOptions(_extends({}, stepMinutes !== undefined && {
|
|
4541
|
+
stepMinutes
|
|
4542
|
+
}));
|
|
4543
|
+
const commitValue = (next)=>{
|
|
4544
|
+
if (isFormMode && bridge) {
|
|
4545
|
+
var _registration_onChange;
|
|
4546
|
+
bridge.setValue == null ? void 0 : bridge.setValue.call(bridge, name, next);
|
|
4547
|
+
void (registration == null ? void 0 : (_registration_onChange = registration.onChange) == null ? void 0 : _registration_onChange.call(registration, {
|
|
4548
|
+
target: {
|
|
4549
|
+
name,
|
|
4550
|
+
value: next != null ? next : ''
|
|
4551
|
+
},
|
|
4552
|
+
type: 'change'
|
|
4553
|
+
}));
|
|
4554
|
+
} else if (explicitValue === undefined) {
|
|
4555
|
+
setInternalValue(next);
|
|
4556
|
+
}
|
|
4557
|
+
onChange == null ? void 0 : onChange(next);
|
|
4558
|
+
};
|
|
4559
|
+
const markTouched = ()=>{
|
|
4560
|
+
if (isFormMode && bridge) {
|
|
4561
|
+
var _registration_onBlur;
|
|
4562
|
+
const committed = bridge.getValue(name);
|
|
4563
|
+
void (registration == null ? void 0 : (_registration_onBlur = registration.onBlur) == null ? void 0 : _registration_onBlur.call(registration, {
|
|
4564
|
+
target: {
|
|
4565
|
+
name,
|
|
4566
|
+
value: committed == null ? '' : String(committed)
|
|
4567
|
+
},
|
|
4568
|
+
type: 'blur'
|
|
4569
|
+
}));
|
|
4570
|
+
}
|
|
4571
|
+
};
|
|
4572
|
+
const handleOpenChange = (next)=>{
|
|
4573
|
+
if (next && !isInteractive) {
|
|
4574
|
+
return;
|
|
4575
|
+
}
|
|
4576
|
+
setIsOpen(next);
|
|
4577
|
+
if (!next) {
|
|
4578
|
+
markTouched();
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
// Picking a date keeps the popover open — the user still needs a time.
|
|
4582
|
+
const handleDateSelect = (nextDate)=>{
|
|
4583
|
+
commitValue(joinDateTime(nextDate, time));
|
|
4584
|
+
};
|
|
4585
|
+
// Picking a time completes the value and closes the popover.
|
|
4586
|
+
const handleTimeSelect = (nextTime)=>{
|
|
4587
|
+
commitValue(joinDateTime(date, nextTime));
|
|
4588
|
+
setIsOpen(false);
|
|
4589
|
+
markTouched();
|
|
4590
|
+
};
|
|
4591
|
+
return jsxs("div", {
|
|
4592
|
+
"data-testid": testId,
|
|
4593
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
4594
|
+
children: [
|
|
4595
|
+
label && jsxs("label", {
|
|
4596
|
+
htmlFor: fieldId,
|
|
4597
|
+
className: cn(v.label(), slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
4598
|
+
children: [
|
|
4599
|
+
label,
|
|
4600
|
+
required && jsx("span", {
|
|
4601
|
+
"aria-hidden": "true",
|
|
4602
|
+
className: cn(v.requiredMark(), slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
4603
|
+
children: "*"
|
|
4604
|
+
})
|
|
4605
|
+
]
|
|
4606
|
+
}),
|
|
4607
|
+
jsx(Popover, {
|
|
4608
|
+
open: isOpen,
|
|
4609
|
+
onOpenChange: handleOpenChange,
|
|
4610
|
+
side: "bottom",
|
|
4611
|
+
align: "start",
|
|
4612
|
+
content: jsxs("div", {
|
|
4613
|
+
className: v.panel(),
|
|
4614
|
+
children: [
|
|
4615
|
+
jsx(Calendar, {
|
|
4616
|
+
value: date,
|
|
4617
|
+
onChange: handleDateSelect,
|
|
4618
|
+
minDate: minDate,
|
|
4619
|
+
maxDate: maxDate,
|
|
4620
|
+
disabledDates: disabledDates,
|
|
4621
|
+
isDateDisabled: isDateDisabled,
|
|
4622
|
+
weekStartDay: weekStartDay,
|
|
4623
|
+
locale: locale,
|
|
4624
|
+
"aria-label": "Choose date",
|
|
4625
|
+
sx: "border-0 bg-transparent p-0"
|
|
4626
|
+
}),
|
|
4627
|
+
jsx("div", {
|
|
4628
|
+
role: "listbox",
|
|
4629
|
+
"aria-label": "Time options",
|
|
4630
|
+
className: v.list(),
|
|
4631
|
+
children: options.map((option)=>jsx("button", {
|
|
4632
|
+
type: "button",
|
|
4633
|
+
role: "option",
|
|
4634
|
+
"aria-selected": option === time,
|
|
4635
|
+
className: v.option(),
|
|
4636
|
+
onClick: ()=>{
|
|
4637
|
+
handleTimeSelect(option);
|
|
4638
|
+
},
|
|
4639
|
+
children: formatTime(option, {
|
|
4640
|
+
hour12
|
|
4641
|
+
})
|
|
4642
|
+
}, option))
|
|
4643
|
+
})
|
|
4644
|
+
]
|
|
4645
|
+
}),
|
|
4646
|
+
children: jsxs("button", {
|
|
4647
|
+
type: "button",
|
|
4648
|
+
id: fieldId,
|
|
4649
|
+
ref: triggerRef,
|
|
4650
|
+
disabled: effectiveDisabled,
|
|
4651
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4652
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4653
|
+
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
4654
|
+
children: [
|
|
4655
|
+
jsx("span", {
|
|
4656
|
+
className: displayValue ? v.value() : v.placeholder(),
|
|
4657
|
+
children: displayValue || placeholder || 'Select date and time'
|
|
4658
|
+
}),
|
|
4659
|
+
jsx("span", {
|
|
4660
|
+
className: v.icon(),
|
|
4661
|
+
children: jsx(CalendarIcon$1, {})
|
|
4662
|
+
})
|
|
4663
|
+
]
|
|
4664
|
+
})
|
|
4665
|
+
}),
|
|
4666
|
+
resolvedHelperText && jsx("p", {
|
|
4667
|
+
id: helperId,
|
|
4668
|
+
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? slotProps == null ? void 0 : (_slotProps_errorText = slotProps.errorText) == null ? void 0 : _slotProps_errorText.className : slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className),
|
|
4669
|
+
children: resolvedHelperText
|
|
4670
|
+
})
|
|
4671
|
+
]
|
|
4672
|
+
});
|
|
4673
|
+
}
|
|
4674
|
+
|
|
4675
|
+
/**
|
|
4676
|
+
* Slots for `<DateRangePicker>` — a bridge-integrated start/end date field
|
|
4677
|
+
* whose trigger is a `<button>` styled as an input, opening a dual-month
|
|
4678
|
+
* range calendar popover.
|
|
4679
|
+
*
|
|
4680
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
4681
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
4682
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
4683
|
+
*/ const dateRangePickerVariants = tv({
|
|
4684
|
+
slots: {
|
|
4685
|
+
root: 'flex flex-col',
|
|
4686
|
+
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
4687
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
4688
|
+
trigger: 'flex items-center gap-2 h-10 min-w-[15rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
|
|
4689
|
+
value: 'flex-1 truncate text-left',
|
|
4690
|
+
placeholder: 'flex-1 truncate text-left text-neutral-400',
|
|
4691
|
+
icon: 'shrink-0 text-[1rem] text-neutral-500',
|
|
4692
|
+
panel: 'flex gap-3 p-3 select-none',
|
|
4693
|
+
month: 'flex flex-col gap-2',
|
|
4694
|
+
header: 'flex items-center justify-between gap-1',
|
|
4695
|
+
navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[1rem] text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
|
|
4696
|
+
navSpacer: 'h-7 w-7 shrink-0',
|
|
4697
|
+
monthLabel: 'flex-1 text-center text-sm font-semibold text-neutral-900',
|
|
4698
|
+
grid: 'flex flex-col gap-1',
|
|
4699
|
+
weekdayRow: 'grid grid-cols-7 gap-1',
|
|
4700
|
+
weekday: 'flex h-8 items-center justify-center text-xs font-semibold text-neutral-500',
|
|
4701
|
+
weekRow: 'grid grid-cols-7 gap-1',
|
|
3466
4702
|
helperText: 'mt-1 text-sm text-neutral-600',
|
|
3467
4703
|
errorText: 'mt-1 text-sm text-danger-600'
|
|
3468
4704
|
},
|
|
3469
4705
|
variants: {
|
|
3470
|
-
size: {
|
|
3471
|
-
sm: {
|
|
3472
|
-
inputWrapper: 'h-8 px-2',
|
|
3473
|
-
input: 'text-sm'
|
|
3474
|
-
},
|
|
3475
|
-
md: {
|
|
3476
|
-
inputWrapper: 'h-10 px-3',
|
|
3477
|
-
input: 'text-base'
|
|
3478
|
-
},
|
|
3479
|
-
lg: {
|
|
3480
|
-
inputWrapper: 'h-12 px-4',
|
|
3481
|
-
input: 'text-lg'
|
|
3482
|
-
}
|
|
3483
|
-
},
|
|
3484
4706
|
layout: {
|
|
3485
|
-
stacked: {
|
|
3486
|
-
root: 'flex-col'
|
|
3487
|
-
},
|
|
4707
|
+
stacked: {},
|
|
3488
4708
|
inline: {
|
|
3489
4709
|
root: 'flex-row items-start gap-3',
|
|
3490
|
-
label: 'mb-0 pt-2 whitespace-nowrap shrink-0'
|
|
4710
|
+
label: 'mb-0 pt-2.5 whitespace-nowrap shrink-0'
|
|
3491
4711
|
}
|
|
3492
4712
|
},
|
|
3493
4713
|
error: {
|
|
3494
4714
|
true: {
|
|
3495
|
-
|
|
4715
|
+
trigger: 'border-danger-500 hover:border-danger-500 focus-visible:border-danger-500 focus-visible:ring-danger-500/30'
|
|
3496
4716
|
}
|
|
3497
4717
|
},
|
|
3498
4718
|
fullWidth: {
|
|
3499
4719
|
true: {
|
|
3500
4720
|
root: 'w-full',
|
|
3501
|
-
|
|
3502
|
-
}
|
|
3503
|
-
},
|
|
3504
|
-
disabled: {
|
|
3505
|
-
true: {
|
|
3506
|
-
inputWrapper: 'opacity-60 cursor-not-allowed bg-neutral-200'
|
|
4721
|
+
trigger: 'w-full min-w-0'
|
|
3507
4722
|
}
|
|
3508
4723
|
}
|
|
3509
4724
|
},
|
|
3510
4725
|
defaultVariants: {
|
|
3511
|
-
size: 'md',
|
|
3512
4726
|
layout: 'stacked',
|
|
3513
4727
|
error: false,
|
|
3514
|
-
fullWidth: false
|
|
4728
|
+
fullWidth: false
|
|
4729
|
+
}
|
|
4730
|
+
});
|
|
4731
|
+
/**
|
|
4732
|
+
* Per-day-cell recipe for the range grid. `endpoint` (a committed start/end)
|
|
4733
|
+
* is the solid fill; `inBand` (inside the committed or previewed range) is
|
|
4734
|
+
* the light tint. Declared `inBand` before `endpoint` so an endpoint cell —
|
|
4735
|
+
* which is also in-band — resolves to the solid fill.
|
|
4736
|
+
*/ const dateRangeDayVariants = tv({
|
|
4737
|
+
base: 'flex h-9 w-9 items-center justify-center rounded-md text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500',
|
|
4738
|
+
variants: {
|
|
4739
|
+
inBand: {
|
|
4740
|
+
true: 'bg-primary-100 hover:bg-primary-100'
|
|
4741
|
+
},
|
|
4742
|
+
endpoint: {
|
|
4743
|
+
true: 'bg-primary-600 text-white hover:bg-primary-600'
|
|
4744
|
+
},
|
|
4745
|
+
siblingMonth: {
|
|
4746
|
+
true: 'text-neutral-400'
|
|
4747
|
+
},
|
|
4748
|
+
today: {
|
|
4749
|
+
true: 'font-bold ring-1 ring-inset ring-primary-400'
|
|
4750
|
+
},
|
|
4751
|
+
disabled: {
|
|
4752
|
+
true: 'cursor-default text-neutral-300 opacity-60 hover:bg-transparent'
|
|
4753
|
+
}
|
|
4754
|
+
},
|
|
4755
|
+
compoundVariants: [
|
|
4756
|
+
{
|
|
4757
|
+
endpoint: true,
|
|
4758
|
+
today: true,
|
|
4759
|
+
class: 'ring-0'
|
|
4760
|
+
},
|
|
4761
|
+
{
|
|
4762
|
+
endpoint: true,
|
|
4763
|
+
siblingMonth: true,
|
|
4764
|
+
class: 'text-white'
|
|
4765
|
+
}
|
|
4766
|
+
],
|
|
4767
|
+
defaultVariants: {
|
|
4768
|
+
inBand: false,
|
|
4769
|
+
endpoint: false,
|
|
4770
|
+
siblingMonth: false,
|
|
4771
|
+
today: false,
|
|
3515
4772
|
disabled: false
|
|
3516
4773
|
}
|
|
3517
4774
|
});
|
|
3518
4775
|
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
4776
|
+
const EMPTY_RANGE = {
|
|
4777
|
+
start: null,
|
|
4778
|
+
end: null
|
|
4779
|
+
};
|
|
4780
|
+
function CalendarIcon() {
|
|
4781
|
+
return jsxs("svg", {
|
|
4782
|
+
width: "1em",
|
|
4783
|
+
height: "1em",
|
|
4784
|
+
viewBox: "0 0 16 16",
|
|
4785
|
+
fill: "none",
|
|
4786
|
+
"aria-hidden": "true",
|
|
4787
|
+
children: [
|
|
4788
|
+
jsx("rect", {
|
|
4789
|
+
x: "2.5",
|
|
4790
|
+
y: "3.5",
|
|
4791
|
+
width: "11",
|
|
4792
|
+
height: "10",
|
|
4793
|
+
rx: "1.5",
|
|
4794
|
+
stroke: "currentColor",
|
|
4795
|
+
strokeWidth: "1.5"
|
|
4796
|
+
}),
|
|
4797
|
+
jsx("path", {
|
|
4798
|
+
d: "M2.5 6.5h11M5.5 2v3M10.5 2v3",
|
|
4799
|
+
stroke: "currentColor",
|
|
4800
|
+
strokeWidth: "1.5",
|
|
4801
|
+
strokeLinecap: "round"
|
|
4802
|
+
})
|
|
4803
|
+
]
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4806
|
+
function ChevronLeftIcon() {
|
|
4807
|
+
return jsx("svg", {
|
|
4808
|
+
width: "1em",
|
|
4809
|
+
height: "1em",
|
|
4810
|
+
viewBox: "0 0 16 16",
|
|
4811
|
+
fill: "none",
|
|
4812
|
+
"aria-hidden": "true",
|
|
4813
|
+
children: jsx("path", {
|
|
4814
|
+
d: "M10 4 6 8l4 4",
|
|
4815
|
+
stroke: "currentColor",
|
|
4816
|
+
strokeWidth: "1.5",
|
|
4817
|
+
strokeLinecap: "round",
|
|
4818
|
+
strokeLinejoin: "round"
|
|
4819
|
+
})
|
|
4820
|
+
});
|
|
4821
|
+
}
|
|
4822
|
+
function ChevronRightIcon$1() {
|
|
4823
|
+
return jsx("svg", {
|
|
4824
|
+
width: "1em",
|
|
4825
|
+
height: "1em",
|
|
4826
|
+
viewBox: "0 0 16 16",
|
|
4827
|
+
fill: "none",
|
|
4828
|
+
"aria-hidden": "true",
|
|
4829
|
+
children: jsx("path", {
|
|
4830
|
+
d: "m6 4 4 4-4 4",
|
|
4831
|
+
stroke: "currentColor",
|
|
4832
|
+
strokeWidth: "1.5",
|
|
4833
|
+
strokeLinecap: "round",
|
|
4834
|
+
strokeLinejoin: "round"
|
|
4835
|
+
})
|
|
4836
|
+
});
|
|
4837
|
+
}
|
|
4838
|
+
/** Formats a stored ISO date for the trigger display (medium style). */ function formatEndpoint(iso, locale) {
|
|
4839
|
+
if (iso === null || iso === '') {
|
|
4840
|
+
return '';
|
|
3530
4841
|
}
|
|
4842
|
+
const parts = parseISODate(iso);
|
|
4843
|
+
if (parts === null) {
|
|
4844
|
+
return iso;
|
|
4845
|
+
}
|
|
4846
|
+
return new Intl.DateTimeFormat(locale, {
|
|
4847
|
+
dateStyle: 'medium',
|
|
4848
|
+
timeZone: 'UTC'
|
|
4849
|
+
}).format(Date.UTC(parts.year, parts.month - 1, parts.day));
|
|
3531
4850
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
* - `date` → `YYYY-MM-DD`
|
|
3538
|
-
* - `time` → `HH:mm` (or `HH:mm:ss` if seconds present)
|
|
3539
|
-
* - `datetime` → `YYYY-MM-DDTHH:mm` (no timezone, datetime-local
|
|
3540
|
-
* rejects `Z` / offset suffixes)
|
|
3541
|
-
*
|
|
3542
|
-
* The function is intentionally lenient:
|
|
3543
|
-
* - Pass-through when the input already matches the expected shape.
|
|
3544
|
-
* - Truncates a richer ISO string (e.g. `2026-05-16T10:23:45.000Z`)
|
|
3545
|
-
* down to the mode-appropriate prefix.
|
|
3546
|
-
* - Returns `''` for null / undefined / unparseable inputs (native
|
|
3547
|
-
* inputs use `''` to represent "no value").
|
|
3548
|
-
*/ function isoToInputValue(mode, raw) {
|
|
3549
|
-
if (raw == null || raw === '') return '';
|
|
3550
|
-
// Fast path: already in the expected shape.
|
|
3551
|
-
if (mode === 'date' && /^\d{4}-\d{2}-\d{2}$/.test(raw)) return raw;
|
|
3552
|
-
if (mode === 'time' && /^\d{2}:\d{2}(:\d{2})?$/.test(raw)) return raw;
|
|
3553
|
-
if (mode === 'datetime' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/.test(raw)) {
|
|
3554
|
-
return raw;
|
|
4851
|
+
function formatRange(range, locale) {
|
|
4852
|
+
const start = formatEndpoint(range.start, locale);
|
|
4853
|
+
const end = formatEndpoint(range.end, locale);
|
|
4854
|
+
if (start === '') {
|
|
4855
|
+
return '';
|
|
3555
4856
|
}
|
|
3556
|
-
|
|
3557
|
-
const d = new Date(raw);
|
|
3558
|
-
if (isNaN(d.getTime())) return '';
|
|
3559
|
-
const pad = (n)=>String(n).padStart(2, '0');
|
|
3560
|
-
const yyyy = d.getFullYear();
|
|
3561
|
-
const mm = pad(d.getMonth() + 1);
|
|
3562
|
-
const dd = pad(d.getDate());
|
|
3563
|
-
const hh = pad(d.getHours());
|
|
3564
|
-
const mi = pad(d.getMinutes());
|
|
3565
|
-
if (mode === 'date') return `${yyyy}-${mm}-${dd}`;
|
|
3566
|
-
if (mode === 'time') return `${hh}:${mi}`;
|
|
3567
|
-
return `${yyyy}-${mm}-${dd}T${hh}:${mi}`;
|
|
4857
|
+
return end === '' ? start : `${start} – ${end}`;
|
|
3568
4858
|
}
|
|
3569
|
-
/**
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
4859
|
+
/** The dual-month range grid rendered inside the popover. */ function RangeCalendar(props) {
|
|
4860
|
+
const { value, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale } = props;
|
|
4861
|
+
const range = useDateRange(_extends({
|
|
4862
|
+
value,
|
|
4863
|
+
onChange,
|
|
4864
|
+
monthCount: 2
|
|
4865
|
+
}, minDate !== undefined && {
|
|
4866
|
+
minDate
|
|
4867
|
+
}, maxDate !== undefined && {
|
|
4868
|
+
maxDate
|
|
4869
|
+
}, disabledDates !== undefined && {
|
|
4870
|
+
disabledDates
|
|
4871
|
+
}, isDateDisabled !== undefined && {
|
|
4872
|
+
isDateDisabled
|
|
4873
|
+
}, weekStartDay !== undefined && {
|
|
4874
|
+
weekStartDay
|
|
4875
|
+
}, locale !== undefined && {
|
|
4876
|
+
locale
|
|
4877
|
+
}));
|
|
4878
|
+
const focusedCellRef = useRef(null);
|
|
4879
|
+
const shouldFocusRef = useRef(false);
|
|
4880
|
+
useEffect(()=>{
|
|
4881
|
+
if (shouldFocusRef.current && focusedCellRef.current) {
|
|
4882
|
+
focusedCellRef.current.focus({
|
|
4883
|
+
preventScroll: true
|
|
4884
|
+
});
|
|
4885
|
+
shouldFocusRef.current = false;
|
|
4886
|
+
}
|
|
4887
|
+
}, [
|
|
4888
|
+
range.focusedDate
|
|
4889
|
+
]);
|
|
4890
|
+
const handleKeyDown = (event)=>{
|
|
4891
|
+
const action = resolveCalendarKey(event.key);
|
|
4892
|
+
if (action === null) {
|
|
4893
|
+
return;
|
|
4894
|
+
}
|
|
4895
|
+
event.preventDefault();
|
|
4896
|
+
shouldFocusRef.current = true;
|
|
4897
|
+
switch(action){
|
|
4898
|
+
case 'select':
|
|
4899
|
+
range.selectDate(range.focusedDate);
|
|
4900
|
+
break;
|
|
4901
|
+
case 'monthForward':
|
|
4902
|
+
range.goToNextMonth();
|
|
4903
|
+
break;
|
|
4904
|
+
case 'monthBackward':
|
|
4905
|
+
range.goToPreviousMonth();
|
|
4906
|
+
break;
|
|
4907
|
+
default:
|
|
4908
|
+
range.navigate(action);
|
|
4909
|
+
}
|
|
4910
|
+
};
|
|
4911
|
+
const v = dateRangePickerVariants();
|
|
4912
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
4913
|
+
const lastIndex = range.months.length - 1;
|
|
4914
|
+
return jsx("div", {
|
|
4915
|
+
className: v.panel(),
|
|
4916
|
+
onMouseLeave: ()=>{
|
|
4917
|
+
range.hoverDate(null);
|
|
4918
|
+
},
|
|
4919
|
+
children: range.months.map((monthView, monthIndex)=>jsxs("div", {
|
|
4920
|
+
className: v.month(),
|
|
4921
|
+
children: [
|
|
4922
|
+
jsxs("div", {
|
|
4923
|
+
className: v.header(),
|
|
4924
|
+
children: [
|
|
4925
|
+
monthIndex === 0 ? jsx("button", {
|
|
4926
|
+
type: "button",
|
|
4927
|
+
className: v.navButton(),
|
|
4928
|
+
"aria-label": "Previous month",
|
|
4929
|
+
onClick: ()=>{
|
|
4930
|
+
range.goToPreviousMonth();
|
|
4931
|
+
},
|
|
4932
|
+
children: jsx(ChevronLeftIcon, {})
|
|
4933
|
+
}) : jsx("span", {
|
|
4934
|
+
className: v.navSpacer()
|
|
4935
|
+
}),
|
|
4936
|
+
jsx("span", {
|
|
4937
|
+
"aria-live": "polite",
|
|
4938
|
+
className: v.monthLabel(),
|
|
4939
|
+
children: monthView.monthLabel
|
|
4940
|
+
}),
|
|
4941
|
+
monthIndex === lastIndex ? jsx("button", {
|
|
4942
|
+
type: "button",
|
|
4943
|
+
className: v.navButton(),
|
|
4944
|
+
"aria-label": "Next month",
|
|
4945
|
+
onClick: ()=>{
|
|
4946
|
+
range.goToNextMonth();
|
|
4947
|
+
},
|
|
4948
|
+
children: jsx(ChevronRightIcon$1, {})
|
|
4949
|
+
}) : jsx("span", {
|
|
4950
|
+
className: v.navSpacer()
|
|
4951
|
+
})
|
|
4952
|
+
]
|
|
4953
|
+
}),
|
|
4954
|
+
jsxs("div", {
|
|
4955
|
+
role: "grid",
|
|
4956
|
+
"aria-label": "Choose date range",
|
|
4957
|
+
onKeyDown: handleKeyDown,
|
|
4958
|
+
className: v.grid(),
|
|
4959
|
+
children: [
|
|
4960
|
+
jsx("div", {
|
|
4961
|
+
role: "row",
|
|
4962
|
+
className: v.weekdayRow(),
|
|
4963
|
+
children: range.weekdayNames.map((dayName, index)=>jsx("span", {
|
|
4964
|
+
role: "columnheader",
|
|
4965
|
+
className: v.weekday(),
|
|
4966
|
+
children: dayName
|
|
4967
|
+
}, `weekday-${String(index)}`))
|
|
4968
|
+
}),
|
|
4969
|
+
monthView.weeks.map((week)=>jsx("div", {
|
|
4970
|
+
role: "row",
|
|
4971
|
+
className: v.weekRow(),
|
|
4972
|
+
children: week.days.map((day)=>{
|
|
4973
|
+
const isActiveCell = day.iso === range.focusedDate;
|
|
4974
|
+
const endpoint = day.isRangeStart || day.isRangeEnd;
|
|
4975
|
+
const inBand = day.isInRange || day.isRangePreview;
|
|
4976
|
+
return jsx("span", {
|
|
4977
|
+
role: "gridcell",
|
|
4978
|
+
className: "flex",
|
|
4979
|
+
children: jsx("button", {
|
|
4980
|
+
type: "button",
|
|
4981
|
+
ref: isActiveCell ? focusedCellRef : null,
|
|
4982
|
+
tabIndex: isActiveCell ? 0 : -1,
|
|
4983
|
+
"aria-label": formatLongDate({
|
|
4984
|
+
year: day.year,
|
|
4985
|
+
month: day.month,
|
|
4986
|
+
day: day.day
|
|
4987
|
+
}, resolvedLocale),
|
|
4988
|
+
"aria-current": day.isToday ? 'date' : undefined,
|
|
4989
|
+
"aria-pressed": endpoint,
|
|
4990
|
+
"aria-disabled": day.isDisabled,
|
|
4991
|
+
onClick: ()=>{
|
|
4992
|
+
range.selectDate(day.iso);
|
|
4993
|
+
},
|
|
4994
|
+
onMouseEnter: ()=>{
|
|
4995
|
+
range.hoverDate(day.iso);
|
|
4996
|
+
},
|
|
4997
|
+
className: dateRangeDayVariants({
|
|
4998
|
+
inBand,
|
|
4999
|
+
endpoint,
|
|
5000
|
+
siblingMonth: day.isSiblingMonth,
|
|
5001
|
+
today: day.isToday,
|
|
5002
|
+
disabled: day.isDisabled
|
|
5003
|
+
}),
|
|
5004
|
+
children: day.day
|
|
5005
|
+
})
|
|
5006
|
+
}, day.iso);
|
|
5007
|
+
})
|
|
5008
|
+
}, week.key))
|
|
5009
|
+
]
|
|
5010
|
+
})
|
|
5011
|
+
]
|
|
5012
|
+
}, `${String(monthView.year)}-${String(monthView.month)}`))
|
|
5013
|
+
});
|
|
3575
5014
|
}
|
|
3576
5015
|
/**
|
|
3577
|
-
* Dashforge TW
|
|
5016
|
+
* Dashforge TW `<DateRangePicker>` — a form-bound start/end date field.
|
|
3578
5017
|
*
|
|
3579
|
-
*
|
|
3580
|
-
*
|
|
3581
|
-
*
|
|
3582
|
-
* - `datetime` — `<input type="datetime-local">` → `YYYY-MM-DDTHH:mm`
|
|
5018
|
+
* A read-only trigger button paired with a dual-month range calendar popover
|
|
5019
|
+
* (Radix Popover), built on the headless `useDateRange` engine. Integrates
|
|
5020
|
+
* with the Dashforge form bridge + RBAC.
|
|
3583
5021
|
*
|
|
3584
|
-
*
|
|
3585
|
-
*
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
* keyboard nav, OS-provided calendar/clock UI.
|
|
3590
|
-
* 4. Predictable state (no internal Aria state machine that fights
|
|
3591
|
-
* controlled props — the F5-A lesson learned in `<Autocomplete>`).
|
|
3592
|
-
*
|
|
3593
|
-
* A richer custom-calendar version (range mode, locale awareness,
|
|
3594
|
-
* custom popover styling) can land as F5-B-bis built on Aria
|
|
3595
|
-
* `<DatePicker>` / `<Calendar>`.
|
|
3596
|
-
*
|
|
3597
|
-
* **A11y**:
|
|
3598
|
-
* - `aria-invalid` driven by the resolved validation state.
|
|
3599
|
-
* - `aria-describedby` links input ↔ helper/error.
|
|
3600
|
-
* - Required fields get the native `required` attribute + a visual `*`.
|
|
3601
|
-
* - `color-scheme: light dark` keeps the OS calendar icon readable
|
|
3602
|
-
* in both color schemes.
|
|
3603
|
-
*/ function DateTimePicker(props) {
|
|
3604
|
-
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_inputWrapper, _slotProps_input, _slotProps_errorText, _slotProps_helperText;
|
|
3605
|
-
const { name, rules, mode = 'date', visibleWhen, layout = 'stacked', size, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, min, max, step, value: explicitValue, defaultValue, onValueChange } = props;
|
|
3606
|
-
// ───── Hooks (unconditional) ─────
|
|
5022
|
+
* Storage contract: a `{ start, end }` pair of ISO `YYYY-MM-DD` dates —
|
|
5023
|
+
* shared with the MUI `@dashforge/ui` `DateRangePicker`.
|
|
5024
|
+
*/ function DateRangePicker(props) {
|
|
5025
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_trigger, _slotProps_errorText, _slotProps_helperText;
|
|
5026
|
+
const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, fullWidth, sx, slotProps, testId } = props;
|
|
3607
5027
|
const bridge = useContext(DashFormContext);
|
|
3608
5028
|
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
3609
|
-
// Per-field reactive snapshot. Same pattern as <TextField> — re-renders
|
|
3610
|
-
// this component when its OWN bridge value changes, but not when a
|
|
3611
|
-
// sibling field changes (perf contract).
|
|
3612
5029
|
const fieldMeta = useDashFieldMeta(name);
|
|
3613
5030
|
const accessState = useAccessState(access);
|
|
3614
|
-
const
|
|
3615
|
-
const helperId = `${
|
|
3616
|
-
|
|
5031
|
+
const fieldId = useId();
|
|
5032
|
+
const helperId = `${fieldId}-help`;
|
|
5033
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
5034
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : EMPTY_RANGE);
|
|
3617
5035
|
const unregisterRef = useRef({
|
|
3618
5036
|
bridge,
|
|
3619
5037
|
name
|
|
@@ -3634,9 +5052,8 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3634
5052
|
});
|
|
3635
5053
|
};
|
|
3636
5054
|
}, []);
|
|
3637
|
-
// ───── Derived ─────
|
|
3638
5055
|
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
3639
|
-
const
|
|
5056
|
+
const isInteractive = !effectiveDisabled && !accessState.readonly;
|
|
3640
5057
|
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
3641
5058
|
let resolvedError = error;
|
|
3642
5059
|
let resolvedHelperText = helperText;
|
|
@@ -3651,65 +5068,81 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3651
5068
|
resolvedValue = explicitValue;
|
|
3652
5069
|
} else {
|
|
3653
5070
|
const bv = fieldMeta.value;
|
|
3654
|
-
resolvedValue = bv
|
|
5071
|
+
resolvedValue = bv != null ? bv : EMPTY_RANGE;
|
|
3655
5072
|
}
|
|
3656
5073
|
} else if (explicitValue !== undefined) {
|
|
3657
5074
|
resolvedValue = explicitValue;
|
|
3658
5075
|
} else {
|
|
3659
|
-
|
|
3660
|
-
resolvedValue = undefined;
|
|
5076
|
+
resolvedValue = internalValue;
|
|
3661
5077
|
}
|
|
3662
|
-
// Registration-supplied callback ref (RHF threads its own ref through
|
|
3663
|
-
// here in form mode). Stable callback to keep React 19 happy.
|
|
3664
5078
|
const registrationRefFn = registration == null ? void 0 : registration.ref;
|
|
3665
|
-
const
|
|
5079
|
+
const triggerRef = useCallback((instance)=>{
|
|
3666
5080
|
if (typeof registrationRefFn === 'function') {
|
|
3667
5081
|
registrationRefFn(instance);
|
|
3668
5082
|
}
|
|
3669
5083
|
}, [
|
|
3670
5084
|
registrationRefFn
|
|
3671
5085
|
]);
|
|
3672
|
-
// ───── Render-time guards (after all hooks) ─────
|
|
3673
5086
|
if (!isVisible) return null;
|
|
3674
5087
|
if (!accessState.visible) return null;
|
|
3675
|
-
const v =
|
|
3676
|
-
size,
|
|
5088
|
+
const v = dateRangePickerVariants({
|
|
3677
5089
|
layout,
|
|
3678
5090
|
error: resolvedError,
|
|
3679
|
-
fullWidth
|
|
3680
|
-
disabled: effectiveDisabled
|
|
5091
|
+
fullWidth
|
|
3681
5092
|
});
|
|
3682
|
-
const
|
|
3683
|
-
const
|
|
3684
|
-
const
|
|
3685
|
-
const nativeMin = min != null ? isoToInputValue(mode, min) : undefined;
|
|
3686
|
-
const nativeMax = max != null ? isoToInputValue(mode, max) : undefined;
|
|
3687
|
-
const handleChange = (event)=>{
|
|
3688
|
-
const next = inputValueToStored(event.target.value);
|
|
5093
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
5094
|
+
const displayValue = formatRange(resolvedValue, resolvedLocale);
|
|
5095
|
+
const commitValue = (next)=>{
|
|
3689
5096
|
if (isFormMode && bridge) {
|
|
3690
5097
|
var _registration_onChange;
|
|
3691
5098
|
bridge.setValue == null ? void 0 : bridge.setValue.call(bridge, name, next);
|
|
3692
5099
|
void (registration == null ? void 0 : (_registration_onChange = registration.onChange) == null ? void 0 : _registration_onChange.call(registration, {
|
|
3693
5100
|
target: {
|
|
3694
5101
|
name,
|
|
3695
|
-
value: next
|
|
5102
|
+
value: next
|
|
3696
5103
|
},
|
|
3697
5104
|
type: 'change'
|
|
3698
5105
|
}));
|
|
5106
|
+
} else if (explicitValue === undefined) {
|
|
5107
|
+
setInternalValue(next);
|
|
3699
5108
|
}
|
|
3700
|
-
|
|
5109
|
+
onChange == null ? void 0 : onChange(next);
|
|
3701
5110
|
};
|
|
3702
|
-
const
|
|
5111
|
+
const markTouched = ()=>{
|
|
3703
5112
|
if (isFormMode && bridge) {
|
|
3704
5113
|
var _registration_onBlur;
|
|
3705
|
-
|
|
5114
|
+
const committed = bridge.getValue(name);
|
|
5115
|
+
void (registration == null ? void 0 : (_registration_onBlur = registration.onBlur) == null ? void 0 : _registration_onBlur.call(registration, {
|
|
5116
|
+
target: {
|
|
5117
|
+
name,
|
|
5118
|
+
value: committed
|
|
5119
|
+
},
|
|
5120
|
+
type: 'blur'
|
|
5121
|
+
}));
|
|
5122
|
+
}
|
|
5123
|
+
};
|
|
5124
|
+
const handleOpenChange = (next)=>{
|
|
5125
|
+
if (next && !isInteractive) {
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
setIsOpen(next);
|
|
5129
|
+
if (!next) {
|
|
5130
|
+
markTouched();
|
|
5131
|
+
}
|
|
5132
|
+
};
|
|
5133
|
+
const handleRangeChange = (next)=>{
|
|
5134
|
+
commitValue(next);
|
|
5135
|
+
if (next.start !== null && next.end !== null) {
|
|
5136
|
+
setIsOpen(false);
|
|
5137
|
+
markTouched();
|
|
3706
5138
|
}
|
|
3707
5139
|
};
|
|
3708
5140
|
return jsxs("div", {
|
|
5141
|
+
"data-testid": testId,
|
|
3709
5142
|
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
3710
5143
|
children: [
|
|
3711
5144
|
label && jsxs("label", {
|
|
3712
|
-
htmlFor:
|
|
5145
|
+
htmlFor: fieldId,
|
|
3713
5146
|
className: cn(v.label(), slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
3714
5147
|
children: [
|
|
3715
5148
|
label,
|
|
@@ -3720,29 +5153,39 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3720
5153
|
})
|
|
3721
5154
|
]
|
|
3722
5155
|
}),
|
|
3723
|
-
jsx(
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
5156
|
+
jsx(Popover, {
|
|
5157
|
+
open: isOpen,
|
|
5158
|
+
onOpenChange: handleOpenChange,
|
|
5159
|
+
side: "bottom",
|
|
5160
|
+
align: "start",
|
|
5161
|
+
content: jsx(RangeCalendar, {
|
|
5162
|
+
value: resolvedValue,
|
|
5163
|
+
onChange: handleRangeChange,
|
|
5164
|
+
minDate: minDate,
|
|
5165
|
+
maxDate: maxDate,
|
|
5166
|
+
disabledDates: disabledDates,
|
|
5167
|
+
isDateDisabled: isDateDisabled,
|
|
5168
|
+
weekStartDay: weekStartDay,
|
|
5169
|
+
locale: locale
|
|
5170
|
+
}),
|
|
5171
|
+
children: jsxs("button", {
|
|
5172
|
+
type: "button",
|
|
5173
|
+
id: fieldId,
|
|
5174
|
+
ref: triggerRef,
|
|
3738
5175
|
disabled: effectiveDisabled,
|
|
3739
|
-
readOnly: effectiveReadOnly,
|
|
3740
|
-
required: required,
|
|
3741
5176
|
"aria-invalid": resolvedError ? true : undefined,
|
|
3742
5177
|
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
5178
|
+
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
5179
|
+
children: [
|
|
5180
|
+
jsx("span", {
|
|
5181
|
+
className: displayValue ? v.value() : v.placeholder(),
|
|
5182
|
+
children: displayValue || placeholder || 'Select a date range'
|
|
5183
|
+
}),
|
|
5184
|
+
jsx("span", {
|
|
5185
|
+
className: v.icon(),
|
|
5186
|
+
children: jsx(CalendarIcon, {})
|
|
5187
|
+
})
|
|
5188
|
+
]
|
|
3746
5189
|
})
|
|
3747
5190
|
}),
|
|
3748
5191
|
resolvedHelperText && jsx("p", {
|
|
@@ -7176,71 +8619,6 @@ const tooltipVariants = tv({
|
|
|
7176
8619
|
});
|
|
7177
8620
|
}
|
|
7178
8621
|
|
|
7179
|
-
const popoverVariants = tv({
|
|
7180
|
-
slots: {
|
|
7181
|
-
content: [
|
|
7182
|
-
// Neutral border auto-inverts via CSS-var swap.
|
|
7183
|
-
'z-50 rounded-md border border-neutral-200',
|
|
7184
|
-
// bg-white hardcoded (no auto-invert) so `dark:` is required.
|
|
7185
|
-
// Target `dark:bg-neutral-100` resolves to #171717 in dark mode
|
|
7186
|
-
// (one elevation tier above the page surface).
|
|
7187
|
-
'bg-white dark:bg-neutral-100 p-4 shadow-lg',
|
|
7188
|
-
// Text auto-inverts.
|
|
7189
|
-
'text-sm text-neutral-900',
|
|
7190
|
-
'focus:outline-none',
|
|
7191
|
-
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
7192
|
-
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
7193
|
-
'motion-reduce:transition-none motion-reduce:duration-0'
|
|
7194
|
-
],
|
|
7195
|
-
// Arrow follows content bg: `fill-white` in light + matching dark target.
|
|
7196
|
-
arrow: 'fill-white dark:fill-neutral-100'
|
|
7197
|
-
}
|
|
7198
|
-
});
|
|
7199
|
-
|
|
7200
|
-
/**
|
|
7201
|
-
* Dashforge TW `<Popover>` — clickable floating content panel.
|
|
7202
|
-
*
|
|
7203
|
-
* Built on `@radix-ui/react-popover`:
|
|
7204
|
-
* - Focus trap inside the popover (Tab cycles within).
|
|
7205
|
-
* - Outside-click + Escape dismiss.
|
|
7206
|
-
* - APG dialog-like semantics (`role="dialog"` on content).
|
|
7207
|
-
* - Portal-rendered.
|
|
7208
|
-
*
|
|
7209
|
-
* Use `<Popover>` for menus / pickers / settings panels with
|
|
7210
|
-
* interactive children. Use `<Tooltip>` for read-only hints.
|
|
7211
|
-
*/ function Popover(props) {
|
|
7212
|
-
var _slotProps_content, _slotProps_arrow;
|
|
7213
|
-
const { children, content, side = 'bottom', align = 'center', showArrow = false, open, onOpenChange, sideOffset = 8, width, slotProps } = props;
|
|
7214
|
-
const v = popoverVariants();
|
|
7215
|
-
return jsxs(RadixPopover.Root, {
|
|
7216
|
-
open: open,
|
|
7217
|
-
onOpenChange: onOpenChange,
|
|
7218
|
-
children: [
|
|
7219
|
-
jsx(RadixPopover.Trigger, {
|
|
7220
|
-
asChild: true,
|
|
7221
|
-
children: children
|
|
7222
|
-
}),
|
|
7223
|
-
jsx(RadixPopover.Portal, {
|
|
7224
|
-
children: jsxs(RadixPopover.Content, {
|
|
7225
|
-
side: side,
|
|
7226
|
-
align: align,
|
|
7227
|
-
sideOffset: sideOffset,
|
|
7228
|
-
style: width != null ? {
|
|
7229
|
-
width
|
|
7230
|
-
} : undefined,
|
|
7231
|
-
className: cn(v.content(), slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
|
|
7232
|
-
children: [
|
|
7233
|
-
content,
|
|
7234
|
-
showArrow && jsx(RadixPopover.Arrow, {
|
|
7235
|
-
className: cn(v.arrow(), slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
|
|
7236
|
-
})
|
|
7237
|
-
]
|
|
7238
|
-
})
|
|
7239
|
-
})
|
|
7240
|
-
]
|
|
7241
|
-
});
|
|
7242
|
-
}
|
|
7243
|
-
|
|
7244
8622
|
const accordionVariants = tv({
|
|
7245
8623
|
slots: {
|
|
7246
8624
|
root: 'w-full',
|
|
@@ -11417,6 +12795,6 @@ function SearchIcon(props) {
|
|
|
11417
12795
|
*/ // ───── Components ─────
|
|
11418
12796
|
/**
|
|
11419
12797
|
* Package version (synced with `package.json` at publish time).
|
|
11420
|
-
*/ const VERSION = '0.
|
|
12798
|
+
*/ const VERSION = '0.9.1-beta';
|
|
11421
12799
|
|
|
11422
|
-
export { Accordion, AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Checkbox, ConfirmDialogProvider, Container, DataGrid, DateTimePicker, Dialog, Divider, Grid, LeftNav, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, Skeleton, SnackbarProvider, Stack, Switch, Table, Tabs, TextField, Textarea, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants,
|
|
12800
|
+
export { Accordion, AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Calendar, Checkbox, ConfirmDialogProvider, Container, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Grid, LeftNav, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, Skeleton, SnackbarProvider, Stack, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants, leftNavVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, radioGroupVariants, skeletonVariants, snackbarVariants, stackVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };
|