@mieweb/ui 0.6.1-dev.168 → 0.6.1-dev.169
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-67CEEDAP.cjs → chunk-6W3HLFAJ.cjs} +6 -6
- package/dist/{chunk-67CEEDAP.cjs.map → chunk-6W3HLFAJ.cjs.map} +1 -1
- package/dist/{chunk-HUXOASEB.js → chunk-FETRH4V5.js} +3 -3
- package/dist/{chunk-HUXOASEB.js.map → chunk-FETRH4V5.js.map} +1 -1
- package/dist/{chunk-NINI4WVS.cjs → chunk-JC6Y3LGF.cjs} +297 -84
- package/dist/chunk-JC6Y3LGF.cjs.map +1 -0
- package/dist/chunk-LIH3IZYK.js +689 -0
- package/dist/chunk-LIH3IZYK.js.map +1 -0
- package/dist/components/DateInput/index.cjs +2 -2
- package/dist/components/DateInput/index.d.cts +8 -2
- package/dist/components/DateInput/index.d.ts +8 -2
- package/dist/components/DateInput/index.js +1 -1
- package/dist/esheet.cjs +5 -5
- package/dist/esheet.js +2 -2
- package/dist/index.cjs +37 -37
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-HYEJLKXI.js +0 -476
- package/dist/chunk-HYEJLKXI.js.map +0 -1
- package/dist/chunk-NINI4WVS.cjs.map +0 -1
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
import { Input } from './chunk-PVUDXJAI.js';
|
|
2
|
+
import { formatDateValue, isValidDate, isDateInFuture, isDateInPast, calculateAge } from './chunk-RC2YMOMS.js';
|
|
3
|
+
import { cn } from './chunk-F3SOEIN2.js';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { createPortal } from 'react-dom';
|
|
6
|
+
import { Calendar } from 'lucide-react';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var widthClasses = {
|
|
10
|
+
full: "w-full",
|
|
11
|
+
fit: "w-fit",
|
|
12
|
+
fixed: "w-44"
|
|
13
|
+
// ~176px - enough for MM/DD/YYYY + calendar icon
|
|
14
|
+
};
|
|
15
|
+
var sizeClasses = {
|
|
16
|
+
sm: "h-8 text-sm",
|
|
17
|
+
md: "h-10 text-base",
|
|
18
|
+
lg: "h-12 text-lg"
|
|
19
|
+
};
|
|
20
|
+
var MONTH_NAMES = [
|
|
21
|
+
"January",
|
|
22
|
+
"February",
|
|
23
|
+
"March",
|
|
24
|
+
"April",
|
|
25
|
+
"May",
|
|
26
|
+
"June",
|
|
27
|
+
"July",
|
|
28
|
+
"August",
|
|
29
|
+
"September",
|
|
30
|
+
"October",
|
|
31
|
+
"November",
|
|
32
|
+
"December"
|
|
33
|
+
];
|
|
34
|
+
function isValidPickerDate(year, month, day) {
|
|
35
|
+
return Number.isFinite(year) && month >= 1 && month <= 12 && day >= 1 && day <= new Date(year, month, 0).getDate();
|
|
36
|
+
}
|
|
37
|
+
function parsePickerDate(value, inputType) {
|
|
38
|
+
if (inputType === "datetime-local") {
|
|
39
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}$/.exec(value);
|
|
40
|
+
if (!match) return void 0;
|
|
41
|
+
const year2 = Number(match[1]);
|
|
42
|
+
const month2 = Number(match[2]);
|
|
43
|
+
const day2 = Number(match[3]);
|
|
44
|
+
const [hour, minute] = value.slice(-5).split(":").map(Number);
|
|
45
|
+
if (!isValidPickerDate(year2, month2, day2) || hour > 23 || minute > 59) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
year: year2,
|
|
50
|
+
month: month2 - 1,
|
|
51
|
+
day: day2
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (inputType === "month") {
|
|
55
|
+
const match = /^(\d{4})-(\d{2})$/.exec(value);
|
|
56
|
+
if (!match) return void 0;
|
|
57
|
+
const year2 = Number(match[1]);
|
|
58
|
+
const month2 = Number(match[2]);
|
|
59
|
+
if (!Number.isFinite(year2) || month2 < 1 || month2 > 12) return void 0;
|
|
60
|
+
return { year: year2, month: month2 - 1, day: null };
|
|
61
|
+
}
|
|
62
|
+
if (!isValidDate(value)) return void 0;
|
|
63
|
+
const [month, day, year] = value.split("/").map(Number);
|
|
64
|
+
return { month: month - 1, year, day };
|
|
65
|
+
}
|
|
66
|
+
function formatPickerValue(value, inputType, timeFormat) {
|
|
67
|
+
if (inputType === "datetime-local") {
|
|
68
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})$/.exec(value);
|
|
69
|
+
if (!match) return "";
|
|
70
|
+
if (timeFormat === "12-hour") {
|
|
71
|
+
const [hour, minute] = match[4].split(":").map(Number);
|
|
72
|
+
const displayHour = hour % 12 || 12;
|
|
73
|
+
const meridiem = hour >= 12 ? "PM" : "AM";
|
|
74
|
+
return `${match[2]}/${match[3]}/${match[1]} ${displayHour}:${String(minute).padStart(2, "0")} ${meridiem}`;
|
|
75
|
+
}
|
|
76
|
+
return `${match[2]}/${match[3]}/${match[1]} ${match[4]}`;
|
|
77
|
+
}
|
|
78
|
+
if (inputType === "month") {
|
|
79
|
+
const date = parsePickerDate(value, inputType);
|
|
80
|
+
return date ? `${MONTH_NAMES[date.month]} ${date.year}` : "";
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
function getValidationError(value, mode, minAge, maxAge) {
|
|
85
|
+
if (!value || value.replace(/\D/g, "").length === 0) {
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
if (!isValidDate(value)) {
|
|
89
|
+
return "Please enter a valid date (MM/DD/YYYY)";
|
|
90
|
+
}
|
|
91
|
+
switch (mode) {
|
|
92
|
+
case "dob": {
|
|
93
|
+
if (!isDateInPast(value)) {
|
|
94
|
+
return "Date of birth must be in the past";
|
|
95
|
+
}
|
|
96
|
+
const age = calculateAge(value);
|
|
97
|
+
if (age !== null) {
|
|
98
|
+
if (minAge !== void 0 && age < minAge) {
|
|
99
|
+
return `Must be at least ${minAge} years old`;
|
|
100
|
+
}
|
|
101
|
+
if (maxAge !== void 0 && age > maxAge) {
|
|
102
|
+
return `Must be no more than ${maxAge} years old`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case "expiration":
|
|
108
|
+
if (!isDateInFuture(value)) {
|
|
109
|
+
return "Expiration date must be in the future";
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case "past":
|
|
113
|
+
if (!isDateInPast(value)) {
|
|
114
|
+
return "Date must be in the past";
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
case "future":
|
|
118
|
+
if (!isDateInFuture(value)) {
|
|
119
|
+
return "Date must be in the future";
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
var DateInput = React.forwardRef(
|
|
126
|
+
({
|
|
127
|
+
value = "",
|
|
128
|
+
onChange,
|
|
129
|
+
inputType = "date",
|
|
130
|
+
timeFormat = "24-hour",
|
|
131
|
+
mode = "default",
|
|
132
|
+
minAge,
|
|
133
|
+
maxAge,
|
|
134
|
+
validateOnBlur,
|
|
135
|
+
showCalendar = false,
|
|
136
|
+
width = "full",
|
|
137
|
+
className,
|
|
138
|
+
onBlur,
|
|
139
|
+
onClick,
|
|
140
|
+
hasError,
|
|
141
|
+
error,
|
|
142
|
+
...props
|
|
143
|
+
}, ref) => {
|
|
144
|
+
const isFormattedDate = inputType === "date";
|
|
145
|
+
const [displayValue, setDisplayValue] = React.useState(
|
|
146
|
+
() => isFormattedDate ? formatDateValue(value) : value
|
|
147
|
+
);
|
|
148
|
+
const [localError, setLocalError] = React.useState();
|
|
149
|
+
React.useEffect(() => {
|
|
150
|
+
setDisplayValue(isFormattedDate ? formatDateValue(value) : value);
|
|
151
|
+
}, [isFormattedDate, value]);
|
|
152
|
+
const handleChange = (e) => {
|
|
153
|
+
const formatted = formatDateValue(e.target.value);
|
|
154
|
+
setDisplayValue(formatted);
|
|
155
|
+
onChange?.(formatted);
|
|
156
|
+
if (localError) {
|
|
157
|
+
setLocalError(void 0);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const handleBlur = (e) => {
|
|
161
|
+
onBlur?.(e);
|
|
162
|
+
if (validateOnBlur) {
|
|
163
|
+
const validationError = getValidationError(
|
|
164
|
+
displayValue,
|
|
165
|
+
mode,
|
|
166
|
+
minAge,
|
|
167
|
+
maxAge
|
|
168
|
+
);
|
|
169
|
+
setLocalError(validationError);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const placeholder = mode === "expiration" ? "MM/DD/YYYY" : "MM/DD/YYYY";
|
|
173
|
+
const autoComplete = mode === "dob" ? "bday" : mode === "expiration" ? "cc-exp" : void 0;
|
|
174
|
+
const generatedId = React.useId();
|
|
175
|
+
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
|
176
|
+
const calendarRef = React.useRef(null);
|
|
177
|
+
const buttonRef = React.useRef(null);
|
|
178
|
+
const [calendarStyle, setCalendarStyle] = React.useState();
|
|
179
|
+
const updateCalendarPosition = React.useCallback(() => {
|
|
180
|
+
if (!buttonRef.current) return;
|
|
181
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
182
|
+
setCalendarStyle({
|
|
183
|
+
position: "fixed",
|
|
184
|
+
top: rect.bottom + 4,
|
|
185
|
+
left: Math.max(8, rect.right - 288),
|
|
186
|
+
zIndex: 9999
|
|
187
|
+
});
|
|
188
|
+
}, []);
|
|
189
|
+
React.useEffect(() => {
|
|
190
|
+
if (!isCalendarOpen) return;
|
|
191
|
+
updateCalendarPosition();
|
|
192
|
+
window.addEventListener("scroll", updateCalendarPosition, true);
|
|
193
|
+
window.addEventListener("resize", updateCalendarPosition);
|
|
194
|
+
return () => {
|
|
195
|
+
window.removeEventListener("scroll", updateCalendarPosition, true);
|
|
196
|
+
window.removeEventListener("resize", updateCalendarPosition);
|
|
197
|
+
};
|
|
198
|
+
}, [isCalendarOpen, updateCalendarPosition]);
|
|
199
|
+
const parsedDate = React.useMemo(() => {
|
|
200
|
+
const fallback = /* @__PURE__ */ new Date();
|
|
201
|
+
return parsePickerDate(displayValue, inputType) ?? {
|
|
202
|
+
month: fallback.getMonth(),
|
|
203
|
+
year: fallback.getFullYear(),
|
|
204
|
+
day: null
|
|
205
|
+
};
|
|
206
|
+
}, [displayValue, inputType]);
|
|
207
|
+
const [calendarMonth, setCalendarMonth] = React.useState(parsedDate.month);
|
|
208
|
+
const [calendarYear, setCalendarYear] = React.useState(parsedDate.year);
|
|
209
|
+
const [selectedTime, setSelectedTime] = React.useState(
|
|
210
|
+
() => inputType === "datetime-local" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(value) ? value.slice(-5) : "00:00"
|
|
211
|
+
);
|
|
212
|
+
React.useEffect(() => {
|
|
213
|
+
const date = parsePickerDate(displayValue, inputType);
|
|
214
|
+
if (!date) return;
|
|
215
|
+
setCalendarMonth(date.month);
|
|
216
|
+
setCalendarYear(date.year);
|
|
217
|
+
if (inputType === "datetime-local") {
|
|
218
|
+
setSelectedTime(displayValue.slice(-5));
|
|
219
|
+
}
|
|
220
|
+
}, [displayValue, inputType]);
|
|
221
|
+
React.useEffect(() => {
|
|
222
|
+
const handleClickOutside = (event) => {
|
|
223
|
+
if (calendarRef.current && !calendarRef.current.contains(event.target) && buttonRef.current && !buttonRef.current.contains(event.target)) {
|
|
224
|
+
setIsCalendarOpen(false);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
if (isCalendarOpen) {
|
|
228
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
229
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
230
|
+
}
|
|
231
|
+
}, [isCalendarOpen]);
|
|
232
|
+
React.useEffect(() => {
|
|
233
|
+
const handleEscape = (event) => {
|
|
234
|
+
if (event.key === "Escape") {
|
|
235
|
+
setIsCalendarOpen(false);
|
|
236
|
+
buttonRef.current?.focus();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
if (isCalendarOpen) {
|
|
240
|
+
document.addEventListener("keydown", handleEscape);
|
|
241
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
242
|
+
}
|
|
243
|
+
}, [isCalendarOpen]);
|
|
244
|
+
const handleDateSelect = (day) => {
|
|
245
|
+
const month = String(calendarMonth + 1).padStart(2, "0");
|
|
246
|
+
const dayStr = String(day).padStart(2, "0");
|
|
247
|
+
const year = String(calendarYear);
|
|
248
|
+
const formatted = inputType === "datetime-local" ? `${year}-${month}-${dayStr}T${selectedTime}` : `${month}/${dayStr}/${year}`;
|
|
249
|
+
setDisplayValue(formatted);
|
|
250
|
+
onChange?.(formatted);
|
|
251
|
+
if (inputType === "date") {
|
|
252
|
+
setIsCalendarOpen(false);
|
|
253
|
+
}
|
|
254
|
+
if (inputType === "date" && validateOnBlur) {
|
|
255
|
+
const validationError = getValidationError(
|
|
256
|
+
formatted,
|
|
257
|
+
mode,
|
|
258
|
+
minAge,
|
|
259
|
+
maxAge
|
|
260
|
+
);
|
|
261
|
+
setLocalError(validationError);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const handleMonthSelect = (month) => {
|
|
265
|
+
const formatted = `${calendarYear}-${String(month + 1).padStart(2, "0")}`;
|
|
266
|
+
setDisplayValue(formatted);
|
|
267
|
+
onChange?.(formatted);
|
|
268
|
+
setIsCalendarOpen(false);
|
|
269
|
+
};
|
|
270
|
+
const handleTimeChange = (part, nextValue) => {
|
|
271
|
+
const [hour, minute] = selectedTime.split(":");
|
|
272
|
+
const nextTime = part === "hour" ? `${nextValue}:${minute}` : `${hour}:${nextValue}`;
|
|
273
|
+
setSelectedTime(nextTime);
|
|
274
|
+
if (parsedDate.day === null) return;
|
|
275
|
+
const month = String(parsedDate.month + 1).padStart(2, "0");
|
|
276
|
+
const day = String(parsedDate.day).padStart(2, "0");
|
|
277
|
+
const formatted = `${parsedDate.year}-${month}-${day}T${nextTime}`;
|
|
278
|
+
setDisplayValue(formatted);
|
|
279
|
+
onChange?.(formatted);
|
|
280
|
+
};
|
|
281
|
+
const handleMeridiemChange = (meridiem) => {
|
|
282
|
+
const [hour] = selectedTime.split(":").map(Number);
|
|
283
|
+
const nextHour = hour % 12 + (meridiem === "PM" ? 12 : 0);
|
|
284
|
+
handleTimeChange("hour", String(nextHour).padStart(2, "0"));
|
|
285
|
+
};
|
|
286
|
+
const getDaysInMonth = (month, year) => {
|
|
287
|
+
return new Date(year, month + 1, 0).getDate();
|
|
288
|
+
};
|
|
289
|
+
const getFirstDayOfMonth = (month, year) => {
|
|
290
|
+
return new Date(year, month, 1).getDay();
|
|
291
|
+
};
|
|
292
|
+
const renderCalendar = () => {
|
|
293
|
+
const daysInMonth = getDaysInMonth(calendarMonth, calendarYear);
|
|
294
|
+
const firstDay = getFirstDayOfMonth(calendarMonth, calendarYear);
|
|
295
|
+
const days = [];
|
|
296
|
+
for (let i = 0; i < firstDay; i++) {
|
|
297
|
+
days.push(null);
|
|
298
|
+
}
|
|
299
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
300
|
+
days.push(i);
|
|
301
|
+
}
|
|
302
|
+
const isSelectedDay = (day) => {
|
|
303
|
+
return parsedDate.day === day && parsedDate.month === calendarMonth && parsedDate.year === calendarYear;
|
|
304
|
+
};
|
|
305
|
+
const isToday = (day) => {
|
|
306
|
+
const today = /* @__PURE__ */ new Date();
|
|
307
|
+
return day === today.getDate() && calendarMonth === today.getMonth() && calendarYear === today.getFullYear();
|
|
308
|
+
};
|
|
309
|
+
return /* @__PURE__ */ jsxs(
|
|
310
|
+
"div",
|
|
311
|
+
{
|
|
312
|
+
ref: calendarRef,
|
|
313
|
+
className: cn(
|
|
314
|
+
"bg-background border-border rounded-lg border shadow-lg",
|
|
315
|
+
"w-72 p-3"
|
|
316
|
+
),
|
|
317
|
+
style: calendarStyle,
|
|
318
|
+
role: "dialog",
|
|
319
|
+
"aria-label": inputType === "month" ? "Choose month" : inputType === "datetime-local" ? "Choose date and time" : "Choose date",
|
|
320
|
+
children: [
|
|
321
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
322
|
+
/* @__PURE__ */ jsx(
|
|
323
|
+
"button",
|
|
324
|
+
{
|
|
325
|
+
type: "button",
|
|
326
|
+
onClick: () => {
|
|
327
|
+
if (inputType === "month") {
|
|
328
|
+
setCalendarYear(calendarYear - 1);
|
|
329
|
+
} else if (calendarMonth === 0) {
|
|
330
|
+
setCalendarMonth(11);
|
|
331
|
+
setCalendarYear(calendarYear - 1);
|
|
332
|
+
} else {
|
|
333
|
+
setCalendarMonth(calendarMonth - 1);
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
className: "hover:bg-muted rounded-md p-1 transition-colors",
|
|
337
|
+
"aria-label": inputType === "month" ? "Previous year" : "Previous month",
|
|
338
|
+
children: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
342
|
+
inputType !== "month" && /* @__PURE__ */ jsx(
|
|
343
|
+
"select",
|
|
344
|
+
{
|
|
345
|
+
value: calendarMonth,
|
|
346
|
+
onChange: (e) => setCalendarMonth(Number(e.target.value)),
|
|
347
|
+
className: "bg-background border-border rounded border px-2 py-1 text-sm",
|
|
348
|
+
"aria-label": "Select month",
|
|
349
|
+
children: MONTH_NAMES.map((name, i) => /* @__PURE__ */ jsx("option", { value: i, children: name }, name))
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
/* @__PURE__ */ jsx(
|
|
353
|
+
"select",
|
|
354
|
+
{
|
|
355
|
+
value: calendarYear,
|
|
356
|
+
onChange: (e) => setCalendarYear(Number(e.target.value)),
|
|
357
|
+
className: "bg-background border-border rounded border px-2 py-1 text-sm",
|
|
358
|
+
"aria-label": "Select year",
|
|
359
|
+
children: Array.from(
|
|
360
|
+
{ length: 150 },
|
|
361
|
+
(_, i) => (/* @__PURE__ */ new Date()).getFullYear() - 100 + i
|
|
362
|
+
).map((year) => /* @__PURE__ */ jsx("option", { value: year, children: year }, year))
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
] }),
|
|
366
|
+
/* @__PURE__ */ jsx(
|
|
367
|
+
"button",
|
|
368
|
+
{
|
|
369
|
+
type: "button",
|
|
370
|
+
onClick: () => {
|
|
371
|
+
if (inputType === "month") {
|
|
372
|
+
setCalendarYear(calendarYear + 1);
|
|
373
|
+
} else if (calendarMonth === 11) {
|
|
374
|
+
setCalendarMonth(0);
|
|
375
|
+
setCalendarYear(calendarYear + 1);
|
|
376
|
+
} else {
|
|
377
|
+
setCalendarMonth(calendarMonth + 1);
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
className: "hover:bg-muted rounded-md p-1 transition-colors",
|
|
381
|
+
"aria-label": inputType === "month" ? "Next year" : "Next month",
|
|
382
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
] }),
|
|
386
|
+
inputType === "month" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1", children: MONTH_NAMES.map((name, month) => /* @__PURE__ */ jsx(
|
|
387
|
+
"button",
|
|
388
|
+
{
|
|
389
|
+
type: "button",
|
|
390
|
+
onClick: () => handleMonthSelect(month),
|
|
391
|
+
className: cn(
|
|
392
|
+
"rounded-md px-2 py-2 text-sm transition-colors",
|
|
393
|
+
"focus:ring-ring focus:ring-2 focus:outline-none",
|
|
394
|
+
"hover:bg-muted",
|
|
395
|
+
parsedDate.month === month && parsedDate.year === calendarYear && "bg-primary-800 hover:bg-primary-900 text-white"
|
|
396
|
+
),
|
|
397
|
+
children: name.slice(0, 3)
|
|
398
|
+
},
|
|
399
|
+
name
|
|
400
|
+
)) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
401
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 grid grid-cols-7 gap-1", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ jsx(
|
|
402
|
+
"div",
|
|
403
|
+
{
|
|
404
|
+
className: "text-muted-foreground py-1 text-center text-xs font-medium",
|
|
405
|
+
children: day
|
|
406
|
+
},
|
|
407
|
+
day
|
|
408
|
+
)) }),
|
|
409
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((day, index) => /* @__PURE__ */ jsx(
|
|
410
|
+
"button",
|
|
411
|
+
{
|
|
412
|
+
type: "button",
|
|
413
|
+
disabled: day === null,
|
|
414
|
+
onClick: () => day && handleDateSelect(day),
|
|
415
|
+
className: cn(
|
|
416
|
+
"h-8 w-8 rounded-md text-sm transition-colors",
|
|
417
|
+
"focus:ring-ring focus:ring-2 focus:outline-none",
|
|
418
|
+
day === null && "invisible",
|
|
419
|
+
day !== null && "hover:bg-muted",
|
|
420
|
+
isSelectedDay(day) && "bg-primary-800 hover:bg-primary-900 text-white",
|
|
421
|
+
isToday(day) && !isSelectedDay(day) && "border-primary-800 text-primary-800 border"
|
|
422
|
+
),
|
|
423
|
+
children: day
|
|
424
|
+
},
|
|
425
|
+
index
|
|
426
|
+
)) })
|
|
427
|
+
] }),
|
|
428
|
+
inputType === "datetime-local" && /* @__PURE__ */ jsxs("div", { className: "border-border mt-3 flex items-center gap-2 border-t pt-3", children: [
|
|
429
|
+
/* @__PURE__ */ jsx(
|
|
430
|
+
"select",
|
|
431
|
+
{
|
|
432
|
+
value: timeFormat === "12-hour" ? String(
|
|
433
|
+
Number(selectedTime.slice(0, 2)) % 12 || 12
|
|
434
|
+
).padStart(2, "0") : selectedTime.slice(0, 2),
|
|
435
|
+
onChange: (event) => {
|
|
436
|
+
const hour = Number(event.target.value);
|
|
437
|
+
const isPm = Number(selectedTime.slice(0, 2)) >= 12;
|
|
438
|
+
handleTimeChange(
|
|
439
|
+
"hour",
|
|
440
|
+
String(
|
|
441
|
+
timeFormat === "12-hour" ? hour % 12 + (isPm ? 12 : 0) : hour
|
|
442
|
+
).padStart(2, "0")
|
|
443
|
+
);
|
|
444
|
+
},
|
|
445
|
+
className: "bg-background border-border rounded border px-2 py-1 text-sm",
|
|
446
|
+
"aria-label": "Select hour",
|
|
447
|
+
children: Array.from(
|
|
448
|
+
{ length: timeFormat === "12-hour" ? 12 : 24 },
|
|
449
|
+
(_, index) => timeFormat === "12-hour" ? index + 1 : index
|
|
450
|
+
).map((hour) => /* @__PURE__ */ jsx("option", { value: String(hour).padStart(2, "0"), children: String(hour).padStart(2, "0") }, hour))
|
|
451
|
+
}
|
|
452
|
+
),
|
|
453
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: ":" }),
|
|
454
|
+
/* @__PURE__ */ jsx(
|
|
455
|
+
"select",
|
|
456
|
+
{
|
|
457
|
+
value: selectedTime.slice(3),
|
|
458
|
+
onChange: (event) => handleTimeChange("minute", event.target.value),
|
|
459
|
+
className: "bg-background border-border rounded border px-2 py-1 text-sm",
|
|
460
|
+
"aria-label": "Select minute",
|
|
461
|
+
children: Array.from({ length: 60 }, (_, minute) => /* @__PURE__ */ jsx("option", { value: String(minute).padStart(2, "0"), children: String(minute).padStart(2, "0") }, minute))
|
|
462
|
+
}
|
|
463
|
+
),
|
|
464
|
+
timeFormat === "12-hour" && /* @__PURE__ */ jsxs(
|
|
465
|
+
"select",
|
|
466
|
+
{
|
|
467
|
+
value: Number(selectedTime.slice(0, 2)) >= 12 ? "PM" : "AM",
|
|
468
|
+
onChange: (event) => handleMeridiemChange(event.target.value),
|
|
469
|
+
className: "bg-background border-border rounded border px-2 py-1 text-sm",
|
|
470
|
+
"aria-label": "Select AM or PM",
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ jsx("option", { value: "AM", children: "AM" }),
|
|
473
|
+
/* @__PURE__ */ jsx("option", { value: "PM", children: "PM" })
|
|
474
|
+
]
|
|
475
|
+
}
|
|
476
|
+
)
|
|
477
|
+
] }),
|
|
478
|
+
/* @__PURE__ */ jsxs("div", { className: "border-border mt-3 flex gap-2 border-t pt-3", children: [
|
|
479
|
+
/* @__PURE__ */ jsx(
|
|
480
|
+
"button",
|
|
481
|
+
{
|
|
482
|
+
type: "button",
|
|
483
|
+
onClick: () => {
|
|
484
|
+
const today = /* @__PURE__ */ new Date();
|
|
485
|
+
setCalendarMonth(today.getMonth());
|
|
486
|
+
setCalendarYear(today.getFullYear());
|
|
487
|
+
if (inputType === "month") {
|
|
488
|
+
handleMonthSelect(today.getMonth());
|
|
489
|
+
} else {
|
|
490
|
+
handleDateSelect(today.getDate());
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
className: "text-primary-800 flex-1 text-sm hover:underline",
|
|
494
|
+
children: "Today"
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
inputType === "datetime-local" && /* @__PURE__ */ jsx(
|
|
498
|
+
"button",
|
|
499
|
+
{
|
|
500
|
+
type: "button",
|
|
501
|
+
onClick: () => setIsCalendarOpen(false),
|
|
502
|
+
className: "text-primary-800 flex-1 text-sm hover:underline",
|
|
503
|
+
children: "Done"
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
] })
|
|
507
|
+
]
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
};
|
|
511
|
+
if (inputType === "year") {
|
|
512
|
+
return /* @__PURE__ */ jsx(
|
|
513
|
+
Input,
|
|
514
|
+
{
|
|
515
|
+
ref,
|
|
516
|
+
type: inputType === "year" ? "text" : inputType,
|
|
517
|
+
inputMode: inputType === "year" ? "numeric" : void 0,
|
|
518
|
+
pattern: inputType === "year" ? "[0-9]{4}" : void 0,
|
|
519
|
+
maxLength: inputType === "year" ? 4 : void 0,
|
|
520
|
+
placeholder: inputType === "year" ? "YYYY" : void 0,
|
|
521
|
+
value: value.replace(/\D/g, "").slice(0, 4),
|
|
522
|
+
onChange: (event) => onChange?.(event.target.value.replace(/\D/g, "").slice(0, 4)),
|
|
523
|
+
onBlur,
|
|
524
|
+
onClick,
|
|
525
|
+
hasError,
|
|
526
|
+
error,
|
|
527
|
+
className: cn(widthClasses[width], className),
|
|
528
|
+
...props
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
if (showCalendar || inputType !== "date") {
|
|
533
|
+
const { label, helperText, hideLabel, required, size, ...inputProps } = props;
|
|
534
|
+
const resolvedSize = size ?? "md";
|
|
535
|
+
const inputId = inputProps.id || generatedId;
|
|
536
|
+
const errorId = `${inputId}-error`;
|
|
537
|
+
const helperId = `${inputId}-helper`;
|
|
538
|
+
const showError = hasError || !!localError;
|
|
539
|
+
const errorMessage = error || localError;
|
|
540
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", widthClasses[width]), children: [
|
|
541
|
+
label && /* @__PURE__ */ jsxs(
|
|
542
|
+
"label",
|
|
543
|
+
{
|
|
544
|
+
htmlFor: inputId,
|
|
545
|
+
className: cn(
|
|
546
|
+
"text-foreground text-sm font-medium",
|
|
547
|
+
hideLabel && "sr-only"
|
|
548
|
+
),
|
|
549
|
+
children: [
|
|
550
|
+
label,
|
|
551
|
+
required && /* @__PURE__ */ jsx(
|
|
552
|
+
"span",
|
|
553
|
+
{
|
|
554
|
+
className: "ml-1",
|
|
555
|
+
style: { color: "#ef4444" },
|
|
556
|
+
"aria-hidden": "true",
|
|
557
|
+
children: "*"
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
]
|
|
561
|
+
}
|
|
562
|
+
),
|
|
563
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
564
|
+
/* @__PURE__ */ jsx(
|
|
565
|
+
"input",
|
|
566
|
+
{
|
|
567
|
+
ref,
|
|
568
|
+
id: inputId,
|
|
569
|
+
type: "text",
|
|
570
|
+
inputMode: inputType === "date" ? "numeric" : void 0,
|
|
571
|
+
autoComplete,
|
|
572
|
+
placeholder: inputType === "month" ? "Select month" : inputType === "datetime-local" ? "Select date and time" : placeholder,
|
|
573
|
+
value: formatPickerValue(displayValue, inputType, timeFormat),
|
|
574
|
+
onChange: handleChange,
|
|
575
|
+
onBlur: inputType === "date" ? handleBlur : onBlur,
|
|
576
|
+
onClick: (event) => {
|
|
577
|
+
onClick?.(event);
|
|
578
|
+
setIsCalendarOpen(true);
|
|
579
|
+
},
|
|
580
|
+
readOnly: !isFormattedDate,
|
|
581
|
+
"aria-invalid": showError,
|
|
582
|
+
"aria-describedby": [errorMessage ? errorId : null, helperText ? helperId : null].filter(Boolean).join(" ") || void 0,
|
|
583
|
+
className: cn(
|
|
584
|
+
"w-full px-3 py-2",
|
|
585
|
+
"rounded-lg border",
|
|
586
|
+
"bg-background text-foreground",
|
|
587
|
+
"placeholder:text-muted-foreground",
|
|
588
|
+
"transition-colors duration-200",
|
|
589
|
+
"focus:ring-ring focus:border-transparent focus:ring-2 focus:outline-none",
|
|
590
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
591
|
+
sizeClasses[resolvedSize],
|
|
592
|
+
showError ? "border-destructive focus:ring-destructive" : "border-input",
|
|
593
|
+
"pr-10",
|
|
594
|
+
className
|
|
595
|
+
),
|
|
596
|
+
...inputProps
|
|
597
|
+
}
|
|
598
|
+
),
|
|
599
|
+
/* @__PURE__ */ jsx(
|
|
600
|
+
"button",
|
|
601
|
+
{
|
|
602
|
+
ref: buttonRef,
|
|
603
|
+
type: "button",
|
|
604
|
+
onClick: () => setIsCalendarOpen(!isCalendarOpen),
|
|
605
|
+
className: cn(
|
|
606
|
+
"absolute top-1/2 right-3 -translate-y-1/2",
|
|
607
|
+
"text-muted-foreground hover:text-foreground",
|
|
608
|
+
"focus:text-foreground focus:outline-none",
|
|
609
|
+
"transition-colors"
|
|
610
|
+
),
|
|
611
|
+
"aria-label": "Open calendar",
|
|
612
|
+
"aria-expanded": isCalendarOpen,
|
|
613
|
+
"aria-haspopup": "dialog",
|
|
614
|
+
children: /* @__PURE__ */ jsx(Calendar, { size: 18 })
|
|
615
|
+
}
|
|
616
|
+
)
|
|
617
|
+
] }),
|
|
618
|
+
isCalendarOpen && createPortal(renderCalendar(), document.body),
|
|
619
|
+
errorMessage && /* @__PURE__ */ jsx(
|
|
620
|
+
"p",
|
|
621
|
+
{
|
|
622
|
+
id: errorId,
|
|
623
|
+
className: "text-sm",
|
|
624
|
+
style: { color: "#ef4444" },
|
|
625
|
+
role: "alert",
|
|
626
|
+
children: errorMessage
|
|
627
|
+
}
|
|
628
|
+
),
|
|
629
|
+
helperText && !errorMessage && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-muted-foreground text-sm", children: helperText })
|
|
630
|
+
] });
|
|
631
|
+
}
|
|
632
|
+
return /* @__PURE__ */ jsx(
|
|
633
|
+
Input,
|
|
634
|
+
{
|
|
635
|
+
ref,
|
|
636
|
+
type: "text",
|
|
637
|
+
inputMode: "numeric",
|
|
638
|
+
autoComplete,
|
|
639
|
+
placeholder,
|
|
640
|
+
value: displayValue,
|
|
641
|
+
onChange: handleChange,
|
|
642
|
+
onBlur: handleBlur,
|
|
643
|
+
hasError: hasError || !!localError,
|
|
644
|
+
error: error || localError,
|
|
645
|
+
className: cn(widthClasses[width], className),
|
|
646
|
+
...props
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
DateInput.displayName = "DateInput";
|
|
652
|
+
function ChevronLeftIcon() {
|
|
653
|
+
return /* @__PURE__ */ jsx(
|
|
654
|
+
"svg",
|
|
655
|
+
{
|
|
656
|
+
"aria-hidden": "true",
|
|
657
|
+
width: "16",
|
|
658
|
+
height: "16",
|
|
659
|
+
viewBox: "0 0 24 24",
|
|
660
|
+
fill: "none",
|
|
661
|
+
stroke: "currentColor",
|
|
662
|
+
strokeWidth: "2",
|
|
663
|
+
strokeLinecap: "round",
|
|
664
|
+
strokeLinejoin: "round",
|
|
665
|
+
children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
function ChevronRightIcon() {
|
|
670
|
+
return /* @__PURE__ */ jsx(
|
|
671
|
+
"svg",
|
|
672
|
+
{
|
|
673
|
+
"aria-hidden": "true",
|
|
674
|
+
width: "16",
|
|
675
|
+
height: "16",
|
|
676
|
+
viewBox: "0 0 24 24",
|
|
677
|
+
fill: "none",
|
|
678
|
+
stroke: "currentColor",
|
|
679
|
+
strokeWidth: "2",
|
|
680
|
+
strokeLinecap: "round",
|
|
681
|
+
strokeLinejoin: "round",
|
|
682
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export { DateInput };
|
|
688
|
+
//# sourceMappingURL=chunk-LIH3IZYK.js.map
|
|
689
|
+
//# sourceMappingURL=chunk-LIH3IZYK.js.map
|