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