@codapet/design-system 0.4.3 → 0.4.5

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/index.d.mts CHANGED
@@ -251,9 +251,9 @@ interface InputProps extends Omit<React$1.ComponentProps<'input'>, 'size'>, Vari
251
251
  declare const Input: React$1.ForwardRefExoticComponent<Omit<InputProps, "ref"> & React$1.RefAttributes<HTMLInputElement>>;
252
252
 
253
253
  type DateFormat = 'MM/DD/YYYY' | 'DD/MM/YYYY' | 'YYYY-MM-DD' | 'DD-MM-YYYY' | 'MM-DD-YYYY' | 'DD.MM.YYYY' | 'MMMM D, YYYY' | 'D MMMM YYYY';
254
- type NativeInputProps$2 = Omit<React$1.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'min' | 'max' | 'size' | 'disabled' | 'onSelect'>;
255
- type FlattenedCalendarProps$1 = Omit<React$1.ComponentProps<typeof Calendar>, keyof React$1.InputHTMLAttributes<HTMLInputElement> | 'className' | 'mode' | 'selected' | 'onSelect' | 'month' | 'onMonthChange' | 'disabled' | 'captionLayout' | 'showOutsideDays' | 'classNames'>;
256
- interface DateInputProps extends NativeInputProps$2, FlattenedCalendarProps$1 {
254
+ type NativeInputProps = Omit<React$1.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'min' | 'max' | 'size' | 'disabled' | 'onSelect'>;
255
+ type FlattenedCalendarProps$1 = Omit<React$1.ComponentProps<typeof Calendar>, keyof React$1.InputHTMLAttributes<HTMLInputElement> | 'className' | 'mode' | 'selected' | 'onSelect' | 'month' | 'onMonthChange' | 'disabled' | 'captionLayout' | 'showOutsideDays' | 'classNames' | 'components' | 'formatters' | 'buttonVariant'>;
256
+ interface DateInputProps extends NativeInputProps, FlattenedCalendarProps$1 {
257
257
  date: Date | null;
258
258
  setDate: (date: Date | null) => void;
259
259
  maxDate?: Date | null;
@@ -273,12 +273,14 @@ interface DateInputProps extends NativeInputProps$2, FlattenedCalendarProps$1 {
273
273
  captionLayout?: React$1.ComponentProps<typeof Calendar>['captionLayout'];
274
274
  showOutsideDays?: React$1.ComponentProps<typeof Calendar>['showOutsideDays'];
275
275
  classNames?: React$1.ComponentProps<typeof Calendar>['classNames'];
276
+ components?: React$1.ComponentProps<typeof Calendar>['components'];
277
+ formatters?: React$1.ComponentProps<typeof Calendar>['formatters'];
278
+ buttonVariant?: React$1.ComponentProps<typeof Calendar>['buttonVariant'];
276
279
  }
277
- declare function DateInput({ date, setDate, maxDate, minDate, disableFuture, className, inputClassName, calendarClassName, inputDisabled, dateFormat, mode, selected, onSelect, month, onMonthChange, disabled: calendarDisabled, captionLayout, showOutsideDays, classNames, placeholder, onBlur, ...restProps }: DateInputProps): react_jsx_runtime.JSX.Element;
280
+ declare function DateInput({ date, setDate, maxDate, minDate, disableFuture, className, inputClassName, calendarClassName, inputDisabled, dateFormat, mode, selected, onSelect, month, onMonthChange, disabled: calendarDisabled, captionLayout, showOutsideDays, classNames, components, formatters, buttonVariant, placeholder, onBlur, ...restProps }: DateInputProps): react_jsx_runtime.JSX.Element;
278
281
 
279
- type NativeInputProps$1 = Omit<React$1.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'min' | 'max' | 'size' | 'disabled' | 'onSelect'>;
280
- type FlattenedCalendarProps = Omit<React$1.ComponentProps<typeof Calendar>, keyof React$1.InputHTMLAttributes<HTMLInputElement> | 'className' | 'mode' | 'selected' | 'onSelect' | 'month' | 'onMonthChange' | 'disabled' | 'captionLayout' | 'showOutsideDays' | 'classNames'>;
281
- interface DateRangeInputProps extends NativeInputProps$1, FlattenedCalendarProps {
282
+ type FlattenedCalendarProps = Omit<React$1.ComponentProps<typeof Calendar>, keyof React$1.InputHTMLAttributes<HTMLInputElement> | 'className' | 'mode' | 'selected' | 'onSelect' | 'month' | 'onMonthChange' | 'disabled' | 'captionLayout' | 'showOutsideDays' | 'classNames' | 'components' | 'formatters' | 'buttonVariant'>;
283
+ interface DateRangeInputProps extends FlattenedCalendarProps {
282
284
  dateRange: DateRange | undefined;
283
285
  setDateRange: (range: DateRange | undefined) => void;
284
286
  maxDate?: Date | null;
@@ -286,9 +288,11 @@ interface DateRangeInputProps extends NativeInputProps$1, FlattenedCalendarProps
286
288
  disableFuture?: boolean;
287
289
  inputDisabled?: boolean;
288
290
  size?: VariantProps<typeof inputVariants>['size'];
291
+ className?: string;
289
292
  inputClassName?: string;
290
293
  calendarClassName?: string;
291
294
  dateFormat?: DateFormat;
295
+ placeholder?: string;
292
296
  selected?: DateRange;
293
297
  onSelect?: (range: DateRange | undefined) => void;
294
298
  month?: React$1.ComponentProps<typeof Calendar>['month'];
@@ -297,8 +301,11 @@ interface DateRangeInputProps extends NativeInputProps$1, FlattenedCalendarProps
297
301
  captionLayout?: React$1.ComponentProps<typeof Calendar>['captionLayout'];
298
302
  showOutsideDays?: React$1.ComponentProps<typeof Calendar>['showOutsideDays'];
299
303
  classNames?: React$1.ComponentProps<typeof Calendar>['classNames'];
304
+ components?: React$1.ComponentProps<typeof Calendar>['components'];
305
+ formatters?: React$1.ComponentProps<typeof Calendar>['formatters'];
306
+ buttonVariant?: React$1.ComponentProps<typeof Calendar>['buttonVariant'];
300
307
  }
301
- declare function DateRangeInput({ dateRange, setDateRange, maxDate, minDate, disableFuture, className, inputClassName, calendarClassName, inputDisabled, dateFormat, selected, onSelect, month, onMonthChange, disabled: calendarDisabled, captionLayout, showOutsideDays, classNames, placeholder, onBlur, ...restProps }: DateRangeInputProps): react_jsx_runtime.JSX.Element;
308
+ declare function DateRangeInput({ dateRange, setDateRange, maxDate, minDate, disableFuture, className, inputClassName, calendarClassName, inputDisabled, dateFormat, selected, onSelect, month, onMonthChange, disabled: calendarDisabled, captionLayout, showOutsideDays, classNames, components, formatters, buttonVariant, placeholder, size, ...restProps }: DateRangeInputProps): react_jsx_runtime.JSX.Element;
302
309
 
303
310
  declare function Drawer({ ...props }: React$1.ComponentProps<typeof Drawer$1.Root>): react_jsx_runtime.JSX.Element;
304
311
  declare function DrawerTrigger({ ...props }: React$1.ComponentProps<typeof Drawer$1.Trigger>): react_jsx_runtime.JSX.Element;
@@ -576,13 +583,13 @@ declare function TabsList({ className, ...props }: React$1.ComponentProps<typeof
576
583
  declare function TabsTrigger({ className, ...props }: React$1.ComponentProps<typeof TabsPrimitive.Trigger>): react_jsx_runtime.JSX.Element;
577
584
  declare function TabsContent({ className, ...props }: React$1.ComponentProps<typeof TabsPrimitive.Content>): react_jsx_runtime.JSX.Element;
578
585
 
579
- type TimeFormat = '12h' | '24h';
586
+ type TimeFormat = '12h' | '24h' | 'h:mm a' | 'h:mm A';
580
587
  interface TimeValue {
581
588
  hours: number;
582
589
  minutes: number;
583
590
  }
584
- type NativeInputProps = Omit<React$1.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'min' | 'max' | 'size' | 'disabled' | 'onSelect'>;
585
- interface TimeInputProps extends NativeInputProps {
591
+ type BaseInputProps = Omit<React$1.ComponentProps<typeof Input>, 'value' | 'onChange' | 'min' | 'max' | 'size' | 'disabled' | 'onSelect' | 'rightIcon' | 'ref'>;
592
+ interface TimeInputProps extends BaseInputProps {
586
593
  time: TimeValue | null;
587
594
  setTime: (time: TimeValue | null) => void;
588
595
  timeFormat?: TimeFormat;
@@ -590,8 +597,10 @@ interface TimeInputProps extends NativeInputProps {
590
597
  inputDisabled?: boolean;
591
598
  size?: VariantProps<typeof inputVariants>['size'];
592
599
  inputClassName?: string;
600
+ icon?: React$1.ReactNode;
601
+ formatDisplay?: (time: TimeValue) => string;
593
602
  }
594
- declare function TimeInput({ time, setTime, timeFormat, minuteStep, inputDisabled, className, inputClassName, size, placeholder, onBlur, ...restProps }: TimeInputProps): react_jsx_runtime.JSX.Element;
603
+ declare function TimeInput({ time, setTime, timeFormat, minuteStep, inputDisabled, className, inputClassName, size, placeholder, onBlur, icon, formatDisplay, ...restProps }: TimeInputProps): react_jsx_runtime.JSX.Element;
595
604
 
596
605
  declare const toggleVariants: (props?: ({
597
606
  variant?: "default" | "outline" | null | undefined;
package/dist/index.mjs CHANGED
@@ -2174,6 +2174,9 @@ function DateInput({
2174
2174
  captionLayout = "dropdown",
2175
2175
  showOutsideDays = false,
2176
2176
  classNames,
2177
+ components,
2178
+ formatters,
2179
+ buttonVariant,
2177
2180
  placeholder,
2178
2181
  onBlur,
2179
2182
  ...restProps
@@ -2269,6 +2272,9 @@ function DateInput({
2269
2272
  calendarClassName
2270
2273
  ),
2271
2274
  classNames,
2275
+ components,
2276
+ formatters,
2277
+ buttonVariant,
2272
2278
  onSelect: onSelect ?? defaultCalendarOnSelect,
2273
2279
  disabled: calendarDisabled ?? defaultCalendarDisabled
2274
2280
  };
@@ -2351,7 +2357,7 @@ function DateInput({
2351
2357
 
2352
2358
  // src/components/ui/date-range-input.tsx
2353
2359
  import "class-variance-authority";
2354
- import { format as dateFnsFormat2, parse as dateFnsParse2, isValid as isValid2 } from "date-fns";
2360
+ import { format as dateFnsFormat2, isValid as isValid2 } from "date-fns";
2355
2361
  import { CalendarDays as CalendarDays2 } from "lucide-react";
2356
2362
  import * as React21 from "react";
2357
2363
  import { jsx as jsx23, jsxs as jsxs11 } from "react/jsx-runtime";
@@ -2375,65 +2381,6 @@ var DATE_FORMAT_PLACEHOLDER2 = {
2375
2381
  "MMMM D, YYYY": "Month d, yyyy",
2376
2382
  "D MMMM YYYY": "d Month yyyy"
2377
2383
  };
2378
- var INPUT_PROP_KEYS2 = /* @__PURE__ */ new Set([
2379
- "accept",
2380
- "alt",
2381
- "autoComplete",
2382
- "autoFocus",
2383
- "capture",
2384
- "checked",
2385
- "dirName",
2386
- "form",
2387
- "formAction",
2388
- "formEncType",
2389
- "formMethod",
2390
- "formNoValidate",
2391
- "formTarget",
2392
- "height",
2393
- "list",
2394
- "maxLength",
2395
- "minLength",
2396
- "multiple",
2397
- "name",
2398
- "pattern",
2399
- "readOnly",
2400
- "required",
2401
- "size",
2402
- "src",
2403
- "step",
2404
- "type",
2405
- "width",
2406
- "id",
2407
- "inputMode",
2408
- "lang",
2409
- "tabIndex",
2410
- "title",
2411
- "role",
2412
- "style",
2413
- "onFocus",
2414
- "onFocusCapture",
2415
- "onBlurCapture",
2416
- "onInput",
2417
- "onInvalid",
2418
- "onKeyDownCapture",
2419
- "onKeyPress",
2420
- "onKeyPressCapture",
2421
- "onKeyUp",
2422
- "onKeyUpCapture",
2423
- "onPaste",
2424
- "onPasteCapture",
2425
- "onPointerDown",
2426
- "onPointerDownCapture",
2427
- "onPointerUp",
2428
- "onPointerUpCapture",
2429
- "onMouseDown",
2430
- "onMouseDownCapture",
2431
- "onMouseUp",
2432
- "onMouseUpCapture",
2433
- "onCompositionEnd",
2434
- "onCompositionStart",
2435
- "onCompositionUpdate"
2436
- ]);
2437
2384
  function formatDate2(date, dateFormat = "MM/DD/YYYY") {
2438
2385
  if (!date || !isValid2(date)) {
2439
2386
  return "";
@@ -2444,23 +2391,10 @@ function formatRange(range, dateFormat = "MM/DD/YYYY") {
2444
2391
  if (!range) return "";
2445
2392
  const from = formatDate2(range.from, dateFormat);
2446
2393
  const to = formatDate2(range.to, dateFormat);
2447
- if (from && to) return `${from} \u2013 ${to}`;
2448
- if (from) return `${from} \u2013`;
2394
+ if (from && to) return from === to ? from : `${from} \u2013 ${to}`;
2395
+ if (from) return from;
2449
2396
  return "";
2450
2397
  }
2451
- function parseRange(value, dateFormat = "MM/DD/YYYY") {
2452
- if (!value) return null;
2453
- const parts = value.split(/\s*[\u2013\-]\s*/);
2454
- const fromStr = parts[0]?.trim();
2455
- if (!fromStr) return null;
2456
- const fromParsed = dateFnsParse2(fromStr, DATE_FORMAT_TOKENS2[dateFormat], /* @__PURE__ */ new Date());
2457
- if (!isValid2(fromParsed)) return null;
2458
- const toStr = parts[1]?.trim();
2459
- if (!toStr) return { from: fromParsed, to: void 0 };
2460
- const toParsed = dateFnsParse2(toStr, DATE_FORMAT_TOKENS2[dateFormat], /* @__PURE__ */ new Date());
2461
- if (!isValid2(toParsed)) return { from: fromParsed, to: void 0 };
2462
- return { from: fromParsed, to: toParsed };
2463
- }
2464
2398
  function rangePlaceholder(dateFormat) {
2465
2399
  const p = DATE_FORMAT_PLACEHOLDER2[dateFormat];
2466
2400
  return `${p} \u2013 ${p}`;
@@ -2484,8 +2418,11 @@ function DateRangeInput({
2484
2418
  captionLayout = "dropdown",
2485
2419
  showOutsideDays = false,
2486
2420
  classNames,
2421
+ components,
2422
+ formatters,
2423
+ buttonVariant,
2487
2424
  placeholder,
2488
- onBlur,
2425
+ size,
2489
2426
  ...restProps
2490
2427
  }) {
2491
2428
  const resolvedPlaceholder = placeholder ?? rangePlaceholder(dateFormat);
@@ -2493,23 +2430,12 @@ function DateRangeInput({
2493
2430
  const [monthState, setMonthState] = React21.useState(
2494
2431
  dateRange?.from ?? null
2495
2432
  );
2496
- const [value, setValue] = React21.useState(formatRange(dateRange, dateFormat));
2497
- const [inputProps, calendarProps] = React21.useMemo(() => {
2498
- const nextInputProps = {};
2499
- const nextCalendarProps = {};
2500
- for (const [key, val] of Object.entries(restProps)) {
2501
- const isInputProp = INPUT_PROP_KEYS2.has(key) || key.startsWith("aria-") || key.startsWith("data-");
2502
- if (isInputProp) {
2503
- nextInputProps[key] = val;
2504
- } else {
2505
- nextCalendarProps[key] = val;
2506
- }
2433
+ const displayValue = formatRange(dateRange, dateFormat);
2434
+ React21.useEffect(() => {
2435
+ if (dateRange?.from) {
2436
+ setMonthState(dateRange.from);
2507
2437
  }
2508
- return [
2509
- nextInputProps,
2510
- nextCalendarProps
2511
- ];
2512
- }, [restProps]);
2438
+ }, [dateRange]);
2513
2439
  const today = React21.useMemo(() => {
2514
2440
  const d = /* @__PURE__ */ new Date();
2515
2441
  d.setHours(0, 0, 0, 0);
@@ -2539,12 +2465,6 @@ function DateRangeInput({
2539
2465
  }
2540
2466
  return null;
2541
2467
  }, [minDate]);
2542
- React21.useEffect(() => {
2543
- setValue(formatRange(dateRange, dateFormat));
2544
- if (dateRange?.from) {
2545
- setMonthState(dateRange.from);
2546
- }
2547
- }, [dateRange, dateFormat]);
2548
2468
  const effectiveMonth = month ?? monthState ?? void 0;
2549
2469
  const effectiveSelected = selected ?? dateRange;
2550
2470
  const isInputDisabled = inputDisabled ?? (typeof calendarDisabled === "boolean" ? calendarDisabled : false);
@@ -2565,8 +2485,14 @@ function DateRangeInput({
2565
2485
  }
2566
2486
  setDateRange(range);
2567
2487
  };
2488
+ const handleClear = () => {
2489
+ setDateRange(void 0);
2490
+ };
2491
+ const handleAdd = () => {
2492
+ setOpen(false);
2493
+ };
2568
2494
  const resolvedCalendarProps = {
2569
- ...calendarProps,
2495
+ ...restProps,
2570
2496
  mode: "range",
2571
2497
  selected: effectiveSelected,
2572
2498
  captionLayout,
@@ -2578,93 +2504,56 @@ function DateRangeInput({
2578
2504
  calendarClassName
2579
2505
  ),
2580
2506
  classNames,
2507
+ components,
2508
+ formatters,
2509
+ buttonVariant,
2581
2510
  onSelect: handleCalendarSelect,
2582
2511
  disabled: calendarDisabled ?? defaultCalendarDisabled
2583
2512
  };
2584
- const handleInputChange = (e) => {
2585
- const inputValue = e.target.value;
2586
- setValue(inputValue);
2587
- if (inputValue === "") {
2588
- setDateRange(void 0);
2589
- return;
2590
- }
2591
- const parsed = parseRange(inputValue, dateFormat);
2592
- if (!parsed || !parsed.from) return;
2593
- const from = new Date(parsed.from);
2594
- from.setHours(0, 0, 0, 0);
2595
- if (!isWithinBounds(from)) return;
2596
- if (parsed.to) {
2597
- const to = new Date(parsed.to);
2598
- to.setHours(0, 0, 0, 0);
2599
- if (isWithinBounds(to) && from <= to) {
2600
- setDateRange(parsed);
2601
- setMonthState(from);
2602
- }
2603
- } else {
2604
- setDateRange({ from: parsed.from, to: void 0 });
2605
- setMonthState(from);
2606
- }
2607
- };
2608
- const handleBlur = (e) => {
2609
- onBlur?.(e);
2610
- if (value === "") {
2611
- if (dateRange !== void 0) {
2612
- setDateRange(void 0);
2613
- }
2614
- return;
2615
- }
2616
- const parsed = parseRange(value, dateFormat);
2617
- if (!parsed || !parsed.from) {
2618
- setValue(formatRange(dateRange, dateFormat));
2619
- return;
2620
- }
2621
- const from = new Date(parsed.from);
2622
- from.setHours(0, 0, 0, 0);
2623
- if (!isWithinBounds(from)) {
2624
- setValue(formatRange(dateRange, dateFormat));
2625
- return;
2626
- }
2627
- if (parsed.to) {
2628
- const to = new Date(parsed.to);
2629
- to.setHours(0, 0, 0, 0);
2630
- if (!isWithinBounds(to) || from > to) {
2631
- setValue(formatRange(dateRange, dateFormat));
2632
- }
2633
- }
2634
- };
2635
2513
  return /* @__PURE__ */ jsx23("div", { className: cn("relative flex gap-2", className), children: /* @__PURE__ */ jsxs11(Popover, { open, onOpenChange: setOpen, children: [
2636
- /* @__PURE__ */ jsx23(PopoverTrigger, { asChild: true, disabled: isInputDisabled, children: /* @__PURE__ */ jsx23("div", { className: "w-full relative", children: /* @__PURE__ */ jsx23(
2637
- Input,
2514
+ /* @__PURE__ */ jsx23(PopoverTrigger, { asChild: true, disabled: isInputDisabled, children: /* @__PURE__ */ jsxs11(
2515
+ Button,
2638
2516
  {
2639
- value,
2640
- placeholder: resolvedPlaceholder,
2641
- className: cn("bg-background cursor-pointer", inputClassName),
2642
- onChange: handleInputChange,
2643
- onBlur: handleBlur,
2517
+ type: "button",
2518
+ variant: "outline",
2519
+ className: cn(
2520
+ inputVariants({ size }),
2521
+ "bg-background cursor-pointer w-full text-left flex items-center justify-between gap-2 font-normal",
2522
+ isInputDisabled && "pointer-events-none cursor-not-allowed opacity-50",
2523
+ inputClassName
2524
+ ),
2644
2525
  disabled: isInputDisabled,
2645
- onKeyDown: (e) => {
2646
- if (e.key === "ArrowDown" && !isInputDisabled) {
2647
- e.preventDefault();
2648
- setOpen(true);
2649
- }
2650
- },
2651
- rightIcon: /* @__PURE__ */ jsx23(CalendarDays2, { className: "h-4 w-4 text-muted-foreground" }),
2652
- rightIconOnClick: isInputDisabled ? void 0 : () => setOpen(!open),
2653
- rightIconButtonProps: { disabled: isInputDisabled },
2654
- ...inputProps
2655
- }
2656
- ) }) }),
2657
- /* @__PURE__ */ jsx23(
2658
- PopoverContent,
2659
- {
2660
- className: "w-auto overflow-hidden p-0",
2661
- align: "end",
2662
- alignOffset: -8,
2663
- sideOffset: 10,
2664
- side: "top",
2665
- children: /* @__PURE__ */ jsx23(Calendar, { ...resolvedCalendarProps })
2526
+ children: [
2527
+ displayValue || resolvedPlaceholder,
2528
+ /* @__PURE__ */ jsx23(CalendarDays2, { className: "h-4 w-4 text-muted-foreground shrink-0" })
2529
+ ]
2666
2530
  }
2667
- )
2531
+ ) }),
2532
+ /* @__PURE__ */ jsxs11(PopoverContent, { className: "p-0 flex flex-col overflow-y-auto max-h-[min(90dvh,520px)] md:w-[350px] w-[var(--radix-popover-trigger-width)] ", children: [
2533
+ /* @__PURE__ */ jsx23(Calendar, { ...resolvedCalendarProps }),
2534
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2 px-2 py-2", children: [
2535
+ /* @__PURE__ */ jsx23(
2536
+ Button,
2537
+ {
2538
+ variant: "ghost-secondary",
2539
+ size: "sm",
2540
+ onClick: handleClear,
2541
+ type: "button",
2542
+ children: "Clear"
2543
+ }
2544
+ ),
2545
+ /* @__PURE__ */ jsx23(
2546
+ Button,
2547
+ {
2548
+ variant: "primary",
2549
+ size: "sm",
2550
+ onClick: handleAdd,
2551
+ type: "button",
2552
+ children: "Add"
2553
+ }
2554
+ )
2555
+ ] })
2556
+ ] })
2668
2557
  ] }) });
2669
2558
  }
2670
2559
 
@@ -5309,26 +5198,47 @@ import "class-variance-authority";
5309
5198
  import { Clock } from "lucide-react";
5310
5199
  import * as React46 from "react";
5311
5200
  import { jsx as jsx49, jsxs as jsxs24 } from "react/jsx-runtime";
5201
+ var TIME_FORMAT_PLACEHOLDER = {
5202
+ "12h": "hh:mm AM/PM",
5203
+ "24h": "HH:mm",
5204
+ "h:mm a": "h:mm am/pm",
5205
+ "h:mm A": "h:mm AM/PM"
5206
+ };
5207
+ function is24HourFormat(tf) {
5208
+ return tf === "24h";
5209
+ }
5312
5210
  function formatTime(time, timeFormat = "12h") {
5313
5211
  if (!time) return "";
5314
- if (timeFormat === "24h") {
5315
- return `${String(time.hours).padStart(2, "0")}:${String(time.minutes).padStart(2, "0")}`;
5212
+ const { hours, minutes } = time;
5213
+ const h24 = String(hours).padStart(2, "0");
5214
+ const m = String(minutes).padStart(2, "0");
5215
+ const h12 = hours % 12 || 12;
5216
+ const h12p = String(h12).padStart(2, "0");
5217
+ const period = hours >= 12 ? "PM" : "AM";
5218
+ switch (timeFormat) {
5219
+ case "24h":
5220
+ return `${h24}:${m}`;
5221
+ case "h:mm a":
5222
+ return `${h12}:${m} ${period.toLowerCase()}`;
5223
+ case "h:mm A":
5224
+ return `${h12}:${m} ${period}`;
5225
+ case "12h":
5226
+ default:
5227
+ return `${h12p}:${m} ${period}`;
5316
5228
  }
5317
- const period = time.hours >= 12 ? "PM" : "AM";
5318
- const h12 = time.hours % 12 || 12;
5319
- return `${String(h12).padStart(2, "0")}:${String(time.minutes).padStart(2, "0")} ${period}`;
5320
5229
  }
5321
5230
  function parseTime(value, timeFormat = "12h") {
5322
5231
  if (!value.trim()) return null;
5323
- if (timeFormat === "24h") {
5324
- const match2 = value.trim().match(/^(\d{1,2}):(\d{2})$/);
5232
+ const v = value.trim();
5233
+ if (is24HourFormat(timeFormat)) {
5234
+ const match2 = v.match(/^(\d{1,2}):(\d{2})$/);
5325
5235
  if (!match2) return null;
5326
5236
  const hours2 = parseInt(match2[1], 10);
5327
5237
  const minutes2 = parseInt(match2[2], 10);
5328
5238
  if (hours2 < 0 || hours2 > 23 || minutes2 < 0 || minutes2 > 59) return null;
5329
5239
  return { hours: hours2, minutes: minutes2 };
5330
5240
  }
5331
- const match = value.trim().match(/^(\d{1,2}):(\d{2})\s*(AM|PM|am|pm)$/i);
5241
+ const match = v.match(/^(\d{1,2}):(\d{2})\s*(AM|PM|am|pm)$/i);
5332
5242
  if (!match) return null;
5333
5243
  let hours = parseInt(match[1], 10);
5334
5244
  const minutes = parseInt(match[2], 10);
@@ -5339,7 +5249,7 @@ function parseTime(value, timeFormat = "12h") {
5339
5249
  return { hours, minutes };
5340
5250
  }
5341
5251
  function getDisplayHour(hours, timeFormat) {
5342
- if (timeFormat === "24h") return hours;
5252
+ if (is24HourFormat(timeFormat)) return hours;
5343
5253
  return hours % 12 || 12;
5344
5254
  }
5345
5255
  function getPeriod(hours) {
@@ -5356,17 +5266,27 @@ function TimeInput({
5356
5266
  size,
5357
5267
  placeholder,
5358
5268
  onBlur,
5269
+ icon,
5270
+ formatDisplay,
5359
5271
  ...restProps
5360
5272
  }) {
5361
- const resolvedPlaceholder = placeholder ?? (timeFormat === "12h" ? "hh:mm AM/PM" : "HH:mm");
5273
+ const resolvedPlaceholder = placeholder ?? TIME_FORMAT_PLACEHOLDER[timeFormat];
5274
+ const displayFormat = React46.useCallback(
5275
+ (t) => {
5276
+ if (!t) return "";
5277
+ if (formatDisplay) return formatDisplay(t);
5278
+ return formatTime(t, timeFormat);
5279
+ },
5280
+ [formatDisplay, timeFormat]
5281
+ );
5362
5282
  const [open, setOpen] = React46.useState(false);
5363
- const [value, setValue] = React46.useState(formatTime(time, timeFormat));
5283
+ const [value, setValue] = React46.useState(displayFormat(time));
5364
5284
  const hoursRef = React46.useRef(null);
5365
5285
  const minutesRef = React46.useRef(null);
5366
5286
  const periodRef = React46.useRef(null);
5367
5287
  React46.useEffect(() => {
5368
- setValue(formatTime(time, timeFormat));
5369
- }, [time, timeFormat]);
5288
+ setValue(displayFormat(time));
5289
+ }, [time, displayFormat]);
5370
5290
  const scrollToSelected = React46.useCallback(() => {
5371
5291
  requestAnimationFrame(() => {
5372
5292
  for (const ref of [hoursRef, minutesRef, periodRef]) {
@@ -5404,12 +5324,12 @@ function TimeInput({
5404
5324
  }
5405
5325
  const parsed = parseTime(value, timeFormat);
5406
5326
  if (!parsed) {
5407
- setValue(formatTime(time, timeFormat));
5327
+ setValue(displayFormat(time));
5408
5328
  }
5409
5329
  };
5410
5330
  const handleHourSelect = (hour) => {
5411
5331
  let h24;
5412
- if (timeFormat === "24h") {
5332
+ if (is24HourFormat(timeFormat)) {
5413
5333
  h24 = hour;
5414
5334
  } else {
5415
5335
  const currentPeriod = time ? getPeriod(time.hours) : "AM";
@@ -5430,7 +5350,7 @@ function TimeInput({
5430
5350
  const newHours = period === "AM" ? currentH12 : currentH12 + 12;
5431
5351
  setTime({ hours: newHours, minutes: time?.minutes ?? 0 });
5432
5352
  };
5433
- const hoursList = timeFormat === "12h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 24 }, (_, i) => i);
5353
+ const hoursList = is24HourFormat(timeFormat) ? Array.from({ length: 24 }, (_, i) => i) : Array.from({ length: 12 }, (_, i) => i + 1);
5434
5354
  const minutesList = Array.from(
5435
5355
  { length: Math.ceil(60 / minuteStep) },
5436
5356
  (_, i) => i * minuteStep
@@ -5458,21 +5378,17 @@ function TimeInput({
5458
5378
  setOpen(true);
5459
5379
  }
5460
5380
  },
5461
- rightIcon: /* @__PURE__ */ jsx49(Clock, { className: "h-4 w-4 text-muted-foreground" }),
5381
+ rightIcon: icon !== void 0 ? icon : /* @__PURE__ */ jsx49(Clock, { className: "h-4 w-4 text-muted-foreground" }),
5462
5382
  ...restProps
5463
5383
  }
5464
5384
  ) }) }),
5465
5385
  /* @__PURE__ */ jsx49(
5466
5386
  PopoverContent,
5467
5387
  {
5468
- className: "w-auto p-0",
5469
- align: "end",
5470
- alignOffset: -8,
5471
- sideOffset: 10,
5472
- side: "top",
5388
+ className: "w-auto p-0 ",
5473
5389
  onOpenAutoFocus: (e) => e.preventDefault(),
5474
5390
  children: /* @__PURE__ */ jsxs24("div", { className: "flex divide-x", children: [
5475
- /* @__PURE__ */ jsx49("div", { className: "h-56 w-16 overflow-y-auto overscroll-contain ", children: /* @__PURE__ */ jsx49("div", { ref: hoursRef, className: "flex flex-col p-1 ", children: hoursList.map((h) => /* @__PURE__ */ jsx49(
5391
+ /* @__PURE__ */ jsx49("div", { className: "h-56 w-16 overflow-y-auto overscroll-y-contain [-webkit-overflow-scrolling:touch]", children: /* @__PURE__ */ jsx49("div", { ref: hoursRef, className: "flex flex-col p-1 ", children: hoursList.map((h) => /* @__PURE__ */ jsx49(
5476
5392
  Button,
5477
5393
  {
5478
5394
  variant: "ghost",
@@ -5487,7 +5403,7 @@ function TimeInput({
5487
5403
  },
5488
5404
  h
5489
5405
  )) }) }),
5490
- /* @__PURE__ */ jsx49("div", { className: "h-56 w-16 overflow-y-auto overscroll-contain", children: /* @__PURE__ */ jsx49("div", { ref: minutesRef, className: "flex flex-col p-1", children: minutesList.map((m) => /* @__PURE__ */ jsx49(
5406
+ /* @__PURE__ */ jsx49("div", { className: "h-56 w-16 overflow-y-auto overscroll-y-contain [-webkit-overflow-scrolling:touch]", children: /* @__PURE__ */ jsx49("div", { ref: minutesRef, className: "flex flex-col p-1 ", children: minutesList.map((m) => /* @__PURE__ */ jsx49(
5491
5407
  Button,
5492
5408
  {
5493
5409
  variant: "ghost",
@@ -5502,7 +5418,7 @@ function TimeInput({
5502
5418
  },
5503
5419
  m
5504
5420
  )) }) }),
5505
- timeFormat === "12h" && /* @__PURE__ */ jsx49("div", { className: "flex flex-col p-1 justify-center gap-1", children: ["AM", "PM"].map((p) => /* @__PURE__ */ jsx49(
5421
+ !is24HourFormat(timeFormat) && /* @__PURE__ */ jsx49("div", { className: "flex flex-col p-1 justify-center gap-1", children: ["AM", "PM"].map((p) => /* @__PURE__ */ jsx49(
5506
5422
  Button,
5507
5423
  {
5508
5424
  variant: "ghost",