@page-speed/forms 0.2.2 → 0.3.0
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/README.md +504 -2
- package/dist/core.cjs +18 -4
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +18 -4
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +18 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +1279 -68
- package/dist/inputs.cjs.map +1 -1
- package/dist/inputs.d.cts +521 -4
- package/dist/inputs.d.ts +521 -4
- package/dist/inputs.js +1275 -68
- package/dist/inputs.js.map +1 -1
- package/dist/{types-Cw5CeZP-.d.cts → types-Dww52PeF.d.cts} +3 -0
- package/dist/{types-Cw5CeZP-.d.ts → types-Dww52PeF.d.ts} +3 -0
- package/dist/validation-rules.d.cts +1 -1
- package/dist/validation-rules.d.ts +1 -1
- package/dist/validation-utils.d.cts +1 -1
- package/dist/validation-utils.d.ts +1 -1
- package/dist/validation-valibot.d.cts +1 -1
- package/dist/validation-valibot.d.ts +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/package.json +5 -2
package/dist/inputs.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React7 = require('react');
|
|
4
4
|
|
|
5
5
|
function _interopNamespace(e) {
|
|
6
6
|
if (e && e.__esModule) return e;
|
|
@@ -20,7 +20,7 @@ function _interopNamespace(e) {
|
|
|
20
20
|
return Object.freeze(n);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
var
|
|
23
|
+
var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
|
|
24
24
|
|
|
25
25
|
// src/inputs/TextInput.tsx
|
|
26
26
|
function TextInput({
|
|
@@ -45,7 +45,7 @@ function TextInput({
|
|
|
45
45
|
const baseClassName = "text-input";
|
|
46
46
|
const errorClassName = error ? "text-input--error" : "";
|
|
47
47
|
const combinedClassName = `${baseClassName} ${errorClassName} ${className}`.trim();
|
|
48
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
49
49
|
"input",
|
|
50
50
|
{
|
|
51
51
|
type,
|
|
@@ -91,7 +91,7 @@ function TextArea({
|
|
|
91
91
|
const baseClassName = "textarea";
|
|
92
92
|
const errorClassName = error ? "textarea--error" : "";
|
|
93
93
|
const combinedClassName = `${baseClassName} ${errorClassName} ${className}`.trim();
|
|
94
|
-
return /* @__PURE__ */
|
|
94
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
95
95
|
"textarea",
|
|
96
96
|
{
|
|
97
97
|
name,
|
|
@@ -128,8 +128,8 @@ function Checkbox({
|
|
|
128
128
|
label,
|
|
129
129
|
...props
|
|
130
130
|
}) {
|
|
131
|
-
const inputRef =
|
|
132
|
-
|
|
131
|
+
const inputRef = React7__namespace.useRef(null);
|
|
132
|
+
React7__namespace.useEffect(() => {
|
|
133
133
|
if (inputRef.current) {
|
|
134
134
|
inputRef.current.indeterminate = indeterminate;
|
|
135
135
|
}
|
|
@@ -143,7 +143,7 @@ function Checkbox({
|
|
|
143
143
|
const baseClassName = "checkbox";
|
|
144
144
|
const errorClassName = error ? "checkbox--error" : "";
|
|
145
145
|
const combinedClassName = `${baseClassName} ${errorClassName} ${className}`.trim();
|
|
146
|
-
const checkbox = /* @__PURE__ */
|
|
146
|
+
const checkbox = /* @__PURE__ */ React7__namespace.createElement(
|
|
147
147
|
"input",
|
|
148
148
|
{
|
|
149
149
|
ref: inputRef,
|
|
@@ -162,7 +162,7 @@ function Checkbox({
|
|
|
162
162
|
}
|
|
163
163
|
);
|
|
164
164
|
if (label) {
|
|
165
|
-
return /* @__PURE__ */
|
|
165
|
+
return /* @__PURE__ */ React7__namespace.createElement("label", { className: "checkbox-label" }, checkbox, /* @__PURE__ */ React7__namespace.createElement("span", { className: "checkbox-label-text" }, label));
|
|
166
166
|
}
|
|
167
167
|
return checkbox;
|
|
168
168
|
}
|
|
@@ -218,7 +218,7 @@ function CheckboxGroup({
|
|
|
218
218
|
const layoutClassName = `checkbox-group--${layout}`;
|
|
219
219
|
const combinedClassName = `${baseClassName} ${errorClassName} ${layoutClassName} ${className}`.trim();
|
|
220
220
|
const maxReached = Boolean(maxSelections && value.length >= maxSelections);
|
|
221
|
-
return /* @__PURE__ */
|
|
221
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
222
222
|
"div",
|
|
223
223
|
{
|
|
224
224
|
className: combinedClassName,
|
|
@@ -231,9 +231,9 @@ function CheckboxGroup({
|
|
|
231
231
|
gridTemplateColumns: `repeat(${gridColumns}, 1fr)`
|
|
232
232
|
} : void 0
|
|
233
233
|
},
|
|
234
|
-
label && /* @__PURE__ */
|
|
235
|
-
description && /* @__PURE__ */
|
|
236
|
-
/* @__PURE__ */
|
|
234
|
+
label && /* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-group-label" }, label),
|
|
235
|
+
description && /* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-group-description" }, description),
|
|
236
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-options" }, showSelectAll && enabledOptions.length > 0 && /* @__PURE__ */ React7__namespace.createElement("label", { className: "checkbox-option checkbox-option--select-all" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
237
237
|
"input",
|
|
238
238
|
{
|
|
239
239
|
type: "checkbox",
|
|
@@ -249,18 +249,18 @@ function CheckboxGroup({
|
|
|
249
249
|
className: "checkbox-input",
|
|
250
250
|
"aria-label": selectAllLabel
|
|
251
251
|
}
|
|
252
|
-
), /* @__PURE__ */
|
|
252
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-content" }, /* @__PURE__ */ React7__namespace.createElement("span", { className: "checkbox-label" }, selectAllLabel))), options.map((option) => {
|
|
253
253
|
const isChecked = value.includes(option.value);
|
|
254
254
|
const isDisabled = disabled || option.disabled || maxReached && !isChecked;
|
|
255
255
|
const checkboxId = `${name}-${option.value}`;
|
|
256
|
-
return /* @__PURE__ */
|
|
256
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
257
257
|
"label",
|
|
258
258
|
{
|
|
259
259
|
key: option.value,
|
|
260
260
|
className: `checkbox-option ${isDisabled ? "checkbox-option--disabled" : ""}`,
|
|
261
261
|
htmlFor: checkboxId
|
|
262
262
|
},
|
|
263
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
264
264
|
"input",
|
|
265
265
|
{
|
|
266
266
|
type: "checkbox",
|
|
@@ -276,7 +276,7 @@ function CheckboxGroup({
|
|
|
276
276
|
"aria-describedby": option.description ? `${checkboxId}-description` : props["aria-describedby"]
|
|
277
277
|
}
|
|
278
278
|
),
|
|
279
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-content" }, renderOption ? renderOption(option) : /* @__PURE__ */ React7__namespace.createElement(React7__namespace.Fragment, null, /* @__PURE__ */ React7__namespace.createElement("span", { className: "checkbox-label" }, option.label), option.description && /* @__PURE__ */ React7__namespace.createElement(
|
|
280
280
|
"span",
|
|
281
281
|
{
|
|
282
282
|
className: "checkbox-description",
|
|
@@ -286,7 +286,7 @@ function CheckboxGroup({
|
|
|
286
286
|
)))
|
|
287
287
|
);
|
|
288
288
|
})),
|
|
289
|
-
(minSelections || maxSelections) && /* @__PURE__ */
|
|
289
|
+
(minSelections || maxSelections) && /* @__PURE__ */ React7__namespace.createElement("div", { className: "checkbox-group-feedback", "aria-live": "polite" }, minSelections && value.length < minSelections && /* @__PURE__ */ React7__namespace.createElement("span", { className: "checkbox-group-feedback-min" }, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""), maxSelections && /* @__PURE__ */ React7__namespace.createElement("span", { className: "checkbox-group-feedback-max" }, value.length, "/", maxSelections, " selected"))
|
|
290
290
|
);
|
|
291
291
|
}
|
|
292
292
|
CheckboxGroup.displayName = "CheckboxGroup";
|
|
@@ -339,7 +339,7 @@ function Radio({
|
|
|
339
339
|
const errorClassName = error ? "radio-group--error" : "";
|
|
340
340
|
const layoutClassName = `radio-group--${layout}`;
|
|
341
341
|
const combinedClassName = `${baseClassName} ${errorClassName} ${layoutClassName} ${className}`.trim();
|
|
342
|
-
return /* @__PURE__ */
|
|
342
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
343
343
|
"div",
|
|
344
344
|
{
|
|
345
345
|
className: combinedClassName,
|
|
@@ -349,19 +349,19 @@ function Radio({
|
|
|
349
349
|
"aria-required": required || props["aria-required"],
|
|
350
350
|
"aria-label": typeof label === "string" ? label : props["aria-label"]
|
|
351
351
|
},
|
|
352
|
-
label && /* @__PURE__ */
|
|
353
|
-
/* @__PURE__ */
|
|
352
|
+
label && /* @__PURE__ */ React7__namespace.createElement("div", { className: "radio-group-label" }, label),
|
|
353
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "radio-options" }, options.map((option, index) => {
|
|
354
354
|
const isChecked = value === option.value;
|
|
355
355
|
const isDisabled = disabled || option.disabled;
|
|
356
356
|
const radioId = `${name}-${option.value}`;
|
|
357
|
-
return /* @__PURE__ */
|
|
357
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
358
358
|
"label",
|
|
359
359
|
{
|
|
360
360
|
key: option.value,
|
|
361
361
|
className: `radio-option ${isDisabled ? "radio-option--disabled" : ""}`,
|
|
362
362
|
htmlFor: radioId
|
|
363
363
|
},
|
|
364
|
-
/* @__PURE__ */
|
|
364
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
365
365
|
"input",
|
|
366
366
|
{
|
|
367
367
|
type: "radio",
|
|
@@ -378,7 +378,7 @@ function Radio({
|
|
|
378
378
|
"aria-describedby": option.description ? `${radioId}-description` : props["aria-describedby"]
|
|
379
379
|
}
|
|
380
380
|
),
|
|
381
|
-
/* @__PURE__ */
|
|
381
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "radio-content" }, /* @__PURE__ */ React7__namespace.createElement("span", { className: "radio-label" }, option.label), option.description && /* @__PURE__ */ React7__namespace.createElement(
|
|
382
382
|
"span",
|
|
383
383
|
{
|
|
384
384
|
className: "radio-description",
|
|
@@ -410,19 +410,19 @@ function Select({
|
|
|
410
410
|
renderOption,
|
|
411
411
|
...props
|
|
412
412
|
}) {
|
|
413
|
-
const [isOpen, setIsOpen] =
|
|
414
|
-
const [searchQuery, setSearchQuery] =
|
|
415
|
-
const [focusedIndex, setFocusedIndex] =
|
|
416
|
-
const selectRef =
|
|
417
|
-
const searchInputRef =
|
|
413
|
+
const [isOpen, setIsOpen] = React7__namespace.useState(false);
|
|
414
|
+
const [searchQuery, setSearchQuery] = React7__namespace.useState("");
|
|
415
|
+
const [focusedIndex, setFocusedIndex] = React7__namespace.useState(-1);
|
|
416
|
+
const selectRef = React7__namespace.useRef(null);
|
|
417
|
+
const searchInputRef = React7__namespace.useRef(null);
|
|
418
418
|
const dropdownId = `${name}-dropdown`;
|
|
419
|
-
const allOptions =
|
|
419
|
+
const allOptions = React7__namespace.useMemo(() => {
|
|
420
420
|
if (optionGroups.length > 0) {
|
|
421
421
|
return optionGroups.flatMap((group) => group.options);
|
|
422
422
|
}
|
|
423
423
|
return options;
|
|
424
424
|
}, [options, optionGroups]);
|
|
425
|
-
const filteredOptions =
|
|
425
|
+
const filteredOptions = React7__namespace.useMemo(() => {
|
|
426
426
|
if (!searchQuery.trim()) {
|
|
427
427
|
return allOptions;
|
|
428
428
|
}
|
|
@@ -432,7 +432,7 @@ function Select({
|
|
|
432
432
|
return label.toLowerCase().includes(query);
|
|
433
433
|
});
|
|
434
434
|
}, [allOptions, searchQuery]);
|
|
435
|
-
const selectedOption =
|
|
435
|
+
const selectedOption = React7__namespace.useMemo(() => {
|
|
436
436
|
return allOptions.find((opt) => opt.value === value);
|
|
437
437
|
}, [allOptions, value]);
|
|
438
438
|
const handleSelect = (optionValue) => {
|
|
@@ -536,7 +536,7 @@ function Select({
|
|
|
536
536
|
const handleBlur = () => {
|
|
537
537
|
onBlur?.();
|
|
538
538
|
};
|
|
539
|
-
|
|
539
|
+
React7__namespace.useEffect(() => {
|
|
540
540
|
const handleClickOutside = (event) => {
|
|
541
541
|
if (selectRef.current && !selectRef.current.contains(event.target)) {
|
|
542
542
|
setIsOpen(false);
|
|
@@ -557,7 +557,7 @@ function Select({
|
|
|
557
557
|
const disabledClassName = disabled ? "select--disabled" : "";
|
|
558
558
|
const openClassName = isOpen ? "select--open" : "";
|
|
559
559
|
const combinedClassName = `${baseClassName} ${errorClassName} ${disabledClassName} ${openClassName} ${className}`.trim();
|
|
560
|
-
return /* @__PURE__ */
|
|
560
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
561
561
|
"div",
|
|
562
562
|
{
|
|
563
563
|
ref: selectRef,
|
|
@@ -565,7 +565,7 @@ function Select({
|
|
|
565
565
|
onKeyDown: handleKeyDown,
|
|
566
566
|
onBlur: handleBlur
|
|
567
567
|
},
|
|
568
|
-
/* @__PURE__ */
|
|
568
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
569
569
|
"select",
|
|
570
570
|
{
|
|
571
571
|
name,
|
|
@@ -578,10 +578,10 @@ function Select({
|
|
|
578
578
|
tabIndex: -1,
|
|
579
579
|
style: { display: "none" }
|
|
580
580
|
},
|
|
581
|
-
/* @__PURE__ */
|
|
582
|
-
allOptions.map((option) => /* @__PURE__ */
|
|
581
|
+
/* @__PURE__ */ React7__namespace.createElement("option", { value: "" }, "Select..."),
|
|
582
|
+
allOptions.map((option) => /* @__PURE__ */ React7__namespace.createElement("option", { key: option.value, value: option.value }, typeof option.label === "string" ? option.label : option.value))
|
|
583
583
|
),
|
|
584
|
-
/* @__PURE__ */
|
|
584
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
585
585
|
"div",
|
|
586
586
|
{
|
|
587
587
|
className: "select-trigger",
|
|
@@ -595,8 +595,8 @@ function Select({
|
|
|
595
595
|
"aria-disabled": disabled,
|
|
596
596
|
tabIndex: disabled ? -1 : 0
|
|
597
597
|
},
|
|
598
|
-
/* @__PURE__ */
|
|
599
|
-
/* @__PURE__ */
|
|
598
|
+
/* @__PURE__ */ React7__namespace.createElement("span", { className: "select-value" }, selectedOption ? renderOption ? renderOption(selectedOption) : selectedOption.label : /* @__PURE__ */ React7__namespace.createElement("span", { className: "select-placeholder" }, placeholder)),
|
|
599
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "select-icons" }, loading && /* @__PURE__ */ React7__namespace.createElement("span", { className: "select-loading" }, "\u23F3"), clearable && value && !disabled && !loading && /* @__PURE__ */ React7__namespace.createElement(
|
|
600
600
|
"button",
|
|
601
601
|
{
|
|
602
602
|
type: "button",
|
|
@@ -606,9 +606,9 @@ function Select({
|
|
|
606
606
|
tabIndex: -1
|
|
607
607
|
},
|
|
608
608
|
"\u2715"
|
|
609
|
-
), /* @__PURE__ */
|
|
609
|
+
), /* @__PURE__ */ React7__namespace.createElement("span", { className: "select-arrow", "aria-hidden": "true" }, isOpen ? "\u25B2" : "\u25BC"))
|
|
610
610
|
),
|
|
611
|
-
isOpen && /* @__PURE__ */
|
|
611
|
+
isOpen && /* @__PURE__ */ React7__namespace.createElement("div", { id: dropdownId, className: "select-dropdown", role: "listbox" }, searchable && /* @__PURE__ */ React7__namespace.createElement("div", { className: "select-search" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
612
612
|
"input",
|
|
613
613
|
{
|
|
614
614
|
ref: searchInputRef,
|
|
@@ -620,19 +620,19 @@ function Select({
|
|
|
620
620
|
onClick: (e) => e.stopPropagation(),
|
|
621
621
|
"aria-label": "Search options"
|
|
622
622
|
}
|
|
623
|
-
)), /* @__PURE__ */
|
|
623
|
+
)), /* @__PURE__ */ React7__namespace.createElement("div", { className: "select-options" }, filteredOptions.length === 0 ? /* @__PURE__ */ React7__namespace.createElement("div", { className: "select-no-options" }, "No options found") : optionGroups.length > 0 ? (
|
|
624
624
|
// Render grouped options
|
|
625
625
|
optionGroups.map((group, groupIndex) => {
|
|
626
626
|
const groupOptions = group.options.filter(
|
|
627
627
|
(opt) => filteredOptions.includes(opt)
|
|
628
628
|
);
|
|
629
629
|
if (groupOptions.length === 0) return null;
|
|
630
|
-
return /* @__PURE__ */
|
|
630
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { key: groupIndex, className: "select-optgroup" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "select-optgroup-label" }, group.label), groupOptions.map((option) => {
|
|
631
631
|
const globalIndex = filteredOptions.indexOf(option);
|
|
632
632
|
const isSelected = value === option.value;
|
|
633
633
|
const isFocused = globalIndex === focusedIndex;
|
|
634
634
|
const isDisabled = option.disabled;
|
|
635
|
-
return /* @__PURE__ */
|
|
635
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
636
636
|
"div",
|
|
637
637
|
{
|
|
638
638
|
key: option.value,
|
|
@@ -652,7 +652,7 @@ function Select({
|
|
|
652
652
|
const isSelected = value === option.value;
|
|
653
653
|
const isFocused = index === focusedIndex;
|
|
654
654
|
const isDisabled = option.disabled;
|
|
655
|
-
return /* @__PURE__ */
|
|
655
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
656
656
|
"div",
|
|
657
657
|
{
|
|
658
658
|
key: option.value,
|
|
@@ -685,13 +685,23 @@ function FileInput({
|
|
|
685
685
|
maxFiles = 1,
|
|
686
686
|
multiple = false,
|
|
687
687
|
showPreview = true,
|
|
688
|
+
showProgress = true,
|
|
689
|
+
uploadProgress = {},
|
|
690
|
+
enableCropping = false,
|
|
691
|
+
cropAspectRatio,
|
|
692
|
+
onCropComplete,
|
|
688
693
|
onValidationError,
|
|
689
694
|
onFileRemove,
|
|
690
695
|
...props
|
|
691
696
|
}) {
|
|
692
|
-
const inputRef =
|
|
693
|
-
const [dragActive, setDragActive] =
|
|
694
|
-
const
|
|
697
|
+
const inputRef = React7__namespace.useRef(null);
|
|
698
|
+
const [dragActive, setDragActive] = React7__namespace.useState(false);
|
|
699
|
+
const [cropperOpen, setCropperOpen] = React7__namespace.useState(false);
|
|
700
|
+
const [imageToCrop, setImageToCrop] = React7__namespace.useState(null);
|
|
701
|
+
const [crop, setCrop] = React7__namespace.useState({ x: 0, y: 0 });
|
|
702
|
+
const [zoom, setZoom] = React7__namespace.useState(1);
|
|
703
|
+
const [croppedAreaPixels, setCroppedAreaPixels] = React7__namespace.useState(null);
|
|
704
|
+
const validateFile = React7__namespace.useCallback(
|
|
695
705
|
(file) => {
|
|
696
706
|
if (accept) {
|
|
697
707
|
const acceptedTypes = accept.split(",").map((t) => t.trim());
|
|
@@ -726,7 +736,7 @@ function FileInput({
|
|
|
726
736
|
},
|
|
727
737
|
[accept, maxSize]
|
|
728
738
|
);
|
|
729
|
-
const handleFiles =
|
|
739
|
+
const handleFiles = React7__namespace.useCallback(
|
|
730
740
|
(fileList) => {
|
|
731
741
|
if (!fileList || fileList.length === 0) return;
|
|
732
742
|
const newFiles = Array.from(fileList);
|
|
@@ -753,14 +763,110 @@ function FileInput({
|
|
|
753
763
|
onValidationError(validationErrors);
|
|
754
764
|
}
|
|
755
765
|
if (validFiles.length > 0 && totalFiles <= maxFiles) {
|
|
756
|
-
const
|
|
757
|
-
|
|
766
|
+
const firstImage = validFiles.find((f) => f.type.startsWith("image/"));
|
|
767
|
+
if (enableCropping && firstImage && !multiple) {
|
|
768
|
+
const previewUrl = URL.createObjectURL(firstImage);
|
|
769
|
+
setImageToCrop({ file: firstImage, url: previewUrl });
|
|
770
|
+
setCropperOpen(true);
|
|
771
|
+
} else {
|
|
772
|
+
const updatedFiles = multiple ? [...value, ...validFiles] : validFiles;
|
|
773
|
+
onChange(updatedFiles.slice(0, maxFiles));
|
|
774
|
+
}
|
|
758
775
|
}
|
|
759
776
|
if (inputRef.current) {
|
|
760
777
|
inputRef.current.value = "";
|
|
761
778
|
}
|
|
762
779
|
},
|
|
763
|
-
[value, onChange, validateFile, maxFiles, multiple, onValidationError]
|
|
780
|
+
[value, onChange, validateFile, maxFiles, multiple, enableCropping, onValidationError]
|
|
781
|
+
);
|
|
782
|
+
const createCroppedImage = React7__namespace.useCallback(
|
|
783
|
+
async (imageUrl, cropArea) => {
|
|
784
|
+
return new Promise((resolve, reject) => {
|
|
785
|
+
const image = new Image();
|
|
786
|
+
image.onload = () => {
|
|
787
|
+
const canvas = document.createElement("canvas");
|
|
788
|
+
const ctx = canvas.getContext("2d");
|
|
789
|
+
if (!ctx) {
|
|
790
|
+
reject(new Error("Failed to get canvas context"));
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
canvas.width = cropArea.width;
|
|
794
|
+
canvas.height = cropArea.height;
|
|
795
|
+
ctx.drawImage(
|
|
796
|
+
image,
|
|
797
|
+
cropArea.x,
|
|
798
|
+
cropArea.y,
|
|
799
|
+
cropArea.width,
|
|
800
|
+
cropArea.height,
|
|
801
|
+
0,
|
|
802
|
+
0,
|
|
803
|
+
cropArea.width,
|
|
804
|
+
cropArea.height
|
|
805
|
+
);
|
|
806
|
+
canvas.toBlob((blob) => {
|
|
807
|
+
if (blob) {
|
|
808
|
+
resolve(blob);
|
|
809
|
+
} else {
|
|
810
|
+
reject(new Error("Failed to create blob from canvas"));
|
|
811
|
+
}
|
|
812
|
+
}, "image/jpeg", 0.95);
|
|
813
|
+
};
|
|
814
|
+
image.onerror = () => {
|
|
815
|
+
reject(new Error("Failed to load image"));
|
|
816
|
+
};
|
|
817
|
+
image.src = imageUrl;
|
|
818
|
+
});
|
|
819
|
+
},
|
|
820
|
+
[]
|
|
821
|
+
);
|
|
822
|
+
const handleCropSave = React7__namespace.useCallback(async () => {
|
|
823
|
+
if (!imageToCrop || !croppedAreaPixels) return;
|
|
824
|
+
try {
|
|
825
|
+
const croppedBlob = await createCroppedImage(
|
|
826
|
+
imageToCrop.url,
|
|
827
|
+
croppedAreaPixels
|
|
828
|
+
);
|
|
829
|
+
if (onCropComplete) {
|
|
830
|
+
onCropComplete(croppedBlob, imageToCrop.file);
|
|
831
|
+
}
|
|
832
|
+
const croppedFile = new File(
|
|
833
|
+
[croppedBlob],
|
|
834
|
+
imageToCrop.file.name,
|
|
835
|
+
{ type: "image/jpeg" }
|
|
836
|
+
);
|
|
837
|
+
const updatedFiles = multiple ? [...value, croppedFile] : [croppedFile];
|
|
838
|
+
onChange(updatedFiles);
|
|
839
|
+
setCropperOpen(false);
|
|
840
|
+
URL.revokeObjectURL(imageToCrop.url);
|
|
841
|
+
setImageToCrop(null);
|
|
842
|
+
setCrop({ x: 0, y: 0 });
|
|
843
|
+
setZoom(1);
|
|
844
|
+
setCroppedAreaPixels(null);
|
|
845
|
+
} catch (error2) {
|
|
846
|
+
console.error("Failed to crop image:", error2);
|
|
847
|
+
}
|
|
848
|
+
}, [imageToCrop, croppedAreaPixels, createCroppedImage, onCropComplete, value, onChange, multiple]);
|
|
849
|
+
const handleCropCancel = React7__namespace.useCallback(() => {
|
|
850
|
+
if (imageToCrop) {
|
|
851
|
+
URL.revokeObjectURL(imageToCrop.url);
|
|
852
|
+
}
|
|
853
|
+
setCropperOpen(false);
|
|
854
|
+
setImageToCrop(null);
|
|
855
|
+
setCrop({ x: 0, y: 0 });
|
|
856
|
+
setZoom(1);
|
|
857
|
+
setCroppedAreaPixels(null);
|
|
858
|
+
}, [imageToCrop]);
|
|
859
|
+
const onCropChange = React7__namespace.useCallback((crop2) => {
|
|
860
|
+
setCrop(crop2);
|
|
861
|
+
}, []);
|
|
862
|
+
const onZoomChange = React7__namespace.useCallback((zoom2) => {
|
|
863
|
+
setZoom(zoom2);
|
|
864
|
+
}, []);
|
|
865
|
+
const onCropCompleteInternal = React7__namespace.useCallback(
|
|
866
|
+
(_, croppedAreaPixels2) => {
|
|
867
|
+
setCroppedAreaPixels(croppedAreaPixels2);
|
|
868
|
+
},
|
|
869
|
+
[]
|
|
764
870
|
);
|
|
765
871
|
const handleChange = (e) => {
|
|
766
872
|
handleFiles(e.target.files);
|
|
@@ -773,6 +879,12 @@ function FileInput({
|
|
|
773
879
|
onFileRemove(fileToRemove, index);
|
|
774
880
|
}
|
|
775
881
|
};
|
|
882
|
+
const handleCrop = (file) => {
|
|
883
|
+
if (!file.type.startsWith("image/")) return;
|
|
884
|
+
const previewUrl = URL.createObjectURL(file);
|
|
885
|
+
setImageToCrop({ file, url: previewUrl });
|
|
886
|
+
setCropperOpen(true);
|
|
887
|
+
};
|
|
776
888
|
const handleDrag = (e) => {
|
|
777
889
|
e.preventDefault();
|
|
778
890
|
e.stopPropagation();
|
|
@@ -811,7 +923,7 @@ function FileInput({
|
|
|
811
923
|
}
|
|
812
924
|
return null;
|
|
813
925
|
};
|
|
814
|
-
|
|
926
|
+
React7__namespace.useEffect(() => {
|
|
815
927
|
return () => {
|
|
816
928
|
value.forEach((file) => {
|
|
817
929
|
const previewUrl = getPreviewUrl(file);
|
|
@@ -819,14 +931,17 @@ function FileInput({
|
|
|
819
931
|
URL.revokeObjectURL(previewUrl);
|
|
820
932
|
}
|
|
821
933
|
});
|
|
934
|
+
if (imageToCrop) {
|
|
935
|
+
URL.revokeObjectURL(imageToCrop.url);
|
|
936
|
+
}
|
|
822
937
|
};
|
|
823
|
-
}, [value]);
|
|
938
|
+
}, [value, imageToCrop]);
|
|
824
939
|
const baseClassName = "file-input";
|
|
825
940
|
const errorClassName = error ? "file-input--error" : "";
|
|
826
941
|
const dragClassName = dragActive ? "file-input--drag-active" : "";
|
|
827
942
|
const disabledClassName = disabled ? "file-input--disabled" : "";
|
|
828
943
|
const combinedClassName = `${baseClassName} ${errorClassName} ${dragClassName} ${disabledClassName} ${className}`.trim();
|
|
829
|
-
return /* @__PURE__ */
|
|
944
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React7__namespace.createElement(
|
|
830
945
|
"input",
|
|
831
946
|
{
|
|
832
947
|
ref: inputRef,
|
|
@@ -844,7 +959,7 @@ function FileInput({
|
|
|
844
959
|
"aria-required": required || props["aria-required"],
|
|
845
960
|
style: { display: "none" }
|
|
846
961
|
}
|
|
847
|
-
), /* @__PURE__ */
|
|
962
|
+
), /* @__PURE__ */ React7__namespace.createElement(
|
|
848
963
|
"div",
|
|
849
964
|
{
|
|
850
965
|
className: "file-input__dropzone",
|
|
@@ -859,7 +974,7 @@ function FileInput({
|
|
|
859
974
|
"aria-label": placeholder,
|
|
860
975
|
"aria-disabled": disabled
|
|
861
976
|
},
|
|
862
|
-
/* @__PURE__ */
|
|
977
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input__dropzone-content" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
863
978
|
"svg",
|
|
864
979
|
{
|
|
865
980
|
className: "file-input__icon",
|
|
@@ -873,13 +988,13 @@ function FileInput({
|
|
|
873
988
|
strokeLinejoin: "round",
|
|
874
989
|
"aria-hidden": "true"
|
|
875
990
|
},
|
|
876
|
-
/* @__PURE__ */
|
|
877
|
-
/* @__PURE__ */
|
|
878
|
-
/* @__PURE__ */
|
|
879
|
-
), /* @__PURE__ */
|
|
880
|
-
), value.length > 0 && /* @__PURE__ */
|
|
991
|
+
/* @__PURE__ */ React7__namespace.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
992
|
+
/* @__PURE__ */ React7__namespace.createElement("polyline", { points: "17 8 12 3 7 8" }),
|
|
993
|
+
/* @__PURE__ */ React7__namespace.createElement("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
994
|
+
), /* @__PURE__ */ React7__namespace.createElement("p", { className: "file-input__placeholder" }, value.length > 0 ? `${value.length} file(s) selected` : placeholder), accept && /* @__PURE__ */ React7__namespace.createElement("p", { className: "file-input__hint" }, "Accepted: ", accept), maxSize && /* @__PURE__ */ React7__namespace.createElement("p", { className: "file-input__hint" }, "Max size: ", formatFileSize(maxSize)))
|
|
995
|
+
), value.length > 0 && /* @__PURE__ */ React7__namespace.createElement("ul", { className: "file-input__list", role: "list" }, value.map((file, index) => {
|
|
881
996
|
const previewUrl = showPreview ? getPreviewUrl(file) : null;
|
|
882
|
-
return /* @__PURE__ */
|
|
997
|
+
return /* @__PURE__ */ React7__namespace.createElement("li", { key: `${file.name}-${index}`, className: "file-input__item" }, previewUrl && /* @__PURE__ */ React7__namespace.createElement(
|
|
883
998
|
"img",
|
|
884
999
|
{
|
|
885
1000
|
src: previewUrl,
|
|
@@ -888,7 +1003,46 @@ function FileInput({
|
|
|
888
1003
|
width: "48",
|
|
889
1004
|
height: "48"
|
|
890
1005
|
}
|
|
891
|
-
), /* @__PURE__ */
|
|
1006
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input__details" }, /* @__PURE__ */ React7__namespace.createElement("span", { className: "file-input__filename" }, file.name), /* @__PURE__ */ React7__namespace.createElement("span", { className: "file-input__filesize" }, formatFileSize(file.size)), showProgress && uploadProgress[file.name] !== void 0 && /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input__progress" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1007
|
+
"div",
|
|
1008
|
+
{
|
|
1009
|
+
className: "file-input__progress-bar",
|
|
1010
|
+
style: { width: `${uploadProgress[file.name]}%` },
|
|
1011
|
+
role: "progressbar",
|
|
1012
|
+
"aria-valuenow": uploadProgress[file.name],
|
|
1013
|
+
"aria-valuemin": 0,
|
|
1014
|
+
"aria-valuemax": 100,
|
|
1015
|
+
"aria-label": `Upload progress: ${uploadProgress[file.name]}%`
|
|
1016
|
+
}
|
|
1017
|
+
), /* @__PURE__ */ React7__namespace.createElement("span", { className: "file-input__progress-text" }, uploadProgress[file.name], "%"))), enableCropping && file.type.startsWith("image/") && /* @__PURE__ */ React7__namespace.createElement(
|
|
1018
|
+
"button",
|
|
1019
|
+
{
|
|
1020
|
+
type: "button",
|
|
1021
|
+
onClick: (e) => {
|
|
1022
|
+
e.stopPropagation();
|
|
1023
|
+
handleCrop(file);
|
|
1024
|
+
},
|
|
1025
|
+
disabled,
|
|
1026
|
+
className: "file-input__crop",
|
|
1027
|
+
"aria-label": `Crop ${file.name}`
|
|
1028
|
+
},
|
|
1029
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
1030
|
+
"svg",
|
|
1031
|
+
{
|
|
1032
|
+
width: "20",
|
|
1033
|
+
height: "20",
|
|
1034
|
+
viewBox: "0 0 24 24",
|
|
1035
|
+
fill: "none",
|
|
1036
|
+
stroke: "currentColor",
|
|
1037
|
+
strokeWidth: "2",
|
|
1038
|
+
strokeLinecap: "round",
|
|
1039
|
+
strokeLinejoin: "round",
|
|
1040
|
+
"aria-hidden": "true"
|
|
1041
|
+
},
|
|
1042
|
+
/* @__PURE__ */ React7__namespace.createElement("path", { d: "M6.13 1L6 16a2 2 0 0 0 2 2h15" }),
|
|
1043
|
+
/* @__PURE__ */ React7__namespace.createElement("path", { d: "M1 6.13L16 6a2 2 0 0 1 2 2v15" })
|
|
1044
|
+
)
|
|
1045
|
+
), /* @__PURE__ */ React7__namespace.createElement(
|
|
892
1046
|
"button",
|
|
893
1047
|
{
|
|
894
1048
|
type: "button",
|
|
@@ -900,7 +1054,7 @@ function FileInput({
|
|
|
900
1054
|
className: "file-input__remove",
|
|
901
1055
|
"aria-label": `Remove ${file.name}`
|
|
902
1056
|
},
|
|
903
|
-
/* @__PURE__ */
|
|
1057
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
904
1058
|
"svg",
|
|
905
1059
|
{
|
|
906
1060
|
width: "20",
|
|
@@ -913,20 +1067,1077 @@ function FileInput({
|
|
|
913
1067
|
strokeLinejoin: "round",
|
|
914
1068
|
"aria-hidden": "true"
|
|
915
1069
|
},
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
1070
|
+
/* @__PURE__ */ React7__namespace.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1071
|
+
/* @__PURE__ */ React7__namespace.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
918
1072
|
)
|
|
919
1073
|
));
|
|
920
|
-
}))
|
|
1074
|
+
})), cropperOpen && imageToCrop && /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-modal" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1075
|
+
"div",
|
|
1076
|
+
{
|
|
1077
|
+
className: "file-input-cropper-overlay",
|
|
1078
|
+
onClick: handleCropCancel,
|
|
1079
|
+
"aria-label": "Close cropper"
|
|
1080
|
+
}
|
|
1081
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-container" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-header" }, /* @__PURE__ */ React7__namespace.createElement("h3", { className: "file-input-cropper-title" }, "Crop Image"), /* @__PURE__ */ React7__namespace.createElement(
|
|
1082
|
+
"button",
|
|
1083
|
+
{
|
|
1084
|
+
type: "button",
|
|
1085
|
+
className: "file-input-cropper-close",
|
|
1086
|
+
onClick: handleCropCancel,
|
|
1087
|
+
"aria-label": "Close"
|
|
1088
|
+
},
|
|
1089
|
+
"\u2715"
|
|
1090
|
+
)), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-content" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1091
|
+
"div",
|
|
1092
|
+
{
|
|
1093
|
+
className: "file-input-cropper-image-container",
|
|
1094
|
+
onMouseDown: (e) => {
|
|
1095
|
+
e.preventDefault();
|
|
1096
|
+
const startX = e.clientX - crop.x;
|
|
1097
|
+
const startY = e.clientY - crop.y;
|
|
1098
|
+
const handleMouseMove = (moveEvent) => {
|
|
1099
|
+
onCropChange({
|
|
1100
|
+
x: moveEvent.clientX - startX,
|
|
1101
|
+
y: moveEvent.clientY - startY
|
|
1102
|
+
});
|
|
1103
|
+
};
|
|
1104
|
+
const handleMouseUp = () => {
|
|
1105
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
1106
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1107
|
+
};
|
|
1108
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1109
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
1113
|
+
"img",
|
|
1114
|
+
{
|
|
1115
|
+
src: imageToCrop.url,
|
|
1116
|
+
alt: "Crop preview",
|
|
1117
|
+
className: "file-input-cropper-image",
|
|
1118
|
+
style: {
|
|
1119
|
+
transform: `translate(${crop.x}px, ${crop.y}px) scale(${zoom})`
|
|
1120
|
+
},
|
|
1121
|
+
draggable: false,
|
|
1122
|
+
onLoad: (e) => {
|
|
1123
|
+
const img = e.currentTarget;
|
|
1124
|
+
const containerWidth = 600;
|
|
1125
|
+
const containerHeight = 400;
|
|
1126
|
+
const cropWidth = cropAspectRatio ? Math.min(containerWidth * 0.8, containerHeight * 0.8 * cropAspectRatio) : containerWidth * 0.8;
|
|
1127
|
+
const cropHeight = cropAspectRatio ? cropWidth / cropAspectRatio : containerHeight * 0.8;
|
|
1128
|
+
const scale = zoom;
|
|
1129
|
+
const imgWidth = img.naturalWidth;
|
|
1130
|
+
const imgHeight = img.naturalHeight;
|
|
1131
|
+
const centerX = containerWidth / 2;
|
|
1132
|
+
const centerY = containerHeight / 2;
|
|
1133
|
+
const cropX = (centerX - crop.x - cropWidth / 2) / scale;
|
|
1134
|
+
const cropY = (centerY - crop.y - cropHeight / 2) / scale;
|
|
1135
|
+
onCropCompleteInternal(null, {
|
|
1136
|
+
x: Math.max(0, cropX),
|
|
1137
|
+
y: Math.max(0, cropY),
|
|
1138
|
+
width: Math.min(cropWidth / scale, imgWidth),
|
|
1139
|
+
height: Math.min(cropHeight / scale, imgHeight)
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
),
|
|
1144
|
+
/* @__PURE__ */ React7__namespace.createElement(
|
|
1145
|
+
"div",
|
|
1146
|
+
{
|
|
1147
|
+
className: "file-input-cropper-overlay-box",
|
|
1148
|
+
style: {
|
|
1149
|
+
width: cropAspectRatio ? `${Math.min(80, 80 * cropAspectRatio)}%` : "80%",
|
|
1150
|
+
aspectRatio: cropAspectRatio ? String(cropAspectRatio) : void 0
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
/* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-grid" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-grid-line" }), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-grid-line" }))
|
|
1154
|
+
)
|
|
1155
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-controls" }, /* @__PURE__ */ React7__namespace.createElement("label", { htmlFor: "zoom-slider", className: "file-input-cropper-label" }, "Zoom: ", zoom.toFixed(1), "x"), /* @__PURE__ */ React7__namespace.createElement(
|
|
1156
|
+
"input",
|
|
1157
|
+
{
|
|
1158
|
+
id: "zoom-slider",
|
|
1159
|
+
type: "range",
|
|
1160
|
+
min: "1",
|
|
1161
|
+
max: "3",
|
|
1162
|
+
step: "0.1",
|
|
1163
|
+
value: zoom,
|
|
1164
|
+
onChange: (e) => onZoomChange(parseFloat(e.target.value)),
|
|
1165
|
+
className: "file-input-cropper-slider",
|
|
1166
|
+
"aria-label": "Zoom level"
|
|
1167
|
+
}
|
|
1168
|
+
))), /* @__PURE__ */ React7__namespace.createElement("div", { className: "file-input-cropper-footer" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1169
|
+
"button",
|
|
1170
|
+
{
|
|
1171
|
+
type: "button",
|
|
1172
|
+
className: "file-input-cropper-button file-input-cropper-button--cancel",
|
|
1173
|
+
onClick: handleCropCancel
|
|
1174
|
+
},
|
|
1175
|
+
"Cancel"
|
|
1176
|
+
), /* @__PURE__ */ React7__namespace.createElement(
|
|
1177
|
+
"button",
|
|
1178
|
+
{
|
|
1179
|
+
type: "button",
|
|
1180
|
+
className: "file-input-cropper-button file-input-cropper-button--save",
|
|
1181
|
+
onClick: handleCropSave
|
|
1182
|
+
},
|
|
1183
|
+
"Save"
|
|
1184
|
+
)))));
|
|
921
1185
|
}
|
|
922
1186
|
FileInput.displayName = "FileInput";
|
|
1187
|
+
function formatDate(date, format) {
|
|
1188
|
+
if (!date) return "";
|
|
1189
|
+
const d = new Date(date);
|
|
1190
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
1191
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
1192
|
+
const year = d.getFullYear();
|
|
1193
|
+
return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
|
|
1194
|
+
}
|
|
1195
|
+
function parseDate(dateString, format) {
|
|
1196
|
+
if (!dateString) return null;
|
|
1197
|
+
try {
|
|
1198
|
+
if (format === "MM/dd/yyyy" || format === "MM-dd-yyyy") {
|
|
1199
|
+
const parts = dateString.split(/[/-]/);
|
|
1200
|
+
if (parts.length === 3) {
|
|
1201
|
+
const month = parseInt(parts[0], 10) - 1;
|
|
1202
|
+
const day = parseInt(parts[1], 10);
|
|
1203
|
+
const year = parseInt(parts[2], 10);
|
|
1204
|
+
const date2 = new Date(year, month, day);
|
|
1205
|
+
if (!isNaN(date2.getTime())) {
|
|
1206
|
+
return date2;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
const date = new Date(dateString);
|
|
1211
|
+
return isNaN(date.getTime()) ? null : date;
|
|
1212
|
+
} catch {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function isDateInArray(date, dates) {
|
|
1217
|
+
const dateStr = date.toDateString();
|
|
1218
|
+
return dates.some((d) => d.toDateString() === dateStr);
|
|
1219
|
+
}
|
|
1220
|
+
function DatePicker({
|
|
1221
|
+
name,
|
|
1222
|
+
value,
|
|
1223
|
+
onChange,
|
|
1224
|
+
onBlur,
|
|
1225
|
+
disabled = false,
|
|
1226
|
+
required = false,
|
|
1227
|
+
error = false,
|
|
1228
|
+
className = "",
|
|
1229
|
+
placeholder = "Select date...",
|
|
1230
|
+
format = "MM/dd/yyyy",
|
|
1231
|
+
minDate,
|
|
1232
|
+
maxDate,
|
|
1233
|
+
disabledDates = [],
|
|
1234
|
+
isDateDisabled,
|
|
1235
|
+
clearable = true,
|
|
1236
|
+
showIcon = true,
|
|
1237
|
+
...props
|
|
1238
|
+
}) {
|
|
1239
|
+
const [isOpen, setIsOpen] = React7__namespace.useState(false);
|
|
1240
|
+
const [inputValue, setInputValue] = React7__namespace.useState("");
|
|
1241
|
+
const [selectedMonth, setSelectedMonth] = React7__namespace.useState(value || /* @__PURE__ */ new Date());
|
|
1242
|
+
const containerRef = React7__namespace.useRef(null);
|
|
1243
|
+
const inputRef = React7__namespace.useRef(null);
|
|
1244
|
+
React7__namespace.useEffect(() => {
|
|
1245
|
+
setInputValue(formatDate(value, format));
|
|
1246
|
+
if (value) {
|
|
1247
|
+
setSelectedMonth(value);
|
|
1248
|
+
}
|
|
1249
|
+
}, [value, format]);
|
|
1250
|
+
const handleDateSelect = (date) => {
|
|
1251
|
+
onChange(date);
|
|
1252
|
+
setIsOpen(false);
|
|
1253
|
+
onBlur?.();
|
|
1254
|
+
};
|
|
1255
|
+
const handleInputChange = (e) => {
|
|
1256
|
+
const newValue = e.target.value;
|
|
1257
|
+
setInputValue(newValue);
|
|
1258
|
+
const parsedDate = parseDate(newValue, format);
|
|
1259
|
+
if (parsedDate && !isNaN(parsedDate.getTime())) {
|
|
1260
|
+
onChange(parsedDate);
|
|
1261
|
+
} else if (newValue === "") {
|
|
1262
|
+
onChange(null);
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
const handleClear = (e) => {
|
|
1266
|
+
e.stopPropagation();
|
|
1267
|
+
onChange(null);
|
|
1268
|
+
setInputValue("");
|
|
1269
|
+
inputRef.current?.focus();
|
|
1270
|
+
};
|
|
1271
|
+
const handleToggle = () => {
|
|
1272
|
+
if (disabled) return;
|
|
1273
|
+
setIsOpen(!isOpen);
|
|
1274
|
+
};
|
|
1275
|
+
const isDisabled = (date) => {
|
|
1276
|
+
if (minDate && date < minDate) return true;
|
|
1277
|
+
if (maxDate && date > maxDate) return true;
|
|
1278
|
+
if (isDateInArray(date, disabledDates)) return true;
|
|
1279
|
+
if (isDateDisabled && isDateDisabled(date)) return true;
|
|
1280
|
+
return false;
|
|
1281
|
+
};
|
|
1282
|
+
React7__namespace.useEffect(() => {
|
|
1283
|
+
const handleClickOutside = (event) => {
|
|
1284
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
1285
|
+
setIsOpen(false);
|
|
1286
|
+
onBlur?.();
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
if (isOpen) {
|
|
1290
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1291
|
+
return () => {
|
|
1292
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
}, [isOpen, onBlur]);
|
|
1296
|
+
const renderCalendar = () => {
|
|
1297
|
+
const year = selectedMonth.getFullYear();
|
|
1298
|
+
const month = selectedMonth.getMonth();
|
|
1299
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
1300
|
+
const firstDayOfMonth = new Date(year, month, 1).getDay();
|
|
1301
|
+
const days = [];
|
|
1302
|
+
for (let i = 0; i < firstDayOfMonth; i++) {
|
|
1303
|
+
days.push(null);
|
|
1304
|
+
}
|
|
1305
|
+
for (let day = 1; day <= daysInMonth; day++) {
|
|
1306
|
+
days.push(new Date(year, month, day));
|
|
1307
|
+
}
|
|
1308
|
+
const monthNames = [
|
|
1309
|
+
"January",
|
|
1310
|
+
"February",
|
|
1311
|
+
"March",
|
|
1312
|
+
"April",
|
|
1313
|
+
"May",
|
|
1314
|
+
"June",
|
|
1315
|
+
"July",
|
|
1316
|
+
"August",
|
|
1317
|
+
"September",
|
|
1318
|
+
"October",
|
|
1319
|
+
"November",
|
|
1320
|
+
"December"
|
|
1321
|
+
];
|
|
1322
|
+
const handlePrevMonth = () => {
|
|
1323
|
+
setSelectedMonth(new Date(year, month - 1, 1));
|
|
1324
|
+
};
|
|
1325
|
+
const handleNextMonth = () => {
|
|
1326
|
+
setSelectedMonth(new Date(year, month + 1, 1));
|
|
1327
|
+
};
|
|
1328
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-calendar", role: "grid", "aria-label": "Calendar" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-calendar-header" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1329
|
+
"button",
|
|
1330
|
+
{
|
|
1331
|
+
type: "button",
|
|
1332
|
+
className: "datepicker-calendar-nav",
|
|
1333
|
+
onClick: handlePrevMonth,
|
|
1334
|
+
"aria-label": "Previous month"
|
|
1335
|
+
},
|
|
1336
|
+
"\u2190"
|
|
1337
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-calendar-month" }, monthNames[month], " ", year), /* @__PURE__ */ React7__namespace.createElement(
|
|
1338
|
+
"button",
|
|
1339
|
+
{
|
|
1340
|
+
type: "button",
|
|
1341
|
+
className: "datepicker-calendar-nav",
|
|
1342
|
+
onClick: handleNextMonth,
|
|
1343
|
+
"aria-label": "Next month"
|
|
1344
|
+
},
|
|
1345
|
+
"\u2192"
|
|
1346
|
+
)), /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-calendar-weekdays" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React7__namespace.createElement("div", { key: day, className: "datepicker-calendar-weekday" }, day))), /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-calendar-days" }, days.map((date, index) => {
|
|
1347
|
+
if (!date) {
|
|
1348
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { key: `empty-${index}`, className: "datepicker-calendar-day datepicker-calendar-day--empty" });
|
|
1349
|
+
}
|
|
1350
|
+
const isSelected = value && date.toDateString() === value.toDateString();
|
|
1351
|
+
const isToday = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
1352
|
+
const disabled2 = isDisabled(date);
|
|
1353
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
1354
|
+
"button",
|
|
1355
|
+
{
|
|
1356
|
+
key: date.toISOString(),
|
|
1357
|
+
type: "button",
|
|
1358
|
+
className: `datepicker-calendar-day ${isSelected ? "datepicker-calendar-day--selected" : ""} ${isToday ? "datepicker-calendar-day--today" : ""} ${disabled2 ? "datepicker-calendar-day--disabled" : ""}`,
|
|
1359
|
+
onClick: () => !disabled2 && handleDateSelect(date),
|
|
1360
|
+
disabled: disabled2,
|
|
1361
|
+
"aria-label": formatDate(date, format)
|
|
1362
|
+
},
|
|
1363
|
+
date.getDate()
|
|
1364
|
+
);
|
|
1365
|
+
})));
|
|
1366
|
+
};
|
|
1367
|
+
const baseClassName = "datepicker";
|
|
1368
|
+
const errorClassName = error ? "datepicker--error" : "";
|
|
1369
|
+
const disabledClassName = disabled ? "datepicker--disabled" : "";
|
|
1370
|
+
const openClassName = isOpen ? "datepicker--open" : "";
|
|
1371
|
+
const combinedClassName = `${baseClassName} ${errorClassName} ${disabledClassName} ${openClassName} ${className}`.trim();
|
|
1372
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1373
|
+
"input",
|
|
1374
|
+
{
|
|
1375
|
+
type: "hidden",
|
|
1376
|
+
name,
|
|
1377
|
+
value: value ? value.toISOString() : ""
|
|
1378
|
+
}
|
|
1379
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-input-wrapper" }, showIcon && /* @__PURE__ */ React7__namespace.createElement("span", { className: "datepicker-icon", "aria-hidden": "true" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1380
|
+
"svg",
|
|
1381
|
+
{
|
|
1382
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1383
|
+
width: "18",
|
|
1384
|
+
height: "18",
|
|
1385
|
+
viewBox: "0 0 24 24",
|
|
1386
|
+
fill: "none",
|
|
1387
|
+
stroke: "currentColor",
|
|
1388
|
+
strokeLinecap: "round",
|
|
1389
|
+
strokeLinejoin: "round",
|
|
1390
|
+
strokeWidth: "2"
|
|
1391
|
+
},
|
|
1392
|
+
/* @__PURE__ */ React7__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" })
|
|
1393
|
+
)), /* @__PURE__ */ React7__namespace.createElement(
|
|
1394
|
+
"input",
|
|
1395
|
+
{
|
|
1396
|
+
ref: inputRef,
|
|
1397
|
+
type: "text",
|
|
1398
|
+
className: "datepicker-input",
|
|
1399
|
+
value: inputValue,
|
|
1400
|
+
onChange: handleInputChange,
|
|
1401
|
+
onClick: handleToggle,
|
|
1402
|
+
onBlur,
|
|
1403
|
+
disabled,
|
|
1404
|
+
required,
|
|
1405
|
+
placeholder,
|
|
1406
|
+
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
1407
|
+
"aria-describedby": props["aria-describedby"],
|
|
1408
|
+
"aria-required": required || props["aria-required"],
|
|
1409
|
+
readOnly: true
|
|
1410
|
+
}
|
|
1411
|
+
), clearable && value && !disabled && /* @__PURE__ */ React7__namespace.createElement(
|
|
1412
|
+
"button",
|
|
1413
|
+
{
|
|
1414
|
+
type: "button",
|
|
1415
|
+
className: "datepicker-clear",
|
|
1416
|
+
onClick: handleClear,
|
|
1417
|
+
"aria-label": "Clear date",
|
|
1418
|
+
tabIndex: -1
|
|
1419
|
+
},
|
|
1420
|
+
"\u2715"
|
|
1421
|
+
)), isOpen && !disabled && /* @__PURE__ */ React7__namespace.createElement("div", { className: "datepicker-dropdown" }, renderCalendar()));
|
|
1422
|
+
}
|
|
1423
|
+
DatePicker.displayName = "DatePicker";
|
|
1424
|
+
function parseTimeString(timeStr, use24Hour) {
|
|
1425
|
+
if (!timeStr) return null;
|
|
1426
|
+
try {
|
|
1427
|
+
if (use24Hour) {
|
|
1428
|
+
const [hourStr, minuteStr] = timeStr.split(":");
|
|
1429
|
+
const hour24 = parseInt(hourStr, 10);
|
|
1430
|
+
const minute = parseInt(minuteStr, 10);
|
|
1431
|
+
if (isNaN(hour24) || isNaN(minute)) return null;
|
|
1432
|
+
if (hour24 < 0 || hour24 > 23) return null;
|
|
1433
|
+
if (minute < 0 || minute > 59) return null;
|
|
1434
|
+
const period = hour24 >= 12 ? "PM" : "AM";
|
|
1435
|
+
const hour = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
|
|
1436
|
+
return { hour, minute, period };
|
|
1437
|
+
} else {
|
|
1438
|
+
const match = timeStr.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
|
|
1439
|
+
if (!match) return null;
|
|
1440
|
+
const hour = parseInt(match[1], 10);
|
|
1441
|
+
const minute = parseInt(match[2], 10);
|
|
1442
|
+
const period = match[3].toUpperCase();
|
|
1443
|
+
if (hour < 1 || hour > 12) return null;
|
|
1444
|
+
if (minute < 0 || minute > 59) return null;
|
|
1445
|
+
return { hour, minute, period };
|
|
1446
|
+
}
|
|
1447
|
+
} catch {
|
|
1448
|
+
return null;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
function formatTimeValue(time, use24Hour) {
|
|
1452
|
+
if (!time) return "";
|
|
1453
|
+
if (use24Hour) {
|
|
1454
|
+
let hour24 = time.hour;
|
|
1455
|
+
if (time.period === "PM" && time.hour !== 12) {
|
|
1456
|
+
hour24 = time.hour + 12;
|
|
1457
|
+
} else if (time.period === "AM" && time.hour === 12) {
|
|
1458
|
+
hour24 = 0;
|
|
1459
|
+
}
|
|
1460
|
+
return `${String(hour24).padStart(2, "0")}:${String(time.minute).padStart(2, "0")}`;
|
|
1461
|
+
} else {
|
|
1462
|
+
return `${time.hour}:${String(time.minute).padStart(2, "0")} ${time.period}`;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
function TimePicker({
|
|
1466
|
+
name,
|
|
1467
|
+
value,
|
|
1468
|
+
onChange,
|
|
1469
|
+
onBlur,
|
|
1470
|
+
disabled = false,
|
|
1471
|
+
required = false,
|
|
1472
|
+
error = false,
|
|
1473
|
+
className = "",
|
|
1474
|
+
placeholder = "Select time...",
|
|
1475
|
+
use24Hour = false,
|
|
1476
|
+
minuteStep = 1,
|
|
1477
|
+
clearable = true,
|
|
1478
|
+
showIcon = true,
|
|
1479
|
+
...props
|
|
1480
|
+
}) {
|
|
1481
|
+
const [isOpen, setIsOpen] = React7__namespace.useState(false);
|
|
1482
|
+
const [timeValue, setTimeValue] = React7__namespace.useState(null);
|
|
1483
|
+
const containerRef = React7__namespace.useRef(null);
|
|
1484
|
+
const inputRef = React7__namespace.useRef(null);
|
|
1485
|
+
React7__namespace.useEffect(() => {
|
|
1486
|
+
const parsed = parseTimeString(value, use24Hour);
|
|
1487
|
+
setTimeValue(parsed);
|
|
1488
|
+
}, [value, use24Hour]);
|
|
1489
|
+
const handleHourChange = (hour) => {
|
|
1490
|
+
const newTime = {
|
|
1491
|
+
hour,
|
|
1492
|
+
minute: timeValue?.minute || 0,
|
|
1493
|
+
period: timeValue?.period || "AM"
|
|
1494
|
+
};
|
|
1495
|
+
setTimeValue(newTime);
|
|
1496
|
+
onChange(formatTimeValue(newTime, use24Hour));
|
|
1497
|
+
};
|
|
1498
|
+
const handleMinuteChange = (minute) => {
|
|
1499
|
+
const newTime = {
|
|
1500
|
+
hour: timeValue?.hour || 12,
|
|
1501
|
+
minute,
|
|
1502
|
+
period: timeValue?.period || "AM"
|
|
1503
|
+
};
|
|
1504
|
+
setTimeValue(newTime);
|
|
1505
|
+
onChange(formatTimeValue(newTime, use24Hour));
|
|
1506
|
+
};
|
|
1507
|
+
const handlePeriodChange = (period) => {
|
|
1508
|
+
const newTime = {
|
|
1509
|
+
hour: timeValue?.hour || 12,
|
|
1510
|
+
minute: timeValue?.minute || 0,
|
|
1511
|
+
period
|
|
1512
|
+
};
|
|
1513
|
+
setTimeValue(newTime);
|
|
1514
|
+
onChange(formatTimeValue(newTime, use24Hour));
|
|
1515
|
+
};
|
|
1516
|
+
const handleClear = (e) => {
|
|
1517
|
+
e.stopPropagation();
|
|
1518
|
+
onChange("");
|
|
1519
|
+
setTimeValue(null);
|
|
1520
|
+
inputRef.current?.focus();
|
|
1521
|
+
};
|
|
1522
|
+
const handleToggle = () => {
|
|
1523
|
+
if (disabled) return;
|
|
1524
|
+
setIsOpen(!isOpen);
|
|
1525
|
+
};
|
|
1526
|
+
React7__namespace.useEffect(() => {
|
|
1527
|
+
const handleClickOutside = (event) => {
|
|
1528
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
1529
|
+
setIsOpen(false);
|
|
1530
|
+
onBlur?.();
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
if (isOpen) {
|
|
1534
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1535
|
+
return () => {
|
|
1536
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
}, [isOpen, onBlur]);
|
|
1540
|
+
const hours = React7__namespace.useMemo(() => {
|
|
1541
|
+
if (use24Hour) {
|
|
1542
|
+
return Array.from({ length: 24 }, (_, i) => i);
|
|
1543
|
+
} else {
|
|
1544
|
+
return Array.from({ length: 12 }, (_, i) => i + 1);
|
|
1545
|
+
}
|
|
1546
|
+
}, [use24Hour]);
|
|
1547
|
+
const minutes = React7__namespace.useMemo(() => {
|
|
1548
|
+
const mins = [];
|
|
1549
|
+
for (let i = 0; i < 60; i += minuteStep) {
|
|
1550
|
+
mins.push(i);
|
|
1551
|
+
}
|
|
1552
|
+
return mins;
|
|
1553
|
+
}, [minuteStep]);
|
|
1554
|
+
const baseClassName = "timepicker";
|
|
1555
|
+
const errorClassName = error ? "timepicker--error" : "";
|
|
1556
|
+
const disabledClassName = disabled ? "timepicker--disabled" : "";
|
|
1557
|
+
const openClassName = isOpen ? "timepicker--open" : "";
|
|
1558
|
+
const combinedClassName = `${baseClassName} ${errorClassName} ${disabledClassName} ${openClassName} ${className}`.trim();
|
|
1559
|
+
const displayValue = formatTimeValue(timeValue, use24Hour);
|
|
1560
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1561
|
+
"input",
|
|
1562
|
+
{
|
|
1563
|
+
type: "hidden",
|
|
1564
|
+
name,
|
|
1565
|
+
value
|
|
1566
|
+
}
|
|
1567
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-input-wrapper" }, showIcon && /* @__PURE__ */ React7__namespace.createElement("span", { className: "timepicker-icon", "aria-hidden": "true" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1568
|
+
"svg",
|
|
1569
|
+
{
|
|
1570
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1571
|
+
width: "18",
|
|
1572
|
+
height: "18",
|
|
1573
|
+
viewBox: "0 0 24 24",
|
|
1574
|
+
fill: "none",
|
|
1575
|
+
stroke: "currentColor",
|
|
1576
|
+
strokeLinecap: "round",
|
|
1577
|
+
strokeLinejoin: "round",
|
|
1578
|
+
strokeWidth: "2"
|
|
1579
|
+
},
|
|
1580
|
+
/* @__PURE__ */ React7__namespace.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1581
|
+
/* @__PURE__ */ React7__namespace.createElement("path", { d: "M12 6v6l4 2" })
|
|
1582
|
+
)), /* @__PURE__ */ React7__namespace.createElement(
|
|
1583
|
+
"input",
|
|
1584
|
+
{
|
|
1585
|
+
ref: inputRef,
|
|
1586
|
+
type: "text",
|
|
1587
|
+
className: "timepicker-input",
|
|
1588
|
+
value: displayValue,
|
|
1589
|
+
onClick: handleToggle,
|
|
1590
|
+
onBlur,
|
|
1591
|
+
disabled,
|
|
1592
|
+
required,
|
|
1593
|
+
placeholder,
|
|
1594
|
+
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
1595
|
+
"aria-describedby": props["aria-describedby"],
|
|
1596
|
+
"aria-required": required || props["aria-required"],
|
|
1597
|
+
readOnly: true
|
|
1598
|
+
}
|
|
1599
|
+
), clearable && value && !disabled && /* @__PURE__ */ React7__namespace.createElement(
|
|
1600
|
+
"button",
|
|
1601
|
+
{
|
|
1602
|
+
type: "button",
|
|
1603
|
+
className: "timepicker-clear",
|
|
1604
|
+
onClick: handleClear,
|
|
1605
|
+
"aria-label": "Clear time",
|
|
1606
|
+
tabIndex: -1
|
|
1607
|
+
},
|
|
1608
|
+
"\u2715"
|
|
1609
|
+
)), isOpen && !disabled && /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-dropdown" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-selectors" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-label" }, use24Hour ? "Hour" : "Hour"), /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-options" }, hours.map((hour) => {
|
|
1610
|
+
const displayHour = use24Hour ? hour : hour;
|
|
1611
|
+
const isSelected = use24Hour ? timeValue?.hour === (hour === 0 ? 12 : hour > 12 ? hour - 12 : hour) && timeValue?.period === (hour >= 12 ? "PM" : "AM") : timeValue?.hour === hour;
|
|
1612
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
1613
|
+
"button",
|
|
1614
|
+
{
|
|
1615
|
+
key: hour,
|
|
1616
|
+
type: "button",
|
|
1617
|
+
className: `timepicker-option ${isSelected ? "timepicker-option--selected" : ""}`,
|
|
1618
|
+
onClick: () => {
|
|
1619
|
+
if (use24Hour) {
|
|
1620
|
+
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
1621
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
1622
|
+
const newTime = {
|
|
1623
|
+
hour: hour12,
|
|
1624
|
+
minute: timeValue?.minute || 0,
|
|
1625
|
+
period
|
|
1626
|
+
};
|
|
1627
|
+
setTimeValue(newTime);
|
|
1628
|
+
onChange(formatTimeValue(newTime, use24Hour));
|
|
1629
|
+
} else {
|
|
1630
|
+
handleHourChange(hour);
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
"aria-label": `${String(displayHour).padStart(2, "0")} hours`
|
|
1634
|
+
},
|
|
1635
|
+
String(displayHour).padStart(2, "0")
|
|
1636
|
+
);
|
|
1637
|
+
}))), /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-label" }, "Minute"), /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-options" }, minutes.map((minute) => {
|
|
1638
|
+
const isSelected = timeValue?.minute === minute;
|
|
1639
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
1640
|
+
"button",
|
|
1641
|
+
{
|
|
1642
|
+
key: minute,
|
|
1643
|
+
type: "button",
|
|
1644
|
+
className: `timepicker-option ${isSelected ? "timepicker-option--selected" : ""}`,
|
|
1645
|
+
onClick: () => handleMinuteChange(minute),
|
|
1646
|
+
"aria-label": `${String(minute).padStart(2, "0")} minutes`
|
|
1647
|
+
},
|
|
1648
|
+
String(minute).padStart(2, "0")
|
|
1649
|
+
);
|
|
1650
|
+
}))), !use24Hour && /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column timepicker-column--period" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-label" }, "Period"), /* @__PURE__ */ React7__namespace.createElement("div", { className: "timepicker-column-options" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1651
|
+
"button",
|
|
1652
|
+
{
|
|
1653
|
+
type: "button",
|
|
1654
|
+
className: `timepicker-option ${timeValue?.period === "AM" ? "timepicker-option--selected" : ""}`,
|
|
1655
|
+
onClick: () => handlePeriodChange("AM")
|
|
1656
|
+
},
|
|
1657
|
+
"AM"
|
|
1658
|
+
), /* @__PURE__ */ React7__namespace.createElement(
|
|
1659
|
+
"button",
|
|
1660
|
+
{
|
|
1661
|
+
type: "button",
|
|
1662
|
+
className: `timepicker-option ${timeValue?.period === "PM" ? "timepicker-option--selected" : ""}`,
|
|
1663
|
+
onClick: () => handlePeriodChange("PM")
|
|
1664
|
+
},
|
|
1665
|
+
"PM"
|
|
1666
|
+
))))));
|
|
1667
|
+
}
|
|
1668
|
+
TimePicker.displayName = "TimePicker";
|
|
1669
|
+
function formatDate2(date, format) {
|
|
1670
|
+
if (!date) return "";
|
|
1671
|
+
const d = new Date(date);
|
|
1672
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
1673
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
1674
|
+
const year = d.getFullYear();
|
|
1675
|
+
return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
|
|
1676
|
+
}
|
|
1677
|
+
function isDateInArray2(date, dates) {
|
|
1678
|
+
const dateStr = date.toDateString();
|
|
1679
|
+
return dates.some((d) => d.toDateString() === dateStr);
|
|
1680
|
+
}
|
|
1681
|
+
function isDateInRange(date, start, end) {
|
|
1682
|
+
if (!start || !end) return false;
|
|
1683
|
+
const time = date.getTime();
|
|
1684
|
+
return time >= start.getTime() && time <= end.getTime();
|
|
1685
|
+
}
|
|
1686
|
+
function DateRangePicker({
|
|
1687
|
+
name,
|
|
1688
|
+
value = { start: null, end: null },
|
|
1689
|
+
onChange,
|
|
1690
|
+
onBlur,
|
|
1691
|
+
disabled = false,
|
|
1692
|
+
required = false,
|
|
1693
|
+
error = false,
|
|
1694
|
+
className = "",
|
|
1695
|
+
placeholder = "Select date range...",
|
|
1696
|
+
format = "MM/dd/yyyy",
|
|
1697
|
+
minDate,
|
|
1698
|
+
maxDate,
|
|
1699
|
+
disabledDates = [],
|
|
1700
|
+
isDateDisabled,
|
|
1701
|
+
clearable = true,
|
|
1702
|
+
showIcon = true,
|
|
1703
|
+
separator = " - ",
|
|
1704
|
+
...props
|
|
1705
|
+
}) {
|
|
1706
|
+
const [isOpen, setIsOpen] = React7__namespace.useState(false);
|
|
1707
|
+
const [selectedMonth, setSelectedMonth] = React7__namespace.useState(value.start || /* @__PURE__ */ new Date());
|
|
1708
|
+
const [rangeStart, setRangeStart] = React7__namespace.useState(value.start);
|
|
1709
|
+
const [rangeEnd, setRangeEnd] = React7__namespace.useState(value.end);
|
|
1710
|
+
const [hoverDate, setHoverDate] = React7__namespace.useState(null);
|
|
1711
|
+
const containerRef = React7__namespace.useRef(null);
|
|
1712
|
+
React7__namespace.useEffect(() => {
|
|
1713
|
+
setRangeStart(value.start);
|
|
1714
|
+
setRangeEnd(value.end);
|
|
1715
|
+
if (value.start) {
|
|
1716
|
+
setSelectedMonth(value.start);
|
|
1717
|
+
}
|
|
1718
|
+
}, [value]);
|
|
1719
|
+
const handleDateSelect = (date) => {
|
|
1720
|
+
if (!rangeStart || rangeStart && rangeEnd) {
|
|
1721
|
+
setRangeStart(date);
|
|
1722
|
+
setRangeEnd(null);
|
|
1723
|
+
onChange({ start: date, end: null });
|
|
1724
|
+
} else {
|
|
1725
|
+
if (date < rangeStart) {
|
|
1726
|
+
setRangeStart(date);
|
|
1727
|
+
setRangeEnd(rangeStart);
|
|
1728
|
+
onChange({ start: date, end: rangeStart });
|
|
1729
|
+
setIsOpen(false);
|
|
1730
|
+
} else {
|
|
1731
|
+
setRangeEnd(date);
|
|
1732
|
+
onChange({ start: rangeStart, end: date });
|
|
1733
|
+
setIsOpen(false);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
onBlur?.();
|
|
1737
|
+
};
|
|
1738
|
+
const handleClear = (e) => {
|
|
1739
|
+
e.stopPropagation();
|
|
1740
|
+
onChange({ start: null, end: null });
|
|
1741
|
+
setRangeStart(null);
|
|
1742
|
+
setRangeEnd(null);
|
|
1743
|
+
};
|
|
1744
|
+
const handleToggle = () => {
|
|
1745
|
+
if (disabled) return;
|
|
1746
|
+
setIsOpen(!isOpen);
|
|
1747
|
+
};
|
|
1748
|
+
const isDisabled = (date) => {
|
|
1749
|
+
if (minDate && date < minDate) return true;
|
|
1750
|
+
if (maxDate && date > maxDate) return true;
|
|
1751
|
+
if (isDateInArray2(date, disabledDates)) return true;
|
|
1752
|
+
if (isDateDisabled && isDateDisabled(date)) return true;
|
|
1753
|
+
return false;
|
|
1754
|
+
};
|
|
1755
|
+
React7__namespace.useEffect(() => {
|
|
1756
|
+
const handleClickOutside = (event) => {
|
|
1757
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
1758
|
+
setIsOpen(false);
|
|
1759
|
+
onBlur?.();
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
if (isOpen) {
|
|
1763
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1764
|
+
return () => {
|
|
1765
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
}, [isOpen, onBlur]);
|
|
1769
|
+
const renderCalendar = () => {
|
|
1770
|
+
const year = selectedMonth.getFullYear();
|
|
1771
|
+
const month = selectedMonth.getMonth();
|
|
1772
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
1773
|
+
const firstDayOfMonth = new Date(year, month, 1).getDay();
|
|
1774
|
+
const days = [];
|
|
1775
|
+
for (let i = 0; i < firstDayOfMonth; i++) {
|
|
1776
|
+
days.push(null);
|
|
1777
|
+
}
|
|
1778
|
+
for (let day = 1; day <= daysInMonth; day++) {
|
|
1779
|
+
days.push(new Date(year, month, day));
|
|
1780
|
+
}
|
|
1781
|
+
const monthNames = [
|
|
1782
|
+
"January",
|
|
1783
|
+
"February",
|
|
1784
|
+
"March",
|
|
1785
|
+
"April",
|
|
1786
|
+
"May",
|
|
1787
|
+
"June",
|
|
1788
|
+
"July",
|
|
1789
|
+
"August",
|
|
1790
|
+
"September",
|
|
1791
|
+
"October",
|
|
1792
|
+
"November",
|
|
1793
|
+
"December"
|
|
1794
|
+
];
|
|
1795
|
+
const handlePrevMonth = () => {
|
|
1796
|
+
setSelectedMonth(new Date(year, month - 1, 1));
|
|
1797
|
+
};
|
|
1798
|
+
const handleNextMonth = () => {
|
|
1799
|
+
setSelectedMonth(new Date(year, month + 1, 1));
|
|
1800
|
+
};
|
|
1801
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-calendar", role: "grid", "aria-label": "Calendar" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-calendar-header" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1802
|
+
"button",
|
|
1803
|
+
{
|
|
1804
|
+
type: "button",
|
|
1805
|
+
className: "daterangepicker-calendar-nav",
|
|
1806
|
+
onClick: handlePrevMonth,
|
|
1807
|
+
"aria-label": "Previous month"
|
|
1808
|
+
},
|
|
1809
|
+
"\u2190"
|
|
1810
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-calendar-month" }, monthNames[month], " ", year), /* @__PURE__ */ React7__namespace.createElement(
|
|
1811
|
+
"button",
|
|
1812
|
+
{
|
|
1813
|
+
type: "button",
|
|
1814
|
+
className: "daterangepicker-calendar-nav",
|
|
1815
|
+
onClick: handleNextMonth,
|
|
1816
|
+
"aria-label": "Next month"
|
|
1817
|
+
},
|
|
1818
|
+
"\u2192"
|
|
1819
|
+
)), /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-calendar-weekdays" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React7__namespace.createElement("div", { key: day, className: "daterangepicker-calendar-weekday" }, day))), /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-calendar-days" }, days.map((date, index) => {
|
|
1820
|
+
if (!date) {
|
|
1821
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { key: `empty-${index}`, className: "daterangepicker-calendar-day daterangepicker-calendar-day--empty" });
|
|
1822
|
+
}
|
|
1823
|
+
const isStart = rangeStart && date.toDateString() === rangeStart.toDateString();
|
|
1824
|
+
const isEnd = rangeEnd && date.toDateString() === rangeEnd.toDateString();
|
|
1825
|
+
const isInRange = rangeStart && rangeEnd && isDateInRange(date, rangeStart, rangeEnd);
|
|
1826
|
+
const isInHoverRange = rangeStart && !rangeEnd && hoverDate && (date >= rangeStart && date <= hoverDate || date <= rangeStart && date >= hoverDate);
|
|
1827
|
+
const isToday = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
1828
|
+
const disabled2 = isDisabled(date);
|
|
1829
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
1830
|
+
"button",
|
|
1831
|
+
{
|
|
1832
|
+
key: date.toISOString(),
|
|
1833
|
+
type: "button",
|
|
1834
|
+
className: `daterangepicker-calendar-day ${isStart || isEnd ? "daterangepicker-calendar-day--selected" : ""} ${isInRange || isInHoverRange ? "daterangepicker-calendar-day--in-range" : ""} ${isToday ? "daterangepicker-calendar-day--today" : ""} ${disabled2 ? "daterangepicker-calendar-day--disabled" : ""}`,
|
|
1835
|
+
onClick: () => !disabled2 && handleDateSelect(date),
|
|
1836
|
+
onMouseEnter: () => setHoverDate(date),
|
|
1837
|
+
onMouseLeave: () => setHoverDate(null),
|
|
1838
|
+
disabled: disabled2,
|
|
1839
|
+
"aria-label": formatDate2(date, format)
|
|
1840
|
+
},
|
|
1841
|
+
date.getDate()
|
|
1842
|
+
);
|
|
1843
|
+
})));
|
|
1844
|
+
};
|
|
1845
|
+
const baseClassName = "daterangepicker";
|
|
1846
|
+
const errorClassName = error ? "daterangepicker--error" : "";
|
|
1847
|
+
const disabledClassName = disabled ? "daterangepicker--disabled" : "";
|
|
1848
|
+
const openClassName = isOpen ? "daterangepicker--open" : "";
|
|
1849
|
+
const combinedClassName = `${baseClassName} ${errorClassName} ${disabledClassName} ${openClassName} ${className}`.trim();
|
|
1850
|
+
const displayValue = rangeStart && rangeEnd ? `${formatDate2(rangeStart, format)}${separator}${formatDate2(rangeEnd, format)}` : rangeStart ? formatDate2(rangeStart, format) : "";
|
|
1851
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { ref: containerRef, className: combinedClassName }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1852
|
+
"input",
|
|
1853
|
+
{
|
|
1854
|
+
type: "hidden",
|
|
1855
|
+
name: `${name}[start]`,
|
|
1856
|
+
value: rangeStart ? rangeStart.toISOString() : ""
|
|
1857
|
+
}
|
|
1858
|
+
), /* @__PURE__ */ React7__namespace.createElement(
|
|
1859
|
+
"input",
|
|
1860
|
+
{
|
|
1861
|
+
type: "hidden",
|
|
1862
|
+
name: `${name}[end]`,
|
|
1863
|
+
value: rangeEnd ? rangeEnd.toISOString() : ""
|
|
1864
|
+
}
|
|
1865
|
+
), /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-input-wrapper" }, showIcon && /* @__PURE__ */ React7__namespace.createElement("span", { className: "daterangepicker-icon", "aria-hidden": "true" }, /* @__PURE__ */ React7__namespace.createElement(
|
|
1866
|
+
"svg",
|
|
1867
|
+
{
|
|
1868
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1869
|
+
width: "18",
|
|
1870
|
+
height: "18",
|
|
1871
|
+
viewBox: "0 0 24 24",
|
|
1872
|
+
fill: "none",
|
|
1873
|
+
stroke: "currentColor",
|
|
1874
|
+
strokeLinecap: "round",
|
|
1875
|
+
strokeLinejoin: "round",
|
|
1876
|
+
strokeWidth: "2"
|
|
1877
|
+
},
|
|
1878
|
+
/* @__PURE__ */ React7__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" })
|
|
1879
|
+
)), /* @__PURE__ */ React7__namespace.createElement(
|
|
1880
|
+
"input",
|
|
1881
|
+
{
|
|
1882
|
+
type: "text",
|
|
1883
|
+
className: "daterangepicker-input",
|
|
1884
|
+
value: displayValue,
|
|
1885
|
+
onClick: handleToggle,
|
|
1886
|
+
onBlur,
|
|
1887
|
+
disabled,
|
|
1888
|
+
required,
|
|
1889
|
+
placeholder,
|
|
1890
|
+
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
1891
|
+
"aria-describedby": props["aria-describedby"],
|
|
1892
|
+
"aria-required": required || props["aria-required"],
|
|
1893
|
+
readOnly: true
|
|
1894
|
+
}
|
|
1895
|
+
), clearable && (rangeStart || rangeEnd) && !disabled && /* @__PURE__ */ React7__namespace.createElement(
|
|
1896
|
+
"button",
|
|
1897
|
+
{
|
|
1898
|
+
type: "button",
|
|
1899
|
+
className: "daterangepicker-clear",
|
|
1900
|
+
onClick: handleClear,
|
|
1901
|
+
"aria-label": "Clear date range",
|
|
1902
|
+
tabIndex: -1
|
|
1903
|
+
},
|
|
1904
|
+
"\u2715"
|
|
1905
|
+
)), isOpen && !disabled && /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-dropdown" }, renderCalendar(), rangeStart && !rangeEnd && /* @__PURE__ */ React7__namespace.createElement("div", { className: "daterangepicker-hint" }, "Select end date")));
|
|
1906
|
+
}
|
|
1907
|
+
DateRangePicker.displayName = "DateRangePicker";
|
|
1908
|
+
function htmlToMarkdown(html) {
|
|
1909
|
+
let markdown = html;
|
|
1910
|
+
markdown = markdown.replace(/<strong>(.*?)<\/strong>/g, "**$1**");
|
|
1911
|
+
markdown = markdown.replace(/<b>(.*?)<\/b>/g, "**$1**");
|
|
1912
|
+
markdown = markdown.replace(/<em>(.*?)<\/em>/g, "*$1*");
|
|
1913
|
+
markdown = markdown.replace(/<i>(.*?)<\/i>/g, "*$1*");
|
|
1914
|
+
markdown = markdown.replace(/<u>(.*?)<\/u>/g, "<u>$1</u>");
|
|
1915
|
+
markdown = markdown.replace(/<h1>(.*?)<\/h1>/g, "# $1\n");
|
|
1916
|
+
markdown = markdown.replace(/<h2>(.*?)<\/h2>/g, "## $1\n");
|
|
1917
|
+
markdown = markdown.replace(/<h3>(.*?)<\/h3>/g, "### $1\n");
|
|
1918
|
+
markdown = markdown.replace(/<a href="(.*?)">(.*?)<\/a>/g, "[$2]($1)");
|
|
1919
|
+
markdown = markdown.replace(/<ul>(.*?)<\/ul>/gs, (_match, content) => {
|
|
1920
|
+
return content.replace(/<li>(.*?)<\/li>/g, "- $1\n");
|
|
1921
|
+
});
|
|
1922
|
+
markdown = markdown.replace(/<ol>(.*?)<\/ol>/gs, (_match, content) => {
|
|
1923
|
+
let counter = 1;
|
|
1924
|
+
return content.replace(/<li>(.*?)<\/li>/g, () => {
|
|
1925
|
+
return `${counter++}. $1
|
|
1926
|
+
`;
|
|
1927
|
+
});
|
|
1928
|
+
});
|
|
1929
|
+
markdown = markdown.replace(/<p>(.*?)<\/p>/g, "$1\n\n");
|
|
1930
|
+
markdown = markdown.replace(/<br\s*\/?>/g, "\n");
|
|
1931
|
+
markdown = markdown.replace(/\n{3,}/g, "\n\n").trim();
|
|
1932
|
+
return markdown;
|
|
1933
|
+
}
|
|
1934
|
+
function markdownToHtml(markdown) {
|
|
1935
|
+
let html = markdown;
|
|
1936
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1937
|
+
html = html.replace(/^### (.*?)$/gm, "<h3>$1</h3>");
|
|
1938
|
+
html = html.replace(/^## (.*?)$/gm, "<h2>$1</h2>");
|
|
1939
|
+
html = html.replace(/^# (.*?)$/gm, "<h1>$1</h1>");
|
|
1940
|
+
html = html.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
|
|
1941
|
+
html = html.replace(/\*(.*?)\*/g, "<em>$1</em>");
|
|
1942
|
+
html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>');
|
|
1943
|
+
html = html.replace(/^- (.*?)$/gm, "<li>$1</li>");
|
|
1944
|
+
html = html.replace(/(<li>.*?<\/li>\n?)+/gs, "<ul>$&</ul>");
|
|
1945
|
+
html = html.replace(/^\d+\. (.*?)$/gm, "<li>$1</li>");
|
|
1946
|
+
html = html.replace(/^(?!<[h|ul|ol|li])(.+)$/gm, "<p>$1</p>");
|
|
1947
|
+
html = html.replace(/\n/g, "<br>");
|
|
1948
|
+
return html;
|
|
1949
|
+
}
|
|
1950
|
+
function RichTextEditor({
|
|
1951
|
+
name,
|
|
1952
|
+
value = "",
|
|
1953
|
+
onChange,
|
|
1954
|
+
onBlur,
|
|
1955
|
+
disabled = false,
|
|
1956
|
+
required = false,
|
|
1957
|
+
error = false,
|
|
1958
|
+
className = "",
|
|
1959
|
+
mode = "wysiwyg",
|
|
1960
|
+
allowModeSwitch = false,
|
|
1961
|
+
placeholder = "Start typing...",
|
|
1962
|
+
minHeight = "200px",
|
|
1963
|
+
maxHeight,
|
|
1964
|
+
showToolbar = true,
|
|
1965
|
+
toolbarButtons = ["bold", "italic", "underline", "heading", "bulletList", "orderedList", "link"],
|
|
1966
|
+
...props
|
|
1967
|
+
}) {
|
|
1968
|
+
const [currentMode, setCurrentMode] = React7__namespace.useState(mode);
|
|
1969
|
+
const [content, setContent] = React7__namespace.useState(value);
|
|
1970
|
+
const editorRef = React7__namespace.useRef(null);
|
|
1971
|
+
const textareaRef = React7__namespace.useRef(null);
|
|
1972
|
+
React7__namespace.useEffect(() => {
|
|
1973
|
+
setContent(value);
|
|
1974
|
+
if (currentMode === "wysiwyg" && editorRef.current) {
|
|
1975
|
+
editorRef.current.innerHTML = value;
|
|
1976
|
+
}
|
|
1977
|
+
}, [value, currentMode]);
|
|
1978
|
+
const handleWysiwygChange = () => {
|
|
1979
|
+
if (editorRef.current) {
|
|
1980
|
+
const newContent = editorRef.current.innerHTML;
|
|
1981
|
+
setContent(newContent);
|
|
1982
|
+
onChange(newContent);
|
|
1983
|
+
}
|
|
1984
|
+
};
|
|
1985
|
+
const handleMarkdownChange = (e) => {
|
|
1986
|
+
const newContent = e.target.value;
|
|
1987
|
+
setContent(newContent);
|
|
1988
|
+
onChange(newContent);
|
|
1989
|
+
};
|
|
1990
|
+
const execCommand = (command, value2) => {
|
|
1991
|
+
document.execCommand(command, false, value2);
|
|
1992
|
+
editorRef.current?.focus();
|
|
1993
|
+
handleWysiwygChange();
|
|
1994
|
+
};
|
|
1995
|
+
const handleModeToggle = () => {
|
|
1996
|
+
const newMode = currentMode === "wysiwyg" ? "markdown" : "wysiwyg";
|
|
1997
|
+
if (newMode === "markdown") {
|
|
1998
|
+
const markdown = htmlToMarkdown(content);
|
|
1999
|
+
setContent(markdown);
|
|
2000
|
+
onChange(markdown);
|
|
2001
|
+
} else {
|
|
2002
|
+
const html = markdownToHtml(content);
|
|
2003
|
+
setContent(html);
|
|
2004
|
+
onChange(html);
|
|
2005
|
+
if (editorRef.current) {
|
|
2006
|
+
editorRef.current.innerHTML = html;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
setCurrentMode(newMode);
|
|
2010
|
+
};
|
|
2011
|
+
const toolbarConfig = {
|
|
2012
|
+
bold: {
|
|
2013
|
+
command: "bold",
|
|
2014
|
+
icon: "B",
|
|
2015
|
+
title: "Bold",
|
|
2016
|
+
action: () => execCommand("bold")
|
|
2017
|
+
},
|
|
2018
|
+
italic: {
|
|
2019
|
+
command: "italic",
|
|
2020
|
+
icon: "I",
|
|
2021
|
+
title: "Italic",
|
|
2022
|
+
action: () => execCommand("italic")
|
|
2023
|
+
},
|
|
2024
|
+
underline: {
|
|
2025
|
+
command: "underline",
|
|
2026
|
+
icon: "U",
|
|
2027
|
+
title: "Underline",
|
|
2028
|
+
action: () => execCommand("underline")
|
|
2029
|
+
},
|
|
2030
|
+
heading: {
|
|
2031
|
+
command: "heading",
|
|
2032
|
+
icon: "H",
|
|
2033
|
+
title: "Heading",
|
|
2034
|
+
action: () => execCommand("formatBlock", "<h2>")
|
|
2035
|
+
},
|
|
2036
|
+
bulletList: {
|
|
2037
|
+
command: "bulletList",
|
|
2038
|
+
icon: "\u2022",
|
|
2039
|
+
title: "Bullet List",
|
|
2040
|
+
action: () => execCommand("insertUnorderedList")
|
|
2041
|
+
},
|
|
2042
|
+
orderedList: {
|
|
2043
|
+
command: "orderedList",
|
|
2044
|
+
icon: "1.",
|
|
2045
|
+
title: "Numbered List",
|
|
2046
|
+
action: () => execCommand("insertOrderedList")
|
|
2047
|
+
},
|
|
2048
|
+
link: {
|
|
2049
|
+
command: "link",
|
|
2050
|
+
icon: "\u{1F517}",
|
|
2051
|
+
title: "Insert Link",
|
|
2052
|
+
action: () => {
|
|
2053
|
+
const url = window.prompt("Enter URL:");
|
|
2054
|
+
if (url) {
|
|
2055
|
+
execCommand("createLink", url);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
const baseClassName = "richtexteditor";
|
|
2061
|
+
const errorClassName = error ? "richtexteditor--error" : "";
|
|
2062
|
+
const disabledClassName = disabled ? "richtexteditor--disabled" : "";
|
|
2063
|
+
const modeClassName = `richtexteditor--${currentMode}`;
|
|
2064
|
+
const combinedClassName = `${baseClassName} ${errorClassName} ${disabledClassName} ${modeClassName} ${className}`.trim();
|
|
2065
|
+
const editorStyle = {
|
|
2066
|
+
minHeight,
|
|
2067
|
+
maxHeight,
|
|
2068
|
+
overflowY: maxHeight ? "auto" : void 0
|
|
2069
|
+
};
|
|
2070
|
+
return /* @__PURE__ */ React7__namespace.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React7__namespace.createElement("input", { type: "hidden", name, value: content }), showToolbar && /* @__PURE__ */ React7__namespace.createElement("div", { className: "richtexteditor-toolbar" }, /* @__PURE__ */ React7__namespace.createElement("div", { className: "richtexteditor-toolbar-buttons" }, toolbarButtons.map((buttonName) => {
|
|
2071
|
+
const button = toolbarConfig[buttonName];
|
|
2072
|
+
if (!button) return null;
|
|
2073
|
+
return /* @__PURE__ */ React7__namespace.createElement(
|
|
2074
|
+
"button",
|
|
2075
|
+
{
|
|
2076
|
+
key: buttonName,
|
|
2077
|
+
type: "button",
|
|
2078
|
+
className: "richtexteditor-toolbar-button",
|
|
2079
|
+
onClick: () => editorRef.current && button.action(editorRef.current),
|
|
2080
|
+
title: button.title,
|
|
2081
|
+
disabled: disabled || currentMode === "markdown",
|
|
2082
|
+
"aria-label": button.title
|
|
2083
|
+
},
|
|
2084
|
+
button.icon
|
|
2085
|
+
);
|
|
2086
|
+
})), allowModeSwitch && /* @__PURE__ */ React7__namespace.createElement(
|
|
2087
|
+
"button",
|
|
2088
|
+
{
|
|
2089
|
+
type: "button",
|
|
2090
|
+
className: "richtexteditor-mode-toggle",
|
|
2091
|
+
onClick: handleModeToggle,
|
|
2092
|
+
disabled,
|
|
2093
|
+
title: `Switch to ${currentMode === "wysiwyg" ? "Markdown" : "WYSIWYG"}`,
|
|
2094
|
+
"aria-label": `Switch to ${currentMode === "wysiwyg" ? "Markdown" : "WYSIWYG"}`
|
|
2095
|
+
},
|
|
2096
|
+
currentMode === "wysiwyg" ? "MD" : "WYSIWYG"
|
|
2097
|
+
)), /* @__PURE__ */ React7__namespace.createElement("div", { className: "richtexteditor-editor", style: editorStyle }, currentMode === "wysiwyg" ? /* @__PURE__ */ React7__namespace.createElement(
|
|
2098
|
+
"div",
|
|
2099
|
+
{
|
|
2100
|
+
ref: editorRef,
|
|
2101
|
+
className: "richtexteditor-content",
|
|
2102
|
+
role: "textbox",
|
|
2103
|
+
contentEditable: !disabled,
|
|
2104
|
+
onInput: handleWysiwygChange,
|
|
2105
|
+
onBlur,
|
|
2106
|
+
"data-placeholder": placeholder,
|
|
2107
|
+
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
2108
|
+
"aria-describedby": props["aria-describedby"],
|
|
2109
|
+
"aria-required": required || props["aria-required"],
|
|
2110
|
+
suppressContentEditableWarning: true
|
|
2111
|
+
}
|
|
2112
|
+
) : /* @__PURE__ */ React7__namespace.createElement(
|
|
2113
|
+
"textarea",
|
|
2114
|
+
{
|
|
2115
|
+
ref: textareaRef,
|
|
2116
|
+
className: "richtexteditor-markdown",
|
|
2117
|
+
value: content,
|
|
2118
|
+
onChange: handleMarkdownChange,
|
|
2119
|
+
onBlur,
|
|
2120
|
+
disabled,
|
|
2121
|
+
required,
|
|
2122
|
+
placeholder,
|
|
2123
|
+
"aria-invalid": error || props["aria-invalid"] ? "true" : "false",
|
|
2124
|
+
"aria-describedby": props["aria-describedby"],
|
|
2125
|
+
"aria-required": required || props["aria-required"]
|
|
2126
|
+
}
|
|
2127
|
+
)));
|
|
2128
|
+
}
|
|
2129
|
+
RichTextEditor.displayName = "RichTextEditor";
|
|
923
2130
|
|
|
924
2131
|
exports.Checkbox = Checkbox;
|
|
925
2132
|
exports.CheckboxGroup = CheckboxGroup;
|
|
2133
|
+
exports.DatePicker = DatePicker;
|
|
2134
|
+
exports.DateRangePicker = DateRangePicker;
|
|
926
2135
|
exports.FileInput = FileInput;
|
|
927
2136
|
exports.Radio = Radio;
|
|
2137
|
+
exports.RichTextEditor = RichTextEditor;
|
|
928
2138
|
exports.Select = Select;
|
|
929
2139
|
exports.TextArea = TextArea;
|
|
930
2140
|
exports.TextInput = TextInput;
|
|
2141
|
+
exports.TimePicker = TimePicker;
|
|
931
2142
|
//# sourceMappingURL=inputs.cjs.map
|
|
932
2143
|
//# sourceMappingURL=inputs.cjs.map
|