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