@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.
- package/dist/{chunk-WVWZ73XT.cjs → chunk-6GU53FA5.cjs} +12 -4
- package/dist/{chunk-EKN2P6XK.mjs → chunk-BLELGRQT.mjs} +13 -5
- package/dist/{chunk-PMOWOLIQ.cjs → chunk-C5CAUNLU.cjs} +232 -229
- package/dist/{chunk-DL6RO422.cjs → chunk-IINDMJBE.cjs} +12 -4
- package/dist/{chunk-EGGXXVN2.mjs → chunk-XAQT2CYE.mjs} +235 -232
- package/dist/{chunk-GQCJTIVO.mjs → chunk-XDN5IK2K.mjs} +13 -5
- package/dist/date-picker/index.cjs +2 -2
- package/dist/date-picker/index.d.mts +1 -0
- package/dist/date-picker/index.d.ts +1 -0
- package/dist/date-picker/index.mjs +1 -1
- package/dist/date-time-picker/index.cjs +2 -2
- package/dist/date-time-picker/index.d.mts +1 -0
- package/dist/date-time-picker/index.d.ts +1 -0
- package/dist/date-time-picker/index.mjs +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.mjs +3 -3
- package/dist/time-picker/index.cjs +2 -2
- package/dist/time-picker/index.d.mts +1 -0
- package/dist/time-picker/index.d.ts +1 -0
- package/dist/time-picker/index.mjs +1 -1
- package/package.json +36 -36
|
@@ -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
|
-
|
|
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
|
|
3
|
-
import { ClockIcon, XMarkIcon,
|
|
4
|
-
import { jsxs, jsx
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
17
|
-
|
|
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(
|
|
20
|
-
if (!
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
177
|
-
sm: "w-
|
|
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
|
-
|
|
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
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
"
|
|
375
|
+
"input",
|
|
372
376
|
{
|
|
377
|
+
ref: inputRef,
|
|
373
378
|
id: inputId,
|
|
374
|
-
type: "
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
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
|
-
|
|
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 }) :
|
|
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,
|
|
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
|
-
|
|
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",
|