@bricks-toolkit/toolkit 0.1.19 → 0.1.20

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.
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ // src/components/DatePicker/calendar.utils.ts
4
+ var MONTH_NAMES = [
5
+ "January",
6
+ "February",
7
+ "March",
8
+ "April",
9
+ "May",
10
+ "June",
11
+ "July",
12
+ "August",
13
+ "September",
14
+ "October",
15
+ "November",
16
+ "December"
17
+ ];
18
+ var MONTH_NAMES_SHORT = [
19
+ "Jan",
20
+ "Feb",
21
+ "Mar",
22
+ "Apr",
23
+ "May",
24
+ "Jun",
25
+ "Jul",
26
+ "Aug",
27
+ "Sep",
28
+ "Oct",
29
+ "Nov",
30
+ "Dec"
31
+ ];
32
+ var DAY_NAMES_SHORT = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
33
+ function getDaysInMonth(year, month) {
34
+ return new Date(year, month + 1, 0).getDate();
35
+ }
36
+ function getFirstDayOfMonth(year, month) {
37
+ return new Date(year, month, 1).getDay();
38
+ }
39
+ function buildCalendarGrid(year, month) {
40
+ const firstDay = getFirstDayOfMonth(year, month);
41
+ const daysInMonth = getDaysInMonth(year, month);
42
+ const grid = [];
43
+ let day = 1;
44
+ for (let row = 0; row < 6; row++) {
45
+ const week = [];
46
+ for (let col = 0; col < 7; col++) {
47
+ const idx = row * 7 + col;
48
+ if (idx < firstDay || day > daysInMonth) {
49
+ week.push(null);
50
+ } else {
51
+ week.push(new Date(year, month, day++));
52
+ }
53
+ }
54
+ grid.push(week);
55
+ if (day > daysInMonth) break;
56
+ }
57
+ return grid;
58
+ }
59
+ function isSameDay(a, b) {
60
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
61
+ }
62
+ function isBeforeDay(date, min) {
63
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
64
+ const m = new Date(min.getFullYear(), min.getMonth(), min.getDate());
65
+ return d < m;
66
+ }
67
+ function isAfterDay(date, max) {
68
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
69
+ const m = new Date(max.getFullYear(), max.getMonth(), max.getDate());
70
+ return d > m;
71
+ }
72
+ function isDisabledDate(date, minDate, maxDate, disabledDates) {
73
+ if (minDate && isBeforeDay(date, minDate)) return true;
74
+ if (maxDate && isAfterDay(date, maxDate)) return true;
75
+ if (disabledDates?.some((d) => isSameDay(d, date))) return true;
76
+ return false;
77
+ }
78
+ function formatDate(date, locale = "en-US") {
79
+ return new Intl.DateTimeFormat(locale, {
80
+ month: "short",
81
+ day: "numeric",
82
+ year: "numeric"
83
+ }).format(date);
84
+ }
85
+ function parseISODate(iso) {
86
+ if (!iso) return null;
87
+ const parts = iso.split("-");
88
+ if (parts.length < 3) return null;
89
+ const year = parseInt(parts[0] ?? "0", 10);
90
+ const month = parseInt(parts[1] ?? "0", 10);
91
+ const day = parseInt(parts[2] ?? "0", 10);
92
+ if (isNaN(year) || isNaN(month) || isNaN(day)) return null;
93
+ return new Date(year, month - 1, day);
94
+ }
95
+ function toISODateString(date) {
96
+ const y = date.getFullYear();
97
+ const m = String(date.getMonth() + 1).padStart(2, "0");
98
+ const d = String(date.getDate()).padStart(2, "0");
99
+ return `${String(y)}-${m}-${d}`;
100
+ }
101
+ function getYearRange(centerYear, span = 12) {
102
+ const start = Math.floor(centerYear / span) * span;
103
+ return Array.from({ length: span }, (_, i) => start + i);
104
+ }
105
+
106
+ exports.DAY_NAMES_SHORT = DAY_NAMES_SHORT;
107
+ exports.MONTH_NAMES = MONTH_NAMES;
108
+ exports.MONTH_NAMES_SHORT = MONTH_NAMES_SHORT;
109
+ exports.buildCalendarGrid = buildCalendarGrid;
110
+ exports.formatDate = formatDate;
111
+ exports.getYearRange = getYearRange;
112
+ exports.isDisabledDate = isDisabledDate;
113
+ exports.isSameDay = isSameDay;
114
+ exports.parseISODate = parseISODate;
115
+ exports.toISODateString = toISODateString;
@@ -0,0 +1,529 @@
1
+ import { cn } from './chunk-OCPFOFJ4.mjs';
2
+ import { useId, useState, useRef, useEffect, useCallback } from 'react';
3
+ import { ClockIcon, XMarkIcon, ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
4
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
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
+ };
15
+ }
16
+ function toTimeString(hours, minutes) {
17
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
18
+ }
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}`;
26
+ }
27
+ return toTimeString(hours, minutes);
28
+ }
29
+ var ITEM_HEIGHT = 36;
30
+ var sizeClasses = {
31
+ xs: "h-6 px-2 text-xs",
32
+ sm: "h-8 px-3 text-sm",
33
+ md: "h-10 px-3 text-sm",
34
+ lg: "h-11 px-4 text-base",
35
+ xl: "h-12 px-4 text-lg"
36
+ };
37
+ var labelSizeClasses = {
38
+ xs: "text-xs",
39
+ sm: "text-xs",
40
+ md: "text-sm",
41
+ lg: "text-sm",
42
+ xl: "text-base"
43
+ };
44
+ var variantClasses = {
45
+ default: "rounded-md border border-border bg-surface shadow-sm focus-within:outline-none focus-within:ring-2 focus-within:ring-primary/40 transition-all duration-200",
46
+ filled: "rounded-md border border-transparent bg-surface-secondary focus-within:bg-surface focus-within:ring-2 focus-within:ring-primary/40 transition-all duration-200",
47
+ flushed: "rounded-none border-0 border-b border-border bg-transparent",
48
+ unstyled: "border-0 bg-transparent p-0"
49
+ };
50
+ var stateVariantClasses = {
51
+ default: {
52
+ default: "border-border focus-within:border-primary",
53
+ filled: "focus-within:border-primary",
54
+ flushed: "border-border focus-within:border-primary",
55
+ unstyled: ""
56
+ },
57
+ error: {
58
+ default: "border-error focus-within:border-error focus-within:ring-error/20",
59
+ filled: "bg-error/5 focus-within:border-error focus-within:ring-error/20",
60
+ flushed: "border-error",
61
+ unstyled: ""
62
+ },
63
+ success: {
64
+ default: "border-success focus-within:border-success focus-within:ring-success/20",
65
+ filled: "bg-success/5 focus-within:border-success focus-within:ring-success/20",
66
+ flushed: "border-success",
67
+ unstyled: ""
68
+ },
69
+ warning: {
70
+ default: "border-warning focus-within:border-warning focus-within:ring-warning/20",
71
+ filled: "bg-warning/5 focus-within:border-warning focus-within:ring-warning/20",
72
+ flushed: "border-warning",
73
+ unstyled: ""
74
+ }
75
+ };
76
+ var stateMessageClasses = {
77
+ default: "text-text-muted",
78
+ error: "text-error font-medium",
79
+ success: "text-success font-medium",
80
+ warning: "text-warning font-medium"
81
+ };
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
+ function AddonIcon({ children, side, size, onClick, asButton = false, ariaLabel }) {
175
+ const iconSize = {
176
+ xs: "w-3.5 h-3.5",
177
+ sm: "w-4 h-4",
178
+ md: "w-4 h-4",
179
+ lg: "w-5 h-5",
180
+ xl: "w-5 h-5"
181
+ };
182
+ const base = cn(
183
+ "absolute top-1/2 -translate-y-1/2 text-text-secondary flex items-center justify-center",
184
+ side === "left" ? "left-3" : "right-3",
185
+ iconSize[size]
186
+ );
187
+ if (asButton && onClick) {
188
+ return /* @__PURE__ */ jsx(
189
+ "button",
190
+ {
191
+ type: "button",
192
+ onClick,
193
+ className: cn(base, "cursor-pointer hover:text-text transition-colors"),
194
+ tabIndex: -1,
195
+ "aria-label": ariaLabel,
196
+ children
197
+ }
198
+ );
199
+ }
200
+ return /* @__PURE__ */ jsx(
201
+ "span",
202
+ {
203
+ "aria-hidden": !ariaLabel,
204
+ className: cn(base, "pointer-events-none"),
205
+ "aria-label": ariaLabel,
206
+ children
207
+ }
208
+ );
209
+ }
210
+ function Addon({ children, side }) {
211
+ return /* @__PURE__ */ jsx(
212
+ "span",
213
+ {
214
+ "aria-hidden": "true",
215
+ className: cn(
216
+ "inline-flex items-center border border-border bg-surface-secondary px-3 text-sm text-text-secondary select-none",
217
+ side === "left" ? "rounded-l-md border-r-0" : "rounded-r-md border-l-0"
218
+ ),
219
+ children
220
+ }
221
+ );
222
+ }
223
+ function TimePicker({
224
+ label,
225
+ required = false,
226
+ helperText,
227
+ errorMessage,
228
+ successMessage,
229
+ warningMessage,
230
+ variant = "default",
231
+ size = "md",
232
+ state = "default",
233
+ fullWidth = true,
234
+ leftElement,
235
+ rightElement,
236
+ prefix,
237
+ suffix,
238
+ wrapperClassName,
239
+ inputGroupClassName,
240
+ inputClassName,
241
+ labelClassName,
242
+ helperClassName,
243
+ id: idProp,
244
+ disabled,
245
+ className,
246
+ clearable,
247
+ isLoading,
248
+ value,
249
+ defaultValue,
250
+ onChange,
251
+ onTimeChange,
252
+ placeholder = "Select time",
253
+ minuteStep = 1,
254
+ use12Hour = false,
255
+ name,
256
+ "aria-label": ariaLabel,
257
+ "aria-describedby": ariaDescribedby
258
+ }) {
259
+ const generatedId = useId();
260
+ const inputId = idProp ?? generatedId;
261
+ const helperId = `${inputId}-helper`;
262
+ const stateMessageId = `${inputId}-state`;
263
+ const popoverId = `${inputId}-popover`;
264
+ const isControlled = value !== void 0;
265
+ const [internalValue, setInternalValue] = useState(defaultValue ?? "");
266
+ const displayValue = isControlled ? value : internalValue;
267
+ const { hours: selHours, minutes: selMinutes } = parseTime(displayValue);
268
+ const selAmpm = selHours >= 12 ? "PM" : "AM";
269
+ const selHours12 = selHours % 12 || 12;
270
+ const pickerHour = String(use12Hour ? selHours12 : selHours).padStart(2, "0");
271
+ const pickerMinute = String(selMinutes).padStart(2, "0");
272
+ const pickerAmpm = selAmpm;
273
+ const [isOpen, setIsOpen] = useState(false);
274
+ const wrapperRef = useRef(null);
275
+ useEffect(() => {
276
+ if (!isOpen) return;
277
+ function handleClick(e) {
278
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
279
+ setIsOpen(false);
280
+ }
281
+ }
282
+ document.addEventListener("mousedown", handleClick);
283
+ return () => {
284
+ document.removeEventListener("mousedown", handleClick);
285
+ };
286
+ }, [isOpen]);
287
+ const hourItems = use12Hour ? Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0")) : Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
288
+ const minuteItems = Array.from(
289
+ { length: Math.ceil(60 / minuteStep) },
290
+ (_, i) => String(i * minuteStep).padStart(2, "0")
291
+ );
292
+ const applyTime = useCallback(
293
+ (h, m, ap) => {
294
+ let hours24 = parseInt(h, 10);
295
+ if (use12Hour) {
296
+ if (ap === "AM" && hours24 === 12) hours24 = 0;
297
+ if (ap === "PM" && hours24 !== 12) hours24 += 12;
298
+ }
299
+ const iso = toTimeString(hours24, parseInt(m, 10));
300
+ if (!isControlled) setInternalValue(iso);
301
+ onChange?.(iso);
302
+ onTimeChange?.(iso);
303
+ },
304
+ [isControlled, onChange, onTimeChange, use12Hour]
305
+ );
306
+ const handleHourSelect = (h) => {
307
+ applyTime(h, pickerMinute, pickerAmpm);
308
+ };
309
+ const handleMinuteSelect = (m) => {
310
+ applyTime(pickerHour, m, pickerAmpm);
311
+ };
312
+ const handleAmpmSelect = (ap) => {
313
+ const a = ap;
314
+ applyTime(pickerHour, pickerMinute, a);
315
+ };
316
+ const handleClear = () => {
317
+ if (!isControlled) setInternalValue("");
318
+ onChange?.("");
319
+ onTimeChange?.(null);
320
+ };
321
+ const toggleOpen = () => {
322
+ if (disabled || isLoading) return;
323
+ setIsOpen((prev) => !prev);
324
+ };
325
+ const showClear = clearable && displayValue.length > 0 && !disabled && !isLoading;
326
+ const rightPad = {
327
+ xs: "pr-6",
328
+ sm: "pr-8",
329
+ md: "pr-9",
330
+ lg: "pr-10",
331
+ xl: "pr-11"
332
+ };
333
+ const stateMessage = state === "error" ? errorMessage : state === "success" ? successMessage : state === "warning" ? warningMessage : void 0;
334
+ const describedBy = [helperText ? helperId : null, stateMessage ? stateMessageId : null, ariaDescribedby ?? null].filter(Boolean).join(" ") || void 0;
335
+ return /* @__PURE__ */ jsxs(
336
+ "div",
337
+ {
338
+ ref: wrapperRef,
339
+ className: cn(
340
+ "flex flex-col gap-1 relative",
341
+ fullWidth ? "w-full" : "w-fit",
342
+ wrapperClassName,
343
+ className
344
+ ),
345
+ children: [
346
+ label !== void 0 && /* @__PURE__ */ jsxs(
347
+ "label",
348
+ {
349
+ htmlFor: inputId,
350
+ className: cn(
351
+ "font-black leading-none text-text uppercase tracking-widest",
352
+ labelSizeClasses[size],
353
+ labelClassName
354
+ ),
355
+ children: [
356
+ label,
357
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-1 text-error", children: "*" })
358
+ ]
359
+ }
360
+ ),
361
+ /* @__PURE__ */ jsxs(
362
+ "div",
363
+ {
364
+ className: cn("flex items-stretch", fullWidth ? "w-full" : "w-fit", inputGroupClassName),
365
+ children: [
366
+ prefix !== void 0 && /* @__PURE__ */ jsx(Addon, { side: "left", children: prefix }),
367
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 items-center", children: [
368
+ /* @__PURE__ */ jsx(AddonIcon, { side: "left", size, children: leftElement ?? /* @__PURE__ */ jsx(ClockIcon, { className: "w-full h-full" }) }),
369
+ /* @__PURE__ */ jsx(
370
+ "button",
371
+ {
372
+ id: inputId,
373
+ type: "button",
374
+ role: "combobox",
375
+ "aria-expanded": isOpen,
376
+ "aria-haspopup": "dialog",
377
+ "aria-controls": popoverId,
378
+ "aria-label": ariaLabel ?? label ?? "Time picker",
379
+ "aria-describedby": describedBy,
380
+ "aria-required": required,
381
+ "aria-invalid": state === "error" ? true : void 0,
382
+ disabled: Boolean(disabled) || Boolean(isLoading),
383
+ onClick: toggleOpen,
384
+ name,
385
+ className: cn(
386
+ "w-full flex items-center text-left transition-colors duration-150 cursor-pointer",
387
+ "disabled:cursor-not-allowed disabled:opacity-50",
388
+ sizeClasses[size],
389
+ variantClasses[variant],
390
+ stateVariantClasses[state][variant],
391
+ "pl-9",
392
+ showClear || rightElement ? rightPad[size] : "",
393
+ prefix !== void 0 && suffix !== void 0 ? "rounded-none" : prefix !== void 0 ? "rounded-l-none" : suffix !== void 0 ? "rounded-r-none" : "",
394
+ inputClassName
395
+ ),
396
+ children: displayValue ? /* @__PURE__ */ jsx("span", { className: "text-text", children: formatDisplay(displayValue, use12Hour) }) : /* @__PURE__ */ jsx("span", { className: "text-text-muted", children: placeholder })
397
+ }
398
+ ),
399
+ isLoading ? /* @__PURE__ */ jsx(AddonIcon, { side: "right", size, children: /* @__PURE__ */ jsxs(
400
+ "svg",
401
+ {
402
+ className: "animate-spin h-full w-full text-text-muted",
403
+ xmlns: "http://www.w3.org/2000/svg",
404
+ fill: "none",
405
+ viewBox: "0 0 24 24",
406
+ children: [
407
+ /* @__PURE__ */ jsx(
408
+ "circle",
409
+ {
410
+ className: "opacity-25",
411
+ cx: "12",
412
+ cy: "12",
413
+ r: "10",
414
+ stroke: "currentColor",
415
+ strokeWidth: "4"
416
+ }
417
+ ),
418
+ /* @__PURE__ */ jsx(
419
+ "path",
420
+ {
421
+ className: "opacity-75",
422
+ fill: "currentColor",
423
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
424
+ }
425
+ )
426
+ ]
427
+ }
428
+ ) }) : showClear ? /* @__PURE__ */ jsx(
429
+ AddonIcon,
430
+ {
431
+ side: "right",
432
+ size,
433
+ asButton: true,
434
+ onClick: handleClear,
435
+ ariaLabel: "Clear time",
436
+ children: /* @__PURE__ */ jsx(XMarkIcon, { className: "w-full h-full", strokeWidth: 2.5 })
437
+ }
438
+ ) : rightElement ? /* @__PURE__ */ jsx(AddonIcon, { side: "right", size, children: rightElement }) : null
439
+ ] }),
440
+ suffix !== void 0 && /* @__PURE__ */ jsx(Addon, { side: "right", children: suffix })
441
+ ]
442
+ }
443
+ ),
444
+ helperText !== void 0 && /* @__PURE__ */ jsx(
445
+ "p",
446
+ {
447
+ id: helperId,
448
+ className: cn("text-xs leading-tight text-text-muted font-medium", helperClassName),
449
+ children: helperText
450
+ }
451
+ ),
452
+ stateMessage !== void 0 && /* @__PURE__ */ jsx(
453
+ "p",
454
+ {
455
+ id: stateMessageId,
456
+ role: state === "error" ? "alert" : void 0,
457
+ className: cn(
458
+ "text-xs leading-tight mt-0.5 font-medium",
459
+ stateMessageClasses[state],
460
+ helperClassName
461
+ ),
462
+ children: stateMessage
463
+ }
464
+ ),
465
+ /* @__PURE__ */ jsx(
466
+ "div",
467
+ {
468
+ id: popoverId,
469
+ role: "dialog",
470
+ "aria-label": "Time picker",
471
+ "aria-modal": "true",
472
+ className: cn(
473
+ "absolute top-full left-0 z-50 mt-1",
474
+ "bg-surface border border-border rounded-xl shadow-xl",
475
+ "transition-all duration-200 origin-top",
476
+ isOpen ? "opacity-100 scale-100 pointer-events-auto" : "opacity-0 scale-95 pointer-events-none"
477
+ ),
478
+ children: /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
479
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-bold text-text-muted uppercase tracking-widest mb-3 text-center", children: "Select Time" }),
480
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1", children: [
481
+ /* @__PURE__ */ jsx(
482
+ ScrollColumn,
483
+ {
484
+ items: hourItems,
485
+ selected: pickerHour,
486
+ onSelect: handleHourSelect,
487
+ label: "Hour"
488
+ }
489
+ ),
490
+ /* @__PURE__ */ jsx(
491
+ "div",
492
+ {
493
+ className: "flex items-center self-center pb-2 text-text-muted font-bold text-lg select-none",
494
+ style: { height: ITEM_HEIGHT },
495
+ children: ":"
496
+ }
497
+ ),
498
+ /* @__PURE__ */ jsx(
499
+ ScrollColumn,
500
+ {
501
+ items: minuteItems,
502
+ selected: pickerMinute,
503
+ onSelect: handleMinuteSelect,
504
+ label: "Minute"
505
+ }
506
+ ),
507
+ use12Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
508
+ /* @__PURE__ */ jsx("div", { className: "w-px self-stretch bg-border mx-1" }),
509
+ /* @__PURE__ */ jsx(
510
+ ScrollColumn,
511
+ {
512
+ items: ["AM", "PM"],
513
+ selected: pickerAmpm,
514
+ onSelect: handleAmpmSelect,
515
+ label: "AM/PM"
516
+ }
517
+ )
518
+ ] })
519
+ ] }),
520
+ /* @__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" }) })
521
+ ] })
522
+ }
523
+ )
524
+ ]
525
+ }
526
+ );
527
+ }
528
+
529
+ export { TimePicker };