acai-ui-library 0.1.18 → 0.1.19
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/dist/components/date-time-picker.cjs +63 -21
- package/dist/components/date-time-picker.cjs.map +1 -1
- package/dist/components/date-time-picker.d.cts +15 -2
- package/dist/components/date-time-picker.d.ts +15 -2
- package/dist/components/date-time-picker.js +63 -21
- package/dist/components/date-time-picker.js.map +1 -1
- package/dist/index.cjs +63 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +63 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -518,6 +518,8 @@ var DateTimePicker = React4.forwardRef(
|
|
|
518
518
|
formatValue,
|
|
519
519
|
clearable = true,
|
|
520
520
|
clearLabel = "Clear",
|
|
521
|
+
applyLabel = "Apply",
|
|
522
|
+
cancelLabel = "Cancel",
|
|
521
523
|
timeLabel = "Time",
|
|
522
524
|
disabled,
|
|
523
525
|
id,
|
|
@@ -530,13 +532,37 @@ var DateTimePicker = React4.forwardRef(
|
|
|
530
532
|
"aria-label": ariaLabel,
|
|
531
533
|
"aria-labelledby": ariaLabelledBy
|
|
532
534
|
}, ref) => {
|
|
535
|
+
const [draft, setDraft] = React4.useState(value);
|
|
536
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React4.useState(false);
|
|
537
|
+
const isOpen = open ?? uncontrolledOpen;
|
|
538
|
+
const wasOpen = React4.useRef(isOpen);
|
|
539
|
+
React4.useEffect(() => {
|
|
540
|
+
if (isOpen && !wasOpen.current) setDraft(value);
|
|
541
|
+
wasOpen.current = isOpen;
|
|
542
|
+
}, [isOpen, value]);
|
|
543
|
+
const setOpenState = (next) => {
|
|
544
|
+
if (open === void 0) setUncontrolledOpen(next);
|
|
545
|
+
onOpenChange?.(next);
|
|
546
|
+
};
|
|
547
|
+
const handleOpenChange = (next) => {
|
|
548
|
+
setDraft(value);
|
|
549
|
+
setOpenState(next);
|
|
550
|
+
};
|
|
551
|
+
const handleCancel = () => {
|
|
552
|
+
setDraft(value);
|
|
553
|
+
setOpenState(false);
|
|
554
|
+
};
|
|
555
|
+
const handleApply = () => {
|
|
556
|
+
onValueChange?.(draft);
|
|
557
|
+
setOpenState(false);
|
|
558
|
+
};
|
|
533
559
|
const activeDay = React4.useMemo(() => {
|
|
534
|
-
if (
|
|
560
|
+
if (draft) return startOfDay(draft);
|
|
535
561
|
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
536
562
|
if (minDate && today < startOfDay(minDate)) return startOfDay(minDate);
|
|
537
563
|
if (maxDate && today > startOfDay(maxDate)) return startOfDay(maxDate);
|
|
538
564
|
return today;
|
|
539
|
-
}, [
|
|
565
|
+
}, [draft, minDate, maxDate]);
|
|
540
566
|
const timeWindowFor = React4.useCallback(
|
|
541
567
|
(day) => ({
|
|
542
568
|
minTime: minDate && isSameDay(day, minDate) ? formatTimeValue(minutesOfDay(minDate)) : void 0,
|
|
@@ -563,12 +589,12 @@ var DateTimePicker = React4.forwardRef(
|
|
|
563
589
|
}, [minDate, maxDate, disabledDates]);
|
|
564
590
|
const handleDaySelect = (day) => {
|
|
565
591
|
if (!day) return;
|
|
566
|
-
const desired =
|
|
592
|
+
const desired = draft ? minutesOfDay(draft) : parseTimeValue(defaultTime) ?? 0;
|
|
567
593
|
const { minTime, maxTime } = timeWindowFor(day);
|
|
568
594
|
const min = parseTimeValue(minTime);
|
|
569
595
|
const max = parseTimeValue(maxTime);
|
|
570
596
|
if (isTimeInRange(desired, min, max)) {
|
|
571
|
-
|
|
597
|
+
setDraft(withTime(day, desired));
|
|
572
598
|
return;
|
|
573
599
|
}
|
|
574
600
|
const snapped = snapToAllowedSlot(
|
|
@@ -576,15 +602,15 @@ var DateTimePicker = React4.forwardRef(
|
|
|
576
602
|
buildAllowedSlots(buildMinuteList(minuteStep, desired % 60), min, max)
|
|
577
603
|
);
|
|
578
604
|
if (snapped === void 0) return;
|
|
579
|
-
|
|
605
|
+
setDraft(withTime(day, snapped));
|
|
580
606
|
};
|
|
581
607
|
const handleTimeChange = (next) => {
|
|
582
608
|
const minutes = parseTimeValue(next);
|
|
583
609
|
if (minutes === void 0) return;
|
|
584
|
-
|
|
610
|
+
setDraft(withTime(draft ?? activeDay, minutes));
|
|
585
611
|
};
|
|
586
612
|
const label = value ? formatValue?.(value) ?? `${value.toLocaleDateString()} ${formatTimeLabel(minutesOfDay(value), hourCycle)}` : placeholder;
|
|
587
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Popover, { open, onOpenChange, children: [
|
|
613
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Popover, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
588
614
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
589
615
|
Button,
|
|
590
616
|
{
|
|
@@ -618,7 +644,7 @@ var DateTimePicker = React4.forwardRef(
|
|
|
618
644
|
Calendar,
|
|
619
645
|
{
|
|
620
646
|
mode: "single",
|
|
621
|
-
selected:
|
|
647
|
+
selected: draft,
|
|
622
648
|
defaultMonth: activeDay,
|
|
623
649
|
startMonth: calendarStartMonth,
|
|
624
650
|
endMonth: calendarEndMonth,
|
|
@@ -632,7 +658,7 @@ var DateTimePicker = React4.forwardRef(
|
|
|
632
658
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
633
659
|
TimePickerPanel,
|
|
634
660
|
{
|
|
635
|
-
value:
|
|
661
|
+
value: draft ? formatTimeValue(minutesOfDay(draft)) : void 0,
|
|
636
662
|
onValueChange: handleTimeChange,
|
|
637
663
|
minTime: activeWindow.minTime,
|
|
638
664
|
maxTime: activeWindow.maxTime,
|
|
@@ -642,18 +668,34 @@ var DateTimePicker = React4.forwardRef(
|
|
|
642
668
|
)
|
|
643
669
|
] })
|
|
644
670
|
] }),
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
671
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 border-t border-border p-2", children: [
|
|
672
|
+
clearable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
673
|
+
Button,
|
|
674
|
+
{
|
|
675
|
+
type: "button",
|
|
676
|
+
variant: "ghost",
|
|
677
|
+
size: "sm",
|
|
678
|
+
className: "font-normal",
|
|
679
|
+
disabled: !draft,
|
|
680
|
+
onClick: () => setDraft(void 0),
|
|
681
|
+
children: clearLabel
|
|
682
|
+
}
|
|
683
|
+
),
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ms-auto flex items-center gap-2", children: [
|
|
685
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
686
|
+
Button,
|
|
687
|
+
{
|
|
688
|
+
type: "button",
|
|
689
|
+
variant: "ghost",
|
|
690
|
+
size: "sm",
|
|
691
|
+
className: "font-normal",
|
|
692
|
+
onClick: handleCancel,
|
|
693
|
+
children: cancelLabel
|
|
694
|
+
}
|
|
695
|
+
),
|
|
696
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { type: "button", variant: "default", size: "sm", onClick: handleApply, children: applyLabel })
|
|
697
|
+
] })
|
|
698
|
+
] })
|
|
657
699
|
]
|
|
658
700
|
}
|
|
659
701
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/date-time-picker.tsx","../../src/lib/utils.ts","../../src/components/button.tsx","../../src/components/calendar.tsx","../../src/components/popover.tsx","../../src/components/time-picker.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { CalendarClockIcon } from \"lucide-react\"\nimport type { Matcher } from \"react-day-picker\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Calendar } from \"./calendar\"\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\"\nimport {\n TimePickerPanel,\n buildAllowedSlots,\n buildMinuteList,\n formatTimeLabel,\n formatTimeValue,\n isTimeInRange,\n parseTimeValue,\n snapToAllowedSlot,\n type TimeValue,\n} from \"./time-picker\"\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n )\n}\n\n/** Minutes since midnight for a Date, in local time. */\nfunction minutesOfDay(date: Date): number {\n return date.getHours() * 60 + date.getMinutes()\n}\n\n/** A new Date on `day` at `minutes` past midnight, seconds zeroed. */\nfunction withTime(day: Date, minutes: number): Date {\n return new Date(\n day.getFullYear(),\n day.getMonth(),\n day.getDate(),\n Math.floor(minutes / 60),\n minutes % 60,\n 0,\n 0\n )\n}\n\nexport interface DateTimePickerProps {\n /** The selected moment. One value covers both the date and the time. */\n value?: Date\n /** Called with the new moment, or with undefined when the value is cleared. */\n onValueChange?: (value: Date | undefined) => void\n /**\n * Earliest selectable moment. Days before it are disabled in the calendar\n * and, on the boundary day itself, the times before it are disabled too.\n */\n minDate?: Date\n /**\n * Latest selectable moment. Days after it are disabled in the calendar and,\n * on the boundary day itself, the times after it are disabled too.\n */\n maxDate?: Date\n /** Extra day matchers passed through to the underlying Calendar. */\n disabledDates?: Matcher | Matcher[]\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /**\n * Time applied when a date is picked before any time has been chosen, as\n * \"HH:mm\". Defaults to \"09:00\" and is moved to the nearest allowed slot when\n * the bound forbids it.\n */\n defaultTime?: TimeValue\n /** Trigger text shown when nothing is selected. */\n placeholder?: string\n /** Overrides how the selected moment is rendered on the trigger. */\n formatValue?: (value: Date) => string\n /** Shows a Clear action in the popover. Defaults to true. */\n clearable?: boolean\n /** Text of the Clear action. Defaults to \"Clear\". */\n clearLabel?: string\n /** Heading above the time columns. Defaults to \"Time\". */\n timeLabel?: string\n /** Disables the whole control. */\n disabled?: boolean\n /** Accessible name for the trigger. Defaults to the placeholder. */\n \"aria-label\"?: string\n /** Id of an element that labels the trigger. */\n \"aria-labelledby\"?: string\n /** Id applied to the trigger button. */\n id?: string\n /** Name reported on a hidden input, so the value posts with a plain form. */\n name?: string\n /** Classname applied to the trigger button. */\n className?: string\n /** Classname applied to the popover content. */\n contentClassName?: string\n /** Controlled open state of the popover. */\n open?: boolean\n /** Called when the popover opens or closes. */\n onOpenChange?: (open: boolean) => void\n /** Side the popover is aligned to. Defaults to \"start\". */\n align?: \"start\" | \"center\" | \"end\"\n}\n\n/**\n * A date and a time in one popover, reporting a single Date. Drop-in\n * replacement for a native `datetime-local` input: the calendar and the time\n * columns write to the same value, and `minDate` / `maxDate` are enforced on\n * both halves, including on the boundary day where only part of the day is\n * still allowed.\n */\nconst DateTimePicker = React.forwardRef<HTMLButtonElement, DateTimePickerProps>(\n (\n {\n value,\n onValueChange,\n minDate,\n maxDate,\n disabledDates,\n minuteStep = 5,\n hourCycle = 12,\n defaultTime = \"09:00\",\n placeholder = \"Pick a date and time\",\n formatValue,\n clearable = true,\n clearLabel = \"Clear\",\n timeLabel = \"Time\",\n disabled,\n id,\n name,\n className,\n contentClassName,\n open,\n onOpenChange,\n align = \"start\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref\n ) => {\n // The day the time columns are bound against. Without a selection yet that\n // is today, pulled inside the allowed range so the columns are usable.\n const activeDay = React.useMemo(() => {\n if (value) return startOfDay(value)\n const today = startOfDay(new Date())\n if (minDate && today < startOfDay(minDate)) return startOfDay(minDate)\n if (maxDate && today > startOfDay(maxDate)) return startOfDay(maxDate)\n return today\n }, [value, minDate, maxDate])\n\n /** The time window still open on a given day, once the bound is applied. */\n const timeWindowFor = React.useCallback(\n (day: Date): { minTime?: TimeValue; maxTime?: TimeValue } => ({\n minTime:\n minDate && isSameDay(day, minDate)\n ? formatTimeValue(minutesOfDay(minDate))\n : undefined,\n maxTime:\n maxDate && isSameDay(day, maxDate)\n ? formatTimeValue(minutesOfDay(maxDate))\n : undefined,\n }),\n [minDate, maxDate]\n )\n\n const activeWindow = timeWindowFor(activeDay)\n\n // The year dropdown follows the bound rather than Calendar's generic\n // default: with minDate set to now, a deadline picker offering 2016 would\n // be offering years every one of whose days it then refuses.\n const calendarStartMonth = React.useMemo(\n () => (minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), 1) : undefined),\n [minDate],\n )\n const calendarEndMonth = React.useMemo(\n () => (maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), 1) : undefined),\n [maxDate],\n )\n\n const calendarDisabled = React.useMemo(() => {\n const matchers: Matcher[] = []\n if (minDate) matchers.push({ before: startOfDay(minDate) })\n if (maxDate) matchers.push({ after: startOfDay(maxDate) })\n if (Array.isArray(disabledDates)) matchers.push(...disabledDates)\n else if (disabledDates !== undefined) matchers.push(disabledDates)\n return matchers\n }, [minDate, maxDate, disabledDates])\n\n const handleDaySelect = (day: Date | undefined) => {\n // Clicking the selected day again deselects in react-day-picker. A\n // deadline should not disappear by accident, so Clear is the only way out.\n if (!day) return\n const desired = value ? minutesOfDay(value) : (parseTimeValue(defaultTime) ?? 0)\n const { minTime, maxTime } = timeWindowFor(day)\n const min = parseTimeValue(minTime)\n const max = parseTimeValue(maxTime)\n if (isTimeInRange(desired, min, max)) {\n onValueChange?.(withTime(day, desired))\n return\n }\n const snapped = snapToAllowedSlot(\n desired,\n buildAllowedSlots(buildMinuteList(minuteStep, desired % 60), min, max)\n )\n if (snapped === undefined) return\n onValueChange?.(withTime(day, snapped))\n }\n\n const handleTimeChange = (next: TimeValue) => {\n const minutes = parseTimeValue(next)\n if (minutes === undefined) return\n onValueChange?.(withTime(value ?? activeDay, minutes))\n }\n\n const label = value\n ? (formatValue?.(value) ??\n `${value.toLocaleDateString()} ${formatTimeLabel(minutesOfDay(value), hourCycle)}`)\n : placeholder\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n id={id}\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n aria-labelledby={ariaLabelledBy}\n className={cn(\n \"w-[280px] justify-start text-left font-normal\",\n !value && \"text-muted-foreground\",\n className\n )}\n >\n <CalendarClockIcon className=\"me-2 h-4 w-4\" aria-hidden=\"true\" />\n {label}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto p-0\", contentClassName)}\n align={align}\n aria-label={ariaLabel ?? placeholder}\n >\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:divide-x sm:divide-border\">\n <Calendar\n mode=\"single\"\n selected={value}\n defaultMonth={activeDay}\n startMonth={calendarStartMonth}\n endMonth={calendarEndMonth}\n onSelect={handleDaySelect}\n disabled={calendarDisabled}\n // Focus moves into the calendar the user just opened (keyboard navigation).\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus\n />\n <div className=\"border-t border-border p-2 sm:border-t-0\">\n <div className=\"px-1 pb-1 text-xs font-medium uppercase tracking-wider text-muted-foreground\">\n {timeLabel}\n </div>\n <TimePickerPanel\n value={value ? formatTimeValue(minutesOfDay(value)) : undefined}\n onValueChange={handleTimeChange}\n minTime={activeWindow.minTime}\n maxTime={activeWindow.maxTime}\n minuteStep={minuteStep}\n hourCycle={hourCycle}\n />\n </div>\n </div>\n {clearable && (\n <div className=\"flex justify-end border-t border-border p-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"font-normal\"\n disabled={!value}\n onClick={() => onValueChange?.(undefined)}\n >\n {clearLabel}\n </Button>\n </div>\n )}\n </PopoverContent>\n {name && (\n <input\n type=\"hidden\"\n name={name}\n value={\n value\n ? `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, \"0\")}-${String(\n value.getDate()\n ).padStart(2, \"0\")}T${formatTimeValue(minutesOfDay(value))}`\n : \"\"\n }\n />\n )}\n </Popover>\n )\n }\n)\nDateTimePicker.displayName = \"DateTimePicker\"\n\nexport { DateTimePicker }\n","import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Loader2 } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground hover:bg-primary-hover\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive-hover\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary-hover\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground\",\n link:\n \"text-primary underline-offset-4 hover:underline\",\n success:\n \"bg-success text-success-foreground hover:bg-success-hover\",\n },\n size: {\n xs: \"h-7 px-2.5 text-xs\",\n sm: \"h-9 px-3\",\n default: \"h-10 px-4 py-2\",\n lg: \"h-11 px-8\",\n icon: \"h-10 w-10\",\n \"icon-sm\": \"h-8 w-8\",\n },\n shape: {\n default: \"rounded-md\",\n rounded: \"rounded-lg\",\n pill: \"rounded-full\",\n square: \"rounded-none\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n shape: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n loading?: boolean\n prefixIcon?: React.ReactNode\n suffixIcon?: React.ReactNode\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n shape,\n asChild = false,\n loading = false,\n prefixIcon,\n suffixIcon,\n children,\n disabled,\n ...props\n },\n ref\n ) => {\n const Comp = asChild ? Slot : \"button\"\n const isDisabled = disabled || loading\n\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, shape, className }))}\n ref={ref}\n disabled={isDisabled}\n {...props}\n >\n {loading && <Loader2 className=\"animate-spin\" />}\n {!loading && prefixIcon}\n {children}\n {suffixIcon}\n </Comp>\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n","import * as React from \"react\"\nimport { DayPicker } from \"react-day-picker\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker>\n\n/**\n * How far the year dropdown reaches when the caller gives no bounds.\n *\n * `captionLayout` defaults to \"dropdown\" here, and react-day-picker then falls\n * back to [currentYear - 100, currentYear] for the year list. That is wrong in\n * both directions: a hundred dead years to scroll past, and no future year at\n * all - a deadline could not be set beyond December of the current year.\n * Callers that know their range pass `startMonth` / `endMonth` and these are\n * not used.\n */\nconst DEFAULT_YEARS_BACK = 10\nconst DEFAULT_YEARS_FORWARD = 10\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"dropdown\",\n startMonth,\n endMonth,\n ...props\n}: CalendarProps) {\n const now = new Date()\n const defaultedStartMonth =\n startMonth ?? new Date(now.getFullYear() - DEFAULT_YEARS_BACK, 0, 1)\n const defaultedEndMonth =\n endMonth ?? new Date(now.getFullYear() + DEFAULT_YEARS_FORWARD, 11, 1)\n\n return (\n <DayPicker\n startMonth={defaultedStartMonth}\n endMonth={defaultedEndMonth}\n showOutsideDays={showOutsideDays}\n className={cn(\"w-[260px] rounded-lg p-3\", className)}\n classNames={{\n months: \"flex flex-col\",\n month: \"space-y-3\",\n month_caption: \"relative flex h-8 items-center justify-center\",\n caption_label: \"text-sm font-medium hidden\",\n nav: \"flex items-center\",\n button_previous: cn(\n \"absolute start-0 inline-flex size-7 items-center justify-center rounded-md bg-transparent p-0 text-muted-foreground hover:bg-[var(--hc-color-border-subtle)] hover:text-foreground transition-colors cursor-pointer border-none\"\n ),\n button_next: cn(\n \"absolute end-0 inline-flex size-7 items-center justify-center rounded-md bg-transparent p-0 text-muted-foreground hover:bg-[var(--hc-color-border-subtle)] hover:text-foreground transition-colors cursor-pointer border-none\"\n ),\n month_grid: \"w-full border-collapse\",\n weekdays: \"flex w-full\",\n weekday:\n \"flex-1 h-8 flex items-center justify-center text-[11px] font-medium text-muted-foreground uppercase tracking-wider\",\n week: \"flex w-full mt-1\",\n day: \"flex-1 h-8 p-0 relative focus-within:z-20 flex items-center justify-center text-sm\",\n day_button: cn(\n \"inline-flex size-8 items-center justify-center rounded-md bg-transparent text-sm font-normal cursor-pointer border-none hover:bg-[var(--hc-color-border-subtle)] transition-colors\"\n ),\n range_end: \"day-range-end\",\n selected:\n \"[&>button]:bg-[var(--hc-primary-600)] [&>button]:text-white [&>button]:hover:bg-[var(--hc-primary-600)] [&>button]:hover:text-white [&>button]:font-medium\",\n today: \"[&>button]:font-semibold [&>button]:text-[var(--hc-primary-600)]\",\n outside:\n \"day-outside [&>button]:bg-transparent [&>button]:text-[var(--hc-grey-200)]\",\n disabled: \"[&>button]:text-[var(--hc-grey-200)] [&>button]:opacity-50 [&>button]:cursor-default\",\n range_middle:\n \"[&>button]:bg-[var(--hc-primary-100)] [&>button]:text-foreground [&>button]:rounded-none\",\n range_start: \"[&>button]:rounded-e-none\",\n dropdowns: \"flex items-center gap-1.5 justify-center [&>select]:flex-1\",\n dropdown:\n \"h-7 w-[88px] rounded-md border border-[var(--hc-color-border-subtle)] bg-background px-2 text-xs font-medium text-foreground cursor-pointer outline-none accent-[var(--hc-primary-600)] focus:border-[var(--hc-primary-500)] focus:ring-[2px] focus:ring-[var(--hc-primary-500)]/20 [&>option:checked]:bg-[var(--hc-primary-600)] [&>option:checked]:text-white [&>option]:py-1\",\n hidden: \"invisible\",\n ...classNames,\n }}\n captionLayout={captionLayout}\n components={{\n Chevron: ({ orientation }) => {\n const Icon = orientation === \"left\" ? ChevronLeft : ChevronRight\n return <Icon className=\"size-4\" />\n },\n }}\n {...props}\n />\n )\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n","import * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\nimport { cn } from \"../lib/utils\"\n\nconst Popover = PopoverPrimitive.Root\nconst PopoverTrigger = PopoverPrimitive.Trigger\n\nconst PopoverContent = React.forwardRef<\n React.ElementRef<typeof PopoverPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n))\nPopoverContent.displayName = PopoverPrimitive.Content.displayName\n\nexport { Popover, PopoverTrigger, PopoverContent }\n","import * as React from \"react\"\nimport { ClockIcon } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\"\n\n/** Matches a 24-hour clock time, \"HH:mm\". */\nconst TIME_VALUE_PATTERN = /^([01]\\d|2[0-3]):([0-5]\\d)$/\n\nconst MINUTES_PER_DAY = 24 * 60\nconst NOON = 12 * 60\n\n/** A 24-hour clock time such as \"09:00\" or \"17:30\". */\nexport type TimeValue = string\n\ntype Period = \"AM\" | \"PM\"\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, \"0\")\n}\n\n/**\n * Turns a \"HH:mm\" string into minutes since midnight. Returns undefined for\n * anything that is not a valid 24-hour time, so a bad prop degrades to\n * \"nothing selected\" rather than throwing inside a render.\n */\nfunction parseTimeValue(value?: string | null): number | undefined {\n if (!value) return undefined\n const match = TIME_VALUE_PATTERN.exec(value.trim())\n if (!match) return undefined\n return Number(match[1]) * 60 + Number(match[2])\n}\n\n/** Turns minutes since midnight back into the canonical \"HH:mm\" string. */\nfunction formatTimeValue(minutesOfDay: number): string {\n const normalised = ((minutesOfDay % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY\n return `${pad2(Math.floor(normalised / 60))}:${pad2(normalised % 60)}`\n}\n\n/** Human-readable time, either \"17:30\" or \"05:30 PM\" depending on the cycle. */\nfunction formatTimeLabel(minutesOfDay: number, hourCycle: 12 | 24): string {\n const normalised = ((minutesOfDay % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY\n const hours = Math.floor(normalised / 60)\n const minutes = normalised % 60\n if (hourCycle === 24) return `${pad2(hours)}:${pad2(minutes)}`\n const period: Period = hours < NOON / 60 ? \"AM\" : \"PM\"\n const hour12 = hours % 12 === 0 ? 12 : hours % 12\n return `${pad2(hour12)}:${pad2(minutes)} ${period}`\n}\n\nfunction isTimeInRange(minutesOfDay: number, min?: number, max?: number): boolean {\n if (min !== undefined && minutesOfDay < min) return false\n if (max !== undefined && minutesOfDay > max) return false\n return true\n}\n\n/** The hour a 12-hour-cycle cell stands for once its period is applied. */\nfunction to24Hour(hour12: number, period: Period): number {\n const base = hour12 % 12\n return period === \"AM\" ? base : base + 12\n}\n\n/**\n * The minute offsets the minute column offers. `extra` keeps a value that does\n * not sit on the step grid visible — a consumer may hand us \"14:07\" with a\n * 5-minute step and we must not silently fail to reflect it.\n */\nfunction buildMinuteList(minuteStep: number, extra?: number): number[] {\n const step = Number.isFinite(minuteStep) ? Math.min(60, Math.max(1, Math.floor(minuteStep))) : 5\n const minutes: number[] = []\n for (let minute = 0; minute < 60; minute += step) minutes.push(minute)\n if (extra !== undefined && extra >= 0 && extra < 60 && !minutes.includes(extra)) {\n minutes.push(extra)\n minutes.sort((a, b) => a - b)\n }\n return minutes\n}\n\n/** Every selectable slot of the day that the bound allows, ascending. */\nfunction buildAllowedSlots(minutes: number[], min?: number, max?: number): number[] {\n const slots: number[] = []\n for (let hour = 0; hour < 24; hour += 1) {\n for (const minute of minutes) {\n const slot = hour * 60 + minute\n if (isTimeInRange(slot, min, max)) slots.push(slot)\n }\n }\n return slots\n}\n\n/**\n * The allowed slot closest to `desired`, preferring one at or after it, so a\n * time that has fallen outside the bound moves forward to the next legal slot\n * instead of backwards.\n */\nfunction snapToAllowedSlot(desired: number, allowedSlots: number[]): number | undefined {\n if (allowedSlots.length === 0) return undefined\n let best = allowedSlots[0]\n let bestScore = Number.POSITIVE_INFINITY\n for (const slot of allowedSlots) {\n const score = slot >= desired ? slot - desired : desired - slot + MINUTES_PER_DAY\n if (score < bestScore) {\n bestScore = score\n best = slot\n }\n }\n return best\n}\n\nconst columnClasses =\n \"flex max-h-[216px] w-16 flex-col gap-1 overflow-y-auto overscroll-contain p-1\"\n\nconst cellClasses =\n \"shrink-0 rounded-md px-2 py-1.5 text-sm font-normal text-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background aria-pressed:bg-primary aria-pressed:font-medium aria-pressed:text-primary-foreground aria-pressed:hover:bg-primary aria-pressed:hover:text-primary-foreground disabled:pointer-events-none disabled:text-muted-foreground disabled:opacity-50\"\n\ninterface TimeCellProps {\n label: string\n accessibleName: string\n pressed: boolean\n disabled: boolean\n onSelect: () => void\n}\n\nfunction TimeCell({ label, accessibleName, pressed, disabled, onSelect }: TimeCellProps) {\n return (\n <button\n type=\"button\"\n aria-label={accessibleName}\n aria-pressed={pressed}\n disabled={disabled}\n onClick={onSelect}\n className={cn(cellClasses)}\n >\n {label}\n </button>\n )\n}\n\nexport interface TimePickerPanelProps {\n /** Selected time as a 24-hour \"HH:mm\" string. */\n value?: TimeValue\n /** Called with the newly selected 24-hour \"HH:mm\" string. */\n onValueChange?: (value: TimeValue) => void\n /** Earliest selectable time as \"HH:mm\". Earlier times are disabled. */\n minTime?: TimeValue\n /** Latest selectable time as \"HH:mm\". Later times are disabled. */\n maxTime?: TimeValue\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /** Accessible name for the hour column. Defaults to \"Hour\". */\n hourLabel?: string\n /** Accessible name for the minute column. Defaults to \"Minute\". */\n minuteLabel?: string\n /** Accessible name for the AM/PM column. Defaults to \"Period\". */\n periodLabel?: string\n /** Classname applied to the panel wrapper. */\n className?: string\n}\n\n/**\n * The bare hour/minute columns, without a popover around them. Used by\n * TimePicker and by DateTimePicker, which supplies its own popover.\n */\nfunction TimePickerPanel({\n value,\n onValueChange,\n minTime,\n maxTime,\n minuteStep = 5,\n hourCycle = 12,\n hourLabel = \"Hour\",\n minuteLabel = \"Minute\",\n periodLabel = \"Period\",\n className,\n}: TimePickerPanelProps) {\n const minMinutes = parseTimeValue(minTime)\n const maxMinutes = parseTimeValue(maxTime)\n const selected = parseTimeValue(value)\n\n const minuteList = React.useMemo(\n () => buildMinuteList(minuteStep, selected === undefined ? undefined : selected % 60),\n [minuteStep, selected]\n )\n const allowedSlots = React.useMemo(\n () => buildAllowedSlots(minuteList, minMinutes, maxMinutes),\n [minuteList, minMinutes, maxMinutes]\n )\n\n // Each click reports a complete time, so with nothing selected yet the\n // columns work against the first slot the bound allows.\n const active = selected ?? allowedSlots[0]\n const activeHour = active === undefined ? undefined : Math.floor(active / 60)\n const activeMinute = active === undefined ? undefined : active % 60\n const activePeriod: Period =\n activeHour !== undefined && activeHour >= 12 ? \"PM\" : \"AM\"\n\n const hourHasSlot = (hour: number) =>\n allowedSlots.some((slot) => Math.floor(slot / 60) === hour)\n const firstSlotInHour = (hour: number) =>\n allowedSlots.find((slot) => Math.floor(slot / 60) === hour)\n const periodHasSlot = (period: Period) =>\n allowedSlots.some((slot) => (slot < NOON ? \"AM\" : \"PM\") === period)\n const firstSlotInPeriod = (period: Period) =>\n allowedSlots.find((slot) => (slot < NOON ? \"AM\" : \"PM\") === period)\n\n const commit = (minutesOfDay: number | undefined) => {\n if (minutesOfDay === undefined) return\n onValueChange?.(formatTimeValue(minutesOfDay))\n }\n\n const selectHour = (hour: number) => {\n const desired = hour * 60 + (activeMinute ?? 0)\n commit(isTimeInRange(desired, minMinutes, maxMinutes) ? desired : firstSlotInHour(hour))\n }\n\n const selectMinute = (minute: number) => {\n if (activeHour === undefined) return\n commit(activeHour * 60 + minute)\n }\n\n const selectPeriod = (period: Period) => {\n if (activeHour === undefined || activeMinute === undefined) {\n commit(firstSlotInPeriod(period))\n return\n }\n const hour12 = activeHour % 12 === 0 ? 12 : activeHour % 12\n const desired = to24Hour(hour12, period) * 60 + activeMinute\n commit(\n isTimeInRange(desired, minMinutes, maxMinutes) ? desired : firstSlotInPeriod(period)\n )\n }\n\n const hourCells =\n hourCycle === 24\n ? Array.from({ length: 24 }, (_, hour) => hour)\n : [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n\n return (\n <div className={cn(\"flex items-start divide-x divide-border\", className)}>\n <div className={cn(columnClasses)} role=\"group\" aria-label={hourLabel}>\n {hourCells.map((cell) => {\n const hour = hourCycle === 24 ? cell : to24Hour(cell, activePeriod)\n const label = pad2(cell)\n return (\n <TimeCell\n key={`hour-${cell}`}\n label={label}\n accessibleName={`${hourLabel} ${label}`}\n pressed={activeHour === hour && selected !== undefined}\n disabled={!hourHasSlot(hour)}\n onSelect={() => selectHour(hour)}\n />\n )\n })}\n </div>\n <div className={cn(columnClasses)} role=\"group\" aria-label={minuteLabel}>\n {minuteList.map((minute) => {\n const label = pad2(minute)\n const disabled =\n activeHour === undefined ||\n !isTimeInRange(activeHour * 60 + minute, minMinutes, maxMinutes)\n return (\n <TimeCell\n key={`minute-${minute}`}\n label={label}\n accessibleName={`${minuteLabel} ${label}`}\n pressed={activeMinute === minute && selected !== undefined}\n disabled={disabled}\n onSelect={() => selectMinute(minute)}\n />\n )\n })}\n </div>\n {hourCycle === 12 && (\n <div className={cn(columnClasses)} role=\"group\" aria-label={periodLabel}>\n {([\"AM\", \"PM\"] as Period[]).map((period) => (\n <TimeCell\n key={period}\n label={period}\n accessibleName={period}\n pressed={activePeriod === period && selected !== undefined}\n disabled={!periodHasSlot(period)}\n onSelect={() => selectPeriod(period)}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\nTimePickerPanel.displayName = \"TimePickerPanel\"\n\nexport interface TimePickerProps {\n /** Selected time as a 24-hour \"HH:mm\" string. */\n value?: TimeValue\n /** Called with the new time, or with undefined when the value is cleared. */\n onValueChange?: (value: TimeValue | undefined) => void\n /** Earliest selectable time as \"HH:mm\". Earlier times are disabled. */\n minTime?: TimeValue\n /** Latest selectable time as \"HH:mm\". Later times are disabled. */\n maxTime?: TimeValue\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /** Trigger text shown when nothing is selected. */\n placeholder?: string\n /** Shows a Clear action in the popover. Defaults to true. */\n clearable?: boolean\n /** Text of the Clear action. Defaults to \"Clear\". */\n clearLabel?: string\n /** Disables the whole control. */\n disabled?: boolean\n /** Accessible name for the trigger. Defaults to the placeholder. */\n \"aria-label\"?: string\n /** Id of an element that labels the trigger. */\n \"aria-labelledby\"?: string\n /** Id applied to the trigger button. */\n id?: string\n /** Name reported on a hidden input, so the value posts with a plain form. */\n name?: string\n /** Classname applied to the trigger button. */\n className?: string\n /** Classname applied to the popover content. */\n contentClassName?: string\n /** Controlled open state of the popover. */\n open?: boolean\n /** Called when the popover opens or closes. */\n onOpenChange?: (open: boolean) => void\n /** Side the popover is aligned to. Defaults to \"start\". */\n align?: \"start\" | \"center\" | \"end\"\n}\n\n/**\n * A time control: a popover trigger showing the selected time, over hour and\n * minute columns. The value is always a 24-hour \"HH:mm\" string, so it survives\n * a round trip through JSON and through a `datetime-local`-shaped API.\n */\nconst TimePicker = React.forwardRef<HTMLButtonElement, TimePickerProps>(\n (\n {\n value,\n onValueChange,\n minTime,\n maxTime,\n minuteStep = 5,\n hourCycle = 12,\n placeholder = \"Pick a time\",\n clearable = true,\n clearLabel = \"Clear\",\n disabled,\n id,\n name,\n className,\n contentClassName,\n open,\n onOpenChange,\n align = \"start\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref\n ) => {\n const selected = parseTimeValue(value)\n const label =\n selected === undefined ? placeholder : formatTimeLabel(selected, hourCycle)\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n id={id}\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n aria-labelledby={ariaLabelledBy}\n className={cn(\n \"w-[180px] justify-start text-left font-normal\",\n selected === undefined && \"text-muted-foreground\",\n className\n )}\n >\n <ClockIcon className=\"me-2 h-4 w-4\" aria-hidden=\"true\" />\n {label}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto p-0\", contentClassName)}\n align={align}\n aria-label={ariaLabel ?? placeholder}\n >\n <TimePickerPanel\n value={value}\n onValueChange={(next) => onValueChange?.(next)}\n minTime={minTime}\n maxTime={maxTime}\n minuteStep={minuteStep}\n hourCycle={hourCycle}\n />\n {clearable && (\n <div className=\"flex justify-end border-t border-border p-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"font-normal\"\n disabled={selected === undefined}\n onClick={() => onValueChange?.(undefined)}\n >\n {clearLabel}\n </Button>\n </div>\n )}\n </PopoverContent>\n {name && <input type=\"hidden\" name={name} value={value ?? \"\"} />}\n </Popover>\n )\n }\n)\nTimePicker.displayName = \"TimePicker\"\n\nexport {\n TimePicker,\n TimePickerPanel,\n formatTimeLabel,\n formatTimeValue,\n parseTimeValue,\n isTimeInRange,\n buildMinuteList,\n buildAllowedSlots,\n snapToAllowedSlot,\n}\n"],"mappings":"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","names":["React","import_lucide_react","import_lucide_react","import_jsx_runtime","React","import_jsx_runtime","React","import_lucide_react","import_jsx_runtime","minutesOfDay","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/date-time-picker.tsx","../../src/lib/utils.ts","../../src/components/button.tsx","../../src/components/calendar.tsx","../../src/components/popover.tsx","../../src/components/time-picker.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { CalendarClockIcon } from \"lucide-react\"\nimport type { Matcher } from \"react-day-picker\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Calendar } from \"./calendar\"\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\"\nimport {\n TimePickerPanel,\n buildAllowedSlots,\n buildMinuteList,\n formatTimeLabel,\n formatTimeValue,\n isTimeInRange,\n parseTimeValue,\n snapToAllowedSlot,\n type TimeValue,\n} from \"./time-picker\"\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n )\n}\n\n/** Minutes since midnight for a Date, in local time. */\nfunction minutesOfDay(date: Date): number {\n return date.getHours() * 60 + date.getMinutes()\n}\n\n/** A new Date on `day` at `minutes` past midnight, seconds zeroed. */\nfunction withTime(day: Date, minutes: number): Date {\n return new Date(\n day.getFullYear(),\n day.getMonth(),\n day.getDate(),\n Math.floor(minutes / 60),\n minutes % 60,\n 0,\n 0\n )\n}\n\nexport interface DateTimePickerProps {\n /** The selected moment. One value covers both the date and the time. */\n value?: Date\n /**\n * Called with the new moment, or with undefined when the value is cleared.\n * Fires only on Apply — edits inside the popover are held as a draft until\n * then.\n */\n onValueChange?: (value: Date | undefined) => void\n /**\n * Earliest selectable moment. Days before it are disabled in the calendar\n * and, on the boundary day itself, the times before it are disabled too.\n */\n minDate?: Date\n /**\n * Latest selectable moment. Days after it are disabled in the calendar and,\n * on the boundary day itself, the times after it are disabled too.\n */\n maxDate?: Date\n /** Extra day matchers passed through to the underlying Calendar. */\n disabledDates?: Matcher | Matcher[]\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /**\n * Time applied when a date is picked before any time has been chosen, as\n * \"HH:mm\". Defaults to \"09:00\" and is moved to the nearest allowed slot when\n * the bound forbids it.\n */\n defaultTime?: TimeValue\n /** Trigger text shown when nothing is selected. */\n placeholder?: string\n /** Overrides how the selected moment is rendered on the trigger. */\n formatValue?: (value: Date) => string\n /** Shows a Clear action in the popover. Defaults to true. */\n clearable?: boolean\n /** Text of the Clear action, which empties the draft. Defaults to \"Clear\". */\n clearLabel?: string\n /** Text of the action that commits the draft. Defaults to \"Apply\". */\n applyLabel?: string\n /** Text of the action that discards the draft. Defaults to \"Cancel\". */\n cancelLabel?: string\n /** Heading above the time columns. Defaults to \"Time\". */\n timeLabel?: string\n /** Disables the whole control. */\n disabled?: boolean\n /** Accessible name for the trigger. Defaults to the placeholder. */\n \"aria-label\"?: string\n /** Id of an element that labels the trigger. */\n \"aria-labelledby\"?: string\n /** Id applied to the trigger button. */\n id?: string\n /** Name reported on a hidden input, so the value posts with a plain form. */\n name?: string\n /** Classname applied to the trigger button. */\n className?: string\n /** Classname applied to the popover content. */\n contentClassName?: string\n /** Controlled open state of the popover. */\n open?: boolean\n /** Called when the popover opens or closes. */\n onOpenChange?: (open: boolean) => void\n /** Side the popover is aligned to. Defaults to \"start\". */\n align?: \"start\" | \"center\" | \"end\"\n}\n\n/**\n * A date and a time in one popover, reporting a single Date. Drop-in\n * replacement for a native `datetime-local` input: the calendar and the time\n * columns write to the same value, and `minDate` / `maxDate` are enforced on\n * both halves, including on the boundary day where only part of the day is\n * still allowed.\n *\n * Edits are committed explicitly. Picking a day, an hour, a minute or a period\n * moves a draft held inside the popover; `onValueChange` fires once, on Apply.\n * Cancel, Escape and a click outside all discard the draft, so a stray click\n * cannot write a deadline.\n */\nconst DateTimePicker = React.forwardRef<HTMLButtonElement, DateTimePickerProps>(\n (\n {\n value,\n onValueChange,\n minDate,\n maxDate,\n disabledDates,\n minuteStep = 5,\n hourCycle = 12,\n defaultTime = \"09:00\",\n placeholder = \"Pick a date and time\",\n formatValue,\n clearable = true,\n clearLabel = \"Clear\",\n applyLabel = \"Apply\",\n cancelLabel = \"Cancel\",\n timeLabel = \"Time\",\n disabled,\n id,\n name,\n className,\n contentClassName,\n open,\n onOpenChange,\n align = \"start\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref\n ) => {\n // Every interaction inside the popover edits this draft, never `value`.\n // The trigger keeps showing `value`, so an uncommitted edit can never be\n // mistaken for a saved one.\n const [draft, setDraft] = React.useState<Date | undefined>(value)\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)\n const isOpen = open ?? uncontrolledOpen\n\n // Opening re-seeds the draft. The effect covers a controlled consumer that\n // flips `open` itself, which never reaches our onOpenChange handler.\n const wasOpen = React.useRef(isOpen)\n React.useEffect(() => {\n if (isOpen && !wasOpen.current) setDraft(value)\n wasOpen.current = isOpen\n }, [isOpen, value])\n\n const setOpenState = (next: boolean) => {\n if (open === undefined) setUncontrolledOpen(next)\n onOpenChange?.(next)\n }\n\n // The trigger, Escape and a click outside all land here. None of them\n // commits: the draft goes back to `value` whichever way the popover moves.\n const handleOpenChange = (next: boolean) => {\n setDraft(value)\n setOpenState(next)\n }\n\n const handleCancel = () => {\n setDraft(value)\n setOpenState(false)\n }\n\n // The one path that reports to the consumer, including the undefined left\n // behind by Clear.\n const handleApply = () => {\n onValueChange?.(draft)\n setOpenState(false)\n }\n\n // The day the time columns are bound against. Without a draft yet that is\n // today, pulled inside the allowed range so the columns are usable.\n const activeDay = React.useMemo(() => {\n if (draft) return startOfDay(draft)\n const today = startOfDay(new Date())\n if (minDate && today < startOfDay(minDate)) return startOfDay(minDate)\n if (maxDate && today > startOfDay(maxDate)) return startOfDay(maxDate)\n return today\n }, [draft, minDate, maxDate])\n\n /** The time window still open on a given day, once the bound is applied. */\n const timeWindowFor = React.useCallback(\n (day: Date): { minTime?: TimeValue; maxTime?: TimeValue } => ({\n minTime:\n minDate && isSameDay(day, minDate)\n ? formatTimeValue(minutesOfDay(minDate))\n : undefined,\n maxTime:\n maxDate && isSameDay(day, maxDate)\n ? formatTimeValue(minutesOfDay(maxDate))\n : undefined,\n }),\n [minDate, maxDate]\n )\n\n const activeWindow = timeWindowFor(activeDay)\n\n // The year dropdown follows the bound rather than Calendar's generic\n // default: with minDate set to now, a deadline picker offering 2016 would\n // be offering years every one of whose days it then refuses.\n const calendarStartMonth = React.useMemo(\n () => (minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), 1) : undefined),\n [minDate],\n )\n const calendarEndMonth = React.useMemo(\n () => (maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), 1) : undefined),\n [maxDate],\n )\n\n const calendarDisabled = React.useMemo(() => {\n const matchers: Matcher[] = []\n if (minDate) matchers.push({ before: startOfDay(minDate) })\n if (maxDate) matchers.push({ after: startOfDay(maxDate) })\n if (Array.isArray(disabledDates)) matchers.push(...disabledDates)\n else if (disabledDates !== undefined) matchers.push(disabledDates)\n return matchers\n }, [minDate, maxDate, disabledDates])\n\n const handleDaySelect = (day: Date | undefined) => {\n // Clicking the selected day again deselects in react-day-picker. A\n // deadline should not disappear by accident, so Clear is the only way out.\n if (!day) return\n const desired = draft ? minutesOfDay(draft) : (parseTimeValue(defaultTime) ?? 0)\n const { minTime, maxTime } = timeWindowFor(day)\n const min = parseTimeValue(minTime)\n const max = parseTimeValue(maxTime)\n if (isTimeInRange(desired, min, max)) {\n setDraft(withTime(day, desired))\n return\n }\n const snapped = snapToAllowedSlot(\n desired,\n buildAllowedSlots(buildMinuteList(minuteStep, desired % 60), min, max)\n )\n if (snapped === undefined) return\n setDraft(withTime(day, snapped))\n }\n\n const handleTimeChange = (next: TimeValue) => {\n const minutes = parseTimeValue(next)\n if (minutes === undefined) return\n setDraft(withTime(draft ?? activeDay, minutes))\n }\n\n const label = value\n ? (formatValue?.(value) ??\n `${value.toLocaleDateString()} ${formatTimeLabel(minutesOfDay(value), hourCycle)}`)\n : placeholder\n\n return (\n <Popover open={isOpen} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n id={id}\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n aria-labelledby={ariaLabelledBy}\n className={cn(\n \"w-[280px] justify-start text-left font-normal\",\n !value && \"text-muted-foreground\",\n className\n )}\n >\n <CalendarClockIcon className=\"me-2 h-4 w-4\" aria-hidden=\"true\" />\n {label}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto p-0\", contentClassName)}\n align={align}\n aria-label={ariaLabel ?? placeholder}\n >\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:divide-x sm:divide-border\">\n <Calendar\n mode=\"single\"\n selected={draft}\n defaultMonth={activeDay}\n startMonth={calendarStartMonth}\n endMonth={calendarEndMonth}\n onSelect={handleDaySelect}\n disabled={calendarDisabled}\n // Focus moves into the calendar the user just opened (keyboard navigation).\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus\n />\n <div className=\"border-t border-border p-2 sm:border-t-0\">\n <div className=\"px-1 pb-1 text-xs font-medium uppercase tracking-wider text-muted-foreground\">\n {timeLabel}\n </div>\n <TimePickerPanel\n value={draft ? formatTimeValue(minutesOfDay(draft)) : undefined}\n onValueChange={handleTimeChange}\n minTime={activeWindow.minTime}\n maxTime={activeWindow.maxTime}\n minuteStep={minuteStep}\n hourCycle={hourCycle}\n />\n </div>\n </div>\n <div className=\"flex items-center gap-2 border-t border-border p-2\">\n {clearable && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"font-normal\"\n disabled={!draft}\n onClick={() => setDraft(undefined)}\n >\n {clearLabel}\n </Button>\n )}\n <div className=\"ms-auto flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"font-normal\"\n onClick={handleCancel}\n >\n {cancelLabel}\n </Button>\n <Button type=\"button\" variant=\"default\" size=\"sm\" onClick={handleApply}>\n {applyLabel}\n </Button>\n </div>\n </div>\n </PopoverContent>\n {name && (\n <input\n type=\"hidden\"\n name={name}\n value={\n value\n ? `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, \"0\")}-${String(\n value.getDate()\n ).padStart(2, \"0\")}T${formatTimeValue(minutesOfDay(value))}`\n : \"\"\n }\n />\n )}\n </Popover>\n )\n }\n)\nDateTimePicker.displayName = \"DateTimePicker\"\n\nexport { DateTimePicker }\n","import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Loader2 } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground hover:bg-primary-hover\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive-hover\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary-hover\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground\",\n link:\n \"text-primary underline-offset-4 hover:underline\",\n success:\n \"bg-success text-success-foreground hover:bg-success-hover\",\n },\n size: {\n xs: \"h-7 px-2.5 text-xs\",\n sm: \"h-9 px-3\",\n default: \"h-10 px-4 py-2\",\n lg: \"h-11 px-8\",\n icon: \"h-10 w-10\",\n \"icon-sm\": \"h-8 w-8\",\n },\n shape: {\n default: \"rounded-md\",\n rounded: \"rounded-lg\",\n pill: \"rounded-full\",\n square: \"rounded-none\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n shape: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n loading?: boolean\n prefixIcon?: React.ReactNode\n suffixIcon?: React.ReactNode\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n shape,\n asChild = false,\n loading = false,\n prefixIcon,\n suffixIcon,\n children,\n disabled,\n ...props\n },\n ref\n ) => {\n const Comp = asChild ? Slot : \"button\"\n const isDisabled = disabled || loading\n\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, shape, className }))}\n ref={ref}\n disabled={isDisabled}\n {...props}\n >\n {loading && <Loader2 className=\"animate-spin\" />}\n {!loading && prefixIcon}\n {children}\n {suffixIcon}\n </Comp>\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n","import * as React from \"react\"\nimport { DayPicker } from \"react-day-picker\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker>\n\n/**\n * How far the year dropdown reaches when the caller gives no bounds.\n *\n * `captionLayout` defaults to \"dropdown\" here, and react-day-picker then falls\n * back to [currentYear - 100, currentYear] for the year list. That is wrong in\n * both directions: a hundred dead years to scroll past, and no future year at\n * all - a deadline could not be set beyond December of the current year.\n * Callers that know their range pass `startMonth` / `endMonth` and these are\n * not used.\n */\nconst DEFAULT_YEARS_BACK = 10\nconst DEFAULT_YEARS_FORWARD = 10\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"dropdown\",\n startMonth,\n endMonth,\n ...props\n}: CalendarProps) {\n const now = new Date()\n const defaultedStartMonth =\n startMonth ?? new Date(now.getFullYear() - DEFAULT_YEARS_BACK, 0, 1)\n const defaultedEndMonth =\n endMonth ?? new Date(now.getFullYear() + DEFAULT_YEARS_FORWARD, 11, 1)\n\n return (\n <DayPicker\n startMonth={defaultedStartMonth}\n endMonth={defaultedEndMonth}\n showOutsideDays={showOutsideDays}\n className={cn(\"w-[260px] rounded-lg p-3\", className)}\n classNames={{\n months: \"flex flex-col\",\n month: \"space-y-3\",\n month_caption: \"relative flex h-8 items-center justify-center\",\n caption_label: \"text-sm font-medium hidden\",\n nav: \"flex items-center\",\n button_previous: cn(\n \"absolute start-0 inline-flex size-7 items-center justify-center rounded-md bg-transparent p-0 text-muted-foreground hover:bg-[var(--hc-color-border-subtle)] hover:text-foreground transition-colors cursor-pointer border-none\"\n ),\n button_next: cn(\n \"absolute end-0 inline-flex size-7 items-center justify-center rounded-md bg-transparent p-0 text-muted-foreground hover:bg-[var(--hc-color-border-subtle)] hover:text-foreground transition-colors cursor-pointer border-none\"\n ),\n month_grid: \"w-full border-collapse\",\n weekdays: \"flex w-full\",\n weekday:\n \"flex-1 h-8 flex items-center justify-center text-[11px] font-medium text-muted-foreground uppercase tracking-wider\",\n week: \"flex w-full mt-1\",\n day: \"flex-1 h-8 p-0 relative focus-within:z-20 flex items-center justify-center text-sm\",\n day_button: cn(\n \"inline-flex size-8 items-center justify-center rounded-md bg-transparent text-sm font-normal cursor-pointer border-none hover:bg-[var(--hc-color-border-subtle)] transition-colors\"\n ),\n range_end: \"day-range-end\",\n selected:\n \"[&>button]:bg-[var(--hc-primary-600)] [&>button]:text-white [&>button]:hover:bg-[var(--hc-primary-600)] [&>button]:hover:text-white [&>button]:font-medium\",\n today: \"[&>button]:font-semibold [&>button]:text-[var(--hc-primary-600)]\",\n outside:\n \"day-outside [&>button]:bg-transparent [&>button]:text-[var(--hc-grey-200)]\",\n disabled: \"[&>button]:text-[var(--hc-grey-200)] [&>button]:opacity-50 [&>button]:cursor-default\",\n range_middle:\n \"[&>button]:bg-[var(--hc-primary-100)] [&>button]:text-foreground [&>button]:rounded-none\",\n range_start: \"[&>button]:rounded-e-none\",\n dropdowns: \"flex items-center gap-1.5 justify-center [&>select]:flex-1\",\n dropdown:\n \"h-7 w-[88px] rounded-md border border-[var(--hc-color-border-subtle)] bg-background px-2 text-xs font-medium text-foreground cursor-pointer outline-none accent-[var(--hc-primary-600)] focus:border-[var(--hc-primary-500)] focus:ring-[2px] focus:ring-[var(--hc-primary-500)]/20 [&>option:checked]:bg-[var(--hc-primary-600)] [&>option:checked]:text-white [&>option]:py-1\",\n hidden: \"invisible\",\n ...classNames,\n }}\n captionLayout={captionLayout}\n components={{\n Chevron: ({ orientation }) => {\n const Icon = orientation === \"left\" ? ChevronLeft : ChevronRight\n return <Icon className=\"size-4\" />\n },\n }}\n {...props}\n />\n )\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n","import * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\nimport { cn } from \"../lib/utils\"\n\nconst Popover = PopoverPrimitive.Root\nconst PopoverTrigger = PopoverPrimitive.Trigger\n\nconst PopoverContent = React.forwardRef<\n React.ElementRef<typeof PopoverPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n))\nPopoverContent.displayName = PopoverPrimitive.Content.displayName\n\nexport { Popover, PopoverTrigger, PopoverContent }\n","import * as React from \"react\"\nimport { ClockIcon } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\"\n\n/** Matches a 24-hour clock time, \"HH:mm\". */\nconst TIME_VALUE_PATTERN = /^([01]\\d|2[0-3]):([0-5]\\d)$/\n\nconst MINUTES_PER_DAY = 24 * 60\nconst NOON = 12 * 60\n\n/** A 24-hour clock time such as \"09:00\" or \"17:30\". */\nexport type TimeValue = string\n\ntype Period = \"AM\" | \"PM\"\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, \"0\")\n}\n\n/**\n * Turns a \"HH:mm\" string into minutes since midnight. Returns undefined for\n * anything that is not a valid 24-hour time, so a bad prop degrades to\n * \"nothing selected\" rather than throwing inside a render.\n */\nfunction parseTimeValue(value?: string | null): number | undefined {\n if (!value) return undefined\n const match = TIME_VALUE_PATTERN.exec(value.trim())\n if (!match) return undefined\n return Number(match[1]) * 60 + Number(match[2])\n}\n\n/** Turns minutes since midnight back into the canonical \"HH:mm\" string. */\nfunction formatTimeValue(minutesOfDay: number): string {\n const normalised = ((minutesOfDay % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY\n return `${pad2(Math.floor(normalised / 60))}:${pad2(normalised % 60)}`\n}\n\n/** Human-readable time, either \"17:30\" or \"05:30 PM\" depending on the cycle. */\nfunction formatTimeLabel(minutesOfDay: number, hourCycle: 12 | 24): string {\n const normalised = ((minutesOfDay % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY\n const hours = Math.floor(normalised / 60)\n const minutes = normalised % 60\n if (hourCycle === 24) return `${pad2(hours)}:${pad2(minutes)}`\n const period: Period = hours < NOON / 60 ? \"AM\" : \"PM\"\n const hour12 = hours % 12 === 0 ? 12 : hours % 12\n return `${pad2(hour12)}:${pad2(minutes)} ${period}`\n}\n\nfunction isTimeInRange(minutesOfDay: number, min?: number, max?: number): boolean {\n if (min !== undefined && minutesOfDay < min) return false\n if (max !== undefined && minutesOfDay > max) return false\n return true\n}\n\n/** The hour a 12-hour-cycle cell stands for once its period is applied. */\nfunction to24Hour(hour12: number, period: Period): number {\n const base = hour12 % 12\n return period === \"AM\" ? base : base + 12\n}\n\n/**\n * The minute offsets the minute column offers. `extra` keeps a value that does\n * not sit on the step grid visible — a consumer may hand us \"14:07\" with a\n * 5-minute step and we must not silently fail to reflect it.\n */\nfunction buildMinuteList(minuteStep: number, extra?: number): number[] {\n const step = Number.isFinite(minuteStep) ? Math.min(60, Math.max(1, Math.floor(minuteStep))) : 5\n const minutes: number[] = []\n for (let minute = 0; minute < 60; minute += step) minutes.push(minute)\n if (extra !== undefined && extra >= 0 && extra < 60 && !minutes.includes(extra)) {\n minutes.push(extra)\n minutes.sort((a, b) => a - b)\n }\n return minutes\n}\n\n/** Every selectable slot of the day that the bound allows, ascending. */\nfunction buildAllowedSlots(minutes: number[], min?: number, max?: number): number[] {\n const slots: number[] = []\n for (let hour = 0; hour < 24; hour += 1) {\n for (const minute of minutes) {\n const slot = hour * 60 + minute\n if (isTimeInRange(slot, min, max)) slots.push(slot)\n }\n }\n return slots\n}\n\n/**\n * The allowed slot closest to `desired`, preferring one at or after it, so a\n * time that has fallen outside the bound moves forward to the next legal slot\n * instead of backwards.\n */\nfunction snapToAllowedSlot(desired: number, allowedSlots: number[]): number | undefined {\n if (allowedSlots.length === 0) return undefined\n let best = allowedSlots[0]\n let bestScore = Number.POSITIVE_INFINITY\n for (const slot of allowedSlots) {\n const score = slot >= desired ? slot - desired : desired - slot + MINUTES_PER_DAY\n if (score < bestScore) {\n bestScore = score\n best = slot\n }\n }\n return best\n}\n\nconst columnClasses =\n \"flex max-h-[216px] w-16 flex-col gap-1 overflow-y-auto overscroll-contain p-1\"\n\nconst cellClasses =\n \"shrink-0 rounded-md px-2 py-1.5 text-sm font-normal text-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background aria-pressed:bg-primary aria-pressed:font-medium aria-pressed:text-primary-foreground aria-pressed:hover:bg-primary aria-pressed:hover:text-primary-foreground disabled:pointer-events-none disabled:text-muted-foreground disabled:opacity-50\"\n\ninterface TimeCellProps {\n label: string\n accessibleName: string\n pressed: boolean\n disabled: boolean\n onSelect: () => void\n}\n\nfunction TimeCell({ label, accessibleName, pressed, disabled, onSelect }: TimeCellProps) {\n return (\n <button\n type=\"button\"\n aria-label={accessibleName}\n aria-pressed={pressed}\n disabled={disabled}\n onClick={onSelect}\n className={cn(cellClasses)}\n >\n {label}\n </button>\n )\n}\n\nexport interface TimePickerPanelProps {\n /** Selected time as a 24-hour \"HH:mm\" string. */\n value?: TimeValue\n /** Called with the newly selected 24-hour \"HH:mm\" string. */\n onValueChange?: (value: TimeValue) => void\n /** Earliest selectable time as \"HH:mm\". Earlier times are disabled. */\n minTime?: TimeValue\n /** Latest selectable time as \"HH:mm\". Later times are disabled. */\n maxTime?: TimeValue\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /** Accessible name for the hour column. Defaults to \"Hour\". */\n hourLabel?: string\n /** Accessible name for the minute column. Defaults to \"Minute\". */\n minuteLabel?: string\n /** Accessible name for the AM/PM column. Defaults to \"Period\". */\n periodLabel?: string\n /** Classname applied to the panel wrapper. */\n className?: string\n}\n\n/**\n * The bare hour/minute columns, without a popover around them. Used by\n * TimePicker and by DateTimePicker, which supplies its own popover.\n */\nfunction TimePickerPanel({\n value,\n onValueChange,\n minTime,\n maxTime,\n minuteStep = 5,\n hourCycle = 12,\n hourLabel = \"Hour\",\n minuteLabel = \"Minute\",\n periodLabel = \"Period\",\n className,\n}: TimePickerPanelProps) {\n const minMinutes = parseTimeValue(minTime)\n const maxMinutes = parseTimeValue(maxTime)\n const selected = parseTimeValue(value)\n\n const minuteList = React.useMemo(\n () => buildMinuteList(minuteStep, selected === undefined ? undefined : selected % 60),\n [minuteStep, selected]\n )\n const allowedSlots = React.useMemo(\n () => buildAllowedSlots(minuteList, minMinutes, maxMinutes),\n [minuteList, minMinutes, maxMinutes]\n )\n\n // Each click reports a complete time, so with nothing selected yet the\n // columns work against the first slot the bound allows.\n const active = selected ?? allowedSlots[0]\n const activeHour = active === undefined ? undefined : Math.floor(active / 60)\n const activeMinute = active === undefined ? undefined : active % 60\n const activePeriod: Period =\n activeHour !== undefined && activeHour >= 12 ? \"PM\" : \"AM\"\n\n const hourHasSlot = (hour: number) =>\n allowedSlots.some((slot) => Math.floor(slot / 60) === hour)\n const firstSlotInHour = (hour: number) =>\n allowedSlots.find((slot) => Math.floor(slot / 60) === hour)\n const periodHasSlot = (period: Period) =>\n allowedSlots.some((slot) => (slot < NOON ? \"AM\" : \"PM\") === period)\n const firstSlotInPeriod = (period: Period) =>\n allowedSlots.find((slot) => (slot < NOON ? \"AM\" : \"PM\") === period)\n\n const commit = (minutesOfDay: number | undefined) => {\n if (minutesOfDay === undefined) return\n onValueChange?.(formatTimeValue(minutesOfDay))\n }\n\n const selectHour = (hour: number) => {\n const desired = hour * 60 + (activeMinute ?? 0)\n commit(isTimeInRange(desired, minMinutes, maxMinutes) ? desired : firstSlotInHour(hour))\n }\n\n const selectMinute = (minute: number) => {\n if (activeHour === undefined) return\n commit(activeHour * 60 + minute)\n }\n\n const selectPeriod = (period: Period) => {\n if (activeHour === undefined || activeMinute === undefined) {\n commit(firstSlotInPeriod(period))\n return\n }\n const hour12 = activeHour % 12 === 0 ? 12 : activeHour % 12\n const desired = to24Hour(hour12, period) * 60 + activeMinute\n commit(\n isTimeInRange(desired, minMinutes, maxMinutes) ? desired : firstSlotInPeriod(period)\n )\n }\n\n const hourCells =\n hourCycle === 24\n ? Array.from({ length: 24 }, (_, hour) => hour)\n : [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n\n return (\n <div className={cn(\"flex items-start divide-x divide-border\", className)}>\n <div className={cn(columnClasses)} role=\"group\" aria-label={hourLabel}>\n {hourCells.map((cell) => {\n const hour = hourCycle === 24 ? cell : to24Hour(cell, activePeriod)\n const label = pad2(cell)\n return (\n <TimeCell\n key={`hour-${cell}`}\n label={label}\n accessibleName={`${hourLabel} ${label}`}\n pressed={activeHour === hour && selected !== undefined}\n disabled={!hourHasSlot(hour)}\n onSelect={() => selectHour(hour)}\n />\n )\n })}\n </div>\n <div className={cn(columnClasses)} role=\"group\" aria-label={minuteLabel}>\n {minuteList.map((minute) => {\n const label = pad2(minute)\n const disabled =\n activeHour === undefined ||\n !isTimeInRange(activeHour * 60 + minute, minMinutes, maxMinutes)\n return (\n <TimeCell\n key={`minute-${minute}`}\n label={label}\n accessibleName={`${minuteLabel} ${label}`}\n pressed={activeMinute === minute && selected !== undefined}\n disabled={disabled}\n onSelect={() => selectMinute(minute)}\n />\n )\n })}\n </div>\n {hourCycle === 12 && (\n <div className={cn(columnClasses)} role=\"group\" aria-label={periodLabel}>\n {([\"AM\", \"PM\"] as Period[]).map((period) => (\n <TimeCell\n key={period}\n label={period}\n accessibleName={period}\n pressed={activePeriod === period && selected !== undefined}\n disabled={!periodHasSlot(period)}\n onSelect={() => selectPeriod(period)}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\nTimePickerPanel.displayName = \"TimePickerPanel\"\n\nexport interface TimePickerProps {\n /** Selected time as a 24-hour \"HH:mm\" string. */\n value?: TimeValue\n /** Called with the new time, or with undefined when the value is cleared. */\n onValueChange?: (value: TimeValue | undefined) => void\n /** Earliest selectable time as \"HH:mm\". Earlier times are disabled. */\n minTime?: TimeValue\n /** Latest selectable time as \"HH:mm\". Later times are disabled. */\n maxTime?: TimeValue\n /** Minute granularity offered by the minute column. Defaults to 5. */\n minuteStep?: number\n /** 12 adds an AM/PM column, 24 lists hours 00-23. Defaults to 12. */\n hourCycle?: 12 | 24\n /** Trigger text shown when nothing is selected. */\n placeholder?: string\n /** Shows a Clear action in the popover. Defaults to true. */\n clearable?: boolean\n /** Text of the Clear action. Defaults to \"Clear\". */\n clearLabel?: string\n /** Disables the whole control. */\n disabled?: boolean\n /** Accessible name for the trigger. Defaults to the placeholder. */\n \"aria-label\"?: string\n /** Id of an element that labels the trigger. */\n \"aria-labelledby\"?: string\n /** Id applied to the trigger button. */\n id?: string\n /** Name reported on a hidden input, so the value posts with a plain form. */\n name?: string\n /** Classname applied to the trigger button. */\n className?: string\n /** Classname applied to the popover content. */\n contentClassName?: string\n /** Controlled open state of the popover. */\n open?: boolean\n /** Called when the popover opens or closes. */\n onOpenChange?: (open: boolean) => void\n /** Side the popover is aligned to. Defaults to \"start\". */\n align?: \"start\" | \"center\" | \"end\"\n}\n\n/**\n * A time control: a popover trigger showing the selected time, over hour and\n * minute columns. The value is always a 24-hour \"HH:mm\" string, so it survives\n * a round trip through JSON and through a `datetime-local`-shaped API.\n */\nconst TimePicker = React.forwardRef<HTMLButtonElement, TimePickerProps>(\n (\n {\n value,\n onValueChange,\n minTime,\n maxTime,\n minuteStep = 5,\n hourCycle = 12,\n placeholder = \"Pick a time\",\n clearable = true,\n clearLabel = \"Clear\",\n disabled,\n id,\n name,\n className,\n contentClassName,\n open,\n onOpenChange,\n align = \"start\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref\n ) => {\n const selected = parseTimeValue(value)\n const label =\n selected === undefined ? placeholder : formatTimeLabel(selected, hourCycle)\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n id={id}\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n aria-labelledby={ariaLabelledBy}\n className={cn(\n \"w-[180px] justify-start text-left font-normal\",\n selected === undefined && \"text-muted-foreground\",\n className\n )}\n >\n <ClockIcon className=\"me-2 h-4 w-4\" aria-hidden=\"true\" />\n {label}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto p-0\", contentClassName)}\n align={align}\n aria-label={ariaLabel ?? placeholder}\n >\n <TimePickerPanel\n value={value}\n onValueChange={(next) => onValueChange?.(next)}\n minTime={minTime}\n maxTime={maxTime}\n minuteStep={minuteStep}\n hourCycle={hourCycle}\n />\n {clearable && (\n <div className=\"flex justify-end border-t border-border p-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"font-normal\"\n disabled={selected === undefined}\n onClick={() => onValueChange?.(undefined)}\n >\n {clearLabel}\n </Button>\n </div>\n )}\n </PopoverContent>\n {name && <input type=\"hidden\" name={name} value={value ?? \"\"} />}\n </Popover>\n )\n }\n)\nTimePicker.displayName = \"TimePicker\"\n\nexport {\n TimePicker,\n TimePickerPanel,\n formatTimeLabel,\n formatTimeValue,\n parseTimeValue,\n isTimeInRange,\n buildMinuteList,\n buildAllowedSlots,\n snapToAllowedSlot,\n}\n"],"mappings":"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","names":["React","import_lucide_react","import_lucide_react","import_jsx_runtime","React","import_jsx_runtime","React","import_lucide_react","import_jsx_runtime","minutesOfDay","import_jsx_runtime"]}
|
|
@@ -6,7 +6,11 @@ import 'react/jsx-runtime';
|
|
|
6
6
|
interface DateTimePickerProps {
|
|
7
7
|
/** The selected moment. One value covers both the date and the time. */
|
|
8
8
|
value?: Date;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Called with the new moment, or with undefined when the value is cleared.
|
|
11
|
+
* Fires only on Apply — edits inside the popover are held as a draft until
|
|
12
|
+
* then.
|
|
13
|
+
*/
|
|
10
14
|
onValueChange?: (value: Date | undefined) => void;
|
|
11
15
|
/**
|
|
12
16
|
* Earliest selectable moment. Days before it are disabled in the calendar
|
|
@@ -36,8 +40,12 @@ interface DateTimePickerProps {
|
|
|
36
40
|
formatValue?: (value: Date) => string;
|
|
37
41
|
/** Shows a Clear action in the popover. Defaults to true. */
|
|
38
42
|
clearable?: boolean;
|
|
39
|
-
/** Text of the Clear action. Defaults to "Clear". */
|
|
43
|
+
/** Text of the Clear action, which empties the draft. Defaults to "Clear". */
|
|
40
44
|
clearLabel?: string;
|
|
45
|
+
/** Text of the action that commits the draft. Defaults to "Apply". */
|
|
46
|
+
applyLabel?: string;
|
|
47
|
+
/** Text of the action that discards the draft. Defaults to "Cancel". */
|
|
48
|
+
cancelLabel?: string;
|
|
41
49
|
/** Heading above the time columns. Defaults to "Time". */
|
|
42
50
|
timeLabel?: string;
|
|
43
51
|
/** Disables the whole control. */
|
|
@@ -67,6 +75,11 @@ interface DateTimePickerProps {
|
|
|
67
75
|
* columns write to the same value, and `minDate` / `maxDate` are enforced on
|
|
68
76
|
* both halves, including on the boundary day where only part of the day is
|
|
69
77
|
* still allowed.
|
|
78
|
+
*
|
|
79
|
+
* Edits are committed explicitly. Picking a day, an hour, a minute or a period
|
|
80
|
+
* moves a draft held inside the popover; `onValueChange` fires once, on Apply.
|
|
81
|
+
* Cancel, Escape and a click outside all discard the draft, so a stray click
|
|
82
|
+
* cannot write a deadline.
|
|
70
83
|
*/
|
|
71
84
|
declare const DateTimePicker: React.ForwardRefExoticComponent<DateTimePickerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
72
85
|
|
|
@@ -6,7 +6,11 @@ import 'react/jsx-runtime';
|
|
|
6
6
|
interface DateTimePickerProps {
|
|
7
7
|
/** The selected moment. One value covers both the date and the time. */
|
|
8
8
|
value?: Date;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Called with the new moment, or with undefined when the value is cleared.
|
|
11
|
+
* Fires only on Apply — edits inside the popover are held as a draft until
|
|
12
|
+
* then.
|
|
13
|
+
*/
|
|
10
14
|
onValueChange?: (value: Date | undefined) => void;
|
|
11
15
|
/**
|
|
12
16
|
* Earliest selectable moment. Days before it are disabled in the calendar
|
|
@@ -36,8 +40,12 @@ interface DateTimePickerProps {
|
|
|
36
40
|
formatValue?: (value: Date) => string;
|
|
37
41
|
/** Shows a Clear action in the popover. Defaults to true. */
|
|
38
42
|
clearable?: boolean;
|
|
39
|
-
/** Text of the Clear action. Defaults to "Clear". */
|
|
43
|
+
/** Text of the Clear action, which empties the draft. Defaults to "Clear". */
|
|
40
44
|
clearLabel?: string;
|
|
45
|
+
/** Text of the action that commits the draft. Defaults to "Apply". */
|
|
46
|
+
applyLabel?: string;
|
|
47
|
+
/** Text of the action that discards the draft. Defaults to "Cancel". */
|
|
48
|
+
cancelLabel?: string;
|
|
41
49
|
/** Heading above the time columns. Defaults to "Time". */
|
|
42
50
|
timeLabel?: string;
|
|
43
51
|
/** Disables the whole control. */
|
|
@@ -67,6 +75,11 @@ interface DateTimePickerProps {
|
|
|
67
75
|
* columns write to the same value, and `minDate` / `maxDate` are enforced on
|
|
68
76
|
* both halves, including on the boundary day where only part of the day is
|
|
69
77
|
* still allowed.
|
|
78
|
+
*
|
|
79
|
+
* Edits are committed explicitly. Picking a day, an hour, a minute or a period
|
|
80
|
+
* moves a draft held inside the popover; `onValueChange` fires once, on Apply.
|
|
81
|
+
* Cancel, Escape and a click outside all discard the draft, so a stray click
|
|
82
|
+
* cannot write a deadline.
|
|
70
83
|
*/
|
|
71
84
|
declare const DateTimePicker: React.ForwardRefExoticComponent<DateTimePickerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
72
85
|
|
|
@@ -484,6 +484,8 @@ var DateTimePicker = React4.forwardRef(
|
|
|
484
484
|
formatValue,
|
|
485
485
|
clearable = true,
|
|
486
486
|
clearLabel = "Clear",
|
|
487
|
+
applyLabel = "Apply",
|
|
488
|
+
cancelLabel = "Cancel",
|
|
487
489
|
timeLabel = "Time",
|
|
488
490
|
disabled,
|
|
489
491
|
id,
|
|
@@ -496,13 +498,37 @@ var DateTimePicker = React4.forwardRef(
|
|
|
496
498
|
"aria-label": ariaLabel,
|
|
497
499
|
"aria-labelledby": ariaLabelledBy
|
|
498
500
|
}, ref) => {
|
|
501
|
+
const [draft, setDraft] = React4.useState(value);
|
|
502
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React4.useState(false);
|
|
503
|
+
const isOpen = open ?? uncontrolledOpen;
|
|
504
|
+
const wasOpen = React4.useRef(isOpen);
|
|
505
|
+
React4.useEffect(() => {
|
|
506
|
+
if (isOpen && !wasOpen.current) setDraft(value);
|
|
507
|
+
wasOpen.current = isOpen;
|
|
508
|
+
}, [isOpen, value]);
|
|
509
|
+
const setOpenState = (next) => {
|
|
510
|
+
if (open === void 0) setUncontrolledOpen(next);
|
|
511
|
+
onOpenChange?.(next);
|
|
512
|
+
};
|
|
513
|
+
const handleOpenChange = (next) => {
|
|
514
|
+
setDraft(value);
|
|
515
|
+
setOpenState(next);
|
|
516
|
+
};
|
|
517
|
+
const handleCancel = () => {
|
|
518
|
+
setDraft(value);
|
|
519
|
+
setOpenState(false);
|
|
520
|
+
};
|
|
521
|
+
const handleApply = () => {
|
|
522
|
+
onValueChange?.(draft);
|
|
523
|
+
setOpenState(false);
|
|
524
|
+
};
|
|
499
525
|
const activeDay = React4.useMemo(() => {
|
|
500
|
-
if (
|
|
526
|
+
if (draft) return startOfDay(draft);
|
|
501
527
|
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
502
528
|
if (minDate && today < startOfDay(minDate)) return startOfDay(minDate);
|
|
503
529
|
if (maxDate && today > startOfDay(maxDate)) return startOfDay(maxDate);
|
|
504
530
|
return today;
|
|
505
|
-
}, [
|
|
531
|
+
}, [draft, minDate, maxDate]);
|
|
506
532
|
const timeWindowFor = React4.useCallback(
|
|
507
533
|
(day) => ({
|
|
508
534
|
minTime: minDate && isSameDay(day, minDate) ? formatTimeValue(minutesOfDay(minDate)) : void 0,
|
|
@@ -529,12 +555,12 @@ var DateTimePicker = React4.forwardRef(
|
|
|
529
555
|
}, [minDate, maxDate, disabledDates]);
|
|
530
556
|
const handleDaySelect = (day) => {
|
|
531
557
|
if (!day) return;
|
|
532
|
-
const desired =
|
|
558
|
+
const desired = draft ? minutesOfDay(draft) : parseTimeValue(defaultTime) ?? 0;
|
|
533
559
|
const { minTime, maxTime } = timeWindowFor(day);
|
|
534
560
|
const min = parseTimeValue(minTime);
|
|
535
561
|
const max = parseTimeValue(maxTime);
|
|
536
562
|
if (isTimeInRange(desired, min, max)) {
|
|
537
|
-
|
|
563
|
+
setDraft(withTime(day, desired));
|
|
538
564
|
return;
|
|
539
565
|
}
|
|
540
566
|
const snapped = snapToAllowedSlot(
|
|
@@ -542,15 +568,15 @@ var DateTimePicker = React4.forwardRef(
|
|
|
542
568
|
buildAllowedSlots(buildMinuteList(minuteStep, desired % 60), min, max)
|
|
543
569
|
);
|
|
544
570
|
if (snapped === void 0) return;
|
|
545
|
-
|
|
571
|
+
setDraft(withTime(day, snapped));
|
|
546
572
|
};
|
|
547
573
|
const handleTimeChange = (next) => {
|
|
548
574
|
const minutes = parseTimeValue(next);
|
|
549
575
|
if (minutes === void 0) return;
|
|
550
|
-
|
|
576
|
+
setDraft(withTime(draft ?? activeDay, minutes));
|
|
551
577
|
};
|
|
552
578
|
const label = value ? formatValue?.(value) ?? `${value.toLocaleDateString()} ${formatTimeLabel(minutesOfDay(value), hourCycle)}` : placeholder;
|
|
553
|
-
return /* @__PURE__ */ jsxs3(Popover, { open, onOpenChange, children: [
|
|
579
|
+
return /* @__PURE__ */ jsxs3(Popover, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
554
580
|
/* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
|
|
555
581
|
Button,
|
|
556
582
|
{
|
|
@@ -584,7 +610,7 @@ var DateTimePicker = React4.forwardRef(
|
|
|
584
610
|
Calendar,
|
|
585
611
|
{
|
|
586
612
|
mode: "single",
|
|
587
|
-
selected:
|
|
613
|
+
selected: draft,
|
|
588
614
|
defaultMonth: activeDay,
|
|
589
615
|
startMonth: calendarStartMonth,
|
|
590
616
|
endMonth: calendarEndMonth,
|
|
@@ -598,7 +624,7 @@ var DateTimePicker = React4.forwardRef(
|
|
|
598
624
|
/* @__PURE__ */ jsx5(
|
|
599
625
|
TimePickerPanel,
|
|
600
626
|
{
|
|
601
|
-
value:
|
|
627
|
+
value: draft ? formatTimeValue(minutesOfDay(draft)) : void 0,
|
|
602
628
|
onValueChange: handleTimeChange,
|
|
603
629
|
minTime: activeWindow.minTime,
|
|
604
630
|
maxTime: activeWindow.maxTime,
|
|
@@ -608,18 +634,34 @@ var DateTimePicker = React4.forwardRef(
|
|
|
608
634
|
)
|
|
609
635
|
] })
|
|
610
636
|
] }),
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
637
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 border-t border-border p-2", children: [
|
|
638
|
+
clearable && /* @__PURE__ */ jsx5(
|
|
639
|
+
Button,
|
|
640
|
+
{
|
|
641
|
+
type: "button",
|
|
642
|
+
variant: "ghost",
|
|
643
|
+
size: "sm",
|
|
644
|
+
className: "font-normal",
|
|
645
|
+
disabled: !draft,
|
|
646
|
+
onClick: () => setDraft(void 0),
|
|
647
|
+
children: clearLabel
|
|
648
|
+
}
|
|
649
|
+
),
|
|
650
|
+
/* @__PURE__ */ jsxs3("div", { className: "ms-auto flex items-center gap-2", children: [
|
|
651
|
+
/* @__PURE__ */ jsx5(
|
|
652
|
+
Button,
|
|
653
|
+
{
|
|
654
|
+
type: "button",
|
|
655
|
+
variant: "ghost",
|
|
656
|
+
size: "sm",
|
|
657
|
+
className: "font-normal",
|
|
658
|
+
onClick: handleCancel,
|
|
659
|
+
children: cancelLabel
|
|
660
|
+
}
|
|
661
|
+
),
|
|
662
|
+
/* @__PURE__ */ jsx5(Button, { type: "button", variant: "default", size: "sm", onClick: handleApply, children: applyLabel })
|
|
663
|
+
] })
|
|
664
|
+
] })
|
|
623
665
|
]
|
|
624
666
|
}
|
|
625
667
|
),
|