@bricks-toolkit/toolkit 0.1.25 → 0.1.27

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.
@@ -135,9 +135,11 @@ var DatePicker = react.forwardRef(
135
135
  disabledDates,
136
136
  locale = "en-US",
137
137
  name,
138
+ type,
138
139
  "aria-label": ariaLabel,
139
140
  "aria-describedby": ariaDescribedby
140
141
  }, ref) => {
142
+ const internalRef = react.useRef(null);
141
143
  const generatedId = react.useId();
142
144
  const inputId = idProp ?? generatedId;
143
145
  const helperId = `${inputId}-helper`;
@@ -164,8 +166,7 @@ var DatePicker = react.forwardRef(
164
166
  react.useEffect(() => {
165
167
  if (!isOpen) return;
166
168
  function handleClick(e) {
167
- const wrapper = ref && typeof ref === "object" && "current" in ref ? ref.current : null;
168
- if (wrapper && !wrapper.contains(e.target)) {
169
+ if (internalRef.current && !internalRef.current.contains(e.target)) {
169
170
  setIsOpen(false);
170
171
  setViewMode("day");
171
172
  }
@@ -238,7 +239,14 @@ var DatePicker = react.forwardRef(
238
239
  return /* @__PURE__ */ jsxRuntime.jsxs(
239
240
  "div",
240
241
  {
241
- ref,
242
+ ref: (node) => {
243
+ internalRef.current = node;
244
+ if (typeof ref === "function") {
245
+ ref(node);
246
+ } else if (ref) {
247
+ ref.current = node;
248
+ }
249
+ },
242
250
  className: chunkL5VQZZVR_cjs.cn(
243
251
  "flex flex-col gap-1 relative",
244
252
  fullWidth ? "w-full" : "w-fit",
@@ -273,7 +281,7 @@ var DatePicker = react.forwardRef(
273
281
  "button",
274
282
  {
275
283
  id: inputId,
276
- type: "button",
284
+ type: type ?? "button",
277
285
  role: "combobox",
278
286
  "aria-expanded": isOpen,
279
287
  "aria-haspopup": "dialog",
@@ -1,32 +1,53 @@
1
1
  import { cn } from './chunk-OCPFOFJ4.mjs';
2
- import { forwardRef, useId, useState, useEffect, useCallback, useRef } from 'react';
3
- import { ClockIcon, XMarkIcon, ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
4
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { forwardRef, useId, useRef, useState, useEffect } from 'react';
3
+ import { ClockIcon, XMarkIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
6
- function parseTime(value) {
7
- if (!value) return { hours: 0, minutes: 0 };
8
- const numbers = value.split(":").map(Number);
9
- const h = numbers[0];
10
- const m = numbers[1];
11
- return {
12
- hours: h === void 0 || isNaN(h) ? 0 : h,
13
- minutes: m === void 0 || isNaN(m) ? 0 : m
14
- };
6
+ function parseTypedTime(str) {
7
+ const clean = str.trim();
8
+ if (!clean) return null;
9
+ const match24 = /^(\d{2}):(\d{2})$/.exec(clean);
10
+ if (match24?.[1] && match24[2]) {
11
+ const hour = parseInt(match24[1], 10);
12
+ const minute = parseInt(match24[2], 10);
13
+ if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59) {
14
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
15
+ }
16
+ }
17
+ return null;
15
18
  }
16
- function toTimeString(hours, minutes) {
17
- return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
19
+ function formatTime12(iso) {
20
+ if (!iso) return "";
21
+ const parts = iso.split(":");
22
+ if (parts.length < 2 || !parts[0] || !parts[1]) return iso;
23
+ const hour = parseInt(parts[0], 10);
24
+ const minute = parseInt(parts[1], 10);
25
+ if (isNaN(hour) || isNaN(minute)) return iso;
26
+ const displayHour = hour % 12 || 12;
27
+ const ampm = hour >= 12 ? "PM" : "AM";
28
+ return `${String(displayHour)}:${String(minute).padStart(2, "0")} ${ampm}`;
18
29
  }
19
- function formatDisplay(value, use12Hour) {
20
- if (!value) return "";
21
- const { hours, minutes } = parseTime(value);
22
- if (use12Hour) {
23
- const ampm = hours >= 12 ? "PM" : "AM";
24
- const h = hours % 12 || 12;
25
- return `${String(h).padStart(2, "0")}:${String(minutes).padStart(2, "0")} ${ampm}`;
30
+ function formatDisplay(iso, use12Hour) {
31
+ if (!iso) return "";
32
+ return use12Hour ? formatTime12(iso) : iso;
33
+ }
34
+ function generateTimeSlots(step, use12Hour) {
35
+ const slots = [];
36
+ const limit = 24 * 60;
37
+ const s = step > 0 && step <= 720 ? step : 1;
38
+ for (let minutes = 0; minutes < limit; minutes += s) {
39
+ const hour = Math.floor(minutes / 60);
40
+ const minute = minutes % 60;
41
+ const value = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
42
+ const label = use12Hour ? (() => {
43
+ const displayHour = hour % 12 || 12;
44
+ const ampm = hour >= 12 ? "PM" : "AM";
45
+ return `${String(displayHour)}:${String(minute).padStart(2, "0")} ${ampm}`;
46
+ })() : value;
47
+ slots.push({ label, value });
26
48
  }
27
- return toTimeString(hours, minutes);
49
+ return slots;
28
50
  }
29
- var ITEM_HEIGHT = 36;
30
51
  var sizeClasses = {
31
52
  xs: "h-6 px-2 text-xs",
32
53
  sm: "h-8 px-3 text-sm",
@@ -79,104 +100,12 @@ var stateMessageClasses = {
79
100
  success: "text-success font-medium",
80
101
  warning: "text-warning font-medium"
81
102
  };
82
- function ScrollColumn({ items, selected, onSelect, label }) {
83
- const listRef = useRef(null);
84
- const scrollToSelected = useCallback(
85
- (behavior = "smooth") => {
86
- if (!listRef.current) return;
87
- const idx = items.indexOf(selected);
88
- if (idx < 0) return;
89
- const top = idx * ITEM_HEIGHT - ITEM_HEIGHT * 2;
90
- if (typeof listRef.current.scrollTo === "function") {
91
- listRef.current.scrollTo({ top: Math.max(0, top), behavior });
92
- }
93
- },
94
- [items, selected]
95
- );
96
- useEffect(() => {
97
- scrollToSelected("instant");
98
- }, []);
99
- useEffect(() => {
100
- scrollToSelected("smooth");
101
- }, [scrollToSelected]);
102
- const step = (dir) => {
103
- const idx = items.indexOf(selected);
104
- const next = items[(idx + dir + items.length) % items.length];
105
- if (next) onSelect(next);
106
- };
107
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", "aria-label": label, role: "group", children: [
108
- /* @__PURE__ */ jsx(
109
- "button",
110
- {
111
- type: "button",
112
- onClick: () => {
113
- step(-1);
114
- },
115
- className: "p-1 rounded hover:bg-hover text-text-secondary hover:text-text transition-colors",
116
- "aria-label": `Decrease ${label}`,
117
- children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "w-4 h-4" })
118
- }
119
- ),
120
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
121
- /* @__PURE__ */ jsx(
122
- "div",
123
- {
124
- className: "absolute inset-x-0 rounded-lg bg-primary/10 border-y border-primary/20 pointer-events-none",
125
- style: { top: ITEM_HEIGHT * 2, height: ITEM_HEIGHT }
126
- }
127
- ),
128
- /* @__PURE__ */ jsxs(
129
- "div",
130
- {
131
- ref: listRef,
132
- className: "overflow-y-auto scrollbar-none",
133
- style: { height: ITEM_HEIGHT * 5, width: 56 },
134
- children: [
135
- /* @__PURE__ */ jsx("div", { style: { height: ITEM_HEIGHT * 2 } }),
136
- items.map((item) => /* @__PURE__ */ jsx(
137
- "button",
138
- {
139
- type: "button",
140
- onClick: () => {
141
- onSelect(item);
142
- },
143
- className: cn(
144
- "w-full flex items-center justify-center rounded-lg text-sm font-medium transition-colors duration-150",
145
- "hover:bg-hover",
146
- selected === item ? "text-primary font-bold" : "text-text-secondary"
147
- ),
148
- style: { height: ITEM_HEIGHT },
149
- "aria-pressed": selected === item,
150
- "aria-label": `${label} ${item}`,
151
- children: item
152
- },
153
- item
154
- )),
155
- /* @__PURE__ */ jsx("div", { style: { height: ITEM_HEIGHT * 2 } })
156
- ]
157
- }
158
- )
159
- ] }),
160
- /* @__PURE__ */ jsx(
161
- "button",
162
- {
163
- type: "button",
164
- onClick: () => {
165
- step(1);
166
- },
167
- className: "p-1 rounded hover:bg-hover text-text-secondary hover:text-text transition-colors",
168
- "aria-label": `Increase ${label}`,
169
- children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "w-4 h-4" })
170
- }
171
- )
172
- ] });
173
- }
174
103
  function AddonIcon({ children, side, size, onClick, asButton = false, ariaLabel }) {
175
104
  const iconSize = {
176
- xs: "w-3.5 h-3.5",
177
- sm: "w-4 h-4",
105
+ xs: "w-3 h-3",
106
+ sm: "w-3.5 h-3.5",
178
107
  md: "w-4 h-4",
179
- lg: "w-5 h-5",
108
+ lg: "w-4.5 h-4.5",
180
109
  xl: "w-5 h-5"
181
110
  };
182
111
  const base = cn(
@@ -190,7 +119,7 @@ function AddonIcon({ children, side, size, onClick, asButton = false, ariaLabel
190
119
  {
191
120
  type: "button",
192
121
  onClick,
193
- className: cn(base, "cursor-pointer hover:text-text transition-colors"),
122
+ className: cn(base, "cursor-pointer hover:text-text transition-colors z-20"),
194
123
  tabIndex: -1,
195
124
  "aria-label": ariaLabel,
196
125
  children
@@ -201,7 +130,7 @@ function AddonIcon({ children, side, size, onClick, asButton = false, ariaLabel
201
130
  "span",
202
131
  {
203
132
  "aria-hidden": !ariaLabel,
204
- className: cn(base, "pointer-events-none"),
133
+ className: cn(base, "pointer-events-none z-20"),
205
134
  "aria-label": ariaLabel,
206
135
  children
207
136
  }
@@ -262,21 +191,43 @@ var TimePicker = forwardRef(
262
191
  const helperId = `${inputId}-helper`;
263
192
  const stateMessageId = `${inputId}-state`;
264
193
  const popoverId = `${inputId}-popover`;
194
+ const internalRef = useRef(null);
195
+ const inputRef = useRef(null);
196
+ const dropdownRef = useRef(null);
197
+ const debounceTimerRef = useRef(null);
265
198
  const isControlled = value !== void 0;
266
199
  const [internalValue, setInternalValue] = useState(defaultValue ?? "");
267
200
  const displayValue = isControlled ? value : internalValue;
268
- const { hours: selHours, minutes: selMinutes } = parseTime(displayValue);
269
- const selAmpm = selHours >= 12 ? "PM" : "AM";
270
- const selHours12 = selHours % 12 || 12;
271
- const pickerHour = String(use12Hour ? selHours12 : selHours).padStart(2, "0");
272
- const pickerMinute = String(selMinutes).padStart(2, "0");
273
- const pickerAmpm = selAmpm;
274
201
  const [isOpen, setIsOpen] = useState(false);
202
+ const [typedValue, setTypedValue] = useState(null);
203
+ const displayInputValue = typedValue ?? formatDisplay(displayValue, use12Hour);
204
+ const timeSlots = generateTimeSlots(minuteStep, use12Hour);
205
+ const filteredSlots = timeSlots.filter((slot) => {
206
+ if (!displayInputValue) return true;
207
+ const typedClean = displayInputValue.replace(/[^0-9]/g, "");
208
+ const slotLabelClean = slot.label.replace(/[^0-9]/g, "");
209
+ const slotValueClean = slot.value.replace(/[^0-9]/g, "");
210
+ return slot.label.toLowerCase().includes(displayInputValue.toLowerCase()) || slot.value.includes(displayInputValue) || slotLabelClean.includes(typedClean) || slotValueClean.includes(typedClean);
211
+ });
212
+ useEffect(() => {
213
+ if (isOpen && dropdownRef.current) {
214
+ const activeItem = dropdownRef.current.querySelector('[data-active="true"]');
215
+ if (activeItem) {
216
+ activeItem.scrollIntoView({ block: "nearest" });
217
+ }
218
+ }
219
+ }, [isOpen]);
220
+ useEffect(() => {
221
+ return () => {
222
+ if (debounceTimerRef.current) {
223
+ clearTimeout(debounceTimerRef.current);
224
+ }
225
+ };
226
+ }, []);
275
227
  useEffect(() => {
276
228
  if (!isOpen) return;
277
229
  function handleClick(e) {
278
- const wrapper = ref && typeof ref === "object" && "current" in ref ? ref.current : null;
279
- if (wrapper && !wrapper.contains(e.target)) {
230
+ if (internalRef.current && !internalRef.current.contains(e.target)) {
280
231
  setIsOpen(false);
281
232
  }
282
233
  }
@@ -284,45 +235,91 @@ var TimePicker = forwardRef(
284
235
  return () => {
285
236
  document.removeEventListener("mousedown", handleClick);
286
237
  };
287
- }, [isOpen, ref]);
288
- const hourItems = use12Hour ? Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0")) : Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
289
- const minuteItems = Array.from(
290
- { length: Math.ceil(60 / minuteStep) },
291
- (_, i) => String(i * minuteStep).padStart(2, "0")
292
- );
293
- const applyTime = useCallback(
294
- (h, m, ap) => {
295
- let hours24 = parseInt(h, 10);
296
- if (use12Hour) {
297
- if (ap === "AM" && hours24 === 12) hours24 = 0;
298
- if (ap === "PM" && hours24 !== 12) hours24 += 12;
299
- }
300
- const iso = toTimeString(hours24, parseInt(m, 10));
301
- if (!isControlled) setInternalValue(iso);
302
- onChange?.(iso);
303
- onTimeChange?.(iso);
304
- },
305
- [isControlled, onChange, onTimeChange, use12Hour]
306
- );
307
- const handleHourSelect = (h) => {
308
- applyTime(h, pickerMinute, pickerAmpm);
309
- };
310
- const handleMinuteSelect = (m) => {
311
- applyTime(pickerHour, m, pickerAmpm);
312
- };
313
- const handleAmpmSelect = (ap) => {
314
- const a = ap;
315
- applyTime(pickerHour, pickerMinute, a);
316
- };
317
- const handleClear = () => {
238
+ }, [isOpen]);
239
+ const handleClear = (e) => {
240
+ e.stopPropagation();
241
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
318
242
  if (!isControlled) setInternalValue("");
319
243
  onChange?.("");
320
244
  onTimeChange?.(null);
245
+ setTypedValue(null);
321
246
  };
322
- const toggleOpen = () => {
247
+ const toggleOpen = (e) => {
248
+ e.stopPropagation();
323
249
  if (disabled || isLoading) return;
324
250
  setIsOpen((prev) => !prev);
325
251
  };
252
+ const handleInputChange = (e) => {
253
+ let val = e.target.value;
254
+ val = val.replace(/[^0-9:]/g, "");
255
+ if (val.length > 5) {
256
+ val = val.slice(0, 5);
257
+ }
258
+ const currentLength = typedValue?.length ?? 0;
259
+ if (val.length === 2 && !val.includes(":") && val.length > currentLength) {
260
+ val = val + ":";
261
+ }
262
+ if (val.length === 5) {
263
+ const parts = val.split(":");
264
+ if (parts[0] && parts[1]) {
265
+ let hours = parseInt(parts[0], 10);
266
+ let minutes = parseInt(parts[1], 10);
267
+ if (hours > 23) hours = 23;
268
+ if (minutes > 59) minutes = 59;
269
+ val = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
270
+ }
271
+ }
272
+ setTypedValue(val);
273
+ if (debounceTimerRef.current) {
274
+ clearTimeout(debounceTimerRef.current);
275
+ }
276
+ debounceTimerRef.current = setTimeout(() => {
277
+ const parsed = parseTypedTime(val);
278
+ if (parsed) {
279
+ if (!isControlled) setInternalValue(parsed);
280
+ onChange?.(parsed);
281
+ onTimeChange?.(parsed);
282
+ }
283
+ }, 1e3);
284
+ };
285
+ const handleKeyDown = (e) => {
286
+ const allowedKeys = [
287
+ "Backspace",
288
+ "Delete",
289
+ "ArrowLeft",
290
+ "ArrowRight",
291
+ "Tab",
292
+ "Escape",
293
+ "Enter"
294
+ ];
295
+ if (allowedKeys.includes(e.key) || e.ctrlKey || e.metaKey) {
296
+ return;
297
+ }
298
+ if (!/[0-9:]/.test(e.key)) {
299
+ e.preventDefault();
300
+ }
301
+ };
302
+ const handleInputBlur = () => {
303
+ setTimeout(() => {
304
+ if (debounceTimerRef.current) {
305
+ clearTimeout(debounceTimerRef.current);
306
+ }
307
+ const finalVal = typedValue ?? displayValue;
308
+ const parsed = parseTypedTime(finalVal);
309
+ if (parsed) {
310
+ if (!isControlled) setInternalValue(parsed);
311
+ onChange?.(parsed);
312
+ onTimeChange?.(parsed);
313
+ }
314
+ setTypedValue(null);
315
+ }, 150);
316
+ };
317
+ const handleInputFocus = () => {
318
+ setTypedValue(displayValue);
319
+ if (!disabled && !isLoading) {
320
+ setIsOpen(true);
321
+ }
322
+ };
326
323
  const showClear = clearable && displayValue.length > 0 && !disabled && !isLoading;
327
324
  const rightPad = {
328
325
  xs: "pr-6",
@@ -336,7 +333,14 @@ var TimePicker = forwardRef(
336
333
  return /* @__PURE__ */ jsxs(
337
334
  "div",
338
335
  {
339
- ref,
336
+ ref: (node) => {
337
+ internalRef.current = node;
338
+ if (typeof ref === "function") {
339
+ ref(node);
340
+ } else if (ref) {
341
+ ref.current = node;
342
+ }
343
+ },
340
344
  className: cn(
341
345
  "flex flex-col gap-1 relative",
342
346
  fullWidth ? "w-full" : "w-fit",
@@ -368,33 +372,35 @@ var TimePicker = forwardRef(
368
372
  /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 items-center", children: [
369
373
  /* @__PURE__ */ jsx(AddonIcon, { side: "left", size, children: leftElement ?? /* @__PURE__ */ jsx(ClockIcon, { className: "w-full h-full" }) }),
370
374
  /* @__PURE__ */ jsx(
371
- "button",
375
+ "input",
372
376
  {
377
+ ref: inputRef,
373
378
  id: inputId,
374
- type: "button",
375
- role: "combobox",
376
- "aria-expanded": isOpen,
377
- "aria-haspopup": "dialog",
378
- "aria-controls": popoverId,
379
+ type: "text",
380
+ value: displayInputValue,
381
+ onChange: handleInputChange,
382
+ onKeyDown: handleKeyDown,
383
+ onFocus: handleInputFocus,
384
+ onBlur: handleInputBlur,
385
+ placeholder,
379
386
  "aria-label": ariaLabel ?? label ?? "Time picker",
380
387
  "aria-describedby": describedBy,
381
388
  "aria-required": required,
382
389
  "aria-invalid": state === "error" ? true : void 0,
383
390
  disabled: Boolean(disabled) || Boolean(isLoading),
384
- onClick: toggleOpen,
385
391
  name,
386
392
  className: cn(
387
- "w-full flex items-center text-left transition-colors duration-150 cursor-pointer",
393
+ "w-full flex items-center text-left transition-colors duration-150",
388
394
  "disabled:cursor-not-allowed disabled:opacity-50",
395
+ "focus:outline-none focus:ring-0",
389
396
  sizeClasses[size],
390
397
  variantClasses[variant],
391
398
  stateVariantClasses[state][variant],
392
399
  "pl-9",
393
- showClear || rightElement ? rightPad[size] : "",
400
+ rightPad[size],
394
401
  prefix !== void 0 && suffix !== void 0 ? "rounded-none" : prefix !== void 0 ? "rounded-l-none" : suffix !== void 0 ? "rounded-r-none" : "",
395
402
  inputClassName
396
- ),
397
- children: displayValue ? /* @__PURE__ */ jsx("span", { className: "text-text", children: formatDisplay(displayValue, use12Hour) }) : /* @__PURE__ */ jsx("span", { className: "text-text-muted", children: placeholder })
403
+ )
398
404
  }
399
405
  ),
400
406
  isLoading ? /* @__PURE__ */ jsx(AddonIcon, { side: "right", size, children: /* @__PURE__ */ jsxs(
@@ -436,12 +442,68 @@ var TimePicker = forwardRef(
436
442
  ariaLabel: "Clear time",
437
443
  children: /* @__PURE__ */ jsx(XMarkIcon, { className: "w-full h-full", strokeWidth: 2.5 })
438
444
  }
439
- ) : rightElement ? /* @__PURE__ */ jsx(AddonIcon, { side: "right", size, children: rightElement }) : null
445
+ ) : rightElement ? /* @__PURE__ */ jsx(AddonIcon, { side: "right", size, children: rightElement }) : /* @__PURE__ */ jsx(
446
+ AddonIcon,
447
+ {
448
+ side: "right",
449
+ size,
450
+ asButton: true,
451
+ onClick: toggleOpen,
452
+ ariaLabel: "Open time picker dropdown",
453
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "w-full h-full text-text-secondary/70 hover:text-text transition-colors" })
454
+ }
455
+ )
440
456
  ] }),
441
457
  suffix !== void 0 && /* @__PURE__ */ jsx(Addon, { side: "right", children: suffix })
442
458
  ]
443
459
  }
444
460
  ),
461
+ /* @__PURE__ */ jsx(
462
+ "div",
463
+ {
464
+ id: popoverId,
465
+ ref: dropdownRef,
466
+ role: "listbox",
467
+ "aria-label": "Time options",
468
+ className: cn(
469
+ "absolute top-full left-0 z-50 mt-1.5 w-full max-h-60 overflow-y-auto",
470
+ "bg-surface/90 backdrop-blur-md border border-border/80 rounded-xl shadow-xl py-1.5",
471
+ "transition-all duration-200 origin-top ease-out",
472
+ isOpen ? "opacity-100 scale-100 pointer-events-auto translate-y-0" : "opacity-0 scale-95 pointer-events-none -translate-y-1"
473
+ ),
474
+ children: filteredSlots.length > 0 ? filteredSlots.map((slot) => {
475
+ const isSelected = slot.value === displayValue;
476
+ return /* @__PURE__ */ jsxs(
477
+ "button",
478
+ {
479
+ type: "button",
480
+ "data-active": isSelected,
481
+ onMouseDown: (e) => {
482
+ e.preventDefault();
483
+ },
484
+ onClick: () => {
485
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
486
+ if (!isControlled) setInternalValue(slot.value);
487
+ onChange?.(slot.value);
488
+ onTimeChange?.(slot.value);
489
+ setTypedValue(null);
490
+ setIsOpen(false);
491
+ },
492
+ className: cn(
493
+ "w-full text-left px-4 py-2.5 text-sm transition-all duration-normal cursor-pointer flex items-center justify-between",
494
+ "hover:bg-primary/5 hover:text-primary hover:pl-5 font-medium",
495
+ isSelected ? "bg-primary/10 text-primary font-bold pl-5" : "text-text-secondary"
496
+ ),
497
+ children: [
498
+ /* @__PURE__ */ jsx("span", { children: slot.label }),
499
+ isSelected && /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-primary animate-pulse" })
500
+ ]
501
+ },
502
+ slot.value
503
+ );
504
+ }) : /* @__PURE__ */ jsx("div", { className: "text-center text-xs text-text-muted py-3", children: "No matching times found" })
505
+ }
506
+ ),
445
507
  helperText !== void 0 && /* @__PURE__ */ jsx(
446
508
  "p",
447
509
  {
@@ -462,65 +524,6 @@ var TimePicker = forwardRef(
462
524
  ),
463
525
  children: stateMessage
464
526
  }
465
- ),
466
- /* @__PURE__ */ jsx(
467
- "div",
468
- {
469
- id: popoverId,
470
- role: "dialog",
471
- "aria-label": "Time picker",
472
- "aria-modal": "true",
473
- className: cn(
474
- "absolute top-full left-0 z-50 mt-1",
475
- "bg-surface border border-border rounded-xl shadow-xl",
476
- "transition-all duration-200 origin-top",
477
- isOpen ? "opacity-100 scale-100 pointer-events-auto" : "opacity-0 scale-95 pointer-events-none"
478
- ),
479
- children: /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
480
- /* @__PURE__ */ jsx("p", { className: "text-xs font-bold text-text-muted uppercase tracking-widest mb-3 text-center", children: "Select Time" }),
481
- /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1", children: [
482
- /* @__PURE__ */ jsx(
483
- ScrollColumn,
484
- {
485
- items: hourItems,
486
- selected: pickerHour,
487
- onSelect: handleHourSelect,
488
- label: "Hour"
489
- }
490
- ),
491
- /* @__PURE__ */ jsx(
492
- "div",
493
- {
494
- className: "flex items-center self-center pb-2 text-text-muted font-bold text-lg select-none",
495
- style: { height: ITEM_HEIGHT },
496
- children: ":"
497
- }
498
- ),
499
- /* @__PURE__ */ jsx(
500
- ScrollColumn,
501
- {
502
- items: minuteItems,
503
- selected: pickerMinute,
504
- onSelect: handleMinuteSelect,
505
- label: "Minute"
506
- }
507
- ),
508
- use12Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
509
- /* @__PURE__ */ jsx("div", { className: "w-px self-stretch bg-border mx-1" }),
510
- /* @__PURE__ */ jsx(
511
- ScrollColumn,
512
- {
513
- items: ["AM", "PM"],
514
- selected: pickerAmpm,
515
- onSelect: handleAmpmSelect,
516
- label: "AM/PM"
517
- }
518
- )
519
- ] })
520
- ] }),
521
- /* @__PURE__ */ jsx("div", { className: "mt-3 pt-3 border-t border-border text-center", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-primary font-mono", children: displayValue ? formatDisplay(displayValue, use12Hour) : "\u2013\u2013:\u2013\u2013" }) })
522
- ] })
523
- }
524
527
  )
525
528
  ]
526
529
  }
@@ -1,6 +1,6 @@
1
1
  import { parseISODate, buildCalendarGrid, getYearRange, formatDate, MONTH_NAMES, DAY_NAMES_SHORT, isSameDay, isDisabledDate, MONTH_NAMES_SHORT, toISODateString } from './chunk-4PRNRENN.mjs';
2
2
  import { cn } from './chunk-OCPFOFJ4.mjs';
3
- import { forwardRef, useId, useState, useMemo, useRef, useEffect } from 'react';
3
+ import { forwardRef, useRef, useId, useState, useMemo, useEffect } from 'react';
4
4
  import { CalendarIcon, XMarkIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
 
@@ -133,9 +133,11 @@ var DatePicker = forwardRef(
133
133
  disabledDates,
134
134
  locale = "en-US",
135
135
  name,
136
+ type,
136
137
  "aria-label": ariaLabel,
137
138
  "aria-describedby": ariaDescribedby
138
139
  }, ref) => {
140
+ const internalRef = useRef(null);
139
141
  const generatedId = useId();
140
142
  const inputId = idProp ?? generatedId;
141
143
  const helperId = `${inputId}-helper`;
@@ -162,8 +164,7 @@ var DatePicker = forwardRef(
162
164
  useEffect(() => {
163
165
  if (!isOpen) return;
164
166
  function handleClick(e) {
165
- const wrapper = ref && typeof ref === "object" && "current" in ref ? ref.current : null;
166
- if (wrapper && !wrapper.contains(e.target)) {
167
+ if (internalRef.current && !internalRef.current.contains(e.target)) {
167
168
  setIsOpen(false);
168
169
  setViewMode("day");
169
170
  }
@@ -236,7 +237,14 @@ var DatePicker = forwardRef(
236
237
  return /* @__PURE__ */ jsxs(
237
238
  "div",
238
239
  {
239
- ref,
240
+ ref: (node) => {
241
+ internalRef.current = node;
242
+ if (typeof ref === "function") {
243
+ ref(node);
244
+ } else if (ref) {
245
+ ref.current = node;
246
+ }
247
+ },
240
248
  className: cn(
241
249
  "flex flex-col gap-1 relative",
242
250
  fullWidth ? "w-full" : "w-fit",
@@ -271,7 +279,7 @@ var DatePicker = forwardRef(
271
279
  "button",
272
280
  {
273
281
  id: inputId,
274
- type: "button",
282
+ type: type ?? "button",
275
283
  role: "combobox",
276
284
  "aria-expanded": isOpen,
277
285
  "aria-haspopup": "dialog",