@yca-software/yca-react-core 0.0.22 → 0.0.24

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.
@@ -4,10 +4,10 @@ import { cn, surfaceOverlay, controlFocus, controlInvalid, controlBase, surfaceC
4
4
  import { cva } from 'class-variance-authority';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
7
- import * as React8 from 'react';
7
+ import * as React20 from 'react';
8
8
  import { useRef, useId, useState, useEffect } from 'react';
9
9
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
10
- import { Check, Calendar as Calendar$1, ChevronLeft, ChevronRight, Loader2, XIcon, ChevronUp, ChevronDown, X, CheckSquare, Search, FileText, ImagePlus, UploadCloud } from 'lucide-react';
10
+ import { Check, Calendar as Calendar$1, ChevronLeft, ChevronRight, Loader2, XIcon, ChevronUp, ChevronDown, Info, X, CheckSquare, Search, FileText, ImagePlus, UploadCloud } from 'lucide-react';
11
11
  import { DayPicker } from 'react-day-picker';
12
12
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
13
13
  import { format, isSameMonth, isSameDay, addDays, startOfMonth, startOfDay, endOfDay, isWithinInterval, addMonths, addWeeks, startOfWeek, endOfWeek, parse } from 'date-fns';
@@ -68,7 +68,7 @@ function AlertDescription({ className, ...props }) {
68
68
  var AlertDialog = AlertDialogPrimitive.Root;
69
69
  var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
70
70
  var AlertDialogPortal = AlertDialogPrimitive.Portal;
71
- var AlertDialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
71
+ var AlertDialogOverlay = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
72
72
  AlertDialogPrimitive.Overlay,
73
73
  {
74
74
  className: cn(
@@ -80,7 +80,7 @@ var AlertDialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /*
80
80
  }
81
81
  ));
82
82
  AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
83
- var AlertDialogContent = React8.forwardRef(({ className, onOverlayClick, overlayClassName, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
83
+ var AlertDialogContent = React20.forwardRef(({ className, onOverlayClick, overlayClassName, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
84
84
  /* @__PURE__ */ jsx(AlertDialogOverlay, { onClick: onOverlayClick, className: overlayClassName }),
85
85
  /* @__PURE__ */ jsx(
86
86
  AlertDialogPrimitive.Content,
@@ -106,7 +106,7 @@ var AlertDialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx(
106
106
  }
107
107
  );
108
108
  AlertDialogFooter.displayName = "AlertDialogFooter";
109
- var AlertDialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
109
+ var AlertDialogTitle = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
110
110
  AlertDialogPrimitive.Title,
111
111
  {
112
112
  ref,
@@ -115,7 +115,7 @@ var AlertDialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @_
115
115
  }
116
116
  ));
117
117
  AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
118
- var AlertDialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
118
+ var AlertDialogDescription = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
119
119
  AlertDialogPrimitive.Description,
120
120
  {
121
121
  ref,
@@ -124,7 +124,7 @@ var AlertDialogDescription = React8.forwardRef(({ className, ...props }, ref) =>
124
124
  }
125
125
  ));
126
126
  AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
127
- var AlertDialogAction = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
127
+ var AlertDialogAction = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
128
128
  AlertDialogPrimitive.Action,
129
129
  {
130
130
  ref,
@@ -134,7 +134,7 @@ var AlertDialogAction = React8.forwardRef(({ className, ...props }, ref) => /* @
134
134
  }
135
135
  ));
136
136
  AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
137
- var AlertDialogCancel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
137
+ var AlertDialogCancel = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
138
138
  AlertDialogPrimitive.Cancel,
139
139
  {
140
140
  ref,
@@ -144,7 +144,7 @@ var AlertDialogCancel = React8.forwardRef(({ className, ...props }, ref) => /* @
144
144
  }
145
145
  ));
146
146
  AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
147
- var Avatar = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
147
+ var Avatar = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
148
148
  AvatarPrimitive.Root,
149
149
  {
150
150
  ref,
@@ -153,7 +153,7 @@ var Avatar = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
153
153
  }
154
154
  ));
155
155
  Avatar.displayName = AvatarPrimitive.Root.displayName;
156
- var AvatarImage = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
156
+ var AvatarImage = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
157
157
  AvatarPrimitive.Image,
158
158
  {
159
159
  ref,
@@ -162,7 +162,7 @@ var AvatarImage = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
162
162
  }
163
163
  ));
164
164
  AvatarImage.displayName = AvatarPrimitive.Image.displayName;
165
- var AvatarFallback = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
165
+ var AvatarFallback = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
166
166
  AvatarPrimitive.Fallback,
167
167
  {
168
168
  ref,
@@ -254,7 +254,7 @@ function Calendar({
254
254
  }
255
255
  );
256
256
  }
257
- var Checkbox = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
257
+ var Checkbox = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
258
258
  CheckboxPrimitive.Root,
259
259
  {
260
260
  ref,
@@ -406,11 +406,11 @@ function DatePicker({
406
406
  className,
407
407
  "aria-label": ariaLabel
408
408
  }) {
409
- const [open, setOpen] = React8.useState(false);
409
+ const [open, setOpen] = React20.useState(false);
410
410
  const normalizedMin = normalizeDate(minDate);
411
411
  const normalizedMax = normalizeDate(maxDate);
412
- const formatOpts = React8.useMemo(() => locale ? { locale } : void 0, [locale]);
413
- const disabledMatchers = React8.useMemo(() => {
412
+ const formatOpts = React20.useMemo(() => locale ? { locale } : void 0, [locale]);
413
+ const disabledMatchers = React20.useMemo(() => {
414
414
  const toStartOfDay = (d) => {
415
415
  const copy = new Date(d);
416
416
  copy.setHours(0, 0, 0, 0);
@@ -481,7 +481,7 @@ function DatePicker({
481
481
  ) })
482
482
  ] });
483
483
  }
484
- var InsideModalScrollLockContext = React8.createContext(false);
484
+ var InsideModalScrollLockContext = React20.createContext(false);
485
485
  function InsideModalScrollLockProvider({
486
486
  children,
487
487
  active = true
@@ -489,7 +489,7 @@ function InsideModalScrollLockProvider({
489
489
  return /* @__PURE__ */ jsx(InsideModalScrollLockContext.Provider, { value: active, children });
490
490
  }
491
491
  function useInsideModalScrollLock() {
492
- return React8.useContext(InsideModalScrollLockContext);
492
+ return React20.useContext(InsideModalScrollLockContext);
493
493
  }
494
494
  function Dialog({ ...props }) {
495
495
  return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
@@ -896,7 +896,7 @@ function DateRangePickerPresetList({
896
896
  })
897
897
  ] }) });
898
898
  }
899
- var DateRangePickerTrigger = React8.forwardRef(
899
+ var DateRangePickerTrigger = React20.forwardRef(
900
900
  function DateRangePickerTrigger2({
901
901
  className,
902
902
  inputText,
@@ -1018,7 +1018,7 @@ function getPresetRange(presetId) {
1018
1018
  }
1019
1019
  }
1020
1020
  function useMediaQuery(query, fallback = false) {
1021
- const subscribe = React8.useCallback(
1021
+ const subscribe = React20.useCallback(
1022
1022
  (onChange) => {
1023
1023
  const mql = window.matchMedia(query);
1024
1024
  mql.addEventListener("change", onChange);
@@ -1026,7 +1026,7 @@ function useMediaQuery(query, fallback = false) {
1026
1026
  },
1027
1027
  [query]
1028
1028
  );
1029
- return React8.useSyncExternalStore(
1029
+ return React20.useSyncExternalStore(
1030
1030
  subscribe,
1031
1031
  () => window.matchMedia(query).matches,
1032
1032
  () => fallback
@@ -1054,7 +1054,7 @@ function DateRangePicker({
1054
1054
  }) {
1055
1055
  const t = { ...DEFAULT_TRANSLATIONS, ...translations };
1056
1056
  const effectivePlaceholder = placeholder ?? t.placeholder;
1057
- const presetList = React8.useMemo(() => {
1057
+ const presetList = React20.useMemo(() => {
1058
1058
  if (customPresets?.length) return customPresets;
1059
1059
  if (!showPresets) return [];
1060
1060
  return DEFAULT_PRESET_IDS.map(({ id }) => ({
@@ -1068,16 +1068,16 @@ function DateRangePicker({
1068
1068
  const isMobileLayout = useIsDatePickerMobileLayout();
1069
1069
  const isWideLayout = useIsWideDatePickerLayout();
1070
1070
  const calendarMonths = isMobileLayout ? 1 : isWideLayout ? 2 : 1;
1071
- const [open, setOpen] = React8.useState(false);
1072
- const [draft, setDraft] = React8.useState(value);
1073
- const [displayedMonth, setDisplayedMonth] = React8.useState(() => value?.from ?? /* @__PURE__ */ new Date());
1074
- const appliedRef = React8.useRef(value);
1075
- const wasOpenRef = React8.useRef(false);
1071
+ const [open, setOpen] = React20.useState(false);
1072
+ const [draft, setDraft] = React20.useState(value);
1073
+ const [displayedMonth, setDisplayedMonth] = React20.useState(() => value?.from ?? /* @__PURE__ */ new Date());
1074
+ const appliedRef = React20.useRef(value);
1075
+ const wasOpenRef = React20.useRef(false);
1076
1076
  const useApplyFlow = !!onApply;
1077
- const idScope = React8.useId();
1077
+ const idScope = React20.useId();
1078
1078
  const startInputId = `date-range-start-${idScope}`;
1079
1079
  const endInputId = `date-range-end-${idScope}`;
1080
- React8.useEffect(() => {
1080
+ React20.useEffect(() => {
1081
1081
  const wasOpen = wasOpenRef.current;
1082
1082
  wasOpenRef.current = open;
1083
1083
  if (open && !wasOpen) {
@@ -1088,7 +1088,7 @@ function DateRangePicker({
1088
1088
  setDraft(value);
1089
1089
  }
1090
1090
  }, [open, value]);
1091
- const handleOpenChange = React8.useCallback(
1091
+ const handleOpenChange = React20.useCallback(
1092
1092
  (next) => {
1093
1093
  if (!next && open && !useApplyFlow) {
1094
1094
  onChange(appliedRef.current);
@@ -1098,14 +1098,14 @@ function DateRangePicker({
1098
1098
  },
1099
1099
  [open, useApplyFlow, onChange]
1100
1100
  );
1101
- const formatOpts = React8.useMemo(() => locale ? { locale } : void 0, [locale]);
1102
- const label = React8.useMemo(
1101
+ const formatOpts = React20.useMemo(() => locale ? { locale } : void 0, [locale]);
1102
+ const label = React20.useMemo(
1103
1103
  () => formatRangeLabel(value, effectivePlaceholder, formatOpts),
1104
1104
  [effectivePlaceholder, value, formatOpts]
1105
1105
  );
1106
- const [inputText, setInputText] = React8.useState(label);
1107
- const inputFocusedRef = React8.useRef(false);
1108
- React8.useEffect(() => {
1106
+ const [inputText, setInputText] = React20.useState(label);
1107
+ const inputFocusedRef = React20.useRef(false);
1108
+ React20.useEffect(() => {
1109
1109
  if (!inputFocusedRef.current) setInputText(label);
1110
1110
  }, [label]);
1111
1111
  const handleInputBlur = () => {
@@ -1124,7 +1124,7 @@ function DateRangePicker({
1124
1124
  setInputText(label);
1125
1125
  }
1126
1126
  };
1127
- const disabledMatchers = React8.useMemo(() => {
1127
+ const disabledMatchers = React20.useMemo(() => {
1128
1128
  const toStartOfDay = (d) => {
1129
1129
  const copy = new Date(d);
1130
1130
  copy.setHours(0, 0, 0, 0);
@@ -1182,9 +1182,9 @@ function DateRangePicker({
1182
1182
  return nextRange;
1183
1183
  });
1184
1184
  };
1185
- const [startInputText, setStartInputText] = React8.useState("");
1186
- const [endInputText, setEndInputText] = React8.useState("");
1187
- React8.useEffect(() => {
1185
+ const [startInputText, setStartInputText] = React20.useState("");
1186
+ const [endInputText, setEndInputText] = React20.useState("");
1187
+ React20.useEffect(() => {
1188
1188
  if (open) {
1189
1189
  setStartInputText(draft?.from ? format(draft.from, INPUT_FORMAT) : "");
1190
1190
  setEndInputText(draft?.to ? format(draft.to, INPUT_FORMAT) : "");
@@ -1384,10 +1384,10 @@ function DropdownScrollableList({
1384
1384
  scrollAreaClassName,
1385
1385
  grow = true
1386
1386
  }) {
1387
- const scrollRef = React8.useRef(null);
1388
- const [showTop, setShowTop] = React8.useState(false);
1389
- const [showBottom, setShowBottom] = React8.useState(false);
1390
- const update = React8.useCallback(() => {
1387
+ const scrollRef = React20.useRef(null);
1388
+ const [showTop, setShowTop] = React20.useState(false);
1389
+ const [showBottom, setShowBottom] = React20.useState(false);
1390
+ const update = React20.useCallback(() => {
1391
1391
  const el = scrollRef.current;
1392
1392
  if (!el) {
1393
1393
  setShowTop(false);
@@ -1404,14 +1404,14 @@ function DropdownScrollableList({
1404
1404
  setShowTop(scrollTop > 2);
1405
1405
  setShowBottom(scrollTop + clientHeight < scrollHeight - 2);
1406
1406
  }, []);
1407
- React8.useLayoutEffect(() => {
1407
+ React20.useLayoutEffect(() => {
1408
1408
  update();
1409
1409
  const id = requestAnimationFrame(() => {
1410
1410
  requestAnimationFrame(update);
1411
1411
  });
1412
1412
  return () => cancelAnimationFrame(id);
1413
1413
  }, [update, scrollKey]);
1414
- React8.useEffect(() => {
1414
+ React20.useEffect(() => {
1415
1415
  const el = scrollRef.current;
1416
1416
  if (!el) return;
1417
1417
  if (typeof ResizeObserver === "undefined") {
@@ -1541,10 +1541,10 @@ function titleForView(anchor, view, weekStartsOnMonday) {
1541
1541
  }
1542
1542
  var MAX_MONTH_CHIPS = 3;
1543
1543
  function useControlledDate(value, defaultValue, onChange) {
1544
- const [internal, setInternal] = React8.useState(defaultValue ?? /* @__PURE__ */ new Date());
1544
+ const [internal, setInternal] = React20.useState(defaultValue ?? /* @__PURE__ */ new Date());
1545
1545
  const isControlled = value !== void 0;
1546
1546
  const current = isControlled ? value : internal;
1547
- const set = React8.useCallback(
1547
+ const set = React20.useCallback(
1548
1548
  (next) => {
1549
1549
  if (!isControlled) setInternal(next);
1550
1550
  onChange?.(next);
@@ -1554,10 +1554,10 @@ function useControlledDate(value, defaultValue, onChange) {
1554
1554
  return [current, set];
1555
1555
  }
1556
1556
  function useControlledView(value, defaultValue, onChange) {
1557
- const [internal, setInternal] = React8.useState(defaultValue);
1557
+ const [internal, setInternal] = React20.useState(defaultValue);
1558
1558
  const isControlled = value !== void 0;
1559
1559
  const current = isControlled ? value : internal;
1560
- const set = React8.useCallback(
1560
+ const set = React20.useCallback(
1561
1561
  (next) => {
1562
1562
  if (!isControlled) setInternal(next);
1563
1563
  onChange?.(next);
@@ -1607,7 +1607,7 @@ function EventCalendar({
1607
1607
  }) {
1608
1608
  const [safeAnchor, setAnchor] = useControlledDate(value, defaultValue, onValueChange);
1609
1609
  const [safeView, setViewSafe] = useControlledView(viewProp, defaultView, onViewChange);
1610
- const weekdays = React8.useMemo(
1610
+ const weekdays = React20.useMemo(
1611
1611
  () => weekdayLabels(weekStartsOnMonday, safeAnchor),
1612
1612
  [weekStartsOnMonday, safeAnchor]
1613
1613
  );
@@ -1819,15 +1819,15 @@ function Label({
1819
1819
  return /* @__PURE__ */ jsx(LabelPrimitive.Root, { "data-slot": "label", className: cn(labelVariants(), className), ...props });
1820
1820
  }
1821
1821
  var Form = FormProvider;
1822
- var FormFieldContext = React8.createContext(null);
1822
+ var FormFieldContext = React20.createContext(null);
1823
1823
  var FormField = ({
1824
1824
  ...props
1825
1825
  }) => {
1826
1826
  return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
1827
1827
  };
1828
1828
  var useFormField = () => {
1829
- const fieldContext = React8.useContext(FormFieldContext);
1830
- const itemContext = React8.useContext(FormItemContext);
1829
+ const fieldContext = React20.useContext(FormFieldContext);
1830
+ const itemContext = React20.useContext(FormItemContext);
1831
1831
  if (fieldContext === null || fieldContext === void 0) {
1832
1832
  throw new Error("useFormField should be used within <FormField>");
1833
1833
  }
@@ -1844,9 +1844,9 @@ var useFormField = () => {
1844
1844
  ...fieldState
1845
1845
  };
1846
1846
  };
1847
- var FormItemContext = React8.createContext({});
1847
+ var FormItemContext = React20.createContext({});
1848
1848
  function FormItem({ className, ...props }) {
1849
- const id = React8.useId();
1849
+ const id = React20.useId();
1850
1850
  return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx("div", { "data-slot": "form-item", className: cn("grid gap-3", className), ...props }) });
1851
1851
  }
1852
1852
  function FormLabel({
@@ -2025,29 +2025,66 @@ function FormDrawer({
2025
2025
  onOpenChange,
2026
2026
  title,
2027
2027
  description,
2028
+ info,
2028
2029
  children,
2029
2030
  className,
2030
2031
  contentClassName
2031
2032
  }) {
2033
+ const bodyRef = React20.useRef(null);
2034
+ const [infoOpen, setInfoOpen] = React20.useState(false);
2035
+ React20.useEffect(() => {
2036
+ if (!open) setInfoOpen(false);
2037
+ }, [open]);
2032
2038
  return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2033
2039
  SheetContent,
2034
2040
  {
2035
2041
  "data-slot": "form-drawer-content",
2042
+ onOpenAutoFocus: (event) => {
2043
+ event.preventDefault();
2044
+ bodyRef.current?.focus({ preventScroll: true });
2045
+ },
2046
+ onCloseAutoFocus: (event) => event.preventDefault(),
2036
2047
  className: cn(
2037
2048
  "flex flex-col gap-4 w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] sm:max-w-lg overflow-hidden",
2038
2049
  contentClassName
2039
2050
  ),
2040
2051
  children: [
2041
2052
  /* @__PURE__ */ jsxs(SheetHeader, { className: "flex-shrink-0 gap-2 pb-2", children: [
2042
- /* @__PURE__ */ jsx(SheetTitle, { className: "text-base sm:text-lg", children: title }),
2043
- description && /* @__PURE__ */ jsx(SheetDescription, { className: "text-sm", children: description })
2053
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-1.5 text-base sm:text-lg", children: [
2054
+ /* @__PURE__ */ jsx("span", { className: "min-w-0", children: title }),
2055
+ info ? /* @__PURE__ */ jsxs(Popover, { open: infoOpen, onOpenChange: setInfoOpen, children: [
2056
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2057
+ "button",
2058
+ {
2059
+ type: "button",
2060
+ className: "inline-flex shrink-0 cursor-help rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2061
+ "aria-label": "More info",
2062
+ "aria-expanded": infoOpen,
2063
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5", "aria-hidden": true })
2064
+ }
2065
+ ) }),
2066
+ /* @__PURE__ */ jsx(
2067
+ PopoverContent,
2068
+ {
2069
+ side: "bottom",
2070
+ align: "start",
2071
+ sideOffset: 6,
2072
+ className: "z-[120] max-w-xs p-3 text-sm leading-snug",
2073
+ children: info
2074
+ }
2075
+ )
2076
+ ] }) : null
2077
+ ] }),
2078
+ description ? /* @__PURE__ */ jsx(SheetDescription, { className: "text-sm", children: description }) : null
2044
2079
  ] }),
2045
2080
  /* @__PURE__ */ jsx(InsideModalScrollLockProvider, { children: /* @__PURE__ */ jsx(
2046
2081
  "div",
2047
2082
  {
2083
+ ref: bodyRef,
2084
+ tabIndex: -1,
2048
2085
  className: cn(
2049
2086
  // Inset so focus rings / box-shadow on full-width inputs are not clipped by overflow-y-auto
2050
- "flex flex-col flex-1 min-h-0 gap-4 overflow-y-auto px-2 py-1",
2087
+ "flex flex-col flex-1 min-h-0 gap-4 overflow-y-auto px-2 py-1 outline-none",
2051
2088
  className
2052
2089
  ),
2053
2090
  children
@@ -2106,14 +2143,14 @@ function MultiSelect({
2106
2143
  }) {
2107
2144
  const t = { ...DEFAULT_TRANSLATIONS2, ...translations };
2108
2145
  const insideModalScrollLock = useInsideModalScrollLock();
2109
- const [search, setSearch] = React8.useState("");
2110
- const selectedSet = React8.useMemo(() => new Set(value), [value]);
2111
- const labelByValue = React8.useMemo(() => {
2146
+ const [search, setSearch] = React20.useState("");
2147
+ const selectedSet = React20.useMemo(() => new Set(value), [value]);
2148
+ const labelByValue = React20.useMemo(() => {
2112
2149
  const map = /* @__PURE__ */ new Map();
2113
2150
  for (const opt of options) map.set(opt.value, opt.label);
2114
2151
  return map;
2115
2152
  }, [options]);
2116
- const filteredOptions = React8.useMemo(() => {
2153
+ const filteredOptions = React20.useMemo(() => {
2117
2154
  const q = search.trim().toLowerCase();
2118
2155
  if (!q) return options;
2119
2156
  return options.filter((opt) => opt.label.toLowerCase().includes(q));
@@ -2209,7 +2246,7 @@ function MultiSelect({
2209
2246
  ] })
2210
2247
  ] });
2211
2248
  }
2212
- var ScrollArea = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
2249
+ var ScrollArea = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
2213
2250
  ScrollAreaPrimitive.Root,
2214
2251
  {
2215
2252
  ref,
@@ -2223,7 +2260,7 @@ var ScrollArea = React8.forwardRef(({ className, children, ...props }, ref) => /
2223
2260
  }
2224
2261
  ));
2225
2262
  ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
2226
- var ScrollBar = React8.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
2263
+ var ScrollBar = React20.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
2227
2264
  ScrollAreaPrimitive.ScrollAreaScrollbar,
2228
2265
  {
2229
2266
  ref,
@@ -2361,10 +2398,10 @@ function Select({
2361
2398
  }) {
2362
2399
  const t = { ...DEFAULT_TRANSLATIONS3, ...translations };
2363
2400
  const insideModalScrollLock = useInsideModalScrollLock();
2364
- const [open, setOpen] = React8.useState(false);
2365
- const [search, setSearch] = React8.useState("");
2401
+ const [open, setOpen] = React20.useState(false);
2402
+ const [search, setSearch] = React20.useState("");
2366
2403
  const showSearch = isSelectSearchable(options.length, searchable);
2367
- const filteredOptions = React8.useMemo(
2404
+ const filteredOptions = React20.useMemo(
2368
2405
  () => showSearch ? filterSelectOptions(options, search) : options,
2369
2406
  [options, search, showSearch]
2370
2407
  );
@@ -2389,7 +2426,7 @@ function Select({
2389
2426
  controlFocus,
2390
2427
  "flex h-9 w-full cursor-pointer items-center justify-between px-3 py-2 text-sm",
2391
2428
  "hover:bg-accent/40 disabled:pointer-events-none disabled:opacity-50",
2392
- !value && "text-muted-foreground",
2429
+ !selectedOption && "text-muted-foreground",
2393
2430
  triggerClassName,
2394
2431
  className
2395
2432
  ),
@@ -2429,10 +2466,8 @@ function Select({
2429
2466
  children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-1.5 text-sm text-muted-foreground", children: t.noOptionsText }) : filteredOptions.map((opt) => /* @__PURE__ */ jsx(
2430
2467
  DropdownMenuItem,
2431
2468
  {
2432
- onClick: () => {
2469
+ onSelect: () => {
2433
2470
  onValueChange(opt.value);
2434
- setOpen(false);
2435
- setSearch("");
2436
2471
  },
2437
2472
  children: opt.label
2438
2473
  },
@@ -2468,7 +2503,7 @@ function ChevronDownIcon({ className, ...props }) {
2468
2503
  }
2469
2504
  );
2470
2505
  }
2471
- var Separator = React8.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
2506
+ var Separator = React20.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
2472
2507
  SeparatorPrimitive.Root,
2473
2508
  {
2474
2509
  ref,
@@ -2483,7 +2518,7 @@ var Separator = React8.forwardRef(({ className, orientation = "horizontal", deco
2483
2518
  }
2484
2519
  ));
2485
2520
  Separator.displayName = SeparatorPrimitive.Root.displayName;
2486
- var Switch = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2521
+ var Switch = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2487
2522
  SwitchPrimitive.Root,
2488
2523
  {
2489
2524
  ref,
@@ -2509,11 +2544,11 @@ var Switch = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
2509
2544
  }
2510
2545
  ));
2511
2546
  Switch.displayName = SwitchPrimitive.Root.displayName;
2512
- var Table = React8.forwardRef(
2547
+ var Table = React20.forwardRef(
2513
2548
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })
2514
2549
  );
2515
2550
  Table.displayName = "Table";
2516
- var TableHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2551
+ var TableHeader = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2517
2552
  "thead",
2518
2553
  {
2519
2554
  ref,
@@ -2522,9 +2557,9 @@ var TableHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
2522
2557
  }
2523
2558
  ));
2524
2559
  TableHeader.displayName = "TableHeader";
2525
- var TableBody = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
2560
+ var TableBody = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
2526
2561
  TableBody.displayName = "TableBody";
2527
- var TableFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2562
+ var TableFooter = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2528
2563
  "tfoot",
2529
2564
  {
2530
2565
  ref,
@@ -2536,7 +2571,7 @@ var TableFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
2536
2571
  }
2537
2572
  ));
2538
2573
  TableFooter.displayName = "TableFooter";
2539
- var TableRow = React8.forwardRef(
2574
+ var TableRow = React20.forwardRef(
2540
2575
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2541
2576
  "tr",
2542
2577
  {
@@ -2550,7 +2585,7 @@ var TableRow = React8.forwardRef(
2550
2585
  )
2551
2586
  );
2552
2587
  TableRow.displayName = "TableRow";
2553
- var TableHead = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2588
+ var TableHead = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2554
2589
  "th",
2555
2590
  {
2556
2591
  ref,
@@ -2562,7 +2597,7 @@ var TableHead = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2562
2597
  }
2563
2598
  ));
2564
2599
  TableHead.displayName = "TableHead";
2565
- var TableCell = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2600
+ var TableCell = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2566
2601
  "td",
2567
2602
  {
2568
2603
  ref,
@@ -2571,7 +2606,7 @@ var TableCell = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2571
2606
  }
2572
2607
  ));
2573
2608
  TableCell.displayName = "TableCell";
2574
- var TableCaption = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
2609
+ var TableCaption = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
2575
2610
  TableCaption.displayName = "TableCaption";
2576
2611
  function Tabs({ className, ...props }) {
2577
2612
  return /* @__PURE__ */ jsx(
@@ -2631,7 +2666,7 @@ function TabsContent({
2631
2666
  }
2632
2667
  );
2633
2668
  }
2634
- var Textarea = React8.forwardRef(
2669
+ var Textarea = React20.forwardRef(
2635
2670
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2636
2671
  "textarea",
2637
2672
  {
@@ -2652,58 +2687,157 @@ var Textarea = React8.forwardRef(
2652
2687
  );
2653
2688
  Textarea.displayName = "Textarea";
2654
2689
  var TooltipProvider = TooltipPrimitive.Provider;
2690
+ function prefersCoarsePointer() {
2691
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
2692
+ return window.matchMedia("(pointer: coarse)").matches;
2693
+ }
2694
+ function isPointerOver(el, x, y) {
2695
+ if (!el || typeof document.elementFromPoint !== "function") return false;
2696
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
2697
+ const under = document.elementFromPoint(x, y);
2698
+ return Boolean(under && (under === el || el.contains(under)));
2699
+ }
2655
2700
  function Tooltip({
2656
2701
  content,
2657
2702
  children,
2658
2703
  side = "top",
2659
2704
  align = "center",
2660
- delayDuration = 400,
2661
- closeDelay = 100
2705
+ delayDuration = 200,
2706
+ closeDelay = 280
2662
2707
  }) {
2663
- const [open, setOpen] = React8.useState(false);
2664
- const openTimeoutRef = React8.useRef(null);
2665
- const closeTimeoutRef = React8.useRef(null);
2666
- const clearOpenTimeout = React8.useCallback(() => {
2708
+ const [open, setOpen] = React20.useState(false);
2709
+ const triggerRef = React20.useRef(null);
2710
+ const stickyRef = React20.useRef(false);
2711
+ const hoveringRef = React20.useRef(false);
2712
+ const openRef = React20.useRef(false);
2713
+ const hoverArmedRef = React20.useRef(false);
2714
+ const layoutReadyRef = React20.useRef(false);
2715
+ const lastPointerRef = React20.useRef({ x: Number.NaN, y: Number.NaN });
2716
+ const openTimeoutRef = React20.useRef(null);
2717
+ const closeTimeoutRef = React20.useRef(null);
2718
+ React20.useEffect(() => {
2719
+ openRef.current = open;
2720
+ }, [open]);
2721
+ React20.useLayoutEffect(() => {
2722
+ hoverArmedRef.current = false;
2723
+ stickyRef.current = false;
2724
+ hoveringRef.current = false;
2725
+ layoutReadyRef.current = false;
2726
+ setOpen(false);
2727
+ const markReady = () => {
2728
+ layoutReadyRef.current = true;
2729
+ const el = triggerRef.current;
2730
+ const { x, y } = lastPointerRef.current;
2731
+ if (isPointerOver(el, x, y)) {
2732
+ hoverArmedRef.current = false;
2733
+ return;
2734
+ }
2735
+ hoverArmedRef.current = true;
2736
+ };
2737
+ let raf2 = 0;
2738
+ const raf1 = window.requestAnimationFrame(() => {
2739
+ raf2 = window.requestAnimationFrame(markReady);
2740
+ });
2741
+ const onPointerMove = (event) => {
2742
+ lastPointerRef.current = { x: event.clientX, y: event.clientY };
2743
+ if (!layoutReadyRef.current) return;
2744
+ if (isPointerOver(triggerRef.current, event.clientX, event.clientY)) return;
2745
+ hoverArmedRef.current = true;
2746
+ };
2747
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
2748
+ return () => {
2749
+ window.cancelAnimationFrame(raf1);
2750
+ window.cancelAnimationFrame(raf2);
2751
+ window.removeEventListener("pointermove", onPointerMove);
2752
+ };
2753
+ }, []);
2754
+ const clearOpenTimeout = React20.useCallback(() => {
2667
2755
  if (openTimeoutRef.current !== null) {
2668
2756
  window.clearTimeout(openTimeoutRef.current);
2669
2757
  openTimeoutRef.current = null;
2670
2758
  }
2671
2759
  }, []);
2672
- const clearCloseTimeout = React8.useCallback(() => {
2760
+ const clearCloseTimeout = React20.useCallback(() => {
2673
2761
  if (closeTimeoutRef.current !== null) {
2674
2762
  window.clearTimeout(closeTimeoutRef.current);
2675
2763
  closeTimeoutRef.current = null;
2676
2764
  }
2677
2765
  }, []);
2678
- const handleOpenChange = React8.useCallback(
2766
+ const scheduleClose = React20.useCallback(() => {
2767
+ if (stickyRef.current || hoveringRef.current) return;
2768
+ clearOpenTimeout();
2769
+ clearCloseTimeout();
2770
+ if (closeDelay === 0) {
2771
+ setOpen(false);
2772
+ return;
2773
+ }
2774
+ closeTimeoutRef.current = window.setTimeout(() => {
2775
+ if (stickyRef.current || hoveringRef.current) return;
2776
+ setOpen(false);
2777
+ }, closeDelay);
2778
+ }, [clearCloseTimeout, clearOpenTimeout, closeDelay]);
2779
+ const handleOpenChange = React20.useCallback(
2679
2780
  (next) => {
2680
- if (!next) {
2781
+ if (next) {
2782
+ if (!hoveringRef.current && !stickyRef.current) return;
2783
+ setOpen(true);
2784
+ return;
2785
+ }
2786
+ if (stickyRef.current) {
2787
+ stickyRef.current = false;
2788
+ hoveringRef.current = false;
2681
2789
  clearOpenTimeout();
2682
2790
  clearCloseTimeout();
2683
2791
  setOpen(false);
2792
+ return;
2684
2793
  }
2794
+ scheduleClose();
2685
2795
  },
2686
- [clearCloseTimeout, clearOpenTimeout]
2796
+ [clearCloseTimeout, clearOpenTimeout, scheduleClose]
2687
2797
  );
2688
- const handlePointerEnter = React8.useCallback(() => {
2798
+ const handlePointerEnter = React20.useCallback(() => {
2799
+ if (!hoverArmedRef.current) return;
2800
+ hoveringRef.current = true;
2689
2801
  clearCloseTimeout();
2690
2802
  clearOpenTimeout();
2803
+ if (openRef.current || delayDuration <= 0) {
2804
+ setOpen(true);
2805
+ return;
2806
+ }
2691
2807
  openTimeoutRef.current = window.setTimeout(() => {
2808
+ if (!hoveringRef.current || !hoverArmedRef.current) return;
2692
2809
  setOpen(true);
2693
2810
  }, delayDuration);
2694
2811
  }, [clearCloseTimeout, clearOpenTimeout, delayDuration]);
2695
- const handlePointerLeave = React8.useCallback(() => {
2696
- clearOpenTimeout();
2697
- clearCloseTimeout();
2698
- if (closeDelay === 0) {
2699
- setOpen(false);
2700
- return;
2701
- }
2702
- closeTimeoutRef.current = window.setTimeout(() => {
2703
- setOpen(false);
2704
- }, closeDelay);
2705
- }, [clearCloseTimeout, clearOpenTimeout, closeDelay]);
2706
- React8.useEffect(
2812
+ const handlePointerLeave = React20.useCallback(() => {
2813
+ hoverArmedRef.current = true;
2814
+ hoveringRef.current = false;
2815
+ scheduleClose();
2816
+ }, [scheduleClose]);
2817
+ const blockRadixFocusOpen = React20.useCallback((e) => {
2818
+ e.preventDefault();
2819
+ }, []);
2820
+ const blockRadixPointerMoveOpen = React20.useCallback((e) => {
2821
+ e.preventDefault();
2822
+ }, []);
2823
+ const handleTriggerClick = React20.useCallback(
2824
+ (e) => {
2825
+ if (!prefersCoarsePointer()) {
2826
+ e.preventDefault();
2827
+ return;
2828
+ }
2829
+ clearOpenTimeout();
2830
+ clearCloseTimeout();
2831
+ const next = !open;
2832
+ stickyRef.current = next;
2833
+ hoveringRef.current = next;
2834
+ hoverArmedRef.current = true;
2835
+ setOpen(next);
2836
+ e.preventDefault();
2837
+ },
2838
+ [clearCloseTimeout, clearOpenTimeout, open]
2839
+ );
2840
+ React20.useEffect(
2707
2841
  () => () => {
2708
2842
  clearOpenTimeout();
2709
2843
  clearCloseTimeout();
@@ -2714,9 +2848,13 @@ function Tooltip({
2714
2848
  /* @__PURE__ */ jsx(
2715
2849
  TooltipPrimitive.Trigger,
2716
2850
  {
2851
+ ref: triggerRef,
2717
2852
  asChild: true,
2853
+ onFocus: blockRadixFocusOpen,
2854
+ onPointerMove: blockRadixPointerMoveOpen,
2718
2855
  onPointerEnter: handlePointerEnter,
2719
2856
  onPointerLeave: handlePointerLeave,
2857
+ onClick: handleTriggerClick,
2720
2858
  children
2721
2859
  }
2722
2860
  ),
@@ -2764,7 +2902,7 @@ var paragraphVariants = cva("text-muted-foreground leading-relaxed", {
2764
2902
  size: "default"
2765
2903
  }
2766
2904
  });
2767
- var Heading = React8.forwardRef(
2905
+ var Heading = React20.forwardRef(
2768
2906
  ({ className, level, as, ...props }, ref) => {
2769
2907
  const Component = as || (level === "h1" ? "h1" : level === "h2" ? "h2" : level === "h3" ? "h3" : "h4");
2770
2908
  return /* @__PURE__ */ jsx(
@@ -2778,13 +2916,13 @@ var Heading = React8.forwardRef(
2778
2916
  }
2779
2917
  );
2780
2918
  Heading.displayName = "Heading";
2781
- var Paragraph = React8.forwardRef(
2919
+ var Paragraph = React20.forwardRef(
2782
2920
  ({ className, size, ...props }, ref) => {
2783
2921
  return /* @__PURE__ */ jsx("p", { ref, className: cn(paragraphVariants({ size }), className), ...props });
2784
2922
  }
2785
2923
  );
2786
2924
  Paragraph.displayName = "Paragraph";
2787
- var H3 = React8.forwardRef(
2925
+ var H3 = React20.forwardRef(
2788
2926
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(Heading, { ref, level: "h3", as: "h3", className, ...props })
2789
2927
  );
2790
2928
  H3.displayName = "H3";
@@ -2794,10 +2932,30 @@ var DEFAULT_TRANSLATIONS4 = {
2794
2932
  chooseImageText: "Choose image",
2795
2933
  chooseFileText: "Choose file",
2796
2934
  removeText: "Remove",
2797
- removeExistingImageText: "Remove existing image"
2935
+ removeExistingImageText: "Remove existing image",
2936
+ removeFileAriaLabel: "Remove file"
2798
2937
  };
2938
+ function fileMatchesAccept(file, accept) {
2939
+ if (!accept?.trim()) return true;
2940
+ const tokens = accept.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
2941
+ if (!tokens.length) return true;
2942
+ const name = file.name.toLowerCase();
2943
+ const type = (file.type || "").toLowerCase();
2944
+ return tokens.some((token) => {
2945
+ if (token.startsWith(".")) return name.endsWith(token);
2946
+ if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
2947
+ return type === token;
2948
+ });
2949
+ }
2950
+ function fileKey(file) {
2951
+ return `${file.name}:${file.size}:${file.lastModified}`;
2952
+ }
2953
+ function isFileLike(value) {
2954
+ return typeof value === "object" && value != null && typeof value.name === "string" && typeof value.size === "number";
2955
+ }
2799
2956
  function UploadDropzone(props) {
2800
2957
  const isFile = props.variant === "file";
2958
+ const isMultiple = isFile && (props.multiple === true || Array.isArray(props.value));
2801
2959
  const inputId = useId();
2802
2960
  const removeExistingLabelId = useId();
2803
2961
  const pickerDisabled = isFile && props.disabled;
@@ -2805,40 +2963,83 @@ function UploadDropzone(props) {
2805
2963
  const inputRef = useRef(null);
2806
2964
  const [isDragging, setIsDragging] = useState(false);
2807
2965
  const [previewUrl, setPreviewUrl] = useState(null);
2966
+ const singleFile = isFile && !isMultiple && isFileLike(props.value) ? props.value : null;
2967
+ const multiFiles = isMultiple && Array.isArray(props.value) ? props.value.filter(isFileLike) : [];
2968
+ const accept = isFile ? props.accept : "image/*";
2969
+ const setSingleFile = (file) => {
2970
+ if (isFile && !isMultiple) {
2971
+ props.onChange(file);
2972
+ return;
2973
+ }
2974
+ if (!isFile) {
2975
+ props.onChange(file);
2976
+ props.onRemoveExistingImageChange?.(false);
2977
+ }
2978
+ };
2979
+ const setMultiFiles = (next) => {
2980
+ if (isMultiple) props.onChange(next);
2981
+ };
2982
+ const imageFile = !isFile && isFileLike(props.value) ? props.value : null;
2808
2983
  useEffect(() => {
2809
2984
  if (isFile) {
2810
- if (!props.value || !props.value.type.startsWith("video/")) {
2985
+ const mime = singleFile?.type || "";
2986
+ if (!singleFile || !mime.startsWith("video/")) {
2811
2987
  setPreviewUrl(null);
2812
2988
  return;
2813
2989
  }
2814
- const nextUrl2 = URL.createObjectURL(props.value);
2990
+ const nextUrl2 = URL.createObjectURL(singleFile);
2815
2991
  setPreviewUrl(nextUrl2);
2816
2992
  return () => URL.revokeObjectURL(nextUrl2);
2817
2993
  }
2818
- if (!props.value) {
2994
+ if (!imageFile) {
2819
2995
  setPreviewUrl(null);
2820
2996
  return;
2821
2997
  }
2822
- const nextUrl = URL.createObjectURL(props.value);
2998
+ const nextUrl = URL.createObjectURL(imageFile);
2823
2999
  setPreviewUrl(nextUrl);
2824
3000
  return () => URL.revokeObjectURL(nextUrl);
2825
- }, [isFile, props.value]);
2826
- const handleFile = (file) => {
2827
- if (!file) return;
2828
- if (props.maxSizeBytes != null && file.size > props.maxSizeBytes) {
2829
- props.onFileTooLarge?.();
2830
- return;
2831
- }
2832
- if (isFile) {
2833
- if (props.disabled) return;
2834
- props.onChange(file);
3001
+ }, [imageFile, isFile, singleFile]);
3002
+ const acceptFiles = (incoming) => {
3003
+ if (!incoming || pickerDisabled) return;
3004
+ const list = Array.from(incoming).filter(isFileLike);
3005
+ if (!list.length) return;
3006
+ const accepted = [];
3007
+ for (const file of list) {
3008
+ if (props.maxSizeBytes != null && file.size > props.maxSizeBytes) {
3009
+ props.onFileTooLarge?.();
3010
+ continue;
3011
+ }
3012
+ if (isFile) {
3013
+ if (!fileMatchesAccept(file, accept)) {
3014
+ props.onInvalidFile?.();
3015
+ continue;
3016
+ }
3017
+ accepted.push(file);
3018
+ continue;
3019
+ }
3020
+ if (!(file.type || "").startsWith("image/")) {
3021
+ props.onInvalidFile?.();
3022
+ continue;
3023
+ }
3024
+ accepted.push(file);
3025
+ }
3026
+ if (!accepted.length) return;
3027
+ if (isMultiple) {
3028
+ const seen = new Set(multiFiles.map(fileKey));
3029
+ const next = [...multiFiles];
3030
+ for (const file of accepted) {
3031
+ const key = fileKey(file);
3032
+ if (seen.has(key)) continue;
3033
+ seen.add(key);
3034
+ next.push(file);
3035
+ }
3036
+ setMultiFiles(next);
2835
3037
  return;
2836
3038
  }
2837
- if (!file.type.startsWith("image/")) return;
2838
- props.onChange(file);
2839
- props.onRemoveExistingImageChange?.(false);
3039
+ setSingleFile(accepted[0] ?? null);
2840
3040
  };
2841
- const displayedImage = !isFile && (previewUrl ?? (!props.removeExistingImage ? props.existingImageUrl : void 0));
3041
+ const imageProps = !isFile ? props : null;
3042
+ const displayedImage = imageProps && (previewUrl ?? (!imageProps.removeExistingImage ? imageProps.existingImageUrl : void 0));
2842
3043
  const dashedClasses = cn(
2843
3044
  "block rounded-xl border-2 border-dashed border-border/55 p-4 transition-colors",
2844
3045
  pickerDisabled && "pointer-events-none opacity-50",
@@ -2862,8 +3063,7 @@ function UploadDropzone(props) {
2862
3063
  event.preventDefault();
2863
3064
  setIsDragging(false);
2864
3065
  if (pickerDisabled) return;
2865
- const file = event.dataTransfer.files?.[0] ?? null;
2866
- handleFile(file);
3066
+ acceptFiles(event.dataTransfer.files);
2867
3067
  },
2868
3068
  children: [
2869
3069
  /* @__PURE__ */ jsx(
@@ -2872,10 +3072,14 @@ function UploadDropzone(props) {
2872
3072
  id: inputId,
2873
3073
  ref: inputRef,
2874
3074
  type: "file",
2875
- accept: isFile ? props.accept : "image/*",
3075
+ accept,
3076
+ multiple: isMultiple || void 0,
2876
3077
  disabled: pickerDisabled || void 0,
2877
3078
  className: "sr-only",
2878
- onChange: (event) => handleFile(event.target.files?.[0] ?? null)
3079
+ onChange: (event) => {
3080
+ acceptFiles(event.target.files);
3081
+ event.target.value = "";
3082
+ }
2879
3083
  }
2880
3084
  ),
2881
3085
  isFile ? /* @__PURE__ */ jsxs("div", { className: "mb-3 flex min-h-28 flex-col items-center justify-center gap-2 rounded-lg border border-border/45 bg-muted/20 px-3 py-4 text-center", children: [
@@ -2887,7 +3091,40 @@ function UploadDropzone(props) {
2887
3091
  className: "max-h-44 w-full rounded-lg border border-border/45 bg-foreground"
2888
3092
  }
2889
3093
  ) : /* @__PURE__ */ jsx(FileText, { className: "h-8 w-8 shrink-0 text-muted-foreground", "aria-hidden": true }),
2890
- props.value ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 w-full break-all text-sm font-medium", children: props.value.name }) : null
3094
+ isMultiple ? multiFiles.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "w-full space-y-1.5 text-left", children: multiFiles.map((file) => /* @__PURE__ */ jsxs(
3095
+ "li",
3096
+ {
3097
+ className: "flex items-center gap-2 rounded-md border border-border/40 bg-background/60 px-2 py-1.5",
3098
+ children: [
3099
+ /* @__PURE__ */ jsx(
3100
+ FileText,
3101
+ {
3102
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
3103
+ "aria-hidden": true
3104
+ }
3105
+ ),
3106
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: file.name }),
3107
+ /* @__PURE__ */ jsx(
3108
+ Button,
3109
+ {
3110
+ type: "button",
3111
+ size: "sm",
3112
+ variant: "ghost",
3113
+ className: "h-7 w-7 shrink-0 p-0",
3114
+ disabled: pickerDisabled || void 0,
3115
+ "aria-label": `${t.removeFileAriaLabel} ${file.name}`,
3116
+ onClick: (event) => {
3117
+ event.preventDefault();
3118
+ event.stopPropagation();
3119
+ setMultiFiles(multiFiles.filter((f) => fileKey(f) !== fileKey(file)));
3120
+ },
3121
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": true })
3122
+ }
3123
+ )
3124
+ ]
3125
+ },
3126
+ fileKey(file)
3127
+ )) }) : null : singleFile ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 w-full break-all text-sm font-medium", children: singleFile.name }) : null
2891
3128
  ] }) : displayedImage ? /* @__PURE__ */ jsx(
2892
3129
  "img",
2893
3130
  {
@@ -2916,7 +3153,22 @@ function UploadDropzone(props) {
2916
3153
  ]
2917
3154
  }
2918
3155
  ),
2919
- props.value ? /* @__PURE__ */ jsx(
3156
+ !isMultiple && isFile && singleFile ? /* @__PURE__ */ jsx(
3157
+ Button,
3158
+ {
3159
+ type: "button",
3160
+ size: "sm",
3161
+ variant: "outline",
3162
+ disabled: pickerDisabled || void 0,
3163
+ onClick: (event) => {
3164
+ event.preventDefault();
3165
+ event.stopPropagation();
3166
+ setSingleFile(null);
3167
+ },
3168
+ children: t.removeText
3169
+ }
3170
+ ) : null,
3171
+ isMultiple && multiFiles.length > 0 ? /* @__PURE__ */ jsx(
2920
3172
  Button,
2921
3173
  {
2922
3174
  type: "button",
@@ -2926,7 +3178,7 @@ function UploadDropzone(props) {
2926
3178
  onClick: (event) => {
2927
3179
  event.preventDefault();
2928
3180
  event.stopPropagation();
2929
- props.onChange(null);
3181
+ setMultiFiles([]);
2930
3182
  },
2931
3183
  children: t.removeText
2932
3184
  }
@@ -2936,20 +3188,20 @@ function UploadDropzone(props) {
2936
3188
  ]
2937
3189
  }
2938
3190
  ),
2939
- !isFile && props.existingImageUrl && props.onRemoveExistingImageChange && t.removeExistingImageText ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border border-border/45 bg-muted/15 px-3 py-2", children: [
3191
+ imageProps?.existingImageUrl && imageProps.onRemoveExistingImageChange && t.removeExistingImageText ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border border-border/45 bg-muted/15 px-3 py-2", children: [
2940
3192
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", id: removeExistingLabelId, children: t.removeExistingImageText }),
2941
3193
  /* @__PURE__ */ jsx(
2942
3194
  Checkbox,
2943
3195
  {
2944
3196
  "aria-labelledby": removeExistingLabelId,
2945
- checked: props.removeExistingImage,
2946
- onCheckedChange: (checked) => props.onRemoveExistingImageChange?.(checked === true)
3197
+ checked: imageProps.removeExistingImage,
3198
+ onCheckedChange: (checked) => imageProps.onRemoveExistingImageChange?.(checked === true)
2947
3199
  }
2948
3200
  )
2949
3201
  ] }) : null
2950
3202
  ] });
2951
3203
  }
2952
3204
 
2953
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarFallback, AvatarImage, Badge, Calendar, Checkbox, ConfirmDialog, DatePicker, DateRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownScrollableList, DropdownSelectSearchInput, EventCalendar, Form, FormControl, FormDescription, FormDrawer, FormField, FormItem, FormLabel, FormMessage, H3, Heading, Label, Link, MultiSelect, Paragraph, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, SEARCHABLE_SELECT_MIN_OPTIONS, ScrollArea, ScrollBar, SearchField, Select, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipProvider, UploadDropzone, badgeVariants, dropdownMenuSelectOptionsMaxHeightClassName, filterSelectOptions, foldSearchText, headingVariants, isSelectSearchable, linkStyles, paragraphVariants, useFormField };
2954
- //# sourceMappingURL=chunk-QHNZTXPJ.js.map
2955
- //# sourceMappingURL=chunk-QHNZTXPJ.js.map
3205
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarFallback, AvatarImage, Badge, Calendar, Checkbox, ConfirmDialog, DatePicker, DateRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownScrollableList, DropdownSelectSearchInput, EventCalendar, Form, FormControl, FormDescription, FormDrawer, FormField, FormItem, FormLabel, FormMessage, H3, Heading, Label, Link, MultiSelect, Paragraph, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, SEARCHABLE_SELECT_MIN_OPTIONS, ScrollArea, ScrollBar, SearchField, Select, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipProvider, UploadDropzone, badgeVariants, dropdownMenuSelectOptionsMaxHeightClassName, fileMatchesAccept, filterSelectOptions, foldSearchText, headingVariants, isSelectSearchable, linkStyles, paragraphVariants, useFormField };
3206
+ //# sourceMappingURL=chunk-GDYI2I74.js.map
3207
+ //# sourceMappingURL=chunk-GDYI2I74.js.map