@page-speed/forms 0.4.2 → 0.4.4
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/core.cjs +119 -27
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +104 -12
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +119 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +104 -12
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +767 -662
- package/dist/inputs.cjs.map +1 -1
- package/dist/inputs.d.cts +21 -61
- package/dist/inputs.d.ts +21 -61
- package/dist/inputs.js +766 -661
- package/dist/inputs.js.map +1 -1
- package/dist/integration.cjs +11 -3
- package/dist/integration.cjs.map +1 -1
- package/dist/integration.js +11 -3
- package/dist/integration.js.map +1 -1
- package/package.json +1 -1
package/dist/inputs.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React8 from 'react';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
3
|
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { useOnClickOutside } from '@opensite/hooks/useOnClickOutside';
|
|
5
|
+
|
|
6
|
+
// src/inputs/TextInput.tsx
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var INPUT_AUTOFILL_RESET_CLASSES = "autofill:bg-transparent autofill:text-foreground [&:-webkit-autofill]:[-webkit-text-fill-color:hsl(var(--foreground))] [&:-webkit-autofill]:[caret-color:hsl(var(--foreground))] [&:-webkit-autofill]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:hover]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:focus]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill]:[transition:background-color_9999s_ease-out,color_9999s_ease-out]";
|
|
4
11
|
|
|
5
12
|
// src/inputs/TextInput.tsx
|
|
6
13
|
function TextInput({
|
|
@@ -23,10 +30,17 @@ function TextInput({
|
|
|
23
30
|
const handleBlur = () => {
|
|
24
31
|
onBlur?.();
|
|
25
32
|
};
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
const hasValue = String(value ?? "").trim().length > 0;
|
|
34
|
+
const combinedClassName = cn(
|
|
35
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors",
|
|
36
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
37
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
38
|
+
INPUT_AUTOFILL_RESET_CLASSES,
|
|
39
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
40
|
+
error && "border-destructive ring-1 ring-destructive",
|
|
41
|
+
className
|
|
42
|
+
);
|
|
43
|
+
return /* @__PURE__ */ React8.createElement(
|
|
30
44
|
"input",
|
|
31
45
|
{
|
|
32
46
|
type,
|
|
@@ -70,10 +84,17 @@ function TextArea({
|
|
|
70
84
|
const handleBlur = () => {
|
|
71
85
|
onBlur?.();
|
|
72
86
|
};
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
const hasValue = String(value ?? "").trim().length > 0;
|
|
88
|
+
const combinedClassName = cn(
|
|
89
|
+
"flex min-h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm transition-colors",
|
|
90
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
91
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
92
|
+
INPUT_AUTOFILL_RESET_CLASSES,
|
|
93
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
94
|
+
error && "border-destructive ring-1 ring-destructive",
|
|
95
|
+
className
|
|
96
|
+
);
|
|
97
|
+
return /* @__PURE__ */ React8.createElement(
|
|
77
98
|
"textarea",
|
|
78
99
|
{
|
|
79
100
|
name,
|
|
@@ -97,9 +118,53 @@ function TextArea({
|
|
|
97
118
|
);
|
|
98
119
|
}
|
|
99
120
|
TextArea.displayName = "TextArea";
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
var LabelGroup = ({
|
|
122
|
+
labelHtmlFor,
|
|
123
|
+
required = false,
|
|
124
|
+
variant = "label",
|
|
125
|
+
secondaryId,
|
|
126
|
+
secondary,
|
|
127
|
+
primary,
|
|
128
|
+
primaryClassName,
|
|
129
|
+
secondaryClassName
|
|
130
|
+
}) => {
|
|
131
|
+
const primaryClasses = cn(
|
|
132
|
+
"text-sm font-medium leading-snug",
|
|
133
|
+
variant === "legend" ? "mb-1.5" : "mb-1 block",
|
|
134
|
+
primaryClassName
|
|
135
|
+
);
|
|
136
|
+
const requiredIndicator = required ? /* @__PURE__ */ React8.createElement("span", { className: "text-destructive pl-0.5", "aria-label": "required" }, "*") : null;
|
|
137
|
+
let primaryElement = null;
|
|
138
|
+
if (primary) {
|
|
139
|
+
if (variant === "label") {
|
|
140
|
+
primaryElement = /* @__PURE__ */ React8.createElement(
|
|
141
|
+
"label",
|
|
142
|
+
{
|
|
143
|
+
htmlFor: labelHtmlFor,
|
|
144
|
+
"data-slot": "field-label",
|
|
145
|
+
className: primaryClasses
|
|
146
|
+
},
|
|
147
|
+
primary,
|
|
148
|
+
requiredIndicator
|
|
149
|
+
);
|
|
150
|
+
} else if (variant === "legend") {
|
|
151
|
+
primaryElement = /* @__PURE__ */ React8.createElement("legend", { "data-slot": "field-legend", className: primaryClasses }, primary, requiredIndicator);
|
|
152
|
+
} else {
|
|
153
|
+
primaryElement = /* @__PURE__ */ React8.createElement("div", { "data-slot": "field-label", className: primaryClasses }, primary, requiredIndicator);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const secondaryElement = secondary ? /* @__PURE__ */ React8.createElement(
|
|
157
|
+
"p",
|
|
158
|
+
{
|
|
159
|
+
"data-slot": "field-description",
|
|
160
|
+
id: secondaryId,
|
|
161
|
+
className: cn("text-sm leading-normal font-normal", secondaryClassName)
|
|
162
|
+
},
|
|
163
|
+
secondary
|
|
164
|
+
) : null;
|
|
165
|
+
if (!primaryElement && !secondaryElement) return null;
|
|
166
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, null, primaryElement, secondaryElement);
|
|
167
|
+
};
|
|
103
168
|
|
|
104
169
|
// src/inputs/Checkbox.tsx
|
|
105
170
|
function Checkbox({
|
|
@@ -114,12 +179,12 @@ function Checkbox({
|
|
|
114
179
|
indeterminate = false,
|
|
115
180
|
label,
|
|
116
181
|
description,
|
|
117
|
-
|
|
182
|
+
useChoiceCard = false,
|
|
118
183
|
...props
|
|
119
184
|
}) {
|
|
120
|
-
const inputRef =
|
|
185
|
+
const inputRef = React8.useRef(null);
|
|
121
186
|
const checkboxId = props.id || `checkbox-${name}`;
|
|
122
|
-
|
|
187
|
+
React8.useEffect(() => {
|
|
123
188
|
if (inputRef.current) {
|
|
124
189
|
inputRef.current.indeterminate = indeterminate;
|
|
125
190
|
}
|
|
@@ -131,7 +196,7 @@ function Checkbox({
|
|
|
131
196
|
onBlur?.();
|
|
132
197
|
};
|
|
133
198
|
const isActive = value || indeterminate && !value;
|
|
134
|
-
const checkbox = /* @__PURE__ */
|
|
199
|
+
const checkbox = /* @__PURE__ */ React8.createElement(
|
|
135
200
|
"div",
|
|
136
201
|
{
|
|
137
202
|
className: cn(
|
|
@@ -139,7 +204,7 @@ function Checkbox({
|
|
|
139
204
|
!label && className
|
|
140
205
|
)
|
|
141
206
|
},
|
|
142
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ React8.createElement(
|
|
143
208
|
"input",
|
|
144
209
|
{
|
|
145
210
|
ref: inputRef,
|
|
@@ -158,7 +223,7 @@ function Checkbox({
|
|
|
158
223
|
...props
|
|
159
224
|
}
|
|
160
225
|
),
|
|
161
|
-
/* @__PURE__ */
|
|
226
|
+
/* @__PURE__ */ React8.createElement(
|
|
162
227
|
"div",
|
|
163
228
|
{
|
|
164
229
|
className: cn(
|
|
@@ -171,7 +236,7 @@ function Checkbox({
|
|
|
171
236
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-ring/50 peer-focus-visible:ring-offset-1"
|
|
172
237
|
)
|
|
173
238
|
},
|
|
174
|
-
value && /* @__PURE__ */
|
|
239
|
+
value && /* @__PURE__ */ React8.createElement(
|
|
175
240
|
"svg",
|
|
176
241
|
{
|
|
177
242
|
className: "size-3.5",
|
|
@@ -182,9 +247,9 @@ function Checkbox({
|
|
|
182
247
|
strokeLinecap: "round",
|
|
183
248
|
strokeLinejoin: "round"
|
|
184
249
|
},
|
|
185
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ React8.createElement("polyline", { points: "20 6 9 17 4 12" })
|
|
186
251
|
),
|
|
187
|
-
indeterminate && !value && /* @__PURE__ */
|
|
252
|
+
indeterminate && !value && /* @__PURE__ */ React8.createElement(
|
|
188
253
|
"svg",
|
|
189
254
|
{
|
|
190
255
|
className: "size-3.5",
|
|
@@ -195,31 +260,44 @@ function Checkbox({
|
|
|
195
260
|
strokeLinecap: "round",
|
|
196
261
|
strokeLinejoin: "round"
|
|
197
262
|
},
|
|
198
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ React8.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
199
264
|
)
|
|
200
265
|
)
|
|
201
266
|
);
|
|
202
267
|
if (label) {
|
|
203
|
-
return /* @__PURE__ */
|
|
268
|
+
return /* @__PURE__ */ React8.createElement(
|
|
204
269
|
"label",
|
|
205
270
|
{
|
|
206
271
|
className: cn(
|
|
207
272
|
"w-full h-full flex gap-3 p-3 duration-200",
|
|
208
|
-
|
|
209
|
-
|
|
273
|
+
useChoiceCard && "border rounded-lg hover:ring-2",
|
|
274
|
+
useChoiceCard && value && "ring-2",
|
|
210
275
|
disabled ? "opacity-50 cursor-not-allowed hover:ring-0" : "cursor-pointer",
|
|
211
276
|
className
|
|
212
277
|
),
|
|
213
278
|
htmlFor: checkboxId
|
|
214
279
|
},
|
|
215
|
-
/* @__PURE__ */
|
|
216
|
-
"
|
|
280
|
+
/* @__PURE__ */ React8.createElement(
|
|
281
|
+
"div",
|
|
217
282
|
{
|
|
218
|
-
className:
|
|
219
|
-
|
|
283
|
+
className: cn(
|
|
284
|
+
"flex w-full flex-row gap-2",
|
|
285
|
+
useChoiceCard ? "items-start" : "items-center"
|
|
286
|
+
)
|
|
220
287
|
},
|
|
221
|
-
|
|
222
|
-
|
|
288
|
+
checkbox,
|
|
289
|
+
/* @__PURE__ */ React8.createElement(
|
|
290
|
+
LabelGroup,
|
|
291
|
+
{
|
|
292
|
+
variant: "text",
|
|
293
|
+
primary: label,
|
|
294
|
+
secondary: description,
|
|
295
|
+
secondaryId: description ? `${checkboxId}-description` : void 0,
|
|
296
|
+
primaryClassName: "mb-0",
|
|
297
|
+
secondaryClassName: "text-xs opacity-75"
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
)
|
|
223
301
|
);
|
|
224
302
|
}
|
|
225
303
|
return checkbox;
|
|
@@ -253,13 +331,11 @@ function CheckboxGroup({
|
|
|
253
331
|
).length;
|
|
254
332
|
const allSelected = selectedEnabledCount === enabledOptions.length;
|
|
255
333
|
const someSelected = selectedEnabledCount > 0 && !allSelected;
|
|
256
|
-
const
|
|
257
|
-
if (options
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
return "inline";
|
|
334
|
+
const useChoiceCard = React8.useMemo(() => {
|
|
335
|
+
if (!options) return false;
|
|
336
|
+
return options?.some((opt) => opt.description);
|
|
261
337
|
}, [options]);
|
|
262
|
-
const countableValue =
|
|
338
|
+
const countableValue = React8.useMemo(() => {
|
|
263
339
|
if (value?.length > 0) {
|
|
264
340
|
return value.length;
|
|
265
341
|
}
|
|
@@ -284,29 +360,37 @@ function CheckboxGroup({
|
|
|
284
360
|
onBlur?.();
|
|
285
361
|
};
|
|
286
362
|
const maxReached = Boolean(maxSelections && countableValue >= maxSelections);
|
|
287
|
-
const containerClass =
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
|
|
363
|
+
const containerClass = React8.useMemo(() => {
|
|
364
|
+
return cn(
|
|
365
|
+
"w-full gap-3 grid grid-cols-1 border-0 m-0 p-0 min-w-0",
|
|
366
|
+
(layout === "grid" || layout === "inline") && "md:grid-cols-2",
|
|
367
|
+
className
|
|
368
|
+
);
|
|
369
|
+
}, [layout, className]);
|
|
370
|
+
const groupDescriptionId = description ? `${name}-description` : void 0;
|
|
371
|
+
const groupAriaDescribedBy = [props["aria-describedby"], groupDescriptionId].filter(Boolean).join(" ") || void 0;
|
|
372
|
+
return /* @__PURE__ */ React8.createElement(
|
|
373
|
+
"fieldset",
|
|
296
374
|
{
|
|
297
375
|
className: containerClass,
|
|
298
376
|
role: "group",
|
|
299
377
|
"aria-invalid": error || props["aria-invalid"],
|
|
300
|
-
"aria-describedby":
|
|
378
|
+
"aria-describedby": groupAriaDescribedBy,
|
|
301
379
|
"aria-required": required || props["aria-required"],
|
|
302
|
-
"aria-label": typeof label === "string" ? label : props["aria-label"]
|
|
303
|
-
style: layout === "grid" ? {
|
|
304
|
-
gridTemplateColumns: `repeat(${gridColumns}, 1fr)`
|
|
305
|
-
} : void 0
|
|
380
|
+
"aria-label": typeof label === "string" ? label : props["aria-label"]
|
|
306
381
|
},
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
382
|
+
/* @__PURE__ */ React8.createElement(
|
|
383
|
+
LabelGroup,
|
|
384
|
+
{
|
|
385
|
+
labelHtmlFor: name,
|
|
386
|
+
required,
|
|
387
|
+
variant: "legend",
|
|
388
|
+
secondaryId: groupDescriptionId,
|
|
389
|
+
secondary: description,
|
|
390
|
+
primary: label
|
|
391
|
+
}
|
|
392
|
+
),
|
|
393
|
+
showSelectAll && enabledOptions.length > 0 && /* @__PURE__ */ React8.createElement(
|
|
310
394
|
Checkbox,
|
|
311
395
|
{
|
|
312
396
|
name: `${name}-select-all`,
|
|
@@ -316,7 +400,7 @@ function CheckboxGroup({
|
|
|
316
400
|
onBlur: handleBlur,
|
|
317
401
|
indeterminate: someSelected,
|
|
318
402
|
label: selectAllLabel,
|
|
319
|
-
|
|
403
|
+
useChoiceCard,
|
|
320
404
|
disabled,
|
|
321
405
|
"aria-label": selectAllLabel
|
|
322
406
|
}
|
|
@@ -324,7 +408,7 @@ function CheckboxGroup({
|
|
|
324
408
|
options.map((option) => {
|
|
325
409
|
const isChecked = value.includes(option.value);
|
|
326
410
|
const isDisabled = disabled || option.disabled || maxReached && !isChecked;
|
|
327
|
-
return /* @__PURE__ */
|
|
411
|
+
return /* @__PURE__ */ React8.createElement(
|
|
328
412
|
Checkbox,
|
|
329
413
|
{
|
|
330
414
|
key: option.value,
|
|
@@ -338,11 +422,11 @@ function CheckboxGroup({
|
|
|
338
422
|
error,
|
|
339
423
|
label: renderOption ? renderOption(option) : option.label,
|
|
340
424
|
description: renderOption ? void 0 : option.description,
|
|
341
|
-
|
|
425
|
+
useChoiceCard
|
|
342
426
|
}
|
|
343
427
|
);
|
|
344
428
|
}),
|
|
345
|
-
(minSelections || maxSelections) && /* @__PURE__ */
|
|
429
|
+
(minSelections || maxSelections) && /* @__PURE__ */ React8.createElement(
|
|
346
430
|
"div",
|
|
347
431
|
{
|
|
348
432
|
className: cn(
|
|
@@ -351,8 +435,8 @@ function CheckboxGroup({
|
|
|
351
435
|
),
|
|
352
436
|
"aria-live": "polite"
|
|
353
437
|
},
|
|
354
|
-
minSelections && countableValue < minSelections && /* @__PURE__ */
|
|
355
|
-
maxSelections && /* @__PURE__ */
|
|
438
|
+
minSelections && countableValue < minSelections && /* @__PURE__ */ React8.createElement("span", null, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""),
|
|
439
|
+
maxSelections && /* @__PURE__ */ React8.createElement("span", null, countableValue, "/", maxSelections, " selected")
|
|
356
440
|
)
|
|
357
441
|
);
|
|
358
442
|
}
|
|
@@ -367,8 +451,8 @@ function Radio({
|
|
|
367
451
|
error = false,
|
|
368
452
|
className = "",
|
|
369
453
|
layout = "stacked",
|
|
370
|
-
radioVariant = "inline",
|
|
371
454
|
label,
|
|
455
|
+
description,
|
|
372
456
|
options,
|
|
373
457
|
...props
|
|
374
458
|
}) {
|
|
@@ -403,29 +487,43 @@ function Radio({
|
|
|
403
487
|
const handleBlur = () => {
|
|
404
488
|
onBlur?.();
|
|
405
489
|
};
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
490
|
+
const useChoiceCard = React8.useMemo(() => {
|
|
491
|
+
return options.some((option) => option.description);
|
|
492
|
+
}, [options]);
|
|
493
|
+
const containerClass = React8.useMemo(() => {
|
|
494
|
+
return cn(
|
|
495
|
+
"w-full gap-3 grid grid-cols-1 border-0 m-0 p-0 min-w-0",
|
|
496
|
+
(layout === "grid" || layout === "inline") && "md:grid-cols-2",
|
|
497
|
+
className
|
|
498
|
+
);
|
|
499
|
+
}, [layout, className]);
|
|
500
|
+
const groupDescriptionId = description ? `${name}-description` : void 0;
|
|
501
|
+
const groupAriaDescribedBy = [props["aria-describedby"], groupDescriptionId].filter(Boolean).join(" ") || void 0;
|
|
502
|
+
return /* @__PURE__ */ React8.createElement(
|
|
503
|
+
"fieldset",
|
|
414
504
|
{
|
|
415
505
|
className: containerClass,
|
|
416
506
|
role: "radiogroup",
|
|
417
507
|
"aria-invalid": error || props["aria-invalid"],
|
|
418
|
-
"aria-describedby":
|
|
508
|
+
"aria-describedby": groupAriaDescribedBy,
|
|
419
509
|
"aria-required": required || props["aria-required"],
|
|
420
510
|
"aria-label": typeof label === "string" ? label : props["aria-label"]
|
|
421
511
|
},
|
|
422
|
-
|
|
512
|
+
/* @__PURE__ */ React8.createElement(
|
|
513
|
+
LabelGroup,
|
|
514
|
+
{
|
|
515
|
+
variant: "legend",
|
|
516
|
+
primary: label,
|
|
517
|
+
secondary: description,
|
|
518
|
+
secondaryId: groupDescriptionId
|
|
519
|
+
}
|
|
520
|
+
),
|
|
423
521
|
options.map((option, index) => {
|
|
424
522
|
const isChecked = value === option.value;
|
|
425
523
|
const isDisabled = disabled || option.disabled;
|
|
426
524
|
const radioId = `${name}-${option.value}`;
|
|
427
525
|
const hasDescription = option.description != null && option.description !== "";
|
|
428
|
-
const radioIndicator = /* @__PURE__ */
|
|
526
|
+
const radioIndicator = /* @__PURE__ */ React8.createElement("div", { className: "relative inline-flex items-center justify-center" }, /* @__PURE__ */ React8.createElement(
|
|
429
527
|
"input",
|
|
430
528
|
{
|
|
431
529
|
type: "radio",
|
|
@@ -440,7 +538,7 @@ function Radio({
|
|
|
440
538
|
className: "peer sr-only",
|
|
441
539
|
"aria-describedby": hasDescription ? `${radioId}-description` : props["aria-describedby"]
|
|
442
540
|
}
|
|
443
|
-
), /* @__PURE__ */
|
|
541
|
+
), /* @__PURE__ */ React8.createElement(
|
|
444
542
|
"div",
|
|
445
543
|
{
|
|
446
544
|
className: cn(
|
|
@@ -453,31 +551,45 @@ function Radio({
|
|
|
453
551
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-ring/50 peer-focus-visible:ring-offset-1"
|
|
454
552
|
)
|
|
455
553
|
},
|
|
456
|
-
isChecked && /* @__PURE__ */
|
|
554
|
+
isChecked && /* @__PURE__ */ React8.createElement("div", { className: "size-3 rounded-full bg-primary" })
|
|
457
555
|
));
|
|
458
|
-
const labelContent = /* @__PURE__ */
|
|
459
|
-
|
|
556
|
+
const labelContent = /* @__PURE__ */ React8.createElement(
|
|
557
|
+
LabelGroup,
|
|
460
558
|
{
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
559
|
+
variant: "text",
|
|
560
|
+
primary: option.label,
|
|
561
|
+
secondary: hasDescription ? option.description : void 0,
|
|
562
|
+
secondaryId: hasDescription ? `${radioId}-description` : void 0,
|
|
563
|
+
primaryClassName: "mb-0",
|
|
564
|
+
secondaryClassName: "text-xs opacity-75"
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
return /* @__PURE__ */ React8.createElement(
|
|
467
568
|
"label",
|
|
468
569
|
{
|
|
469
570
|
key: option.value,
|
|
470
571
|
className: cn(
|
|
471
572
|
"w-full h-full flex gap-3 p-3 duration-200",
|
|
472
|
-
|
|
473
|
-
|
|
573
|
+
useChoiceCard && "border rounded-lg hover:ring-2",
|
|
574
|
+
useChoiceCard && isChecked && "ring-2",
|
|
474
575
|
isDisabled ? "opacity-50 cursor-not-allowed hover:ring-0" : "cursor-pointer"
|
|
475
576
|
),
|
|
476
577
|
htmlFor: radioId,
|
|
477
578
|
onKeyDown: (e) => handleKeyDown(e, index),
|
|
478
579
|
tabIndex: isDisabled ? -1 : 0
|
|
479
580
|
},
|
|
480
|
-
/* @__PURE__ */
|
|
581
|
+
/* @__PURE__ */ React8.createElement(
|
|
582
|
+
"div",
|
|
583
|
+
{
|
|
584
|
+
className: cn(
|
|
585
|
+
"flex w-full flex-row gap-2",
|
|
586
|
+
useChoiceCard ? "items-start" : "items-center"
|
|
587
|
+
)
|
|
588
|
+
},
|
|
589
|
+
!useChoiceCard && radioIndicator,
|
|
590
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, labelContent),
|
|
591
|
+
useChoiceCard && radioIndicator
|
|
592
|
+
)
|
|
481
593
|
);
|
|
482
594
|
})
|
|
483
595
|
);
|
|
@@ -502,19 +614,19 @@ function Select({
|
|
|
502
614
|
renderOption,
|
|
503
615
|
...props
|
|
504
616
|
}) {
|
|
505
|
-
const [isOpen, setIsOpen] =
|
|
506
|
-
const [searchQuery, setSearchQuery] =
|
|
507
|
-
const [focusedIndex, setFocusedIndex] =
|
|
508
|
-
const selectRef =
|
|
509
|
-
const searchInputRef =
|
|
617
|
+
const [isOpen, setIsOpen] = React8.useState(false);
|
|
618
|
+
const [searchQuery, setSearchQuery] = React8.useState("");
|
|
619
|
+
const [focusedIndex, setFocusedIndex] = React8.useState(-1);
|
|
620
|
+
const selectRef = React8.useRef(null);
|
|
621
|
+
const searchInputRef = React8.useRef(null);
|
|
510
622
|
const dropdownId = `${name}-dropdown`;
|
|
511
|
-
const allOptions =
|
|
623
|
+
const allOptions = React8.useMemo(() => {
|
|
512
624
|
if (optionGroups.length > 0) {
|
|
513
625
|
return optionGroups.flatMap((group) => group.options);
|
|
514
626
|
}
|
|
515
627
|
return options;
|
|
516
628
|
}, [options, optionGroups]);
|
|
517
|
-
const filteredOptions =
|
|
629
|
+
const filteredOptions = React8.useMemo(() => {
|
|
518
630
|
if (!searchQuery.trim()) {
|
|
519
631
|
return allOptions;
|
|
520
632
|
}
|
|
@@ -524,9 +636,10 @@ function Select({
|
|
|
524
636
|
return label.toLowerCase().includes(query);
|
|
525
637
|
});
|
|
526
638
|
}, [allOptions, searchQuery]);
|
|
527
|
-
const selectedOption =
|
|
639
|
+
const selectedOption = React8.useMemo(() => {
|
|
528
640
|
return allOptions.find((opt) => opt.value === value);
|
|
529
641
|
}, [allOptions, value]);
|
|
642
|
+
const hasValue = Boolean(value);
|
|
530
643
|
const handleSelect = (optionValue) => {
|
|
531
644
|
onChange(optionValue);
|
|
532
645
|
setIsOpen(false);
|
|
@@ -567,9 +680,7 @@ function Select({
|
|
|
567
680
|
if (enabledOptions.length > 0) {
|
|
568
681
|
const currentIndexInFiltered = focusedIndex;
|
|
569
682
|
const nextIndex = (currentIndexInFiltered + 1) % enabledOptions.length;
|
|
570
|
-
setFocusedIndex(
|
|
571
|
-
filteredOptions.indexOf(enabledOptions[nextIndex])
|
|
572
|
-
);
|
|
683
|
+
setFocusedIndex(filteredOptions.indexOf(enabledOptions[nextIndex]));
|
|
573
684
|
}
|
|
574
685
|
}
|
|
575
686
|
break;
|
|
@@ -580,9 +691,7 @@ function Select({
|
|
|
580
691
|
if (enabledOptions.length > 0) {
|
|
581
692
|
const currentIndexInFiltered = focusedIndex;
|
|
582
693
|
const prevIndex = (currentIndexInFiltered - 1 + enabledOptions.length) % enabledOptions.length;
|
|
583
|
-
setFocusedIndex(
|
|
584
|
-
filteredOptions.indexOf(enabledOptions[prevIndex])
|
|
585
|
-
);
|
|
694
|
+
setFocusedIndex(filteredOptions.indexOf(enabledOptions[prevIndex]));
|
|
586
695
|
}
|
|
587
696
|
}
|
|
588
697
|
break;
|
|
@@ -625,27 +734,22 @@ function Select({
|
|
|
625
734
|
break;
|
|
626
735
|
}
|
|
627
736
|
};
|
|
628
|
-
const handleBlur = () => {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const handleClickOutside = (event) => {
|
|
633
|
-
if (selectRef.current && !selectRef.current.contains(event.target)) {
|
|
634
|
-
setIsOpen(false);
|
|
635
|
-
setSearchQuery("");
|
|
636
|
-
setFocusedIndex(-1);
|
|
637
|
-
handleBlur();
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
if (isOpen) {
|
|
641
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
642
|
-
return () => {
|
|
643
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
644
|
-
};
|
|
737
|
+
const handleBlur = (event) => {
|
|
738
|
+
const nextTarget = event?.relatedTarget;
|
|
739
|
+
if (!nextTarget || !selectRef.current?.contains(nextTarget)) {
|
|
740
|
+
onBlur?.();
|
|
645
741
|
}
|
|
646
|
-
}
|
|
647
|
-
const
|
|
648
|
-
|
|
742
|
+
};
|
|
743
|
+
const closeDropdown = React8.useCallback(() => {
|
|
744
|
+
if (!isOpen) return;
|
|
745
|
+
setIsOpen(false);
|
|
746
|
+
setSearchQuery("");
|
|
747
|
+
setFocusedIndex(-1);
|
|
748
|
+
onBlur?.();
|
|
749
|
+
}, [isOpen, onBlur]);
|
|
750
|
+
useOnClickOutside(selectRef, closeDropdown, "pointerdown", true);
|
|
751
|
+
const combinedClassName = cn("relative w-full", className);
|
|
752
|
+
return /* @__PURE__ */ React8.createElement(
|
|
649
753
|
"div",
|
|
650
754
|
{
|
|
651
755
|
ref: selectRef,
|
|
@@ -653,7 +757,7 @@ function Select({
|
|
|
653
757
|
onKeyDown: handleKeyDown,
|
|
654
758
|
onBlur: handleBlur
|
|
655
759
|
},
|
|
656
|
-
/* @__PURE__ */
|
|
760
|
+
/* @__PURE__ */ React8.createElement(
|
|
657
761
|
"select",
|
|
658
762
|
{
|
|
659
763
|
name,
|
|
@@ -666,13 +770,19 @@ function Select({
|
|
|
666
770
|
tabIndex: -1,
|
|
667
771
|
style: { display: "none" }
|
|
668
772
|
},
|
|
669
|
-
/* @__PURE__ */
|
|
670
|
-
allOptions.map((option) => /* @__PURE__ */
|
|
773
|
+
/* @__PURE__ */ React8.createElement("option", { value: "" }, "Select..."),
|
|
774
|
+
allOptions.map((option) => /* @__PURE__ */ React8.createElement("option", { key: option.value, value: option.value }, typeof option.label === "string" ? option.label : option.value))
|
|
671
775
|
),
|
|
672
|
-
/* @__PURE__ */
|
|
776
|
+
/* @__PURE__ */ React8.createElement(
|
|
673
777
|
"div",
|
|
674
778
|
{
|
|
675
|
-
className:
|
|
779
|
+
className: cn(
|
|
780
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm",
|
|
781
|
+
"cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
782
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
783
|
+
disabled && "cursor-not-allowed opacity-50 pointer-events-none",
|
|
784
|
+
error && "border-destructive ring-1 ring-destructive"
|
|
785
|
+
),
|
|
676
786
|
onClick: handleToggle,
|
|
677
787
|
role: "combobox",
|
|
678
788
|
"aria-expanded": isOpen,
|
|
@@ -683,48 +793,79 @@ function Select({
|
|
|
683
793
|
"aria-disabled": disabled,
|
|
684
794
|
tabIndex: disabled ? -1 : 0
|
|
685
795
|
},
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
796
|
+
/* @__PURE__ */ React8.createElement("span", { className: "flex items-center flex-1 overflow-hidden text-ellipsis" }, selectedOption ? renderOption ? renderOption(selectedOption) : selectedOption.label : /* @__PURE__ */ React8.createElement("span", { className: "relative" }, placeholder)),
|
|
797
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-1 ml-2" }, loading && /* @__PURE__ */ React8.createElement("span", { className: "text-xs" }, "\u23F3"), clearable && value && !disabled && !loading && /* @__PURE__ */ React8.createElement(
|
|
688
798
|
"button",
|
|
689
799
|
{
|
|
690
800
|
type: "button",
|
|
691
|
-
className: "flex items-center justify-center h-4 w-4 rounded-sm border-none bg-transparent
|
|
801
|
+
className: "flex items-center justify-center h-4 w-4 rounded-sm border-none bg-transparent cursor-pointer text-xs p-0 transition-opacity hover:opacity-70",
|
|
692
802
|
onClick: handleClear,
|
|
693
803
|
"aria-label": "Clear selection",
|
|
694
804
|
tabIndex: -1
|
|
695
805
|
},
|
|
696
806
|
"\u2715"
|
|
697
|
-
), /* @__PURE__ */
|
|
807
|
+
), /* @__PURE__ */ React8.createElement("span", { className: "text-xs leading-none", "aria-hidden": "true" }, isOpen ? "\u25B2" : "\u25BC"))
|
|
698
808
|
),
|
|
699
|
-
isOpen && /* @__PURE__ */
|
|
700
|
-
"
|
|
809
|
+
isOpen && /* @__PURE__ */ React8.createElement(
|
|
810
|
+
"div",
|
|
701
811
|
{
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
812
|
+
id: dropdownId,
|
|
813
|
+
className: "absolute z-50 top-full mt-1 min-w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md",
|
|
814
|
+
role: "listbox"
|
|
815
|
+
},
|
|
816
|
+
searchable && /* @__PURE__ */ React8.createElement("div", { className: "p-2 border-b border-border" }, /* @__PURE__ */ React8.createElement(
|
|
817
|
+
"input",
|
|
818
|
+
{
|
|
819
|
+
ref: searchInputRef,
|
|
820
|
+
type: "text",
|
|
821
|
+
className: cn(
|
|
822
|
+
"w-full border border-input rounded px-2 py-1 text-sm bg-transparent outline-none focus:ring-1 focus:ring-ring",
|
|
823
|
+
INPUT_AUTOFILL_RESET_CLASSES
|
|
824
|
+
),
|
|
825
|
+
placeholder: "Search...",
|
|
826
|
+
value: searchQuery,
|
|
827
|
+
onChange: handleSearchChange,
|
|
828
|
+
onClick: (e) => e.stopPropagation(),
|
|
829
|
+
"aria-label": "Search options"
|
|
830
|
+
}
|
|
831
|
+
)),
|
|
832
|
+
/* @__PURE__ */ React8.createElement("div", { className: "max-h-64 overflow-y-auto p-1" }, filteredOptions.length === 0 ? /* @__PURE__ */ React8.createElement("div", { className: "py-2 px-3 text-center text-sm " }, "No options found") : optionGroups.length > 0 ? (
|
|
833
|
+
// Render grouped options
|
|
834
|
+
optionGroups.map((group, groupIndex) => {
|
|
835
|
+
const groupOptions = group.options.filter(
|
|
836
|
+
(opt) => filteredOptions.includes(opt)
|
|
837
|
+
);
|
|
838
|
+
if (groupOptions.length === 0) return null;
|
|
839
|
+
return /* @__PURE__ */ React8.createElement("div", { key: groupIndex, className: "py-1" }, /* @__PURE__ */ React8.createElement("div", { className: "py-1.5 px-2 text-xs font-semibold " }, group.label), groupOptions.map((option) => {
|
|
840
|
+
const globalIndex = filteredOptions.indexOf(option);
|
|
841
|
+
const isSelected = value === option.value;
|
|
842
|
+
const isFocused = globalIndex === focusedIndex;
|
|
843
|
+
const isDisabled = option.disabled;
|
|
844
|
+
return /* @__PURE__ */ React8.createElement(
|
|
845
|
+
"div",
|
|
846
|
+
{
|
|
847
|
+
key: option.value,
|
|
848
|
+
className: `relative flex w-full cursor-pointer items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-muted ${isFocused ? "bg-muted" : ""} ${isSelected ? "font-medium bg-muted" : ""} ${isDisabled ? "pointer-events-none opacity-50" : ""}`,
|
|
849
|
+
onClick: () => !isDisabled && handleSelect(option.value),
|
|
850
|
+
role: "option",
|
|
851
|
+
"aria-selected": isSelected,
|
|
852
|
+
"aria-disabled": isDisabled
|
|
853
|
+
},
|
|
854
|
+
renderOption ? renderOption(option) : option.label
|
|
855
|
+
);
|
|
856
|
+
}));
|
|
857
|
+
})
|
|
858
|
+
) : (
|
|
859
|
+
// Render flat options
|
|
860
|
+
filteredOptions.map((option, index) => {
|
|
720
861
|
const isSelected = value === option.value;
|
|
721
|
-
const isFocused =
|
|
862
|
+
const isFocused = index === focusedIndex;
|
|
722
863
|
const isDisabled = option.disabled;
|
|
723
|
-
return /* @__PURE__ */
|
|
864
|
+
return /* @__PURE__ */ React8.createElement(
|
|
724
865
|
"div",
|
|
725
866
|
{
|
|
726
867
|
key: option.value,
|
|
727
|
-
className: `relative flex w-full cursor-pointer items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-
|
|
868
|
+
className: `relative flex w-full cursor-pointer items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-muted ${isFocused ? "bg-muted" : ""} ${isSelected ? "font-medium bg-muted" : ""} ${isDisabled ? "pointer-events-none opacity-50" : ""}`,
|
|
728
869
|
onClick: () => !isDisabled && handleSelect(option.value),
|
|
729
870
|
role: "option",
|
|
730
871
|
"aria-selected": isSelected,
|
|
@@ -732,28 +873,9 @@ function Select({
|
|
|
732
873
|
},
|
|
733
874
|
renderOption ? renderOption(option) : option.label
|
|
734
875
|
);
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
)
|
|
738
|
-
// Render flat options
|
|
739
|
-
filteredOptions.map((option, index) => {
|
|
740
|
-
const isSelected = value === option.value;
|
|
741
|
-
const isFocused = index === focusedIndex;
|
|
742
|
-
const isDisabled = option.disabled;
|
|
743
|
-
return /* @__PURE__ */ React7.createElement(
|
|
744
|
-
"div",
|
|
745
|
-
{
|
|
746
|
-
key: option.value,
|
|
747
|
-
className: `relative flex w-full cursor-pointer items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground ${isFocused ? "bg-accent text-accent-foreground" : ""} ${isSelected ? "font-medium bg-accent" : ""} ${isDisabled ? "pointer-events-none opacity-50" : ""}`,
|
|
748
|
-
onClick: () => !isDisabled && handleSelect(option.value),
|
|
749
|
-
role: "option",
|
|
750
|
-
"aria-selected": isSelected,
|
|
751
|
-
"aria-disabled": isDisabled
|
|
752
|
-
},
|
|
753
|
-
renderOption ? renderOption(option) : option.label
|
|
754
|
-
);
|
|
755
|
-
})
|
|
756
|
-
)))
|
|
876
|
+
})
|
|
877
|
+
))
|
|
878
|
+
)
|
|
757
879
|
);
|
|
758
880
|
}
|
|
759
881
|
Select.displayName = "Select";
|
|
@@ -762,7 +884,7 @@ function FileInput({
|
|
|
762
884
|
value = [],
|
|
763
885
|
onChange,
|
|
764
886
|
onBlur,
|
|
765
|
-
placeholder = "Choose file
|
|
887
|
+
placeholder = "Choose file...",
|
|
766
888
|
disabled = false,
|
|
767
889
|
required = false,
|
|
768
890
|
error = false,
|
|
@@ -782,14 +904,14 @@ function FileInput({
|
|
|
782
904
|
onFileRemove,
|
|
783
905
|
...props
|
|
784
906
|
}) {
|
|
785
|
-
const inputRef =
|
|
786
|
-
const [dragActive, setDragActive] =
|
|
787
|
-
const [cropperOpen, setCropperOpen] =
|
|
788
|
-
const [imageToCrop, setImageToCrop] =
|
|
789
|
-
const [crop, setCrop] =
|
|
790
|
-
const [zoom, setZoom] =
|
|
791
|
-
const [croppedAreaPixels, setCroppedAreaPixels] =
|
|
792
|
-
const validateFile =
|
|
907
|
+
const inputRef = React8.useRef(null);
|
|
908
|
+
const [dragActive, setDragActive] = React8.useState(false);
|
|
909
|
+
const [cropperOpen, setCropperOpen] = React8.useState(false);
|
|
910
|
+
const [imageToCrop, setImageToCrop] = React8.useState(null);
|
|
911
|
+
const [crop, setCrop] = React8.useState({ x: 0, y: 0 });
|
|
912
|
+
const [zoom, setZoom] = React8.useState(1);
|
|
913
|
+
const [croppedAreaPixels, setCroppedAreaPixels] = React8.useState(null);
|
|
914
|
+
const validateFile = React8.useCallback(
|
|
793
915
|
(file) => {
|
|
794
916
|
if (accept) {
|
|
795
917
|
const acceptedTypes = accept.split(",").map((t) => t.trim());
|
|
@@ -824,7 +946,7 @@ function FileInput({
|
|
|
824
946
|
},
|
|
825
947
|
[accept, maxSize]
|
|
826
948
|
);
|
|
827
|
-
const handleFiles =
|
|
949
|
+
const handleFiles = React8.useCallback(
|
|
828
950
|
(fileList) => {
|
|
829
951
|
if (!fileList || fileList.length === 0) return;
|
|
830
952
|
const newFiles = Array.from(fileList);
|
|
@@ -865,9 +987,17 @@ function FileInput({
|
|
|
865
987
|
inputRef.current.value = "";
|
|
866
988
|
}
|
|
867
989
|
},
|
|
868
|
-
[
|
|
990
|
+
[
|
|
991
|
+
value,
|
|
992
|
+
onChange,
|
|
993
|
+
validateFile,
|
|
994
|
+
maxFiles,
|
|
995
|
+
multiple,
|
|
996
|
+
enableCropping,
|
|
997
|
+
onValidationError
|
|
998
|
+
]
|
|
869
999
|
);
|
|
870
|
-
const createCroppedImage =
|
|
1000
|
+
const createCroppedImage = React8.useCallback(
|
|
871
1001
|
async (imageUrl, cropArea) => {
|
|
872
1002
|
return new Promise((resolve, reject) => {
|
|
873
1003
|
const image = new Image();
|
|
@@ -891,13 +1021,17 @@ function FileInput({
|
|
|
891
1021
|
cropArea.width,
|
|
892
1022
|
cropArea.height
|
|
893
1023
|
);
|
|
894
|
-
canvas.toBlob(
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1024
|
+
canvas.toBlob(
|
|
1025
|
+
(blob) => {
|
|
1026
|
+
if (blob) {
|
|
1027
|
+
resolve(blob);
|
|
1028
|
+
} else {
|
|
1029
|
+
reject(new Error("Failed to create blob from canvas"));
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
"image/jpeg",
|
|
1033
|
+
0.95
|
|
1034
|
+
);
|
|
901
1035
|
};
|
|
902
1036
|
image.onerror = () => {
|
|
903
1037
|
reject(new Error("Failed to load image"));
|
|
@@ -907,7 +1041,7 @@ function FileInput({
|
|
|
907
1041
|
},
|
|
908
1042
|
[]
|
|
909
1043
|
);
|
|
910
|
-
const handleCropSave =
|
|
1044
|
+
const handleCropSave = React8.useCallback(async () => {
|
|
911
1045
|
if (!imageToCrop || !croppedAreaPixels) return;
|
|
912
1046
|
try {
|
|
913
1047
|
const croppedBlob = await createCroppedImage(
|
|
@@ -917,11 +1051,9 @@ function FileInput({
|
|
|
917
1051
|
if (onCropComplete) {
|
|
918
1052
|
onCropComplete(croppedBlob, imageToCrop.file);
|
|
919
1053
|
}
|
|
920
|
-
const croppedFile = new File(
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
{ type: "image/jpeg" }
|
|
924
|
-
);
|
|
1054
|
+
const croppedFile = new File([croppedBlob], imageToCrop.file.name, {
|
|
1055
|
+
type: "image/jpeg"
|
|
1056
|
+
});
|
|
925
1057
|
const updatedFiles = multiple ? [...value, croppedFile] : [croppedFile];
|
|
926
1058
|
onChange(updatedFiles);
|
|
927
1059
|
setCropperOpen(false);
|
|
@@ -933,8 +1065,16 @@ function FileInput({
|
|
|
933
1065
|
} catch (error2) {
|
|
934
1066
|
console.error("Failed to crop image:", error2);
|
|
935
1067
|
}
|
|
936
|
-
}, [
|
|
937
|
-
|
|
1068
|
+
}, [
|
|
1069
|
+
imageToCrop,
|
|
1070
|
+
croppedAreaPixels,
|
|
1071
|
+
createCroppedImage,
|
|
1072
|
+
onCropComplete,
|
|
1073
|
+
value,
|
|
1074
|
+
onChange,
|
|
1075
|
+
multiple
|
|
1076
|
+
]);
|
|
1077
|
+
const handleCropCancel = React8.useCallback(() => {
|
|
938
1078
|
if (imageToCrop) {
|
|
939
1079
|
URL.revokeObjectURL(imageToCrop.url);
|
|
940
1080
|
}
|
|
@@ -944,13 +1084,13 @@ function FileInput({
|
|
|
944
1084
|
setZoom(1);
|
|
945
1085
|
setCroppedAreaPixels(null);
|
|
946
1086
|
}, [imageToCrop]);
|
|
947
|
-
const onCropChange =
|
|
1087
|
+
const onCropChange = React8.useCallback((crop2) => {
|
|
948
1088
|
setCrop(crop2);
|
|
949
1089
|
}, []);
|
|
950
|
-
const onZoomChange =
|
|
1090
|
+
const onZoomChange = React8.useCallback((zoom2) => {
|
|
951
1091
|
setZoom(zoom2);
|
|
952
1092
|
}, []);
|
|
953
|
-
const onCropCompleteInternal =
|
|
1093
|
+
const onCropCompleteInternal = React8.useCallback(
|
|
954
1094
|
(_, croppedAreaPixels2) => {
|
|
955
1095
|
setCroppedAreaPixels(croppedAreaPixels2);
|
|
956
1096
|
},
|
|
@@ -1011,7 +1151,7 @@ function FileInput({
|
|
|
1011
1151
|
}
|
|
1012
1152
|
return null;
|
|
1013
1153
|
};
|
|
1014
|
-
|
|
1154
|
+
React8.useEffect(() => {
|
|
1015
1155
|
return () => {
|
|
1016
1156
|
value.forEach((file) => {
|
|
1017
1157
|
const previewUrl = getPreviewUrl(file);
|
|
@@ -1025,7 +1165,7 @@ function FileInput({
|
|
|
1025
1165
|
};
|
|
1026
1166
|
}, [value, imageToCrop]);
|
|
1027
1167
|
const combinedClassName = `${className}`.trim();
|
|
1028
|
-
return /* @__PURE__ */
|
|
1168
|
+
return /* @__PURE__ */ React8.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React8.createElement(
|
|
1029
1169
|
"input",
|
|
1030
1170
|
{
|
|
1031
1171
|
ref: inputRef,
|
|
@@ -1042,10 +1182,10 @@ function FileInput({
|
|
|
1042
1182
|
"aria-required": required || props["aria-required"],
|
|
1043
1183
|
style: { display: "none" }
|
|
1044
1184
|
}
|
|
1045
|
-
), /* @__PURE__ */
|
|
1185
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1046
1186
|
"div",
|
|
1047
1187
|
{
|
|
1048
|
-
className: `flex min-h-32 w-full cursor-pointer items-center justify-center rounded-md border-2 border-dashed border-input bg-transparent p-6 transition-colors hover:bg-
|
|
1188
|
+
className: `flex min-h-32 w-full cursor-pointer items-center justify-center rounded-md border-2 border-dashed border-input bg-transparent p-6 transition-colors hover:bg-primary/50 hover:border-ring ${dragActive ? "bg-primary text-primary-foreground border-ring" : ""} ${disabled ? "cursor-not-allowed opacity-50" : ""} ${error ? "border-destructive" : ""}`,
|
|
1049
1189
|
onDragEnter: handleDrag,
|
|
1050
1190
|
onDragLeave: handleDrag,
|
|
1051
1191
|
onDragOver: handleDrag,
|
|
@@ -1057,10 +1197,9 @@ function FileInput({
|
|
|
1057
1197
|
"aria-label": placeholder,
|
|
1058
1198
|
"aria-disabled": disabled
|
|
1059
1199
|
},
|
|
1060
|
-
/* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex flex-col items-center gap-2 text-center" }, /* @__PURE__ */ React8.createElement(
|
|
1061
1201
|
"svg",
|
|
1062
1202
|
{
|
|
1063
|
-
className: "text-muted-foreground",
|
|
1064
1203
|
width: "48",
|
|
1065
1204
|
height: "48",
|
|
1066
1205
|
viewBox: "0 0 24 24",
|
|
@@ -1071,112 +1210,122 @@ function FileInput({
|
|
|
1071
1210
|
strokeLinejoin: "round",
|
|
1072
1211
|
"aria-hidden": "true"
|
|
1073
1212
|
},
|
|
1074
|
-
/* @__PURE__ */
|
|
1075
|
-
/* @__PURE__ */
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
), /* @__PURE__ */
|
|
1078
|
-
), value.length > 0 && /* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
1214
|
+
/* @__PURE__ */ React8.createElement("polyline", { points: "17 8 12 3 7 8" }),
|
|
1215
|
+
/* @__PURE__ */ React8.createElement("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
1216
|
+
), /* @__PURE__ */ React8.createElement("p", { className: "text-sm font-medium" }, value.length > 0 ? `${value.length} file(s) selected` : placeholder), accept && /* @__PURE__ */ React8.createElement("p", { className: "text-xs" }, "Accepted: ", accept), maxSize && /* @__PURE__ */ React8.createElement("p", { className: "text-xs " }, "Max size: ", formatFileSize(maxSize)))
|
|
1217
|
+
), value.length > 0 && /* @__PURE__ */ React8.createElement("ul", { className: "flex flex-col gap-2 mt-4", role: "list" }, value.map((file, index) => {
|
|
1079
1218
|
const previewUrl = showPreview ? getPreviewUrl(file) : null;
|
|
1080
|
-
return /* @__PURE__ */
|
|
1081
|
-
"
|
|
1082
|
-
{
|
|
1083
|
-
src: previewUrl,
|
|
1084
|
-
alt: file.name,
|
|
1085
|
-
className: "w-12 h-12 rounded object-cover",
|
|
1086
|
-
width: "48",
|
|
1087
|
-
height: "48"
|
|
1088
|
-
}
|
|
1089
|
-
), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col flex-1 min-w-0" }, /* @__PURE__ */ React7.createElement("span", { className: "text-sm font-medium truncate" }, file.name), /* @__PURE__ */ React7.createElement("span", { className: "text-xs text-muted-foreground" }, formatFileSize(file.size)), showProgress && uploadProgress[file.name] !== void 0 && /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 mt-1" }, /* @__PURE__ */ React7.createElement(
|
|
1090
|
-
"div",
|
|
1219
|
+
return /* @__PURE__ */ React8.createElement(
|
|
1220
|
+
"li",
|
|
1091
1221
|
{
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
"aria-valuenow": uploadProgress[file.name],
|
|
1095
|
-
"aria-valuemin": 0,
|
|
1096
|
-
"aria-valuemax": 100,
|
|
1097
|
-
"aria-label": `Upload progress: ${uploadProgress[file.name]}%`
|
|
1222
|
+
key: `${file.name}-${index}`,
|
|
1223
|
+
className: "flex items-center gap-3 p-3 rounded-md border border-border bg-card text-card-foreground hover:bg-primary/50 transition-colors"
|
|
1098
1224
|
},
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
"
|
|
1225
|
+
previewUrl && /* @__PURE__ */ React8.createElement(
|
|
1226
|
+
"img",
|
|
1101
1227
|
{
|
|
1102
|
-
|
|
1103
|
-
|
|
1228
|
+
src: previewUrl,
|
|
1229
|
+
alt: file.name,
|
|
1230
|
+
className: "w-12 h-12 rounded object-cover",
|
|
1231
|
+
width: "48",
|
|
1232
|
+
height: "48"
|
|
1104
1233
|
}
|
|
1105
|
-
)
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
{
|
|
1109
|
-
type: "button",
|
|
1110
|
-
onClick: (e) => {
|
|
1111
|
-
e.stopPropagation();
|
|
1112
|
-
handleCrop(file);
|
|
1113
|
-
},
|
|
1114
|
-
disabled,
|
|
1115
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
|
|
1116
|
-
"aria-label": `Crop ${file.name}`
|
|
1117
|
-
},
|
|
1118
|
-
/* @__PURE__ */ React7.createElement(
|
|
1119
|
-
"svg",
|
|
1234
|
+
),
|
|
1235
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex flex-col flex-1 min-w-0" }, /* @__PURE__ */ React8.createElement("span", { className: "text-sm font-medium truncate" }, file.name), /* @__PURE__ */ React8.createElement("span", { className: "text-xs" }, formatFileSize(file.size)), showProgress && uploadProgress[file.name] !== void 0 && /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mt-1" }, /* @__PURE__ */ React8.createElement(
|
|
1236
|
+
"div",
|
|
1120
1237
|
{
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
strokeLinecap: "round",
|
|
1128
|
-
strokeLinejoin: "round",
|
|
1129
|
-
"aria-hidden": "true"
|
|
1238
|
+
className: "h-1.5 bg-muted rounded-full overflow-hidden flex-1",
|
|
1239
|
+
role: "progressbar",
|
|
1240
|
+
"aria-valuenow": uploadProgress[file.name],
|
|
1241
|
+
"aria-valuemin": 0,
|
|
1242
|
+
"aria-valuemax": 100,
|
|
1243
|
+
"aria-label": `Upload progress: ${uploadProgress[file.name]}%`
|
|
1130
1244
|
},
|
|
1131
|
-
/* @__PURE__ */
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1245
|
+
/* @__PURE__ */ React8.createElement(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
className: "h-full bg-primary transition-all",
|
|
1249
|
+
style: { width: `${uploadProgress[file.name]}%` }
|
|
1250
|
+
}
|
|
1251
|
+
)
|
|
1252
|
+
), /* @__PURE__ */ React8.createElement("span", { className: "text-xs " }, uploadProgress[file.name], "%"))),
|
|
1253
|
+
enableCropping && file.type.startsWith("image/") && /* @__PURE__ */ React8.createElement(
|
|
1254
|
+
"button",
|
|
1255
|
+
{
|
|
1256
|
+
type: "button",
|
|
1257
|
+
onClick: (e) => {
|
|
1258
|
+
e.stopPropagation();
|
|
1259
|
+
handleCrop(file);
|
|
1260
|
+
},
|
|
1261
|
+
disabled,
|
|
1262
|
+
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-primary hover:text-primary-foreground transition-colors",
|
|
1263
|
+
"aria-label": `Crop ${file.name}`
|
|
1141
1264
|
},
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1265
|
+
/* @__PURE__ */ React8.createElement(
|
|
1266
|
+
"svg",
|
|
1267
|
+
{
|
|
1268
|
+
width: "20",
|
|
1269
|
+
height: "20",
|
|
1270
|
+
viewBox: "0 0 24 24",
|
|
1271
|
+
fill: "none",
|
|
1272
|
+
stroke: "currentColor",
|
|
1273
|
+
strokeWidth: "2",
|
|
1274
|
+
strokeLinecap: "round",
|
|
1275
|
+
strokeLinejoin: "round",
|
|
1276
|
+
"aria-hidden": "true"
|
|
1277
|
+
},
|
|
1278
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M6.13 1L6 16a2 2 0 0 0 2 2h15" }),
|
|
1279
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M1 6.13L16 6a2 2 0 0 1 2 2v15" })
|
|
1280
|
+
)
|
|
1281
|
+
),
|
|
1282
|
+
/* @__PURE__ */ React8.createElement(
|
|
1283
|
+
"button",
|
|
1148
1284
|
{
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
"aria-hidden": "true"
|
|
1285
|
+
type: "button",
|
|
1286
|
+
onClick: (e) => {
|
|
1287
|
+
e.stopPropagation();
|
|
1288
|
+
handleRemove(index);
|
|
1289
|
+
},
|
|
1290
|
+
disabled,
|
|
1291
|
+
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-primary hover:text-primary-foreground transition-colors",
|
|
1292
|
+
"aria-label": `Remove ${file.name}`
|
|
1158
1293
|
},
|
|
1159
|
-
/* @__PURE__ */
|
|
1160
|
-
|
|
1294
|
+
/* @__PURE__ */ React8.createElement(
|
|
1295
|
+
"svg",
|
|
1296
|
+
{
|
|
1297
|
+
width: "20",
|
|
1298
|
+
height: "20",
|
|
1299
|
+
viewBox: "0 0 24 24",
|
|
1300
|
+
fill: "none",
|
|
1301
|
+
stroke: "currentColor",
|
|
1302
|
+
strokeWidth: "2",
|
|
1303
|
+
strokeLinecap: "round",
|
|
1304
|
+
strokeLinejoin: "round",
|
|
1305
|
+
"aria-hidden": "true"
|
|
1306
|
+
},
|
|
1307
|
+
/* @__PURE__ */ React8.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1308
|
+
/* @__PURE__ */ React8.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1309
|
+
)
|
|
1161
1310
|
)
|
|
1162
|
-
)
|
|
1163
|
-
})), cropperOpen && imageToCrop && /* @__PURE__ */
|
|
1311
|
+
);
|
|
1312
|
+
})), cropperOpen && imageToCrop && /* @__PURE__ */ React8.createElement("div", { className: "fixed inset-0 z-50 flex items-center justify-center" }, /* @__PURE__ */ React8.createElement(
|
|
1164
1313
|
"div",
|
|
1165
1314
|
{
|
|
1166
1315
|
className: "absolute inset-0 bg-black/50",
|
|
1167
1316
|
onClick: handleCropCancel,
|
|
1168
1317
|
"aria-label": "Close cropper"
|
|
1169
1318
|
}
|
|
1170
|
-
), /* @__PURE__ */
|
|
1319
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "relative bg-popover border border-border rounded-lg shadow-lg max-w-3xl w-full mx-4" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between p-4 border-b border-border" }, /* @__PURE__ */ React8.createElement("h3", { className: "text-lg font-semibold" }, "Crop Image"), /* @__PURE__ */ React8.createElement(
|
|
1171
1320
|
"button",
|
|
1172
1321
|
{
|
|
1173
1322
|
type: "button",
|
|
1174
|
-
className: "flex items-center justify-center h-8 w-8 rounded hover:bg-
|
|
1323
|
+
className: "flex items-center justify-center h-8 w-8 rounded hover:bg-primary hover:text-primary-foreground transition-colors",
|
|
1175
1324
|
onClick: handleCropCancel,
|
|
1176
1325
|
"aria-label": "Close"
|
|
1177
1326
|
},
|
|
1178
1327
|
"\u2715"
|
|
1179
|
-
)), /* @__PURE__ */
|
|
1328
|
+
)), /* @__PURE__ */ React8.createElement("div", { className: "p-4" }, /* @__PURE__ */ React8.createElement(
|
|
1180
1329
|
"div",
|
|
1181
1330
|
{
|
|
1182
1331
|
className: "relative w-full h-96 bg-muted rounded-md overflow-hidden",
|
|
@@ -1198,7 +1347,7 @@ function FileInput({
|
|
|
1198
1347
|
document.addEventListener("mouseup", handleMouseUp);
|
|
1199
1348
|
}
|
|
1200
1349
|
},
|
|
1201
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ React8.createElement(
|
|
1202
1351
|
"img",
|
|
1203
1352
|
{
|
|
1204
1353
|
src: imageToCrop.url,
|
|
@@ -1212,7 +1361,10 @@ function FileInput({
|
|
|
1212
1361
|
const img = e.currentTarget;
|
|
1213
1362
|
const containerWidth = 600;
|
|
1214
1363
|
const containerHeight = 400;
|
|
1215
|
-
const cropWidth = cropAspectRatio ? Math.min(
|
|
1364
|
+
const cropWidth = cropAspectRatio ? Math.min(
|
|
1365
|
+
containerWidth * 0.8,
|
|
1366
|
+
containerHeight * 0.8 * cropAspectRatio
|
|
1367
|
+
) : containerWidth * 0.8;
|
|
1216
1368
|
const cropHeight = cropAspectRatio ? cropWidth / cropAspectRatio : containerHeight * 0.8;
|
|
1217
1369
|
const scale = zoom;
|
|
1218
1370
|
const imgWidth = img.naturalWidth;
|
|
@@ -1230,7 +1382,7 @@ function FileInput({
|
|
|
1230
1382
|
}
|
|
1231
1383
|
}
|
|
1232
1384
|
),
|
|
1233
|
-
/* @__PURE__ */
|
|
1385
|
+
/* @__PURE__ */ React8.createElement(
|
|
1234
1386
|
"div",
|
|
1235
1387
|
{
|
|
1236
1388
|
className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 border-2 border-primary rounded pointer-events-none",
|
|
@@ -1239,9 +1391,18 @@ function FileInput({
|
|
|
1239
1391
|
aspectRatio: cropAspectRatio ? String(cropAspectRatio) : void 0
|
|
1240
1392
|
}
|
|
1241
1393
|
},
|
|
1242
|
-
/* @__PURE__ */
|
|
1394
|
+
/* @__PURE__ */ React8.createElement("div", { className: "absolute inset-0 grid grid-cols-3 grid-rows-3" }, /* @__PURE__ */ React8.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React8.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React8.createElement("div", null))
|
|
1243
1395
|
)
|
|
1244
|
-
), /* @__PURE__ */
|
|
1396
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-3 mt-4" }, /* @__PURE__ */ React8.createElement(
|
|
1397
|
+
"label",
|
|
1398
|
+
{
|
|
1399
|
+
htmlFor: "zoom-slider",
|
|
1400
|
+
className: "text-sm font-medium whitespace-nowrap"
|
|
1401
|
+
},
|
|
1402
|
+
"Zoom: ",
|
|
1403
|
+
zoom.toFixed(1),
|
|
1404
|
+
"x"
|
|
1405
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1245
1406
|
"input",
|
|
1246
1407
|
{
|
|
1247
1408
|
id: "zoom-slider",
|
|
@@ -1254,15 +1415,15 @@ function FileInput({
|
|
|
1254
1415
|
className: "flex-1 h-2 bg-muted rounded-lg appearance-none cursor-pointer",
|
|
1255
1416
|
"aria-label": "Zoom level"
|
|
1256
1417
|
}
|
|
1257
|
-
))), /* @__PURE__ */
|
|
1418
|
+
))), /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-end gap-2 p-4 border-t border-border" }, /* @__PURE__ */ React8.createElement(
|
|
1258
1419
|
"button",
|
|
1259
1420
|
{
|
|
1260
1421
|
type: "button",
|
|
1261
|
-
className: "inline-flex items-center justify-center h-9 rounded-md px-4 text-sm font-medium border border-input bg-transparent hover:bg-
|
|
1422
|
+
className: "inline-flex items-center justify-center h-9 rounded-md px-4 text-sm font-medium border border-input bg-transparent hover:bg-primary hover:text-primary-foreground transition-colors",
|
|
1262
1423
|
onClick: handleCropCancel
|
|
1263
1424
|
},
|
|
1264
1425
|
"Cancel"
|
|
1265
|
-
), /* @__PURE__ */
|
|
1426
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1266
1427
|
"button",
|
|
1267
1428
|
{
|
|
1268
1429
|
type: "button",
|
|
@@ -1281,27 +1442,6 @@ function formatDate(date, format) {
|
|
|
1281
1442
|
const year = d.getFullYear();
|
|
1282
1443
|
return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
|
|
1283
1444
|
}
|
|
1284
|
-
function parseDate(dateString, format) {
|
|
1285
|
-
if (!dateString) return null;
|
|
1286
|
-
try {
|
|
1287
|
-
if (format === "MM/dd/yyyy" || format === "MM-dd-yyyy") {
|
|
1288
|
-
const parts = dateString.split(/[/-]/);
|
|
1289
|
-
if (parts.length === 3) {
|
|
1290
|
-
const month = parseInt(parts[0], 10) - 1;
|
|
1291
|
-
const day = parseInt(parts[1], 10);
|
|
1292
|
-
const year = parseInt(parts[2], 10);
|
|
1293
|
-
const date2 = new Date(year, month, day);
|
|
1294
|
-
if (!isNaN(date2.getTime())) {
|
|
1295
|
-
return date2;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
const date = new Date(dateString);
|
|
1300
|
-
return isNaN(date.getTime()) ? null : date;
|
|
1301
|
-
} catch {
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
1445
|
function isDateInArray(date, dates) {
|
|
1306
1446
|
const dateStr = date.toDateString();
|
|
1307
1447
|
return dates.some((d) => d.toDateString() === dateStr);
|
|
@@ -1325,43 +1465,30 @@ function DatePicker({
|
|
|
1325
1465
|
showIcon = true,
|
|
1326
1466
|
...props
|
|
1327
1467
|
}) {
|
|
1328
|
-
const [isOpen, setIsOpen] =
|
|
1329
|
-
const [
|
|
1330
|
-
const [selectedMonth, setSelectedMonth] = React7.useState(
|
|
1468
|
+
const [isOpen, setIsOpen] = React8.useState(false);
|
|
1469
|
+
const [selectedMonth, setSelectedMonth] = React8.useState(
|
|
1331
1470
|
value || /* @__PURE__ */ new Date()
|
|
1332
1471
|
);
|
|
1333
|
-
const containerRef =
|
|
1334
|
-
const inputRef =
|
|
1335
|
-
|
|
1336
|
-
setInputValue(formatDate(value, format));
|
|
1472
|
+
const containerRef = React8.useRef(null);
|
|
1473
|
+
const inputRef = React8.useRef(null);
|
|
1474
|
+
React8.useEffect(() => {
|
|
1337
1475
|
if (value) {
|
|
1338
1476
|
setSelectedMonth(value);
|
|
1339
1477
|
}
|
|
1340
|
-
}, [value
|
|
1478
|
+
}, [value]);
|
|
1341
1479
|
const handleDateSelect = (date) => {
|
|
1342
1480
|
onChange(date);
|
|
1343
1481
|
setIsOpen(false);
|
|
1344
1482
|
onBlur?.();
|
|
1345
1483
|
};
|
|
1346
|
-
const handleInputChange = (e) => {
|
|
1347
|
-
const newValue = e.target.value;
|
|
1348
|
-
setInputValue(newValue);
|
|
1349
|
-
const parsedDate = parseDate(newValue, format);
|
|
1350
|
-
if (parsedDate && !isNaN(parsedDate.getTime())) {
|
|
1351
|
-
onChange(parsedDate);
|
|
1352
|
-
} else if (newValue === "") {
|
|
1353
|
-
onChange(null);
|
|
1354
|
-
}
|
|
1355
|
-
};
|
|
1356
1484
|
const handleClear = (e) => {
|
|
1357
1485
|
e.stopPropagation();
|
|
1358
1486
|
onChange(null);
|
|
1359
|
-
setInputValue("");
|
|
1360
1487
|
inputRef.current?.focus();
|
|
1361
1488
|
};
|
|
1362
1489
|
const handleToggle = () => {
|
|
1363
1490
|
if (disabled) return;
|
|
1364
|
-
setIsOpen(!
|
|
1491
|
+
setIsOpen((prev) => !prev);
|
|
1365
1492
|
};
|
|
1366
1493
|
const isDisabled = (date) => {
|
|
1367
1494
|
if (minDate && date < minDate) return true;
|
|
@@ -1370,20 +1497,17 @@ function DatePicker({
|
|
|
1370
1497
|
if (isDateDisabled && isDateDisabled(date)) return true;
|
|
1371
1498
|
return false;
|
|
1372
1499
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
onBlur?.();
|
|
1378
|
-
}
|
|
1379
|
-
};
|
|
1380
|
-
if (isOpen) {
|
|
1381
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1382
|
-
return () => {
|
|
1383
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1500
|
+
const closeCalendar = React8.useCallback(() => {
|
|
1501
|
+
if (!isOpen) return;
|
|
1502
|
+
setIsOpen(false);
|
|
1503
|
+
onBlur?.();
|
|
1386
1504
|
}, [isOpen, onBlur]);
|
|
1505
|
+
useOnClickOutside(containerRef, closeCalendar, "pointerdown", true);
|
|
1506
|
+
const dayGridStyle = {
|
|
1507
|
+
gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
|
|
1508
|
+
};
|
|
1509
|
+
const hasValue = Boolean(value);
|
|
1510
|
+
const displayValue = formatDate(value, format);
|
|
1387
1511
|
const renderCalendar = () => {
|
|
1388
1512
|
const year = selectedMonth.getFullYear();
|
|
1389
1513
|
const month = selectedMonth.getMonth();
|
|
@@ -1416,44 +1540,57 @@ function DatePicker({
|
|
|
1416
1540
|
const handleNextMonth = () => {
|
|
1417
1541
|
setSelectedMonth(new Date(year, month + 1, 1));
|
|
1418
1542
|
};
|
|
1419
|
-
return /* @__PURE__ */
|
|
1543
|
+
return /* @__PURE__ */ React8.createElement("div", { role: "grid", "aria-label": "Calendar", className: "w-[248px] max-w-full" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between pb-3" }, /* @__PURE__ */ React8.createElement(
|
|
1420
1544
|
"button",
|
|
1421
1545
|
{
|
|
1422
1546
|
type: "button",
|
|
1423
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-
|
|
1547
|
+
className: "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent hover:bg-muted cursor-pointer transition-colors",
|
|
1424
1548
|
onClick: handlePrevMonth,
|
|
1425
1549
|
"aria-label": "Previous month"
|
|
1426
1550
|
},
|
|
1427
|
-
"\
|
|
1428
|
-
), /* @__PURE__ */
|
|
1551
|
+
"\u2039"
|
|
1552
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "font-medium text-sm" }, `${monthNames[month]} ${year}`), /* @__PURE__ */ React8.createElement(
|
|
1429
1553
|
"button",
|
|
1430
1554
|
{
|
|
1431
1555
|
type: "button",
|
|
1432
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-
|
|
1556
|
+
className: "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent hover:bg-muted cursor-pointer transition-colors",
|
|
1433
1557
|
onClick: handleNextMonth,
|
|
1434
1558
|
"aria-label": "Next month"
|
|
1435
1559
|
},
|
|
1436
|
-
"\
|
|
1437
|
-
)), /* @__PURE__ */
|
|
1560
|
+
"\u203A"
|
|
1561
|
+
)), /* @__PURE__ */ React8.createElement(
|
|
1438
1562
|
"div",
|
|
1439
1563
|
{
|
|
1440
|
-
|
|
1441
|
-
|
|
1564
|
+
className: "grid gap-1 text-xs text-muted-foreground",
|
|
1565
|
+
style: dayGridStyle
|
|
1442
1566
|
},
|
|
1443
|
-
day
|
|
1444
|
-
|
|
1567
|
+
["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React8.createElement(
|
|
1568
|
+
"div",
|
|
1569
|
+
{
|
|
1570
|
+
key: day,
|
|
1571
|
+
className: "flex items-center justify-center h-8 w-8 font-medium"
|
|
1572
|
+
},
|
|
1573
|
+
day
|
|
1574
|
+
))
|
|
1575
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "grid gap-1", style: dayGridStyle }, days.map((date, index) => {
|
|
1445
1576
|
if (!date) {
|
|
1446
|
-
return /* @__PURE__ */
|
|
1577
|
+
return /* @__PURE__ */ React8.createElement("div", { key: `empty-${index}`, className: "h-8 w-8" });
|
|
1447
1578
|
}
|
|
1448
1579
|
const isSelected = value && date.toDateString() === value.toDateString();
|
|
1449
1580
|
const isToday = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
1450
1581
|
const disabled2 = isDisabled(date);
|
|
1451
|
-
return /* @__PURE__ */
|
|
1582
|
+
return /* @__PURE__ */ React8.createElement(
|
|
1452
1583
|
"button",
|
|
1453
1584
|
{
|
|
1454
1585
|
key: date.toISOString(),
|
|
1455
1586
|
type: "button",
|
|
1456
|
-
className:
|
|
1587
|
+
className: cn(
|
|
1588
|
+
"flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
|
|
1589
|
+
"hover:bg-muted",
|
|
1590
|
+
isSelected && "bg-primary text-primary-foreground font-semibold",
|
|
1591
|
+
!isSelected && isToday && "border border-primary",
|
|
1592
|
+
disabled2 && "cursor-not-allowed opacity-50 pointer-events-none"
|
|
1593
|
+
),
|
|
1457
1594
|
onClick: () => !disabled2 && handleDateSelect(date),
|
|
1458
1595
|
disabled: disabled2,
|
|
1459
1596
|
"aria-label": formatDate(date, format)
|
|
@@ -1462,21 +1599,21 @@ function DatePicker({
|
|
|
1462
1599
|
);
|
|
1463
1600
|
})));
|
|
1464
1601
|
};
|
|
1465
|
-
const combinedClassName =
|
|
1466
|
-
return /* @__PURE__ */
|
|
1602
|
+
const combinedClassName = cn("relative", className);
|
|
1603
|
+
return /* @__PURE__ */ React8.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React8.createElement(
|
|
1467
1604
|
"input",
|
|
1468
1605
|
{
|
|
1469
1606
|
type: "hidden",
|
|
1470
1607
|
name,
|
|
1471
1608
|
value: value ? value.toISOString() : ""
|
|
1472
1609
|
}
|
|
1473
|
-
), /* @__PURE__ */
|
|
1610
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React8.createElement(
|
|
1474
1611
|
"span",
|
|
1475
1612
|
{
|
|
1476
1613
|
className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
|
|
1477
1614
|
"aria-hidden": "true"
|
|
1478
1615
|
},
|
|
1479
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ React8.createElement(
|
|
1480
1617
|
"svg",
|
|
1481
1618
|
{
|
|
1482
1619
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1489,16 +1626,24 @@ function DatePicker({
|
|
|
1489
1626
|
strokeLinejoin: "round",
|
|
1490
1627
|
strokeWidth: "2"
|
|
1491
1628
|
},
|
|
1492
|
-
/* @__PURE__ */
|
|
1629
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
|
|
1493
1630
|
)
|
|
1494
|
-
), /* @__PURE__ */
|
|
1631
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1495
1632
|
"input",
|
|
1496
1633
|
{
|
|
1497
1634
|
ref: inputRef,
|
|
1498
1635
|
type: "text",
|
|
1499
|
-
className:
|
|
1500
|
-
|
|
1501
|
-
|
|
1636
|
+
className: cn(
|
|
1637
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
|
|
1638
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1639
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1640
|
+
INPUT_AUTOFILL_RESET_CLASSES,
|
|
1641
|
+
showIcon ? "pl-10" : "pl-3",
|
|
1642
|
+
clearable && value ? "pr-10" : "pr-3",
|
|
1643
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
1644
|
+
error && "border-destructive ring-1 ring-destructive"
|
|
1645
|
+
),
|
|
1646
|
+
value: displayValue,
|
|
1502
1647
|
onClick: handleToggle,
|
|
1503
1648
|
onBlur,
|
|
1504
1649
|
disabled,
|
|
@@ -1509,7 +1654,7 @@ function DatePicker({
|
|
|
1509
1654
|
"aria-required": required || props["aria-required"],
|
|
1510
1655
|
readOnly: true
|
|
1511
1656
|
}
|
|
1512
|
-
), clearable && value && !disabled && /* @__PURE__ */
|
|
1657
|
+
), clearable && value && !disabled && /* @__PURE__ */ React8.createElement(
|
|
1513
1658
|
"button",
|
|
1514
1659
|
{
|
|
1515
1660
|
type: "button",
|
|
@@ -1519,49 +1664,49 @@ function DatePicker({
|
|
|
1519
1664
|
tabIndex: -1
|
|
1520
1665
|
},
|
|
1521
1666
|
"\u2715"
|
|
1522
|
-
)), isOpen && !disabled && /* @__PURE__ */
|
|
1667
|
+
)), isOpen && !disabled && /* @__PURE__ */ React8.createElement("div", { className: "absolute z-50 top-full mt-1 w-fit rounded-md border border-border bg-popover text-popover-foreground shadow-md p-3" }, renderCalendar()));
|
|
1523
1668
|
}
|
|
1524
1669
|
DatePicker.displayName = "DatePicker";
|
|
1525
|
-
function
|
|
1526
|
-
if (!
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
const hour = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
|
|
1537
|
-
return { hour, minute, period };
|
|
1538
|
-
} else {
|
|
1539
|
-
const match = timeStr.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
|
|
1540
|
-
if (!match) return null;
|
|
1541
|
-
const hour = parseInt(match[1], 10);
|
|
1542
|
-
const minute = parseInt(match[2], 10);
|
|
1543
|
-
const period = match[3].toUpperCase();
|
|
1544
|
-
if (hour < 1 || hour > 12) return null;
|
|
1545
|
-
if (minute < 0 || minute > 59) return null;
|
|
1546
|
-
return { hour, minute, period };
|
|
1670
|
+
function normalizeToNativeTime(value) {
|
|
1671
|
+
if (!value) return "";
|
|
1672
|
+
const twelveHourMatch = value.match(
|
|
1673
|
+
/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(AM|PM)$/i
|
|
1674
|
+
);
|
|
1675
|
+
if (twelveHourMatch) {
|
|
1676
|
+
const rawHour = parseInt(twelveHourMatch[1], 10);
|
|
1677
|
+
const minute = parseInt(twelveHourMatch[2], 10);
|
|
1678
|
+
const period = twelveHourMatch[4].toUpperCase();
|
|
1679
|
+
if (Number.isNaN(rawHour) || Number.isNaN(minute) || rawHour < 1 || rawHour > 12 || minute < 0 || minute > 59) {
|
|
1680
|
+
return "";
|
|
1547
1681
|
}
|
|
1548
|
-
|
|
1549
|
-
return
|
|
1682
|
+
const normalizedHour = period === "PM" ? rawHour === 12 ? 12 : rawHour + 12 : rawHour === 12 ? 0 : rawHour;
|
|
1683
|
+
return `${String(normalizedHour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
1684
|
+
}
|
|
1685
|
+
const twentyFourHourMatch = value.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
|
|
1686
|
+
if (twentyFourHourMatch) {
|
|
1687
|
+
const hour = parseInt(twentyFourHourMatch[1], 10);
|
|
1688
|
+
const minute = parseInt(twentyFourHourMatch[2], 10);
|
|
1689
|
+
if (Number.isNaN(hour) || Number.isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
|
|
1690
|
+
return "";
|
|
1691
|
+
}
|
|
1692
|
+
return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
1550
1693
|
}
|
|
1694
|
+
return "";
|
|
1551
1695
|
}
|
|
1552
|
-
function
|
|
1553
|
-
if (!
|
|
1696
|
+
function formatFromNativeTime(nativeValue, use24Hour) {
|
|
1697
|
+
if (!nativeValue) return "";
|
|
1698
|
+
const [hourValue, minuteValue] = nativeValue.split(":");
|
|
1699
|
+
const hour = parseInt(hourValue, 10);
|
|
1700
|
+
const minute = parseInt(minuteValue, 10);
|
|
1701
|
+
if (Number.isNaN(hour) || Number.isNaN(minute)) {
|
|
1702
|
+
return "";
|
|
1703
|
+
}
|
|
1554
1704
|
if (use24Hour) {
|
|
1555
|
-
|
|
1556
|
-
if (time.period === "PM" && time.hour !== 12) {
|
|
1557
|
-
hour24 = time.hour + 12;
|
|
1558
|
-
} else if (time.period === "AM" && time.hour === 12) {
|
|
1559
|
-
hour24 = 0;
|
|
1560
|
-
}
|
|
1561
|
-
return `${String(hour24).padStart(2, "0")}:${String(time.minute).padStart(2, "0")}`;
|
|
1562
|
-
} else {
|
|
1563
|
-
return `${time.hour}:${String(time.minute).padStart(2, "0")} ${time.period}`;
|
|
1705
|
+
return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
1564
1706
|
}
|
|
1707
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
1708
|
+
const hour12 = hour % 12 || 12;
|
|
1709
|
+
return `${hour12}:${String(minute).padStart(2, "0")} ${period}`;
|
|
1565
1710
|
}
|
|
1566
1711
|
function TimePicker({
|
|
1567
1712
|
name,
|
|
@@ -1579,188 +1724,87 @@ function TimePicker({
|
|
|
1579
1724
|
showIcon = true,
|
|
1580
1725
|
...props
|
|
1581
1726
|
}) {
|
|
1582
|
-
const
|
|
1583
|
-
const [
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
minute: timeValue?.minute || 0,
|
|
1594
|
-
period: timeValue?.period || "AM"
|
|
1595
|
-
};
|
|
1596
|
-
setTimeValue(newTime);
|
|
1597
|
-
onChange(formatTimeValue(newTime, use24Hour));
|
|
1598
|
-
};
|
|
1599
|
-
const handleMinuteChange = (minute) => {
|
|
1600
|
-
const newTime = {
|
|
1601
|
-
hour: timeValue?.hour || 12,
|
|
1602
|
-
minute,
|
|
1603
|
-
period: timeValue?.period || "AM"
|
|
1604
|
-
};
|
|
1605
|
-
setTimeValue(newTime);
|
|
1606
|
-
onChange(formatTimeValue(newTime, use24Hour));
|
|
1607
|
-
};
|
|
1608
|
-
const handlePeriodChange = (period) => {
|
|
1609
|
-
const newTime = {
|
|
1610
|
-
hour: timeValue?.hour || 12,
|
|
1611
|
-
minute: timeValue?.minute || 0,
|
|
1612
|
-
period
|
|
1613
|
-
};
|
|
1614
|
-
setTimeValue(newTime);
|
|
1615
|
-
onChange(formatTimeValue(newTime, use24Hour));
|
|
1727
|
+
const inputRef = React8.useRef(null);
|
|
1728
|
+
const [nativeValue, setNativeValue] = React8.useState(
|
|
1729
|
+
normalizeToNativeTime(value)
|
|
1730
|
+
);
|
|
1731
|
+
React8.useEffect(() => {
|
|
1732
|
+
setNativeValue(normalizeToNativeTime(value));
|
|
1733
|
+
}, [value]);
|
|
1734
|
+
const handleChange = (e) => {
|
|
1735
|
+
const nextNativeValue = e.target.value;
|
|
1736
|
+
setNativeValue(nextNativeValue);
|
|
1737
|
+
onChange(formatFromNativeTime(nextNativeValue, use24Hour));
|
|
1616
1738
|
};
|
|
1617
1739
|
const handleClear = (e) => {
|
|
1618
1740
|
e.stopPropagation();
|
|
1741
|
+
setNativeValue("");
|
|
1619
1742
|
onChange("");
|
|
1620
|
-
setTimeValue(null);
|
|
1621
1743
|
inputRef.current?.focus();
|
|
1622
1744
|
};
|
|
1623
|
-
const
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
React7.useEffect(() => {
|
|
1628
|
-
const handleClickOutside = (event) => {
|
|
1629
|
-
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
1630
|
-
setIsOpen(false);
|
|
1631
|
-
onBlur?.();
|
|
1632
|
-
}
|
|
1633
|
-
};
|
|
1634
|
-
if (isOpen) {
|
|
1635
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1636
|
-
return () => {
|
|
1637
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
}, [isOpen, onBlur]);
|
|
1641
|
-
const hours = React7.useMemo(() => {
|
|
1642
|
-
if (use24Hour) {
|
|
1643
|
-
return Array.from({ length: 24 }, (_, i) => i);
|
|
1644
|
-
} else {
|
|
1645
|
-
return Array.from({ length: 12 }, (_, i) => i + 1);
|
|
1646
|
-
}
|
|
1647
|
-
}, [use24Hour]);
|
|
1648
|
-
const minutes = React7.useMemo(() => {
|
|
1649
|
-
const mins = [];
|
|
1650
|
-
for (let i = 0; i < 60; i += minuteStep) {
|
|
1651
|
-
mins.push(i);
|
|
1652
|
-
}
|
|
1653
|
-
return mins;
|
|
1654
|
-
}, [minuteStep]);
|
|
1655
|
-
const combinedClassName = `relative ${className}`.trim();
|
|
1656
|
-
const displayValue = formatTimeValue(timeValue, use24Hour);
|
|
1657
|
-
return /* @__PURE__ */ React7.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React7.createElement(
|
|
1658
|
-
"input",
|
|
1659
|
-
{
|
|
1660
|
-
type: "hidden",
|
|
1661
|
-
name,
|
|
1662
|
-
value
|
|
1663
|
-
}
|
|
1664
|
-
), /* @__PURE__ */ React7.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React7.createElement("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none", "aria-hidden": "true" }, /* @__PURE__ */ React7.createElement(
|
|
1665
|
-
"svg",
|
|
1745
|
+
const hasValue = Boolean(value);
|
|
1746
|
+
const stepInSeconds = Math.max(1, minuteStep * 60);
|
|
1747
|
+
return /* @__PURE__ */ React8.createElement("div", { className: cn("relative", className) }, /* @__PURE__ */ React8.createElement("input", { type: "hidden", name, value }), /* @__PURE__ */ React8.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React8.createElement(
|
|
1748
|
+
"span",
|
|
1666
1749
|
{
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
height: "18",
|
|
1670
|
-
viewBox: "0 0 24 24",
|
|
1671
|
-
fill: "none",
|
|
1672
|
-
stroke: "currentColor",
|
|
1673
|
-
strokeLinecap: "round",
|
|
1674
|
-
strokeLinejoin: "round",
|
|
1675
|
-
strokeWidth: "2"
|
|
1750
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
|
|
1751
|
+
"aria-hidden": "true"
|
|
1676
1752
|
},
|
|
1677
|
-
/* @__PURE__ */
|
|
1678
|
-
|
|
1679
|
-
|
|
1753
|
+
/* @__PURE__ */ React8.createElement(
|
|
1754
|
+
"svg",
|
|
1755
|
+
{
|
|
1756
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1757
|
+
width: "18",
|
|
1758
|
+
height: "18",
|
|
1759
|
+
viewBox: "0 0 24 24",
|
|
1760
|
+
fill: "none",
|
|
1761
|
+
stroke: "currentColor",
|
|
1762
|
+
strokeLinecap: "round",
|
|
1763
|
+
strokeLinejoin: "round",
|
|
1764
|
+
strokeWidth: "2"
|
|
1765
|
+
},
|
|
1766
|
+
/* @__PURE__ */ React8.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1767
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M12 6v6l4 2" })
|
|
1768
|
+
)
|
|
1769
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1680
1770
|
"input",
|
|
1681
1771
|
{
|
|
1682
1772
|
ref: inputRef,
|
|
1683
|
-
type: "
|
|
1684
|
-
className:
|
|
1685
|
-
|
|
1686
|
-
|
|
1773
|
+
type: "time",
|
|
1774
|
+
className: cn(
|
|
1775
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
|
|
1776
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1777
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1778
|
+
"appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none",
|
|
1779
|
+
INPUT_AUTOFILL_RESET_CLASSES,
|
|
1780
|
+
showIcon ? "pl-10" : "pl-3",
|
|
1781
|
+
clearable && value ? "pr-10" : "pr-3",
|
|
1782
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
1783
|
+
error && "border-destructive ring-1 ring-destructive"
|
|
1784
|
+
),
|
|
1785
|
+
value: nativeValue,
|
|
1786
|
+
onChange: handleChange,
|
|
1687
1787
|
onBlur,
|
|
1688
1788
|
disabled,
|
|
1689
1789
|
required,
|
|
1790
|
+
step: stepInSeconds,
|
|
1690
1791
|
placeholder,
|
|
1691
1792
|
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
1692
1793
|
"aria-describedby": props["aria-describedby"],
|
|
1693
1794
|
"aria-required": required || props["aria-required"],
|
|
1694
|
-
|
|
1795
|
+
...props
|
|
1695
1796
|
}
|
|
1696
|
-
), clearable && value && !disabled && /* @__PURE__ */
|
|
1797
|
+
), clearable && value && !disabled && /* @__PURE__ */ React8.createElement(
|
|
1697
1798
|
"button",
|
|
1698
1799
|
{
|
|
1699
1800
|
type: "button",
|
|
1700
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2
|
|
1801
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
|
|
1701
1802
|
onClick: handleClear,
|
|
1702
1803
|
"aria-label": "Clear time",
|
|
1703
1804
|
tabIndex: -1
|
|
1704
1805
|
},
|
|
1705
1806
|
"\u2715"
|
|
1706
|
-
))
|
|
1707
|
-
const displayHour = use24Hour ? hour : hour;
|
|
1708
|
-
const isSelected = use24Hour ? timeValue?.hour === (hour === 0 ? 12 : hour > 12 ? hour - 12 : hour) && timeValue?.period === (hour >= 12 ? "PM" : "AM") : timeValue?.hour === hour;
|
|
1709
|
-
return /* @__PURE__ */ React7.createElement(
|
|
1710
|
-
"button",
|
|
1711
|
-
{
|
|
1712
|
-
key: hour,
|
|
1713
|
-
type: "button",
|
|
1714
|
-
className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-accent hover:text-accent-foreground ${isSelected ? "bg-primary text-primary-foreground font-semibold" : ""}`,
|
|
1715
|
-
onClick: () => {
|
|
1716
|
-
if (use24Hour) {
|
|
1717
|
-
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
1718
|
-
const period = hour >= 12 ? "PM" : "AM";
|
|
1719
|
-
const newTime = {
|
|
1720
|
-
hour: hour12,
|
|
1721
|
-
minute: timeValue?.minute || 0,
|
|
1722
|
-
period
|
|
1723
|
-
};
|
|
1724
|
-
setTimeValue(newTime);
|
|
1725
|
-
onChange(formatTimeValue(newTime, use24Hour));
|
|
1726
|
-
} else {
|
|
1727
|
-
handleHourChange(hour);
|
|
1728
|
-
}
|
|
1729
|
-
},
|
|
1730
|
-
"aria-label": `${String(displayHour).padStart(2, "0")} hours`
|
|
1731
|
-
},
|
|
1732
|
-
String(displayHour).padStart(2, "0")
|
|
1733
|
-
);
|
|
1734
|
-
}))), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col flex-1" }, /* @__PURE__ */ React7.createElement("div", { className: "text-xs font-medium text-muted-foreground mb-2 text-center" }, "Minute"), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col gap-1 max-h-48 overflow-y-auto" }, minutes.map((minute) => {
|
|
1735
|
-
const isSelected = timeValue?.minute === minute;
|
|
1736
|
-
return /* @__PURE__ */ React7.createElement(
|
|
1737
|
-
"button",
|
|
1738
|
-
{
|
|
1739
|
-
key: minute,
|
|
1740
|
-
type: "button",
|
|
1741
|
-
className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-accent hover:text-accent-foreground ${isSelected ? "bg-primary text-primary-foreground font-semibold" : ""}`,
|
|
1742
|
-
onClick: () => handleMinuteChange(minute),
|
|
1743
|
-
"aria-label": `${String(minute).padStart(2, "0")} minutes`
|
|
1744
|
-
},
|
|
1745
|
-
String(minute).padStart(2, "0")
|
|
1746
|
-
);
|
|
1747
|
-
}))), !use24Hour && /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col w-20" }, /* @__PURE__ */ React7.createElement("div", { className: "text-xs font-medium text-muted-foreground mb-2 text-center" }, "Period"), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React7.createElement(
|
|
1748
|
-
"button",
|
|
1749
|
-
{
|
|
1750
|
-
type: "button",
|
|
1751
|
-
className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-accent hover:text-accent-foreground ${timeValue?.period === "AM" ? "bg-primary text-primary-foreground font-semibold" : ""}`,
|
|
1752
|
-
onClick: () => handlePeriodChange("AM")
|
|
1753
|
-
},
|
|
1754
|
-
"AM"
|
|
1755
|
-
), /* @__PURE__ */ React7.createElement(
|
|
1756
|
-
"button",
|
|
1757
|
-
{
|
|
1758
|
-
type: "button",
|
|
1759
|
-
className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-accent hover:text-accent-foreground ${timeValue?.period === "PM" ? "bg-primary text-primary-foreground font-semibold" : ""}`,
|
|
1760
|
-
onClick: () => handlePeriodChange("PM")
|
|
1761
|
-
},
|
|
1762
|
-
"PM"
|
|
1763
|
-
))))));
|
|
1807
|
+
)));
|
|
1764
1808
|
}
|
|
1765
1809
|
TimePicker.displayName = "TimePicker";
|
|
1766
1810
|
function formatDate2(date, format) {
|
|
@@ -1780,6 +1824,9 @@ function isDateInRange(date, start, end) {
|
|
|
1780
1824
|
const time = date.getTime();
|
|
1781
1825
|
return time >= start.getTime() && time <= end.getTime();
|
|
1782
1826
|
}
|
|
1827
|
+
function addMonths(date, delta) {
|
|
1828
|
+
return new Date(date.getFullYear(), date.getMonth() + delta, 1);
|
|
1829
|
+
}
|
|
1783
1830
|
function DateRangePicker({
|
|
1784
1831
|
name,
|
|
1785
1832
|
value = { start: null, end: null },
|
|
@@ -1800,13 +1847,15 @@ function DateRangePicker({
|
|
|
1800
1847
|
separator = " - ",
|
|
1801
1848
|
...props
|
|
1802
1849
|
}) {
|
|
1803
|
-
const [isOpen, setIsOpen] =
|
|
1804
|
-
const [selectedMonth, setSelectedMonth] =
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
const [
|
|
1808
|
-
const
|
|
1809
|
-
|
|
1850
|
+
const [isOpen, setIsOpen] = React8.useState(false);
|
|
1851
|
+
const [selectedMonth, setSelectedMonth] = React8.useState(
|
|
1852
|
+
value.start || /* @__PURE__ */ new Date()
|
|
1853
|
+
);
|
|
1854
|
+
const [rangeStart, setRangeStart] = React8.useState(value.start);
|
|
1855
|
+
const [rangeEnd, setRangeEnd] = React8.useState(value.end);
|
|
1856
|
+
const [hoverDate, setHoverDate] = React8.useState(null);
|
|
1857
|
+
const containerRef = React8.useRef(null);
|
|
1858
|
+
React8.useEffect(() => {
|
|
1810
1859
|
setRangeStart(value.start);
|
|
1811
1860
|
setRangeEnd(value.end);
|
|
1812
1861
|
if (value.start) {
|
|
@@ -1840,7 +1889,7 @@ function DateRangePicker({
|
|
|
1840
1889
|
};
|
|
1841
1890
|
const handleToggle = () => {
|
|
1842
1891
|
if (disabled) return;
|
|
1843
|
-
setIsOpen(!
|
|
1892
|
+
setIsOpen((prev) => !prev);
|
|
1844
1893
|
};
|
|
1845
1894
|
const isDisabled = (date) => {
|
|
1846
1895
|
if (minDate && date < minDate) return true;
|
|
@@ -1849,23 +1898,36 @@ function DateRangePicker({
|
|
|
1849
1898
|
if (isDateDisabled && isDateDisabled(date)) return true;
|
|
1850
1899
|
return false;
|
|
1851
1900
|
};
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
onBlur?.();
|
|
1857
|
-
}
|
|
1858
|
-
};
|
|
1859
|
-
if (isOpen) {
|
|
1860
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1861
|
-
return () => {
|
|
1862
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1901
|
+
const closeCalendar = React8.useCallback(() => {
|
|
1902
|
+
if (!isOpen) return;
|
|
1903
|
+
setIsOpen(false);
|
|
1904
|
+
onBlur?.();
|
|
1865
1905
|
}, [isOpen, onBlur]);
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1906
|
+
useOnClickOutside(containerRef, closeCalendar, "pointerdown", true);
|
|
1907
|
+
const dayGridStyle = {
|
|
1908
|
+
gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
|
|
1909
|
+
};
|
|
1910
|
+
const monthsGridStyle = {
|
|
1911
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))"
|
|
1912
|
+
};
|
|
1913
|
+
const monthNames = [
|
|
1914
|
+
"January",
|
|
1915
|
+
"February",
|
|
1916
|
+
"March",
|
|
1917
|
+
"April",
|
|
1918
|
+
"May",
|
|
1919
|
+
"June",
|
|
1920
|
+
"July",
|
|
1921
|
+
"August",
|
|
1922
|
+
"September",
|
|
1923
|
+
"October",
|
|
1924
|
+
"November",
|
|
1925
|
+
"December"
|
|
1926
|
+
];
|
|
1927
|
+
const hasValue = Boolean(rangeStart || rangeEnd);
|
|
1928
|
+
const renderMonth = (monthDate, controls) => {
|
|
1929
|
+
const year = monthDate.getFullYear();
|
|
1930
|
+
const month = monthDate.getMonth();
|
|
1869
1931
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
1870
1932
|
const firstDayOfMonth = new Date(year, month, 1).getDay();
|
|
1871
1933
|
const days = [];
|
|
@@ -1875,60 +1937,65 @@ function DateRangePicker({
|
|
|
1875
1937
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
1876
1938
|
days.push(new Date(year, month, day));
|
|
1877
1939
|
}
|
|
1878
|
-
|
|
1879
|
-
"January",
|
|
1880
|
-
"February",
|
|
1881
|
-
"March",
|
|
1882
|
-
"April",
|
|
1883
|
-
"May",
|
|
1884
|
-
"June",
|
|
1885
|
-
"July",
|
|
1886
|
-
"August",
|
|
1887
|
-
"September",
|
|
1888
|
-
"October",
|
|
1889
|
-
"November",
|
|
1890
|
-
"December"
|
|
1891
|
-
];
|
|
1892
|
-
const handlePrevMonth = () => {
|
|
1893
|
-
setSelectedMonth(new Date(year, month - 1, 1));
|
|
1894
|
-
};
|
|
1895
|
-
const handleNextMonth = () => {
|
|
1896
|
-
setSelectedMonth(new Date(year, month + 1, 1));
|
|
1897
|
-
};
|
|
1898
|
-
return /* @__PURE__ */ React7.createElement("div", { role: "grid", "aria-label": "Calendar" }, /* @__PURE__ */ React7.createElement("div", { className: "flex items-center justify-between pb-2 border-b border-border" }, /* @__PURE__ */ React7.createElement(
|
|
1940
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "w-[240px] max-w-full" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between pb-3" }, controls?.prev ? /* @__PURE__ */ React8.createElement(
|
|
1899
1941
|
"button",
|
|
1900
1942
|
{
|
|
1901
1943
|
type: "button",
|
|
1902
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-
|
|
1903
|
-
onClick:
|
|
1944
|
+
className: "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent hover:bg-muted cursor-pointer transition-colors",
|
|
1945
|
+
onClick: () => setSelectedMonth((prev) => addMonths(prev, -1)),
|
|
1904
1946
|
"aria-label": "Previous month"
|
|
1905
1947
|
},
|
|
1906
|
-
"\
|
|
1907
|
-
), /* @__PURE__ */
|
|
1948
|
+
"\u2039"
|
|
1949
|
+
) : /* @__PURE__ */ React8.createElement("div", { className: "h-8 w-8", "aria-hidden": "true" }), /* @__PURE__ */ React8.createElement("div", { className: "font-medium text-sm" }, `${monthNames[month]} ${year}`), controls?.next ? /* @__PURE__ */ React8.createElement(
|
|
1908
1950
|
"button",
|
|
1909
1951
|
{
|
|
1910
1952
|
type: "button",
|
|
1911
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-
|
|
1912
|
-
onClick:
|
|
1953
|
+
className: "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent hover:bg-muted cursor-pointer transition-colors",
|
|
1954
|
+
onClick: () => setSelectedMonth((prev) => addMonths(prev, 1)),
|
|
1913
1955
|
"aria-label": "Next month"
|
|
1914
1956
|
},
|
|
1915
|
-
"\
|
|
1916
|
-
)
|
|
1957
|
+
"\u203A"
|
|
1958
|
+
) : /* @__PURE__ */ React8.createElement("div", { className: "h-8 w-8", "aria-hidden": "true" })), /* @__PURE__ */ React8.createElement(
|
|
1959
|
+
"div",
|
|
1960
|
+
{
|
|
1961
|
+
className: "grid gap-1 text-xs text-muted-foreground",
|
|
1962
|
+
style: dayGridStyle
|
|
1963
|
+
},
|
|
1964
|
+
["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React8.createElement(
|
|
1965
|
+
"div",
|
|
1966
|
+
{
|
|
1967
|
+
key: `${month}-${day}`,
|
|
1968
|
+
className: "flex items-center justify-center h-8 w-8 font-medium"
|
|
1969
|
+
},
|
|
1970
|
+
day
|
|
1971
|
+
))
|
|
1972
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "grid gap-1", style: dayGridStyle }, days.map((date, index) => {
|
|
1917
1973
|
if (!date) {
|
|
1918
|
-
return /* @__PURE__ */
|
|
1974
|
+
return /* @__PURE__ */ React8.createElement("div", { key: `empty-${month}-${index}`, className: "h-8 w-8" });
|
|
1919
1975
|
}
|
|
1920
1976
|
const isStart = rangeStart && date.toDateString() === rangeStart.toDateString();
|
|
1921
1977
|
const isEnd = rangeEnd && date.toDateString() === rangeEnd.toDateString();
|
|
1978
|
+
const isRangeEndpoint = Boolean(isStart || isEnd);
|
|
1922
1979
|
const isInRange = rangeStart && rangeEnd && isDateInRange(date, rangeStart, rangeEnd);
|
|
1923
1980
|
const isInHoverRange = rangeStart && !rangeEnd && hoverDate && (date >= rangeStart && date <= hoverDate || date <= rangeStart && date >= hoverDate);
|
|
1981
|
+
const isRangeHighlight = Boolean(
|
|
1982
|
+
(isInRange || isInHoverRange) && !isRangeEndpoint
|
|
1983
|
+
);
|
|
1924
1984
|
const isToday = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
1925
1985
|
const disabled2 = isDisabled(date);
|
|
1926
|
-
return /* @__PURE__ */
|
|
1986
|
+
return /* @__PURE__ */ React8.createElement(
|
|
1927
1987
|
"button",
|
|
1928
1988
|
{
|
|
1929
1989
|
key: date.toISOString(),
|
|
1930
1990
|
type: "button",
|
|
1931
|
-
className:
|
|
1991
|
+
className: cn(
|
|
1992
|
+
"flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
|
|
1993
|
+
"hover:bg-muted",
|
|
1994
|
+
isRangeEndpoint && "bg-primary text-primary-foreground font-semibold",
|
|
1995
|
+
isRangeHighlight && "bg-muted",
|
|
1996
|
+
!isRangeEndpoint && !isRangeHighlight && isToday && "border border-primary",
|
|
1997
|
+
disabled2 && "cursor-not-allowed opacity-50 pointer-events-none"
|
|
1998
|
+
),
|
|
1932
1999
|
onClick: () => !disabled2 && handleDateSelect(date),
|
|
1933
2000
|
onMouseEnter: () => setHoverDate(date),
|
|
1934
2001
|
onMouseLeave: () => setHoverDate(null),
|
|
@@ -1939,41 +2006,57 @@ function DateRangePicker({
|
|
|
1939
2006
|
);
|
|
1940
2007
|
})));
|
|
1941
2008
|
};
|
|
1942
|
-
const combinedClassName =
|
|
2009
|
+
const combinedClassName = cn("relative", className);
|
|
1943
2010
|
const displayValue = rangeStart && rangeEnd ? `${formatDate2(rangeStart, format)}${separator}${formatDate2(rangeEnd, format)}` : rangeStart ? formatDate2(rangeStart, format) : "";
|
|
1944
|
-
return /* @__PURE__ */
|
|
2011
|
+
return /* @__PURE__ */ React8.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React8.createElement(
|
|
1945
2012
|
"input",
|
|
1946
2013
|
{
|
|
1947
2014
|
type: "hidden",
|
|
1948
2015
|
name: `${name}[start]`,
|
|
1949
2016
|
value: rangeStart ? rangeStart.toISOString() : ""
|
|
1950
2017
|
}
|
|
1951
|
-
), /* @__PURE__ */
|
|
2018
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1952
2019
|
"input",
|
|
1953
2020
|
{
|
|
1954
2021
|
type: "hidden",
|
|
1955
2022
|
name: `${name}[end]`,
|
|
1956
2023
|
value: rangeEnd ? rangeEnd.toISOString() : ""
|
|
1957
2024
|
}
|
|
1958
|
-
), /* @__PURE__ */
|
|
1959
|
-
"
|
|
2025
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React8.createElement(
|
|
2026
|
+
"span",
|
|
1960
2027
|
{
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
height: "18",
|
|
1964
|
-
viewBox: "0 0 24 24",
|
|
1965
|
-
fill: "none",
|
|
1966
|
-
stroke: "currentColor",
|
|
1967
|
-
strokeLinecap: "round",
|
|
1968
|
-
strokeLinejoin: "round",
|
|
1969
|
-
strokeWidth: "2"
|
|
2028
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
|
|
2029
|
+
"aria-hidden": "true"
|
|
1970
2030
|
},
|
|
1971
|
-
/* @__PURE__ */
|
|
1972
|
-
|
|
2031
|
+
/* @__PURE__ */ React8.createElement(
|
|
2032
|
+
"svg",
|
|
2033
|
+
{
|
|
2034
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2035
|
+
width: "18",
|
|
2036
|
+
height: "18",
|
|
2037
|
+
viewBox: "0 0 24 24",
|
|
2038
|
+
fill: "none",
|
|
2039
|
+
stroke: "currentColor",
|
|
2040
|
+
strokeLinecap: "round",
|
|
2041
|
+
strokeLinejoin: "round",
|
|
2042
|
+
strokeWidth: "2"
|
|
2043
|
+
},
|
|
2044
|
+
/* @__PURE__ */ React8.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
|
|
2045
|
+
)
|
|
2046
|
+
), /* @__PURE__ */ React8.createElement(
|
|
1973
2047
|
"input",
|
|
1974
2048
|
{
|
|
1975
2049
|
type: "text",
|
|
1976
|
-
className:
|
|
2050
|
+
className: cn(
|
|
2051
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
|
|
2052
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2053
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
2054
|
+
INPUT_AUTOFILL_RESET_CLASSES,
|
|
2055
|
+
showIcon ? "pl-10" : "pl-3",
|
|
2056
|
+
clearable && (rangeStart || rangeEnd) ? "pr-10" : "pr-3",
|
|
2057
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
2058
|
+
error && "border-destructive ring-1 ring-destructive"
|
|
2059
|
+
),
|
|
1977
2060
|
value: displayValue,
|
|
1978
2061
|
onClick: handleToggle,
|
|
1979
2062
|
onBlur,
|
|
@@ -1985,17 +2068,17 @@ function DateRangePicker({
|
|
|
1985
2068
|
"aria-required": required || props["aria-required"],
|
|
1986
2069
|
readOnly: true
|
|
1987
2070
|
}
|
|
1988
|
-
), clearable && (rangeStart || rangeEnd) && !disabled && /* @__PURE__ */
|
|
2071
|
+
), clearable && (rangeStart || rangeEnd) && !disabled && /* @__PURE__ */ React8.createElement(
|
|
1989
2072
|
"button",
|
|
1990
2073
|
{
|
|
1991
2074
|
type: "button",
|
|
1992
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2
|
|
2075
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
|
|
1993
2076
|
onClick: handleClear,
|
|
1994
2077
|
"aria-label": "Clear date range",
|
|
1995
2078
|
tabIndex: -1
|
|
1996
2079
|
},
|
|
1997
2080
|
"\u2715"
|
|
1998
|
-
)), isOpen && !disabled && /* @__PURE__ */
|
|
2081
|
+
)), isOpen && !disabled && /* @__PURE__ */ React8.createElement("div", { className: "absolute z-50 top-full mt-1 w-fit rounded-md border border-border bg-popover text-popover-foreground shadow-md p-3" }, /* @__PURE__ */ React8.createElement("div", { role: "grid", "aria-label": "Calendar" }, /* @__PURE__ */ React8.createElement("div", { className: "grid gap-4", style: monthsGridStyle }, renderMonth(selectedMonth, { prev: true }), renderMonth(addMonths(selectedMonth, 1), { next: true }))), rangeStart && !rangeEnd && /* @__PURE__ */ React8.createElement("div", { className: "text-xs text-center pt-2 border-t border-border mt-2" }, "Select end date")));
|
|
1999
2082
|
}
|
|
2000
2083
|
DateRangePicker.displayName = "DateRangePicker";
|
|
2001
2084
|
function htmlToMarkdown(html) {
|
|
@@ -2051,18 +2134,26 @@ function RichTextEditor({
|
|
|
2051
2134
|
className = "",
|
|
2052
2135
|
mode = "wysiwyg",
|
|
2053
2136
|
allowModeSwitch = false,
|
|
2054
|
-
placeholder = "
|
|
2137
|
+
placeholder = "Your message...",
|
|
2055
2138
|
minHeight = "200px",
|
|
2056
2139
|
maxHeight,
|
|
2057
2140
|
showToolbar = true,
|
|
2058
|
-
toolbarButtons = [
|
|
2141
|
+
toolbarButtons = [
|
|
2142
|
+
"bold",
|
|
2143
|
+
"italic",
|
|
2144
|
+
"underline",
|
|
2145
|
+
"heading",
|
|
2146
|
+
"bulletList",
|
|
2147
|
+
"orderedList",
|
|
2148
|
+
"link"
|
|
2149
|
+
],
|
|
2059
2150
|
...props
|
|
2060
2151
|
}) {
|
|
2061
|
-
const [currentMode, setCurrentMode] =
|
|
2062
|
-
const [content, setContent] =
|
|
2063
|
-
const editorRef =
|
|
2064
|
-
const textareaRef =
|
|
2065
|
-
|
|
2152
|
+
const [currentMode, setCurrentMode] = React8.useState(mode);
|
|
2153
|
+
const [content, setContent] = React8.useState(value);
|
|
2154
|
+
const editorRef = React8.useRef(null);
|
|
2155
|
+
const textareaRef = React8.useRef(null);
|
|
2156
|
+
React8.useEffect(() => {
|
|
2066
2157
|
setContent(value);
|
|
2067
2158
|
if (currentMode === "wysiwyg" && editorRef.current) {
|
|
2068
2159
|
editorRef.current.innerHTML = value;
|
|
@@ -2150,21 +2241,32 @@ function RichTextEditor({
|
|
|
2150
2241
|
}
|
|
2151
2242
|
}
|
|
2152
2243
|
};
|
|
2153
|
-
const
|
|
2244
|
+
const hasValue = React8.useMemo(() => {
|
|
2245
|
+
if (!content) return false;
|
|
2246
|
+
const stripped = content.replace(/<[^>]+>/g, "").trim();
|
|
2247
|
+
return stripped.length > 0;
|
|
2248
|
+
}, [content]);
|
|
2249
|
+
const combinedClassName = cn(
|
|
2250
|
+
"rounded-md border border-input",
|
|
2251
|
+
!error && hasValue && "ring-2 ring-ring",
|
|
2252
|
+
error && "border-destructive ring-1 ring-destructive",
|
|
2253
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
2254
|
+
className
|
|
2255
|
+
);
|
|
2154
2256
|
const editorStyle = {
|
|
2155
2257
|
minHeight,
|
|
2156
2258
|
maxHeight,
|
|
2157
2259
|
overflowY: maxHeight ? "auto" : void 0
|
|
2158
2260
|
};
|
|
2159
|
-
return /* @__PURE__ */
|
|
2261
|
+
return /* @__PURE__ */ React8.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React8.createElement("input", { type: "hidden", name, value: content }), showToolbar && /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between p-2 border-b border-border bg-muted/50" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-1" }, toolbarButtons.map((buttonName) => {
|
|
2160
2262
|
const button = toolbarConfig[buttonName];
|
|
2161
2263
|
if (!button) return null;
|
|
2162
|
-
return /* @__PURE__ */
|
|
2264
|
+
return /* @__PURE__ */ React8.createElement(
|
|
2163
2265
|
"button",
|
|
2164
2266
|
{
|
|
2165
2267
|
key: buttonName,
|
|
2166
2268
|
type: "button",
|
|
2167
|
-
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-
|
|
2269
|
+
className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-muted cursor-pointer transition-colors disabled:cursor-not-allowed disabled:opacity-50",
|
|
2168
2270
|
onClick: () => editorRef.current && button.action(editorRef.current),
|
|
2169
2271
|
title: button.title,
|
|
2170
2272
|
disabled: disabled || currentMode === "markdown",
|
|
@@ -2172,22 +2274,22 @@ function RichTextEditor({
|
|
|
2172
2274
|
},
|
|
2173
2275
|
button.icon
|
|
2174
2276
|
);
|
|
2175
|
-
})), allowModeSwitch && /* @__PURE__ */
|
|
2277
|
+
})), allowModeSwitch && /* @__PURE__ */ React8.createElement(
|
|
2176
2278
|
"button",
|
|
2177
2279
|
{
|
|
2178
2280
|
type: "button",
|
|
2179
|
-
className: "flex items-center justify-center h-8 px-3 rounded border-none bg-transparent hover:bg-
|
|
2281
|
+
className: "flex items-center justify-center h-8 px-3 rounded border-none bg-transparent hover:bg-muted text-xs font-medium cursor-pointer transition-colors disabled:cursor-not-allowed disabled:opacity-50",
|
|
2180
2282
|
onClick: handleModeToggle,
|
|
2181
2283
|
disabled,
|
|
2182
2284
|
title: `Switch to ${currentMode === "wysiwyg" ? "Markdown" : "WYSIWYG"}`,
|
|
2183
2285
|
"aria-label": `Switch to ${currentMode === "wysiwyg" ? "Markdown" : "WYSIWYG"}`
|
|
2184
2286
|
},
|
|
2185
2287
|
currentMode === "wysiwyg" ? "MD" : "WYSIWYG"
|
|
2186
|
-
)), /* @__PURE__ */
|
|
2288
|
+
)), /* @__PURE__ */ React8.createElement("div", { style: editorStyle }, currentMode === "wysiwyg" ? /* @__PURE__ */ React8.createElement(
|
|
2187
2289
|
"div",
|
|
2188
2290
|
{
|
|
2189
2291
|
ref: editorRef,
|
|
2190
|
-
className: "w-full p-3 text-base md:text-sm outline-none bg-transparent focus-visible:outline-none [&:empty:before]:content-[attr(data-placeholder)]
|
|
2292
|
+
className: "w-full p-3 text-base md:text-sm outline-none bg-transparent focus-visible:outline-none [&:empty:before]:content-[attr(data-placeholder)]",
|
|
2191
2293
|
role: "textbox",
|
|
2192
2294
|
contentEditable: !disabled,
|
|
2193
2295
|
onInput: handleWysiwygChange,
|
|
@@ -2198,11 +2300,14 @@ function RichTextEditor({
|
|
|
2198
2300
|
"aria-required": required || props["aria-required"],
|
|
2199
2301
|
suppressContentEditableWarning: true
|
|
2200
2302
|
}
|
|
2201
|
-
) : /* @__PURE__ */
|
|
2303
|
+
) : /* @__PURE__ */ React8.createElement(
|
|
2202
2304
|
"textarea",
|
|
2203
2305
|
{
|
|
2204
2306
|
ref: textareaRef,
|
|
2205
|
-
className:
|
|
2307
|
+
className: cn(
|
|
2308
|
+
"w-full p-3 text-base md:text-sm outline-none bg-transparent resize-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
2309
|
+
INPUT_AUTOFILL_RESET_CLASSES
|
|
2310
|
+
),
|
|
2206
2311
|
value: content,
|
|
2207
2312
|
onChange: handleMarkdownChange,
|
|
2208
2313
|
onBlur,
|