@yca-software/yca-react-core 0.0.23 → 0.0.25

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) : "");
@@ -1339,6 +1339,7 @@ function DropdownMenuTrigger({
1339
1339
  }
1340
1340
  function DropdownMenuContent({
1341
1341
  className,
1342
+ animated = true,
1342
1343
  sideOffset = 4,
1343
1344
  portalled = true,
1344
1345
  ...props
@@ -1350,7 +1351,8 @@ function DropdownMenuContent({
1350
1351
  sideOffset,
1351
1352
  className: cn(
1352
1353
  surfaceOverlay,
1353
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto p-1",
1354
+ animated && "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
1355
+ "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto p-1",
1354
1356
  className
1355
1357
  ),
1356
1358
  ...props
@@ -1384,10 +1386,10 @@ function DropdownScrollableList({
1384
1386
  scrollAreaClassName,
1385
1387
  grow = true
1386
1388
  }) {
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(() => {
1389
+ const scrollRef = React20.useRef(null);
1390
+ const [showTop, setShowTop] = React20.useState(false);
1391
+ const [showBottom, setShowBottom] = React20.useState(false);
1392
+ const update = React20.useCallback(() => {
1391
1393
  const el = scrollRef.current;
1392
1394
  if (!el) {
1393
1395
  setShowTop(false);
@@ -1404,14 +1406,14 @@ function DropdownScrollableList({
1404
1406
  setShowTop(scrollTop > 2);
1405
1407
  setShowBottom(scrollTop + clientHeight < scrollHeight - 2);
1406
1408
  }, []);
1407
- React8.useLayoutEffect(() => {
1409
+ React20.useLayoutEffect(() => {
1408
1410
  update();
1409
1411
  const id = requestAnimationFrame(() => {
1410
1412
  requestAnimationFrame(update);
1411
1413
  });
1412
1414
  return () => cancelAnimationFrame(id);
1413
1415
  }, [update, scrollKey]);
1414
- React8.useEffect(() => {
1416
+ React20.useEffect(() => {
1415
1417
  const el = scrollRef.current;
1416
1418
  if (!el) return;
1417
1419
  if (typeof ResizeObserver === "undefined") {
@@ -1541,10 +1543,10 @@ function titleForView(anchor, view, weekStartsOnMonday) {
1541
1543
  }
1542
1544
  var MAX_MONTH_CHIPS = 3;
1543
1545
  function useControlledDate(value, defaultValue, onChange) {
1544
- const [internal, setInternal] = React8.useState(defaultValue ?? /* @__PURE__ */ new Date());
1546
+ const [internal, setInternal] = React20.useState(defaultValue ?? /* @__PURE__ */ new Date());
1545
1547
  const isControlled = value !== void 0;
1546
1548
  const current = isControlled ? value : internal;
1547
- const set = React8.useCallback(
1549
+ const set = React20.useCallback(
1548
1550
  (next) => {
1549
1551
  if (!isControlled) setInternal(next);
1550
1552
  onChange?.(next);
@@ -1554,10 +1556,10 @@ function useControlledDate(value, defaultValue, onChange) {
1554
1556
  return [current, set];
1555
1557
  }
1556
1558
  function useControlledView(value, defaultValue, onChange) {
1557
- const [internal, setInternal] = React8.useState(defaultValue);
1559
+ const [internal, setInternal] = React20.useState(defaultValue);
1558
1560
  const isControlled = value !== void 0;
1559
1561
  const current = isControlled ? value : internal;
1560
- const set = React8.useCallback(
1562
+ const set = React20.useCallback(
1561
1563
  (next) => {
1562
1564
  if (!isControlled) setInternal(next);
1563
1565
  onChange?.(next);
@@ -1607,7 +1609,7 @@ function EventCalendar({
1607
1609
  }) {
1608
1610
  const [safeAnchor, setAnchor] = useControlledDate(value, defaultValue, onValueChange);
1609
1611
  const [safeView, setViewSafe] = useControlledView(viewProp, defaultView, onViewChange);
1610
- const weekdays = React8.useMemo(
1612
+ const weekdays = React20.useMemo(
1611
1613
  () => weekdayLabels(weekStartsOnMonday, safeAnchor),
1612
1614
  [weekStartsOnMonday, safeAnchor]
1613
1615
  );
@@ -1819,15 +1821,15 @@ function Label({
1819
1821
  return /* @__PURE__ */ jsx(LabelPrimitive.Root, { "data-slot": "label", className: cn(labelVariants(), className), ...props });
1820
1822
  }
1821
1823
  var Form = FormProvider;
1822
- var FormFieldContext = React8.createContext(null);
1824
+ var FormFieldContext = React20.createContext(null);
1823
1825
  var FormField = ({
1824
1826
  ...props
1825
1827
  }) => {
1826
1828
  return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
1827
1829
  };
1828
1830
  var useFormField = () => {
1829
- const fieldContext = React8.useContext(FormFieldContext);
1830
- const itemContext = React8.useContext(FormItemContext);
1831
+ const fieldContext = React20.useContext(FormFieldContext);
1832
+ const itemContext = React20.useContext(FormItemContext);
1831
1833
  if (fieldContext === null || fieldContext === void 0) {
1832
1834
  throw new Error("useFormField should be used within <FormField>");
1833
1835
  }
@@ -1844,9 +1846,9 @@ var useFormField = () => {
1844
1846
  ...fieldState
1845
1847
  };
1846
1848
  };
1847
- var FormItemContext = React8.createContext({});
1849
+ var FormItemContext = React20.createContext({});
1848
1850
  function FormItem({ className, ...props }) {
1849
- const id = React8.useId();
1851
+ const id = React20.useId();
1850
1852
  return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx("div", { "data-slot": "form-item", className: cn("grid gap-3", className), ...props }) });
1851
1853
  }
1852
1854
  function FormLabel({
@@ -2025,29 +2027,66 @@ function FormDrawer({
2025
2027
  onOpenChange,
2026
2028
  title,
2027
2029
  description,
2030
+ info,
2028
2031
  children,
2029
2032
  className,
2030
2033
  contentClassName
2031
2034
  }) {
2035
+ const bodyRef = React20.useRef(null);
2036
+ const [infoOpen, setInfoOpen] = React20.useState(false);
2037
+ React20.useEffect(() => {
2038
+ if (!open) setInfoOpen(false);
2039
+ }, [open]);
2032
2040
  return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2033
2041
  SheetContent,
2034
2042
  {
2035
2043
  "data-slot": "form-drawer-content",
2044
+ onOpenAutoFocus: (event) => {
2045
+ event.preventDefault();
2046
+ bodyRef.current?.focus({ preventScroll: true });
2047
+ },
2048
+ onCloseAutoFocus: (event) => event.preventDefault(),
2036
2049
  className: cn(
2037
2050
  "flex flex-col gap-4 w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] sm:max-w-lg overflow-hidden",
2038
2051
  contentClassName
2039
2052
  ),
2040
2053
  children: [
2041
2054
  /* @__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 })
2055
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-1.5 text-base sm:text-lg", children: [
2056
+ /* @__PURE__ */ jsx("span", { className: "min-w-0", children: title }),
2057
+ info ? /* @__PURE__ */ jsxs(Popover, { open: infoOpen, onOpenChange: setInfoOpen, children: [
2058
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2059
+ "button",
2060
+ {
2061
+ type: "button",
2062
+ 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",
2063
+ "aria-label": "More info",
2064
+ "aria-expanded": infoOpen,
2065
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5", "aria-hidden": true })
2066
+ }
2067
+ ) }),
2068
+ /* @__PURE__ */ jsx(
2069
+ PopoverContent,
2070
+ {
2071
+ side: "bottom",
2072
+ align: "start",
2073
+ sideOffset: 6,
2074
+ className: "z-[120] max-w-xs p-3 text-sm leading-snug",
2075
+ children: info
2076
+ }
2077
+ )
2078
+ ] }) : null
2079
+ ] }),
2080
+ description ? /* @__PURE__ */ jsx(SheetDescription, { className: "text-sm", children: description }) : null
2044
2081
  ] }),
2045
2082
  /* @__PURE__ */ jsx(InsideModalScrollLockProvider, { children: /* @__PURE__ */ jsx(
2046
2083
  "div",
2047
2084
  {
2085
+ ref: bodyRef,
2086
+ tabIndex: -1,
2048
2087
  className: cn(
2049
2088
  // 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",
2089
+ "flex flex-col flex-1 min-h-0 gap-4 overflow-y-auto px-2 py-1 outline-none",
2051
2090
  className
2052
2091
  ),
2053
2092
  children
@@ -2106,14 +2145,14 @@ function MultiSelect({
2106
2145
  }) {
2107
2146
  const t = { ...DEFAULT_TRANSLATIONS2, ...translations };
2108
2147
  const insideModalScrollLock = useInsideModalScrollLock();
2109
- const [search, setSearch] = React8.useState("");
2110
- const selectedSet = React8.useMemo(() => new Set(value), [value]);
2111
- const labelByValue = React8.useMemo(() => {
2148
+ const [search, setSearch] = React20.useState("");
2149
+ const selectedSet = React20.useMemo(() => new Set(value), [value]);
2150
+ const labelByValue = React20.useMemo(() => {
2112
2151
  const map = /* @__PURE__ */ new Map();
2113
2152
  for (const opt of options) map.set(opt.value, opt.label);
2114
2153
  return map;
2115
2154
  }, [options]);
2116
- const filteredOptions = React8.useMemo(() => {
2155
+ const filteredOptions = React20.useMemo(() => {
2117
2156
  const q = search.trim().toLowerCase();
2118
2157
  if (!q) return options;
2119
2158
  return options.filter((opt) => opt.label.toLowerCase().includes(q));
@@ -2209,7 +2248,7 @@ function MultiSelect({
2209
2248
  ] })
2210
2249
  ] });
2211
2250
  }
2212
- var ScrollArea = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
2251
+ var ScrollArea = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
2213
2252
  ScrollAreaPrimitive.Root,
2214
2253
  {
2215
2254
  ref,
@@ -2223,7 +2262,7 @@ var ScrollArea = React8.forwardRef(({ className, children, ...props }, ref) => /
2223
2262
  }
2224
2263
  ));
2225
2264
  ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
2226
- var ScrollBar = React8.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
2265
+ var ScrollBar = React20.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
2227
2266
  ScrollAreaPrimitive.ScrollAreaScrollbar,
2228
2267
  {
2229
2268
  ref,
@@ -2361,10 +2400,10 @@ function Select({
2361
2400
  }) {
2362
2401
  const t = { ...DEFAULT_TRANSLATIONS3, ...translations };
2363
2402
  const insideModalScrollLock = useInsideModalScrollLock();
2364
- const [open, setOpen] = React8.useState(false);
2365
- const [search, setSearch] = React8.useState("");
2403
+ const [open, setOpen] = React20.useState(false);
2404
+ const [search, setSearch] = React20.useState("");
2366
2405
  const showSearch = isSelectSearchable(options.length, searchable);
2367
- const filteredOptions = React8.useMemo(
2406
+ const filteredOptions = React20.useMemo(
2368
2407
  () => showSearch ? filterSelectOptions(options, search) : options,
2369
2408
  [options, search, showSearch]
2370
2409
  );
@@ -2389,7 +2428,7 @@ function Select({
2389
2428
  controlFocus,
2390
2429
  "flex h-9 w-full cursor-pointer items-center justify-between px-3 py-2 text-sm",
2391
2430
  "hover:bg-accent/40 disabled:pointer-events-none disabled:opacity-50",
2392
- !value && "text-muted-foreground",
2431
+ !selectedOption && "text-muted-foreground",
2393
2432
  triggerClassName,
2394
2433
  className
2395
2434
  ),
@@ -2402,6 +2441,7 @@ function Select({
2402
2441
  /* @__PURE__ */ jsxs(
2403
2442
  DropdownMenuContent,
2404
2443
  {
2444
+ animated: false,
2405
2445
  align: "start",
2406
2446
  sideOffset: 0,
2407
2447
  portalled: !insideModalScrollLock,
@@ -2429,10 +2469,8 @@ function Select({
2429
2469
  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
2470
  DropdownMenuItem,
2431
2471
  {
2432
- onClick: () => {
2472
+ onSelect: () => {
2433
2473
  onValueChange(opt.value);
2434
- setOpen(false);
2435
- setSearch("");
2436
2474
  },
2437
2475
  children: opt.label
2438
2476
  },
@@ -2468,7 +2506,7 @@ function ChevronDownIcon({ className, ...props }) {
2468
2506
  }
2469
2507
  );
2470
2508
  }
2471
- var Separator = React8.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
2509
+ var Separator = React20.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
2472
2510
  SeparatorPrimitive.Root,
2473
2511
  {
2474
2512
  ref,
@@ -2483,7 +2521,7 @@ var Separator = React8.forwardRef(({ className, orientation = "horizontal", deco
2483
2521
  }
2484
2522
  ));
2485
2523
  Separator.displayName = SeparatorPrimitive.Root.displayName;
2486
- var Switch = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2524
+ var Switch = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2487
2525
  SwitchPrimitive.Root,
2488
2526
  {
2489
2527
  ref,
@@ -2509,11 +2547,11 @@ var Switch = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
2509
2547
  }
2510
2548
  ));
2511
2549
  Switch.displayName = SwitchPrimitive.Root.displayName;
2512
- var Table = React8.forwardRef(
2550
+ var Table = React20.forwardRef(
2513
2551
  ({ 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
2552
  );
2515
2553
  Table.displayName = "Table";
2516
- var TableHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2554
+ var TableHeader = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2517
2555
  "thead",
2518
2556
  {
2519
2557
  ref,
@@ -2522,9 +2560,9 @@ var TableHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
2522
2560
  }
2523
2561
  ));
2524
2562
  TableHeader.displayName = "TableHeader";
2525
- var TableBody = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
2563
+ var TableBody = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
2526
2564
  TableBody.displayName = "TableBody";
2527
- var TableFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2565
+ var TableFooter = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2528
2566
  "tfoot",
2529
2567
  {
2530
2568
  ref,
@@ -2536,7 +2574,7 @@ var TableFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
2536
2574
  }
2537
2575
  ));
2538
2576
  TableFooter.displayName = "TableFooter";
2539
- var TableRow = React8.forwardRef(
2577
+ var TableRow = React20.forwardRef(
2540
2578
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2541
2579
  "tr",
2542
2580
  {
@@ -2550,7 +2588,7 @@ var TableRow = React8.forwardRef(
2550
2588
  )
2551
2589
  );
2552
2590
  TableRow.displayName = "TableRow";
2553
- var TableHead = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2591
+ var TableHead = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2554
2592
  "th",
2555
2593
  {
2556
2594
  ref,
@@ -2562,7 +2600,7 @@ var TableHead = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2562
2600
  }
2563
2601
  ));
2564
2602
  TableHead.displayName = "TableHead";
2565
- var TableCell = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2603
+ var TableCell = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2566
2604
  "td",
2567
2605
  {
2568
2606
  ref,
@@ -2571,7 +2609,7 @@ var TableCell = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2571
2609
  }
2572
2610
  ));
2573
2611
  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 }));
2612
+ var TableCaption = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
2575
2613
  TableCaption.displayName = "TableCaption";
2576
2614
  function Tabs({ className, ...props }) {
2577
2615
  return /* @__PURE__ */ jsx(
@@ -2631,7 +2669,7 @@ function TabsContent({
2631
2669
  }
2632
2670
  );
2633
2671
  }
2634
- var Textarea = React8.forwardRef(
2672
+ var Textarea = React20.forwardRef(
2635
2673
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2636
2674
  "textarea",
2637
2675
  {
@@ -2652,58 +2690,157 @@ var Textarea = React8.forwardRef(
2652
2690
  );
2653
2691
  Textarea.displayName = "Textarea";
2654
2692
  var TooltipProvider = TooltipPrimitive.Provider;
2693
+ function prefersCoarsePointer() {
2694
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
2695
+ return window.matchMedia("(pointer: coarse)").matches;
2696
+ }
2697
+ function isPointerOver(el, x, y) {
2698
+ if (!el || typeof document.elementFromPoint !== "function") return false;
2699
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
2700
+ const under = document.elementFromPoint(x, y);
2701
+ return Boolean(under && (under === el || el.contains(under)));
2702
+ }
2655
2703
  function Tooltip({
2656
2704
  content,
2657
2705
  children,
2658
2706
  side = "top",
2659
2707
  align = "center",
2660
- delayDuration = 400,
2661
- closeDelay = 100
2708
+ delayDuration = 200,
2709
+ closeDelay = 280
2662
2710
  }) {
2663
- const [open, setOpen] = React8.useState(false);
2664
- const openTimeoutRef = React8.useRef(null);
2665
- const closeTimeoutRef = React8.useRef(null);
2666
- const clearOpenTimeout = React8.useCallback(() => {
2711
+ const [open, setOpen] = React20.useState(false);
2712
+ const triggerRef = React20.useRef(null);
2713
+ const stickyRef = React20.useRef(false);
2714
+ const hoveringRef = React20.useRef(false);
2715
+ const openRef = React20.useRef(false);
2716
+ const hoverArmedRef = React20.useRef(false);
2717
+ const layoutReadyRef = React20.useRef(false);
2718
+ const lastPointerRef = React20.useRef({ x: Number.NaN, y: Number.NaN });
2719
+ const openTimeoutRef = React20.useRef(null);
2720
+ const closeTimeoutRef = React20.useRef(null);
2721
+ React20.useEffect(() => {
2722
+ openRef.current = open;
2723
+ }, [open]);
2724
+ React20.useLayoutEffect(() => {
2725
+ hoverArmedRef.current = false;
2726
+ stickyRef.current = false;
2727
+ hoveringRef.current = false;
2728
+ layoutReadyRef.current = false;
2729
+ setOpen(false);
2730
+ const markReady = () => {
2731
+ layoutReadyRef.current = true;
2732
+ const el = triggerRef.current;
2733
+ const { x, y } = lastPointerRef.current;
2734
+ if (isPointerOver(el, x, y)) {
2735
+ hoverArmedRef.current = false;
2736
+ return;
2737
+ }
2738
+ hoverArmedRef.current = true;
2739
+ };
2740
+ let raf2 = 0;
2741
+ const raf1 = window.requestAnimationFrame(() => {
2742
+ raf2 = window.requestAnimationFrame(markReady);
2743
+ });
2744
+ const onPointerMove = (event) => {
2745
+ lastPointerRef.current = { x: event.clientX, y: event.clientY };
2746
+ if (!layoutReadyRef.current) return;
2747
+ if (isPointerOver(triggerRef.current, event.clientX, event.clientY)) return;
2748
+ hoverArmedRef.current = true;
2749
+ };
2750
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
2751
+ return () => {
2752
+ window.cancelAnimationFrame(raf1);
2753
+ window.cancelAnimationFrame(raf2);
2754
+ window.removeEventListener("pointermove", onPointerMove);
2755
+ };
2756
+ }, []);
2757
+ const clearOpenTimeout = React20.useCallback(() => {
2667
2758
  if (openTimeoutRef.current !== null) {
2668
2759
  window.clearTimeout(openTimeoutRef.current);
2669
2760
  openTimeoutRef.current = null;
2670
2761
  }
2671
2762
  }, []);
2672
- const clearCloseTimeout = React8.useCallback(() => {
2763
+ const clearCloseTimeout = React20.useCallback(() => {
2673
2764
  if (closeTimeoutRef.current !== null) {
2674
2765
  window.clearTimeout(closeTimeoutRef.current);
2675
2766
  closeTimeoutRef.current = null;
2676
2767
  }
2677
2768
  }, []);
2678
- const handleOpenChange = React8.useCallback(
2769
+ const scheduleClose = React20.useCallback(() => {
2770
+ if (stickyRef.current || hoveringRef.current) return;
2771
+ clearOpenTimeout();
2772
+ clearCloseTimeout();
2773
+ if (closeDelay === 0) {
2774
+ setOpen(false);
2775
+ return;
2776
+ }
2777
+ closeTimeoutRef.current = window.setTimeout(() => {
2778
+ if (stickyRef.current || hoveringRef.current) return;
2779
+ setOpen(false);
2780
+ }, closeDelay);
2781
+ }, [clearCloseTimeout, clearOpenTimeout, closeDelay]);
2782
+ const handleOpenChange = React20.useCallback(
2679
2783
  (next) => {
2680
- if (!next) {
2784
+ if (next) {
2785
+ if (!hoveringRef.current && !stickyRef.current) return;
2786
+ setOpen(true);
2787
+ return;
2788
+ }
2789
+ if (stickyRef.current) {
2790
+ stickyRef.current = false;
2791
+ hoveringRef.current = false;
2681
2792
  clearOpenTimeout();
2682
2793
  clearCloseTimeout();
2683
2794
  setOpen(false);
2795
+ return;
2684
2796
  }
2797
+ scheduleClose();
2685
2798
  },
2686
- [clearCloseTimeout, clearOpenTimeout]
2799
+ [clearCloseTimeout, clearOpenTimeout, scheduleClose]
2687
2800
  );
2688
- const handlePointerEnter = React8.useCallback(() => {
2801
+ const handlePointerEnter = React20.useCallback(() => {
2802
+ if (!hoverArmedRef.current) return;
2803
+ hoveringRef.current = true;
2689
2804
  clearCloseTimeout();
2690
2805
  clearOpenTimeout();
2806
+ if (openRef.current || delayDuration <= 0) {
2807
+ setOpen(true);
2808
+ return;
2809
+ }
2691
2810
  openTimeoutRef.current = window.setTimeout(() => {
2811
+ if (!hoveringRef.current || !hoverArmedRef.current) return;
2692
2812
  setOpen(true);
2693
2813
  }, delayDuration);
2694
2814
  }, [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(
2815
+ const handlePointerLeave = React20.useCallback(() => {
2816
+ hoverArmedRef.current = true;
2817
+ hoveringRef.current = false;
2818
+ scheduleClose();
2819
+ }, [scheduleClose]);
2820
+ const blockRadixFocusOpen = React20.useCallback((e) => {
2821
+ e.preventDefault();
2822
+ }, []);
2823
+ const blockRadixPointerMoveOpen = React20.useCallback((e) => {
2824
+ e.preventDefault();
2825
+ }, []);
2826
+ const handleTriggerClick = React20.useCallback(
2827
+ (e) => {
2828
+ if (!prefersCoarsePointer()) {
2829
+ e.preventDefault();
2830
+ return;
2831
+ }
2832
+ clearOpenTimeout();
2833
+ clearCloseTimeout();
2834
+ const next = !open;
2835
+ stickyRef.current = next;
2836
+ hoveringRef.current = next;
2837
+ hoverArmedRef.current = true;
2838
+ setOpen(next);
2839
+ e.preventDefault();
2840
+ },
2841
+ [clearCloseTimeout, clearOpenTimeout, open]
2842
+ );
2843
+ React20.useEffect(
2707
2844
  () => () => {
2708
2845
  clearOpenTimeout();
2709
2846
  clearCloseTimeout();
@@ -2714,9 +2851,13 @@ function Tooltip({
2714
2851
  /* @__PURE__ */ jsx(
2715
2852
  TooltipPrimitive.Trigger,
2716
2853
  {
2854
+ ref: triggerRef,
2717
2855
  asChild: true,
2856
+ onFocus: blockRadixFocusOpen,
2857
+ onPointerMove: blockRadixPointerMoveOpen,
2718
2858
  onPointerEnter: handlePointerEnter,
2719
2859
  onPointerLeave: handlePointerLeave,
2860
+ onClick: handleTriggerClick,
2720
2861
  children
2721
2862
  }
2722
2863
  ),
@@ -2764,7 +2905,7 @@ var paragraphVariants = cva("text-muted-foreground leading-relaxed", {
2764
2905
  size: "default"
2765
2906
  }
2766
2907
  });
2767
- var Heading = React8.forwardRef(
2908
+ var Heading = React20.forwardRef(
2768
2909
  ({ className, level, as, ...props }, ref) => {
2769
2910
  const Component = as || (level === "h1" ? "h1" : level === "h2" ? "h2" : level === "h3" ? "h3" : "h4");
2770
2911
  return /* @__PURE__ */ jsx(
@@ -2778,13 +2919,13 @@ var Heading = React8.forwardRef(
2778
2919
  }
2779
2920
  );
2780
2921
  Heading.displayName = "Heading";
2781
- var Paragraph = React8.forwardRef(
2922
+ var Paragraph = React20.forwardRef(
2782
2923
  ({ className, size, ...props }, ref) => {
2783
2924
  return /* @__PURE__ */ jsx("p", { ref, className: cn(paragraphVariants({ size }), className), ...props });
2784
2925
  }
2785
2926
  );
2786
2927
  Paragraph.displayName = "Paragraph";
2787
- var H3 = React8.forwardRef(
2928
+ var H3 = React20.forwardRef(
2788
2929
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(Heading, { ref, level: "h3", as: "h3", className, ...props })
2789
2930
  );
2790
2931
  H3.displayName = "H3";
@@ -2794,10 +2935,30 @@ var DEFAULT_TRANSLATIONS4 = {
2794
2935
  chooseImageText: "Choose image",
2795
2936
  chooseFileText: "Choose file",
2796
2937
  removeText: "Remove",
2797
- removeExistingImageText: "Remove existing image"
2938
+ removeExistingImageText: "Remove existing image",
2939
+ removeFileAriaLabel: "Remove file"
2798
2940
  };
2941
+ function fileMatchesAccept(file, accept) {
2942
+ if (!accept?.trim()) return true;
2943
+ const tokens = accept.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
2944
+ if (!tokens.length) return true;
2945
+ const name = file.name.toLowerCase();
2946
+ const type = (file.type || "").toLowerCase();
2947
+ return tokens.some((token) => {
2948
+ if (token.startsWith(".")) return name.endsWith(token);
2949
+ if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
2950
+ return type === token;
2951
+ });
2952
+ }
2953
+ function fileKey(file) {
2954
+ return `${file.name}:${file.size}:${file.lastModified}`;
2955
+ }
2956
+ function isFileLike(value) {
2957
+ return typeof value === "object" && value != null && typeof value.name === "string" && typeof value.size === "number";
2958
+ }
2799
2959
  function UploadDropzone(props) {
2800
2960
  const isFile = props.variant === "file";
2961
+ const isMultiple = isFile && (props.multiple === true || Array.isArray(props.value));
2801
2962
  const inputId = useId();
2802
2963
  const removeExistingLabelId = useId();
2803
2964
  const pickerDisabled = isFile && props.disabled;
@@ -2805,40 +2966,83 @@ function UploadDropzone(props) {
2805
2966
  const inputRef = useRef(null);
2806
2967
  const [isDragging, setIsDragging] = useState(false);
2807
2968
  const [previewUrl, setPreviewUrl] = useState(null);
2969
+ const singleFile = isFile && !isMultiple && isFileLike(props.value) ? props.value : null;
2970
+ const multiFiles = isMultiple && Array.isArray(props.value) ? props.value.filter(isFileLike) : [];
2971
+ const accept = isFile ? props.accept : "image/*";
2972
+ const setSingleFile = (file) => {
2973
+ if (isFile && !isMultiple) {
2974
+ props.onChange(file);
2975
+ return;
2976
+ }
2977
+ if (!isFile) {
2978
+ props.onChange(file);
2979
+ props.onRemoveExistingImageChange?.(false);
2980
+ }
2981
+ };
2982
+ const setMultiFiles = (next) => {
2983
+ if (isMultiple) props.onChange(next);
2984
+ };
2985
+ const imageFile = !isFile && isFileLike(props.value) ? props.value : null;
2808
2986
  useEffect(() => {
2809
2987
  if (isFile) {
2810
- if (!props.value || !props.value.type.startsWith("video/")) {
2988
+ const mime = singleFile?.type || "";
2989
+ if (!singleFile || !mime.startsWith("video/")) {
2811
2990
  setPreviewUrl(null);
2812
2991
  return;
2813
2992
  }
2814
- const nextUrl2 = URL.createObjectURL(props.value);
2993
+ const nextUrl2 = URL.createObjectURL(singleFile);
2815
2994
  setPreviewUrl(nextUrl2);
2816
2995
  return () => URL.revokeObjectURL(nextUrl2);
2817
2996
  }
2818
- if (!props.value) {
2997
+ if (!imageFile) {
2819
2998
  setPreviewUrl(null);
2820
2999
  return;
2821
3000
  }
2822
- const nextUrl = URL.createObjectURL(props.value);
3001
+ const nextUrl = URL.createObjectURL(imageFile);
2823
3002
  setPreviewUrl(nextUrl);
2824
3003
  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);
3004
+ }, [imageFile, isFile, singleFile]);
3005
+ const acceptFiles = (incoming) => {
3006
+ if (!incoming || pickerDisabled) return;
3007
+ const list = Array.from(incoming).filter(isFileLike);
3008
+ if (!list.length) return;
3009
+ const accepted = [];
3010
+ for (const file of list) {
3011
+ if (props.maxSizeBytes != null && file.size > props.maxSizeBytes) {
3012
+ props.onFileTooLarge?.();
3013
+ continue;
3014
+ }
3015
+ if (isFile) {
3016
+ if (!fileMatchesAccept(file, accept)) {
3017
+ props.onInvalidFile?.();
3018
+ continue;
3019
+ }
3020
+ accepted.push(file);
3021
+ continue;
3022
+ }
3023
+ if (!(file.type || "").startsWith("image/")) {
3024
+ props.onInvalidFile?.();
3025
+ continue;
3026
+ }
3027
+ accepted.push(file);
3028
+ }
3029
+ if (!accepted.length) return;
3030
+ if (isMultiple) {
3031
+ const seen = new Set(multiFiles.map(fileKey));
3032
+ const next = [...multiFiles];
3033
+ for (const file of accepted) {
3034
+ const key = fileKey(file);
3035
+ if (seen.has(key)) continue;
3036
+ seen.add(key);
3037
+ next.push(file);
3038
+ }
3039
+ setMultiFiles(next);
2835
3040
  return;
2836
3041
  }
2837
- if (!file.type.startsWith("image/")) return;
2838
- props.onChange(file);
2839
- props.onRemoveExistingImageChange?.(false);
3042
+ setSingleFile(accepted[0] ?? null);
2840
3043
  };
2841
- const displayedImage = !isFile && (previewUrl ?? (!props.removeExistingImage ? props.existingImageUrl : void 0));
3044
+ const imageProps = !isFile ? props : null;
3045
+ const displayedImage = imageProps && (previewUrl ?? (!imageProps.removeExistingImage ? imageProps.existingImageUrl : void 0));
2842
3046
  const dashedClasses = cn(
2843
3047
  "block rounded-xl border-2 border-dashed border-border/55 p-4 transition-colors",
2844
3048
  pickerDisabled && "pointer-events-none opacity-50",
@@ -2862,8 +3066,7 @@ function UploadDropzone(props) {
2862
3066
  event.preventDefault();
2863
3067
  setIsDragging(false);
2864
3068
  if (pickerDisabled) return;
2865
- const file = event.dataTransfer.files?.[0] ?? null;
2866
- handleFile(file);
3069
+ acceptFiles(event.dataTransfer.files);
2867
3070
  },
2868
3071
  children: [
2869
3072
  /* @__PURE__ */ jsx(
@@ -2872,10 +3075,14 @@ function UploadDropzone(props) {
2872
3075
  id: inputId,
2873
3076
  ref: inputRef,
2874
3077
  type: "file",
2875
- accept: isFile ? props.accept : "image/*",
3078
+ accept,
3079
+ multiple: isMultiple || void 0,
2876
3080
  disabled: pickerDisabled || void 0,
2877
3081
  className: "sr-only",
2878
- onChange: (event) => handleFile(event.target.files?.[0] ?? null)
3082
+ onChange: (event) => {
3083
+ acceptFiles(event.target.files);
3084
+ event.target.value = "";
3085
+ }
2879
3086
  }
2880
3087
  ),
2881
3088
  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 +3094,40 @@ function UploadDropzone(props) {
2887
3094
  className: "max-h-44 w-full rounded-lg border border-border/45 bg-foreground"
2888
3095
  }
2889
3096
  ) : /* @__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
3097
+ isMultiple ? multiFiles.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "w-full space-y-1.5 text-left", children: multiFiles.map((file) => /* @__PURE__ */ jsxs(
3098
+ "li",
3099
+ {
3100
+ className: "flex items-center gap-2 rounded-md border border-border/40 bg-background/60 px-2 py-1.5",
3101
+ children: [
3102
+ /* @__PURE__ */ jsx(
3103
+ FileText,
3104
+ {
3105
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
3106
+ "aria-hidden": true
3107
+ }
3108
+ ),
3109
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: file.name }),
3110
+ /* @__PURE__ */ jsx(
3111
+ Button,
3112
+ {
3113
+ type: "button",
3114
+ size: "sm",
3115
+ variant: "ghost",
3116
+ className: "h-7 w-7 shrink-0 p-0",
3117
+ disabled: pickerDisabled || void 0,
3118
+ "aria-label": `${t.removeFileAriaLabel} ${file.name}`,
3119
+ onClick: (event) => {
3120
+ event.preventDefault();
3121
+ event.stopPropagation();
3122
+ setMultiFiles(multiFiles.filter((f) => fileKey(f) !== fileKey(file)));
3123
+ },
3124
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": true })
3125
+ }
3126
+ )
3127
+ ]
3128
+ },
3129
+ fileKey(file)
3130
+ )) }) : null : singleFile ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 w-full break-all text-sm font-medium", children: singleFile.name }) : null
2891
3131
  ] }) : displayedImage ? /* @__PURE__ */ jsx(
2892
3132
  "img",
2893
3133
  {
@@ -2916,7 +3156,22 @@ function UploadDropzone(props) {
2916
3156
  ]
2917
3157
  }
2918
3158
  ),
2919
- props.value ? /* @__PURE__ */ jsx(
3159
+ !isMultiple && isFile && singleFile ? /* @__PURE__ */ jsx(
3160
+ Button,
3161
+ {
3162
+ type: "button",
3163
+ size: "sm",
3164
+ variant: "outline",
3165
+ disabled: pickerDisabled || void 0,
3166
+ onClick: (event) => {
3167
+ event.preventDefault();
3168
+ event.stopPropagation();
3169
+ setSingleFile(null);
3170
+ },
3171
+ children: t.removeText
3172
+ }
3173
+ ) : null,
3174
+ isMultiple && multiFiles.length > 0 ? /* @__PURE__ */ jsx(
2920
3175
  Button,
2921
3176
  {
2922
3177
  type: "button",
@@ -2926,7 +3181,7 @@ function UploadDropzone(props) {
2926
3181
  onClick: (event) => {
2927
3182
  event.preventDefault();
2928
3183
  event.stopPropagation();
2929
- props.onChange(null);
3184
+ setMultiFiles([]);
2930
3185
  },
2931
3186
  children: t.removeText
2932
3187
  }
@@ -2936,20 +3191,20 @@ function UploadDropzone(props) {
2936
3191
  ]
2937
3192
  }
2938
3193
  ),
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: [
3194
+ 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
3195
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", id: removeExistingLabelId, children: t.removeExistingImageText }),
2941
3196
  /* @__PURE__ */ jsx(
2942
3197
  Checkbox,
2943
3198
  {
2944
3199
  "aria-labelledby": removeExistingLabelId,
2945
- checked: props.removeExistingImage,
2946
- onCheckedChange: (checked) => props.onRemoveExistingImageChange?.(checked === true)
3200
+ checked: imageProps.removeExistingImage,
3201
+ onCheckedChange: (checked) => imageProps.onRemoveExistingImageChange?.(checked === true)
2947
3202
  }
2948
3203
  )
2949
3204
  ] }) : null
2950
3205
  ] });
2951
3206
  }
2952
3207
 
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
3208
+ 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 };
3209
+ //# sourceMappingURL=chunk-FJUWTB3A.js.map
3210
+ //# sourceMappingURL=chunk-FJUWTB3A.js.map