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