@bigtablet/design-system 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import './index.css';
3
- import * as React20 from 'react';
3
+ import * as React19 from 'react';
4
4
  import { createContext, useRef, useState, useId, useEffect, useContext, useCallback, useMemo, Fragment, useLayoutEffect } from 'react';
5
5
  import { useSpring, animated } from '@react-spring/web';
6
- import { ChevronDown, Globe, ChevronRight, ChevronLeft, Check, Image, X, TriangleAlert, XCircle, AlertTriangle, CheckCircle2, Info, Bell } from 'lucide-react';
6
+ import { ChevronDown, ChevronRight, Globe, ChevronLeft, XCircle, AlertTriangle, CheckCircle2, Info, Search, Check, X, Image, ArrowUp, ArrowDown, ArrowUpDown, Bell, TriangleAlert } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
8
8
  import { createPortal } from 'react-dom';
9
9
 
@@ -38,8 +38,8 @@ var FOCUSABLE_SELECTORS = [
38
38
  '[tabindex]:not([tabindex="-1"])'
39
39
  ].join(", ");
40
40
  function useFocusTrap(containerRef, isActive) {
41
- const previousActiveElement = React20.useRef(null);
42
- React20.useEffect(() => {
41
+ const previousActiveElement = React19.useRef(null);
42
+ React19.useEffect(() => {
43
43
  if (!isActive) return;
44
44
  const container = containerRef.current;
45
45
  if (!container) return;
@@ -116,14 +116,14 @@ function getServerSnapshot() {
116
116
  return false;
117
117
  }
118
118
  function useReducedMotion() {
119
- return React20.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
119
+ return React19.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
120
120
  }
121
121
  var useSafeLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
122
122
  function useSpringHover({
123
123
  scale = 1.02,
124
124
  lift = -2
125
125
  } = {}) {
126
- const [hovered, setHovered] = React20.useState(false);
126
+ const [hovered, setHovered] = React19.useState(false);
127
127
  const reduced = useReducedMotion();
128
128
  const style = useSpring({
129
129
  transform: hovered ? `translateY(${lift}px) scale(${scale})` : "translateY(0px) scale(1)",
@@ -167,6 +167,15 @@ function useSpringPresence({
167
167
  }
168
168
  });
169
169
  }
170
+
171
+ // src/styles/icon/index.ts
172
+ var iconSize = {
173
+ xs: 14,
174
+ sm: 16,
175
+ md: 18,
176
+ lg: 20,
177
+ xl: 32
178
+ };
170
179
  var Accordion = ({
171
180
  items,
172
181
  multiple = false,
@@ -178,7 +187,7 @@ var Accordion = ({
178
187
  ...props
179
188
  }) => {
180
189
  const isControlled = controlledKeys !== void 0;
181
- const [internalKeys, setInternalKeys] = React20.useState(defaultOpenKeys);
190
+ const [internalKeys, setInternalKeys] = React19.useState(defaultOpenKeys);
182
191
  const open = isControlled ? controlledKeys ?? [] : internalKeys;
183
192
  const toggle = (key) => {
184
193
  const isOpen = open.includes(key);
@@ -206,7 +215,7 @@ var Accordion = ({
206
215
  /* @__PURE__ */ jsx(
207
216
  ChevronDown,
208
217
  {
209
- size: 20,
218
+ size: iconSize.lg,
210
219
  className: cn("accordion_chevron", isOpen && "accordion_chevron_open"),
211
220
  "aria-hidden": "true"
212
221
  }
@@ -245,7 +254,7 @@ var Avatar = ({
245
254
  style,
246
255
  ...props
247
256
  }) => {
248
- const [imgFailed, setImgFailed] = React20.useState(false);
257
+ const [imgFailed, setImgFailed] = React19.useState(false);
249
258
  const showImage = src && !imgFailed;
250
259
  const initials = getInitials(name);
251
260
  return /* @__PURE__ */ jsx(
@@ -394,7 +403,7 @@ var Breadcrumb = ({
394
403
  className,
395
404
  ...props
396
405
  }) => {
397
- const sep = separator ?? /* @__PURE__ */ jsx(ChevronRight, { size: 14, "aria-hidden": "true" });
406
+ const sep = separator ?? /* @__PURE__ */ jsx(ChevronRight, { size: iconSize.xs, "aria-hidden": "true" });
398
407
  return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: cn("breadcrumb", className), ...props, children: /* @__PURE__ */ jsx("ol", { className: "breadcrumb_list", children: items.map((item, idx) => {
399
408
  const isLast = idx === items.length - 1;
400
409
  return (
@@ -463,12 +472,12 @@ var ErrorState = ({
463
472
  );
464
473
  };
465
474
  var Menu = ({ items, trigger, align = "start" }) => {
466
- const [open, setOpen] = React20.useState(false);
467
- const wrapperRef = React20.useRef(null);
468
- const itemRefs = React20.useRef([]);
469
- const menuId = React20.useId();
475
+ const [open, setOpen] = React19.useState(false);
476
+ const wrapperRef = React19.useRef(null);
477
+ const itemRefs = React19.useRef([]);
478
+ const menuId = React19.useId();
470
479
  const style = useSpringPresence({ visible: open, from: "translateY(-4px)" });
471
- React20.useEffect(() => {
480
+ React19.useEffect(() => {
472
481
  if (!open) return;
473
482
  const handleClick = (e) => {
474
483
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -483,7 +492,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
483
492
  document.removeEventListener("keydown", handleEsc);
484
493
  };
485
494
  }, [open]);
486
- React20.useEffect(() => {
495
+ React19.useEffect(() => {
487
496
  const active = document.activeElement;
488
497
  if (!open || active && itemRefs.current.includes(active)) return;
489
498
  const first = items.findIndex((it) => !it.disabled);
@@ -529,7 +538,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
529
538
  break;
530
539
  }
531
540
  };
532
- const triggerWithProps = React20.cloneElement(
541
+ const triggerWithProps = React19.cloneElement(
533
542
  trigger,
534
543
  {
535
544
  onClick: () => setOpen((o) => !o),
@@ -746,9 +755,16 @@ var LocaleSwitcher = ({ locale }) => {
746
755
  "aria-controls": menuId,
747
756
  onClick: () => setOpen((p) => !p),
748
757
  children: [
749
- /* @__PURE__ */ jsx(Globe, { size: 16, "aria-hidden": "true" }),
758
+ /* @__PURE__ */ jsx(Globe, { size: iconSize.sm, "aria-hidden": "true" }),
750
759
  !locale.hideLabel && /* @__PURE__ */ jsx("span", { className: "nav_bar_locale_label", children: currentOption?.label ?? locale.current.toUpperCase() }),
751
- /* @__PURE__ */ jsx(ChevronDown, { size: 14, "aria-hidden": "true", className: "nav_bar_locale_chevron" })
760
+ /* @__PURE__ */ jsx(
761
+ ChevronDown,
762
+ {
763
+ size: iconSize.xs,
764
+ "aria-hidden": "true",
765
+ className: "nav_bar_locale_chevron"
766
+ }
767
+ )
752
768
  ]
753
769
  }
754
770
  ),
@@ -812,9 +828,9 @@ var Sidebar = ({
812
828
  ...props
813
829
  }) => {
814
830
  const isControlled = collapsedProp !== void 0;
815
- const [internalCollapsed, setInternalCollapsed] = React20.useState(defaultCollapsed);
831
+ const [internalCollapsed, setInternalCollapsed] = React19.useState(defaultCollapsed);
816
832
  const collapsed = isControlled ? collapsedProp : internalCollapsed;
817
- const toggle = React20.useCallback(() => {
833
+ const toggle = React19.useCallback(() => {
818
834
  const next = !collapsed;
819
835
  if (!isControlled) setInternalCollapsed(next);
820
836
  onCollapsedChange?.(next);
@@ -846,7 +862,7 @@ var Sidebar = ({
846
862
  onClick: toggle,
847
863
  "aria-label": toggleLabel,
848
864
  "aria-expanded": !collapsed,
849
- children: collapsed ? /* @__PURE__ */ jsx(ChevronRight, { size: 14 }) : /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
865
+ children: collapsed ? /* @__PURE__ */ jsx(ChevronRight, { size: iconSize.xs }) : /* @__PURE__ */ jsx(ChevronLeft, { size: iconSize.xs })
850
866
  }
851
867
  )
852
868
  ]
@@ -887,9 +903,9 @@ var SidebarSection = ({ label, className, children, ...props }) => {
887
903
  children
888
904
  ] });
889
905
  };
890
- var TabsContext = React20.createContext(null);
906
+ var TabsContext = React19.createContext(null);
891
907
  function useTabsContext() {
892
- const ctx = React20.useContext(TabsContext);
908
+ const ctx = React19.useContext(TabsContext);
893
909
  if (!ctx) {
894
910
  throw new Error(
895
911
  '[Bigtablet DS] <Tab>, <TabList>, <TabPanel>\uC740 \uBC18\uB4DC\uC2DC <Tabs> \uC548\uC5D0 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.\n\n\uC62C\uBC14\uB978 \uC0AC\uC6A9 \uC608:\n <Tabs defaultValue="a">\n <TabList>\n <Tab value="a">A</Tab>\n </TabList>\n <TabPanel value="a">...</TabPanel>\n </Tabs>'
@@ -908,17 +924,17 @@ var Tabs = ({
908
924
  ...props
909
925
  }) => {
910
926
  const isControlled = controlledValue !== void 0;
911
- const [internalValue, setInternalValue] = React20.useState(defaultValue);
927
+ const [internalValue, setInternalValue] = React19.useState(defaultValue);
912
928
  const value = isControlled ? controlledValue : internalValue;
913
- const setValue = React20.useCallback(
929
+ const setValue = React19.useCallback(
914
930
  (v) => {
915
931
  if (!isControlled) setInternalValue(v);
916
932
  onValueChange?.(v);
917
933
  },
918
934
  [isControlled, onValueChange]
919
935
  );
920
- const idPrefix = React20.useId();
921
- const ctx = React20.useMemo(
936
+ const idPrefix = React19.useId();
937
+ const ctx = React19.useMemo(
922
938
  () => ({ value, setValue, variant, size, idPrefix }),
923
939
  [value, setValue, variant, size, idPrefix]
924
940
  );
@@ -926,9 +942,9 @@ var Tabs = ({
926
942
  };
927
943
  var TabList = ({ ariaLabel, className, children, ...props }) => {
928
944
  const { variant } = useTabsContext();
929
- const listRef = React20.useRef(null);
930
- const [indicator, setIndicator] = React20.useState(null);
931
- const [hasMounted, setHasMounted] = React20.useState(false);
945
+ const listRef = React19.useRef(null);
946
+ const [indicator, setIndicator] = React19.useState(null);
947
+ const [hasMounted, setHasMounted] = React19.useState(false);
932
948
  useSafeLayoutEffect(() => {
933
949
  const list = listRef.current;
934
950
  if (!list) return;
@@ -1077,15 +1093,15 @@ var Popover = ({
1077
1093
  className
1078
1094
  }) => {
1079
1095
  const isControlled = openProp !== void 0;
1080
- const [internalOpen, setInternalOpen] = React20.useState(defaultOpen);
1096
+ const [internalOpen, setInternalOpen] = React19.useState(defaultOpen);
1081
1097
  const open = isControlled ? openProp : internalOpen;
1082
- const [shouldRender, setShouldRender] = React20.useState(open ?? false);
1098
+ const [shouldRender, setShouldRender] = React19.useState(open ?? false);
1083
1099
  if (open && !shouldRender) setShouldRender(true);
1084
- const wrapperRef = React20.useRef(null);
1085
- const popoverRef = React20.useRef(null);
1086
- const previousFocusRef = React20.useRef(null);
1087
- const popoverId = React20.useId();
1088
- const setOpen = React20.useCallback(
1100
+ const wrapperRef = React19.useRef(null);
1101
+ const popoverRef = React19.useRef(null);
1102
+ const previousFocusRef = React19.useRef(null);
1103
+ const popoverId = React19.useId();
1104
+ const setOpen = React19.useCallback(
1089
1105
  (next) => {
1090
1106
  if (!isControlled) setInternalOpen(next);
1091
1107
  onOpenChange?.(next);
@@ -1105,7 +1121,7 @@ var Popover = ({
1105
1121
  }
1106
1122
  })();
1107
1123
  const style = useSpringPresence({ visible: open, from: fromTransform, onExitComplete: () => setShouldRender(false) });
1108
- React20.useEffect(() => {
1124
+ React19.useEffect(() => {
1109
1125
  if (!open) return;
1110
1126
  previousFocusRef.current = document.activeElement;
1111
1127
  const node = popoverRef.current;
@@ -1113,7 +1129,7 @@ var Popover = ({
1113
1129
  const focusable = node.querySelector(FOCUSABLE_SELECTORS2);
1114
1130
  (focusable ?? node).focus();
1115
1131
  }, [open]);
1116
- React20.useEffect(() => {
1132
+ React19.useEffect(() => {
1117
1133
  if (!open) return;
1118
1134
  const handleClick = (e) => {
1119
1135
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -1131,7 +1147,7 @@ var Popover = ({
1131
1147
  document.removeEventListener("keydown", handleKeyDown);
1132
1148
  };
1133
1149
  }, [open, setOpen]);
1134
- const triggerWithProps = React20.cloneElement(
1150
+ const triggerWithProps = React19.cloneElement(
1135
1151
  trigger,
1136
1152
  {
1137
1153
  onClick: (e) => {
@@ -1170,18 +1186,18 @@ var Tooltip = ({
1170
1186
  disabled = false,
1171
1187
  children
1172
1188
  }) => {
1173
- const [open, setOpen] = React20.useState(false);
1174
- const timerRef = React20.useRef(null);
1175
- const tooltipId = React20.useId();
1176
- const show = React20.useCallback(() => {
1189
+ const [open, setOpen] = React19.useState(false);
1190
+ const timerRef = React19.useRef(null);
1191
+ const tooltipId = React19.useId();
1192
+ const show = React19.useCallback(() => {
1177
1193
  if (timerRef.current) clearTimeout(timerRef.current);
1178
1194
  timerRef.current = setTimeout(() => setOpen(true), delay);
1179
1195
  }, [delay]);
1180
- const hide = React20.useCallback(() => {
1196
+ const hide = React19.useCallback(() => {
1181
1197
  if (timerRef.current) clearTimeout(timerRef.current);
1182
1198
  setOpen(false);
1183
1199
  }, []);
1184
- React20.useEffect(() => {
1200
+ React19.useEffect(() => {
1185
1201
  return () => {
1186
1202
  if (timerRef.current) clearTimeout(timerRef.current);
1187
1203
  };
@@ -1201,7 +1217,7 @@ var Tooltip = ({
1201
1217
  const style = useSpringPresence({ visible: open, from: fromTransform });
1202
1218
  const child = children;
1203
1219
  const childProps = child.props;
1204
- const trigger = React20.cloneElement(child, {
1220
+ const trigger = React19.cloneElement(child, {
1205
1221
  onMouseEnter: (e) => {
1206
1222
  childProps.onMouseEnter?.(e);
1207
1223
  show();
@@ -1745,10 +1761,10 @@ var Button = ({
1745
1761
  ] });
1746
1762
  };
1747
1763
  var ICONS = {
1748
- info: /* @__PURE__ */ jsx(Info, { size: 20, "aria-hidden": "true" }),
1749
- success: /* @__PURE__ */ jsx(CheckCircle2, { size: 20, "aria-hidden": "true" }),
1750
- warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 20, "aria-hidden": "true" }),
1751
- error: /* @__PURE__ */ jsx(XCircle, { size: 20, "aria-hidden": "true" })
1764
+ info: /* @__PURE__ */ jsx(Info, { size: iconSize.lg, "aria-hidden": "true" }),
1765
+ success: /* @__PURE__ */ jsx(CheckCircle2, { size: iconSize.lg, "aria-hidden": "true" }),
1766
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { size: iconSize.lg, "aria-hidden": "true" }),
1767
+ error: /* @__PURE__ */ jsx(XCircle, { size: iconSize.lg, "aria-hidden": "true" })
1752
1768
  };
1753
1769
  var AlertContext = createContext(null);
1754
1770
  var useAlert = () => {
@@ -1804,12 +1820,12 @@ var AlertModal = ({
1804
1820
  onCancel,
1805
1821
  onClose
1806
1822
  }) => {
1807
- const panelRef = React20.useRef(null);
1808
- const titleId = React20.useId();
1809
- const messageId = React20.useId();
1823
+ const panelRef = React19.useRef(null);
1824
+ const titleId = React19.useId();
1825
+ const messageId = React19.useId();
1810
1826
  const [shouldRender, setShouldRender] = useState(isOpen);
1811
1827
  useFocusTrap(panelRef, isOpen);
1812
- React20.useEffect(() => {
1828
+ React19.useEffect(() => {
1813
1829
  if (isOpen) setShouldRender(true);
1814
1830
  }, [isOpen]);
1815
1831
  const overlayStyle = useSpring({
@@ -1917,12 +1933,12 @@ var Checkbox = ({
1917
1933
  ref,
1918
1934
  ...props
1919
1935
  }) => {
1920
- const inputRef = React20.useRef(null);
1921
- React20.useImperativeHandle(
1936
+ const inputRef = React19.useRef(null);
1937
+ React19.useImperativeHandle(
1922
1938
  ref,
1923
1939
  () => inputRef.current
1924
1940
  );
1925
- React20.useEffect(() => {
1941
+ React19.useEffect(() => {
1926
1942
  if (!inputRef.current) return;
1927
1943
  inputRef.current.indeterminate = Boolean(indeterminate);
1928
1944
  }, [indeterminate]);
@@ -2094,40 +2110,91 @@ var Chip = ({
2094
2110
  )
2095
2111
  ] });
2096
2112
  };
2097
- var Dropdown = ({
2098
- id,
2099
- label,
2100
- placeholder = "Select\u2026",
2101
- options,
2102
- value,
2103
- onValueChange,
2104
- onChange,
2105
- defaultValue = null,
2106
- disabled,
2107
- size = "md",
2108
- className
2109
- }) => {
2113
+ var normalizeForSearch = (s) => s.toLowerCase().replace(/\s+/g, "");
2114
+ var Dropdown = (props) => {
2115
+ const {
2116
+ id,
2117
+ label,
2118
+ placeholder = "Select\u2026",
2119
+ options,
2120
+ disabled,
2121
+ size = "md",
2122
+ className,
2123
+ searchable = false,
2124
+ searchPlaceholder = "\uAC80\uC0C9\u2026",
2125
+ emptyText = "\uACB0\uACFC \uC5C6\uC74C",
2126
+ selectedSummary = (count) => `${count}\uAC1C \uC120\uD0DD`
2127
+ } = props;
2128
+ const multiple = props.multiple === true;
2110
2129
  const internalId = useId();
2111
2130
  const dropdownId = id ?? internalId;
2112
- const isControlled = value !== void 0;
2113
- const [internalValue, setInternalValue] = useState(defaultValue);
2114
- const currentValue = isControlled ? value ?? null : internalValue;
2131
+ const isControlled = props.value !== void 0;
2132
+ const [internalSingle, setInternalSingle] = useState(
2133
+ () => props.multiple === true ? null : props.defaultValue ?? null
2134
+ );
2135
+ const [internalMulti, setInternalMulti] = useState(
2136
+ () => props.multiple === true ? props.defaultValue ?? [] : []
2137
+ );
2115
2138
  const [isOpen, setIsOpen] = useState(false);
2116
2139
  const [activeIndex, setActiveIndex] = useState(-1);
2117
2140
  const [dropUp, setDropUp] = useState(false);
2141
+ const [searchText, setSearchText] = useState("");
2142
+ const [committedQuery, setCommittedQuery] = useState("");
2143
+ const isComposingRef = useRef(false);
2118
2144
  const wrapperRef = useRef(null);
2119
2145
  const controlRef = useRef(null);
2120
- const currentOption = useMemo(
2121
- () => options.find((o) => o.value === currentValue) ?? null,
2122
- [options, currentValue]
2123
- );
2124
- const setValue = useCallback(
2146
+ const searchRef = useRef(null);
2147
+ const selectedValues = useMemo(() => {
2148
+ if (multiple) {
2149
+ const v2 = isControlled ? props.value : internalMulti;
2150
+ return v2 ?? [];
2151
+ }
2152
+ const v = isControlled ? props.value : internalSingle;
2153
+ return v != null ? [v] : [];
2154
+ }, [multiple, isControlled, props.value, internalMulti, internalSingle]);
2155
+ const visibleOptions = useMemo(() => {
2156
+ if (!searchable) return options;
2157
+ const q = normalizeForSearch(committedQuery);
2158
+ if (q === "") return options;
2159
+ return options.filter((o) => normalizeForSearch(o.label).includes(q));
2160
+ }, [options, searchable, committedQuery]);
2161
+ const selectSingle = useCallback(
2125
2162
  (next) => {
2126
2163
  const option = options.find((o) => o.value === next) ?? null;
2127
- if (!isControlled) setInternalValue(next);
2128
- (onValueChange ?? onChange)?.(next, option);
2164
+ if (!isControlled) setInternalSingle(next);
2165
+ if (props.multiple !== true) {
2166
+ (props.onValueChange ?? props.onChange)?.(next, option);
2167
+ }
2129
2168
  },
2130
- [isControlled, onValueChange, onChange, options]
2169
+ [isControlled, options, props.multiple, props.onValueChange, props.onChange]
2170
+ );
2171
+ const toggleMultiple = useCallback(
2172
+ (opt) => {
2173
+ const exists = selectedValues.includes(opt.value);
2174
+ const nextValues = exists ? selectedValues.filter((v) => v !== opt.value) : [...selectedValues, opt.value];
2175
+ const nextOptions = nextValues.map((v) => options.find((o) => o.value === v)).filter((o) => Boolean(o));
2176
+ if (!isControlled) setInternalMulti(nextValues);
2177
+ if (props.multiple === true) {
2178
+ (props.onValueChange ?? props.onChange)?.(nextValues, nextOptions);
2179
+ }
2180
+ },
2181
+ [selectedValues, options, isControlled, props.multiple, props.onValueChange, props.onChange]
2182
+ );
2183
+ const closePanel = useCallback(() => {
2184
+ setIsOpen(false);
2185
+ if (searchable) controlRef.current?.focus();
2186
+ }, [searchable]);
2187
+ const selectOption = useCallback(
2188
+ (opt) => {
2189
+ if (opt.disabled) return;
2190
+ if (multiple) {
2191
+ toggleMultiple(opt);
2192
+ } else {
2193
+ selectSingle(opt.value);
2194
+ closePanel();
2195
+ }
2196
+ },
2197
+ [multiple, toggleMultiple, selectSingle, closePanel]
2131
2198
  );
2132
2199
  useEffect(() => {
2133
2200
  const handleOutsideClick = (e) => {
@@ -2138,31 +2205,33 @@ var Dropdown = ({
2138
2205
  document.addEventListener("mousedown", handleOutsideClick);
2139
2206
  return () => document.removeEventListener("mousedown", handleOutsideClick);
2140
2207
  }, []);
2141
- const moveActive = (dir) => {
2142
- if (options.length === 0) return;
2143
- if (!isOpen) {
2144
- setIsOpen(true);
2145
- return;
2146
- }
2147
- let i = activeIndex;
2148
- const len = options.length;
2149
- for (let step = 0; step < len; step++) {
2150
- i = (i + dir + len) % len;
2151
- if (!options[i].disabled) {
2152
- setActiveIndex(i);
2153
- break;
2208
+ const moveActive = useCallback(
2209
+ (dir) => {
2210
+ if (visibleOptions.length === 0) return;
2211
+ if (!isOpen) {
2212
+ setIsOpen(true);
2213
+ return;
2154
2214
  }
2155
- }
2156
- };
2157
- const commitActive = () => {
2158
- if (activeIndex < 0 || activeIndex >= options.length) return;
2159
- const opt = options[activeIndex];
2160
- if (!opt.disabled) {
2161
- setValue(opt.value);
2162
- setIsOpen(false);
2163
- }
2164
- };
2165
- const onKeyDown = (e) => {
2215
+ let i = activeIndex;
2216
+ if (i === -1) {
2217
+ i = dir === 1 ? -1 : 0;
2218
+ }
2219
+ const len = visibleOptions.length;
2220
+ for (let step = 0; step < len; step++) {
2221
+ i = (i + dir + len) % len;
2222
+ if (!visibleOptions[i].disabled) {
2223
+ setActiveIndex(i);
2224
+ break;
2225
+ }
2226
+ }
2227
+ },
2228
+ [visibleOptions, isOpen, activeIndex]
2229
+ );
2230
+ const commitActive = useCallback(() => {
2231
+ if (activeIndex < 0 || activeIndex >= visibleOptions.length) return;
2232
+ selectOption(visibleOptions[activeIndex]);
2233
+ }, [activeIndex, visibleOptions, selectOption]);
2234
+ const onControlKeyDown = (e) => {
2166
2235
  if (disabled) return;
2167
2236
  switch (e.key) {
2168
2237
  case " ":
@@ -2182,13 +2251,13 @@ var Dropdown = ({
2182
2251
  case "Home":
2183
2252
  e.preventDefault();
2184
2253
  setIsOpen(true);
2185
- setActiveIndex(options.findIndex((o) => !o.disabled));
2254
+ setActiveIndex(visibleOptions.findIndex((o) => !o.disabled));
2186
2255
  break;
2187
2256
  case "End":
2188
2257
  e.preventDefault();
2189
2258
  setIsOpen(true);
2190
- for (let i = options.length - 1; i >= 0; i--) {
2191
- if (!options[i].disabled) {
2259
+ for (let i = visibleOptions.length - 1; i >= 0; i--) {
2260
+ if (!visibleOptions[i].disabled) {
2192
2261
  setActiveIndex(i);
2193
2262
  break;
2194
2263
  }
@@ -2200,16 +2269,46 @@ var Dropdown = ({
2200
2269
  break;
2201
2270
  }
2202
2271
  };
2272
+ const onSearchKeyDown = (e) => {
2273
+ if (e.nativeEvent.isComposing) return;
2274
+ switch (e.key) {
2275
+ case "ArrowDown":
2276
+ e.preventDefault();
2277
+ moveActive(1);
2278
+ break;
2279
+ case "ArrowUp":
2280
+ e.preventDefault();
2281
+ moveActive(-1);
2282
+ break;
2283
+ case "Enter":
2284
+ e.preventDefault();
2285
+ commitActive();
2286
+ break;
2287
+ case "Escape":
2288
+ e.preventDefault();
2289
+ closePanel();
2290
+ break;
2291
+ }
2292
+ };
2203
2293
  useEffect(() => {
2204
2294
  if (!isOpen) return;
2205
- const matchedIndex = options.findIndex((o) => o.value === currentValue && !o.disabled);
2206
- setActiveIndex(
2207
- matchedIndex >= 0 ? matchedIndex : Math.max(
2208
- 0,
2209
- options.findIndex((o) => !o.disabled)
2210
- )
2295
+ const selectedIdx = visibleOptions.findIndex(
2296
+ (o) => selectedValues.includes(o.value) && !o.disabled
2211
2297
  );
2212
- }, [isOpen, options, currentValue]);
2298
+ setActiveIndex(selectedIdx >= 0 ? selectedIdx : visibleOptions.findIndex((o) => !o.disabled));
2299
+ }, [isOpen, visibleOptions]);
2300
+ useEffect(() => {
2301
+ if (!isOpen) {
2302
+ setSearchText("");
2303
+ setCommittedQuery("");
2304
+ isComposingRef.current = false;
2305
+ }
2306
+ }, [isOpen]);
2307
+ useEffect(() => {
2308
+ if (isOpen && searchable) {
2309
+ searchRef.current?.focus();
2310
+ }
2311
+ }, [isOpen, searchable]);
2213
2312
  useEffect(() => {
2214
2313
  if (!isOpen || !controlRef.current) return;
2215
2314
  const rect = controlRef.current.getBoundingClientRect();
@@ -2218,6 +2317,13 @@ var Dropdown = ({
2218
2317
  const MIN_BELOW = 120;
2219
2318
  setDropUp(spaceBelow < MIN_BELOW && spaceAbove > spaceBelow);
2220
2319
  }, [isOpen]);
2320
+ const currentOption = useMemo(
2321
+ () => multiple ? null : options.find((o) => o.value === selectedValues[0]) ?? null,
2322
+ [multiple, options, selectedValues]
2323
+ );
2324
+ const hasSelection = selectedValues.length > 0;
2325
+ const showValue = multiple ? hasSelection : currentOption != null;
2326
+ const controlText = multiple ? hasSelection ? selectedSummary(selectedValues.length) : placeholder : currentOption ? currentOption.label : placeholder;
2221
2327
  const rootClassName = cn("dropdown", `dropdown_size_${size}`, className);
2222
2328
  const fieldsetClassName = cn("dropdown_fieldset", { is_open: isOpen, is_disabled: disabled });
2223
2329
  const listClassName = cn("dropdown_list", { dropdown_list_up: dropUp });
@@ -2225,86 +2331,107 @@ var Dropdown = ({
2225
2331
  visible: isOpen,
2226
2332
  from: dropUp ? "translateY(4px)" : "translateY(-4px)"
2227
2333
  });
2228
- return /* @__PURE__ */ jsxs(
2229
- "div",
2230
- {
2231
- ref: wrapperRef,
2232
- className: rootClassName,
2233
- children: [
2234
- label && /* @__PURE__ */ jsx("label", { htmlFor: dropdownId, className: "dropdown_label", children: label }),
2235
- /* @__PURE__ */ jsx("div", { className: fieldsetClassName, children: /* @__PURE__ */ jsxs(
2236
- "button",
2334
+ return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: rootClassName, children: [
2335
+ label && /* @__PURE__ */ jsx("label", { htmlFor: dropdownId, className: "dropdown_label", children: label }),
2336
+ /* @__PURE__ */ jsx("div", { className: fieldsetClassName, children: /* @__PURE__ */ jsxs(
2337
+ "button",
2338
+ {
2339
+ ref: controlRef,
2340
+ id: dropdownId,
2341
+ type: "button",
2342
+ className: cn("dropdown_control", { is_disabled: disabled }),
2343
+ "aria-haspopup": "listbox",
2344
+ "aria-expanded": isOpen,
2345
+ "aria-controls": `${dropdownId}_listbox`,
2346
+ onClick: () => !disabled && setIsOpen((o) => !o),
2347
+ onKeyDown: onControlKeyDown,
2348
+ disabled,
2349
+ children: [
2350
+ /* @__PURE__ */ jsx("span", { className: showValue ? "dropdown_value" : "dropdown_placeholder", children: controlText }),
2351
+ /* @__PURE__ */ jsx("span", { className: cn("dropdown_icon", { dropdown_icon_open: isOpen }), "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize.lg }) })
2352
+ ]
2353
+ }
2354
+ ) }),
2355
+ isOpen && /* @__PURE__ */ jsxs(animated.div, { className: listClassName, style: listStyle, children: [
2356
+ searchable && /* @__PURE__ */ jsxs("div", { className: "dropdown_search", children: [
2357
+ /* @__PURE__ */ jsx("span", { className: "dropdown_search_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Search, { size: iconSize.sm }) }),
2358
+ /* @__PURE__ */ jsx(
2359
+ "input",
2237
2360
  {
2238
- ref: controlRef,
2239
- id: dropdownId,
2240
- type: "button",
2241
- className: cn("dropdown_control", { is_disabled: disabled }),
2242
- "aria-haspopup": "listbox",
2361
+ ref: searchRef,
2362
+ type: "text",
2363
+ className: "dropdown_search_input",
2364
+ placeholder: searchPlaceholder,
2365
+ "aria-label": searchPlaceholder,
2366
+ autoComplete: "off",
2367
+ role: "combobox",
2368
+ "aria-autocomplete": "list",
2243
2369
  "aria-expanded": isOpen,
2244
2370
  "aria-controls": `${dropdownId}_listbox`,
2245
- onClick: () => !disabled && setIsOpen((o) => !o),
2246
- onKeyDown,
2247
- disabled,
2248
- children: [
2249
- /* @__PURE__ */ jsx("span", { className: currentOption ? "dropdown_value" : "dropdown_placeholder", children: currentOption ? currentOption.label : placeholder }),
2250
- /* @__PURE__ */ jsx(
2251
- "span",
2252
- {
2253
- className: cn("dropdown_icon", { dropdown_icon_open: isOpen }),
2254
- "aria-hidden": "true",
2255
- children: /* @__PURE__ */ jsx(ChevronDown, { size: 20 })
2256
- }
2257
- )
2258
- ]
2259
- }
2260
- ) }),
2261
- isOpen && /* @__PURE__ */ jsx(
2262
- animated.div,
2263
- {
2264
- id: `${dropdownId}_listbox`,
2265
- role: "listbox",
2266
- className: listClassName,
2267
- style: listStyle,
2268
- children: options.map((opt, i) => {
2269
- const selected = currentValue === opt.value;
2270
- const active = i === activeIndex;
2271
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2272
- /* @__PURE__ */ jsxs(
2273
- "div",
2274
- {
2275
- role: "option",
2276
- tabIndex: -1,
2277
- "aria-selected": selected,
2278
- "aria-disabled": opt.disabled ? true : void 0,
2279
- className: cn("dropdown_option", {
2280
- is_selected: selected,
2281
- is_active: active,
2282
- is_disabled: opt.disabled
2283
- }),
2284
- onMouseEnter: () => !opt.disabled && setActiveIndex(i),
2285
- onClick: () => {
2286
- if (opt.disabled) return;
2287
- setValue(opt.value);
2288
- setIsOpen(false);
2289
- },
2290
- children: [
2291
- opt.leadingIcon && /* @__PURE__ */ jsx("span", { className: "dropdown_option_icon", children: opt.leadingIcon }),
2292
- /* @__PURE__ */ jsxs("span", { className: "dropdown_option_content", children: [
2293
- /* @__PURE__ */ jsx("span", { className: "dropdown_option_label", children: opt.label }),
2294
- opt.supportingText && /* @__PURE__ */ jsx("span", { className: "dropdown_option_supporting", children: opt.supportingText })
2295
- ] }),
2296
- (selected || opt.trailingIcon) && /* @__PURE__ */ jsx("span", { className: "dropdown_option_trailing", children: opt.trailingIcon ?? /* @__PURE__ */ jsx(Check, { size: 16, "aria-hidden": "true" }) })
2297
- ]
2298
- }
2299
- ),
2300
- opt.showDivider && /* @__PURE__ */ jsx("hr", { className: "dropdown_option_divider" })
2301
- ] }, opt.value);
2302
- })
2371
+ "aria-activedescendant": activeIndex >= 0 && visibleOptions[activeIndex] ? `${dropdownId}_option_${activeIndex}` : void 0,
2372
+ value: searchText,
2373
+ onChange: (e) => {
2374
+ const v = e.target.value;
2375
+ setSearchText(v);
2376
+ if (!isComposingRef.current) setCommittedQuery(v);
2377
+ },
2378
+ onCompositionStart: () => {
2379
+ isComposingRef.current = true;
2380
+ },
2381
+ onCompositionEnd: (e) => {
2382
+ isComposingRef.current = false;
2383
+ const v = e.currentTarget.value;
2384
+ setSearchText(v);
2385
+ setCommittedQuery(v);
2386
+ },
2387
+ onKeyDown: onSearchKeyDown
2303
2388
  }
2304
2389
  )
2305
- ]
2306
- }
2307
- );
2390
+ ] }),
2391
+ /* @__PURE__ */ jsx(
2392
+ "div",
2393
+ {
2394
+ id: `${dropdownId}_listbox`,
2395
+ role: "listbox",
2396
+ className: "dropdown_options",
2397
+ "aria-multiselectable": multiple || void 0,
2398
+ children: visibleOptions.length === 0 ? searchable && /* @__PURE__ */ jsx("div", { className: "dropdown_empty", children: emptyText }) : visibleOptions.map((opt, i) => {
2399
+ const selected = selectedValues.includes(opt.value);
2400
+ const active = i === activeIndex;
2401
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2402
+ /* @__PURE__ */ jsxs(
2403
+ "div",
2404
+ {
2405
+ id: `${dropdownId}_option_${i}`,
2406
+ role: "option",
2407
+ tabIndex: -1,
2408
+ "aria-selected": selected,
2409
+ "aria-disabled": opt.disabled ? true : void 0,
2410
+ className: cn("dropdown_option", {
2411
+ is_selected: selected,
2412
+ is_active: active,
2413
+ is_disabled: opt.disabled
2414
+ }),
2415
+ onMouseEnter: () => !opt.disabled && setActiveIndex(i),
2416
+ onClick: () => selectOption(opt),
2417
+ children: [
2418
+ multiple && /* @__PURE__ */ jsx("span", { className: "dropdown_option_check", "aria-hidden": "true", children: selected && /* @__PURE__ */ jsx(Check, { size: iconSize.sm }) }),
2419
+ opt.leadingIcon && /* @__PURE__ */ jsx("span", { className: "dropdown_option_icon", children: opt.leadingIcon }),
2420
+ /* @__PURE__ */ jsxs("span", { className: "dropdown_option_content", children: [
2421
+ /* @__PURE__ */ jsx("span", { className: "dropdown_option_label", children: opt.label }),
2422
+ opt.supportingText && /* @__PURE__ */ jsx("span", { className: "dropdown_option_supporting", children: opt.supportingText })
2423
+ ] }),
2424
+ (opt.trailingIcon || !multiple && selected) && /* @__PURE__ */ jsx("span", { className: "dropdown_option_trailing", children: opt.trailingIcon ?? /* @__PURE__ */ jsx(Check, { size: iconSize.sm, "aria-hidden": "true" }) })
2425
+ ]
2426
+ }
2427
+ ),
2428
+ opt.showDivider && /* @__PURE__ */ jsx("hr", { className: "dropdown_option_divider" })
2429
+ ] }, opt.value);
2430
+ })
2431
+ }
2432
+ )
2433
+ ] })
2434
+ ] });
2308
2435
  };
2309
2436
  var pad = (n) => String(n).padStart(2, "0");
2310
2437
  var getDaysInMonth = (year, month) => new Date(year, month, 0).getDate();
@@ -2329,9 +2456,9 @@ var DatePicker = ({
2329
2456
  minDateSrFormat = "Minimum date: {date}",
2330
2457
  selectableRangeUntilTodaySrText = "Selectable up to today"
2331
2458
  }) => {
2332
- const groupId = React20.useId();
2333
- const constraintId = React20.useId();
2334
- const { todayYear, todayMonth, todayDay } = React20.useMemo(() => {
2459
+ const groupId = React19.useId();
2460
+ const constraintId = React19.useId();
2461
+ const { todayYear, todayMonth, todayDay } = React19.useMemo(() => {
2335
2462
  const now = /* @__PURE__ */ new Date();
2336
2463
  return {
2337
2464
  todayYear: now.getFullYear(),
@@ -2340,7 +2467,7 @@ var DatePicker = ({
2340
2467
  };
2341
2468
  }, []);
2342
2469
  const endYear = endYearProp ?? todayYear + 10;
2343
- const parsed = React20.useMemo(() => {
2470
+ const parsed = React19.useMemo(() => {
2344
2471
  if (!value) return { year: 0, month: 0, day: 0 };
2345
2472
  const [y, m, d] = value.split("-").map(Number);
2346
2473
  return {
@@ -2349,7 +2476,7 @@ var DatePicker = ({
2349
2476
  day: d || 0
2350
2477
  };
2351
2478
  }, [value]);
2352
- const min = React20.useMemo(() => {
2479
+ const min = React19.useMemo(() => {
2353
2480
  if (!minDate) return { year: 0, month: 0, day: 0 };
2354
2481
  const [y, m, d] = minDate.split("-").map(Number);
2355
2482
  return {
@@ -2369,7 +2496,7 @@ var DatePicker = ({
2369
2496
  min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1
2370
2497
  )
2371
2498
  );
2372
- const maxDay = React20.useMemo(() => {
2499
+ const maxDay = React19.useMemo(() => {
2373
2500
  if (!year || !month) return 31;
2374
2501
  const daysInMonth = getDaysInMonth(year, month);
2375
2502
  if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
@@ -2377,28 +2504,28 @@ var DatePicker = ({
2377
2504
  }
2378
2505
  return daysInMonth;
2379
2506
  }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
2380
- const yearOptions = React20.useMemo(
2507
+ const yearOptions = React19.useMemo(
2381
2508
  () => range(startYear, maxYear).map((y) => ({
2382
2509
  value: String(y),
2383
2510
  label: String(y)
2384
2511
  })),
2385
2512
  [startYear, maxYear]
2386
2513
  );
2387
- const monthOptions = React20.useMemo(
2514
+ const monthOptions = React19.useMemo(
2388
2515
  () => range(minMonth, Math.max(minMonth, maxMonth)).map((m) => ({
2389
2516
  value: String(m),
2390
2517
  label: pad(m)
2391
2518
  })),
2392
2519
  [minMonth, maxMonth]
2393
2520
  );
2394
- const dayOptions = React20.useMemo(
2521
+ const dayOptions = React19.useMemo(
2395
2522
  () => range(minDay, Math.max(minDay, maxDay)).map((d) => ({
2396
2523
  value: String(d),
2397
2524
  label: pad(d)
2398
2525
  })),
2399
2526
  [minDay, maxDay]
2400
2527
  );
2401
- const emit = React20.useCallback(
2528
+ const emit = React19.useCallback(
2402
2529
  (yy, mm, dd) => {
2403
2530
  const cb = onValueChange ?? onChange;
2404
2531
  if (mode === "year-month") {
@@ -2410,7 +2537,7 @@ var DatePicker = ({
2410
2537
  },
2411
2538
  [mode, onValueChange, onChange]
2412
2539
  );
2413
- const handleYearChange = React20.useCallback(
2540
+ const handleYearChange = React19.useCallback(
2414
2541
  (raw) => {
2415
2542
  if (!raw) return;
2416
2543
  const newYear = Number(raw);
@@ -2427,14 +2554,14 @@ var DatePicker = ({
2427
2554
  },
2428
2555
  [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
2429
2556
  );
2430
- const handleMonthChange = React20.useCallback(
2557
+ const handleMonthChange = React19.useCallback(
2431
2558
  (raw) => {
2432
2559
  if (!raw || !year) return;
2433
2560
  emit(year, Number(raw), day || void 0);
2434
2561
  },
2435
2562
  [year, day, emit]
2436
2563
  );
2437
- const handleDayChange = React20.useCallback(
2564
+ const handleDayChange = React19.useCallback(
2438
2565
  (raw) => {
2439
2566
  if (!raw || !year || !month) return;
2440
2567
  emit(year, month, Number(raw));
@@ -2509,6 +2636,119 @@ var Divider = ({ weight = "standard", className, ...props }) => {
2509
2636
  const dividerClassName = cn("divider", `divider_weight_${weight}`, className);
2510
2637
  return /* @__PURE__ */ jsx("hr", { className: dividerClassName, ...props });
2511
2638
  };
2639
+ var SLIDE_FROM = {
2640
+ left: "translateX(-100%)",
2641
+ right: "translateX(100%)",
2642
+ bottom: "translateY(100%)"
2643
+ };
2644
+ var Drawer = ({
2645
+ open,
2646
+ onClose,
2647
+ placement = "right",
2648
+ size = 360,
2649
+ title,
2650
+ footer,
2651
+ closeOnOverlay = true,
2652
+ showCloseIcon = true,
2653
+ closeLabel = "\uB2EB\uAE30",
2654
+ ariaLabel,
2655
+ children,
2656
+ className,
2657
+ ...props
2658
+ }) => {
2659
+ const panelRef = React19.useRef(null);
2660
+ const titleId = React19.useId();
2661
+ const [shouldRender, setShouldRender] = React19.useState(open);
2662
+ const reduced = useReducedMotion();
2663
+ useFocusTrap(panelRef, open);
2664
+ if (open && !shouldRender) setShouldRender(true);
2665
+ const overlayStyle = useSpringPresence({
2666
+ visible: open,
2667
+ from: "translateY(0px)",
2668
+ onExitComplete: () => setShouldRender(false)
2669
+ });
2670
+ const slideFrom = SLIDE_FROM[placement];
2671
+ const slideRest = placement === "bottom" ? "translateY(0%)" : "translateX(0%)";
2672
+ const panelStyle = useSpring({
2673
+ from: { transform: slideFrom },
2674
+ to: { transform: open ? slideRest : slideFrom },
2675
+ immediate: reduced,
2676
+ config: { tension: 280, friction: 28, clamp: !open }
2677
+ });
2678
+ React19.useEffect(() => {
2679
+ if (!open) return;
2680
+ const body = document.body;
2681
+ const openModals = parseInt(body.dataset.openModals || "0", 10);
2682
+ if (openModals === 0) {
2683
+ body.dataset.originalOverflow = window.getComputedStyle(body).overflow;
2684
+ body.style.overflow = "hidden";
2685
+ }
2686
+ body.dataset.openModals = String(openModals + 1);
2687
+ return () => {
2688
+ const currentOpenModals = parseInt(body.dataset.openModals || "1", 10);
2689
+ const nextOpenModals = currentOpenModals - 1;
2690
+ if (nextOpenModals === 0) {
2691
+ body.style.overflow = body.dataset.originalOverflow || "";
2692
+ delete body.dataset.openModals;
2693
+ delete body.dataset.originalOverflow;
2694
+ } else {
2695
+ body.dataset.openModals = String(nextOpenModals);
2696
+ }
2697
+ };
2698
+ }, [open]);
2699
+ if (!open && !shouldRender) return null;
2700
+ const hasTitle = !!title;
2701
+ const sizeValue = typeof size === "number" ? `${size}px` : size;
2702
+ const panelSizeStyle = placement === "bottom" ? { height: sizeValue } : { width: sizeValue };
2703
+ return /* @__PURE__ */ jsx(
2704
+ animated.div,
2705
+ {
2706
+ className: cn("drawer", `drawer_placement_${placement}`),
2707
+ style: overlayStyle,
2708
+ role: "dialog",
2709
+ "aria-modal": "true",
2710
+ "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
2711
+ "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
2712
+ onClick: () => closeOnOverlay && onClose?.(),
2713
+ onKeyDown: (e) => {
2714
+ if (e.key === "Escape") {
2715
+ e.stopPropagation();
2716
+ onClose?.();
2717
+ }
2718
+ },
2719
+ children: /* @__PURE__ */ jsxs(
2720
+ animated.div,
2721
+ {
2722
+ ref: panelRef,
2723
+ className: cn("drawer_panel", className),
2724
+ style: { ...panelStyle, ...panelSizeStyle },
2725
+ role: "document",
2726
+ onClick: (e) => e.stopPropagation(),
2727
+ onKeyDown: (e) => {
2728
+ if (e.key !== "Escape") e.stopPropagation();
2729
+ },
2730
+ ...props,
2731
+ children: [
2732
+ showCloseIcon && onClose && /* @__PURE__ */ jsx(
2733
+ "button",
2734
+ {
2735
+ type: "button",
2736
+ className: "drawer_close",
2737
+ onClick: onClose,
2738
+ "aria-label": closeLabel,
2739
+ children: /* @__PURE__ */ jsx(X, { size: iconSize.md, "aria-hidden": "true" })
2740
+ }
2741
+ ),
2742
+ title && /* @__PURE__ */ jsx("div", { className: "drawer_header", children: /* @__PURE__ */ jsx("h2", { id: titleId, className: "drawer_title", children: title }) }),
2743
+ children && /* @__PURE__ */ jsx("div", { className: "drawer_body", children }),
2744
+ footer && /* @__PURE__ */ jsx("div", { className: "drawer_footer", children: footer })
2745
+ ]
2746
+ }
2747
+ )
2748
+ }
2749
+ );
2750
+ };
2751
+ Drawer.displayName = "Drawer";
2512
2752
  var FileInput = ({
2513
2753
  label = "Choose file",
2514
2754
  onFiles,
@@ -2522,11 +2762,11 @@ var FileInput = ({
2522
2762
  onChange,
2523
2763
  ...props
2524
2764
  }) => {
2525
- const inputId = React20.useId();
2526
- const helperId = React20.useId();
2527
- const inputRef = React20.useRef(null);
2528
- const [previewUrls, setPreviewUrls] = React20.useState([]);
2529
- const previewUrlsRef = React20.useRef([]);
2765
+ const inputId = React19.useId();
2766
+ const helperId = React19.useId();
2767
+ const inputRef = React19.useRef(null);
2768
+ const [previewUrls, setPreviewUrls] = React19.useState([]);
2769
+ const previewUrlsRef = React19.useRef([]);
2530
2770
  const isPreviewVariant = variant === "preview";
2531
2771
  const showPreview = isPreviewVariant || preview;
2532
2772
  const handleChange = (e) => {
@@ -2567,7 +2807,7 @@ var FileInput = ({
2567
2807
  }
2568
2808
  onFiles?.(null);
2569
2809
  };
2570
- React20.useEffect(() => {
2810
+ React19.useEffect(() => {
2571
2811
  return () => {
2572
2812
  for (const url of previewUrlsRef.current) {
2573
2813
  URL.revokeObjectURL(url);
@@ -2614,7 +2854,7 @@ var FileInput = ({
2614
2854
  }
2615
2855
  )
2616
2856
  ) : /* @__PURE__ */ jsxs("span", { className: "file_input_preview_empty", children: [
2617
- /* @__PURE__ */ jsx(Image, { size: 32, "aria-hidden": "true" }),
2857
+ /* @__PURE__ */ jsx(Image, { size: iconSize.xl, "aria-hidden": "true" }),
2618
2858
  /* @__PURE__ */ jsx("span", { className: "file_input_preview_empty_text", children: label })
2619
2859
  ] })
2620
2860
  }
@@ -2626,7 +2866,7 @@ var FileInput = ({
2626
2866
  className: "file_input_preview_remove",
2627
2867
  onClick: handleRemove,
2628
2868
  "aria-label": "\uC774\uBBF8\uC9C0 \uC81C\uAC70",
2629
- children: /* @__PURE__ */ jsx(X, { size: 14, "aria-hidden": "true" })
2869
+ children: /* @__PURE__ */ jsx(X, { size: iconSize.xs, "aria-hidden": "true" })
2630
2870
  }
2631
2871
  ),
2632
2872
  supportingText && /* @__PURE__ */ jsx("span", { id: helperId, className: "file_input_helper", children: supportingText }),
@@ -2866,13 +3106,11 @@ var Modal = ({
2866
3106
  ariaLabel,
2867
3107
  ...props
2868
3108
  }) => {
2869
- const panelRef = React20.useRef(null);
2870
- const titleId = React20.useId();
2871
- const [shouldRender, setShouldRender] = React20.useState(open);
3109
+ const panelRef = React19.useRef(null);
3110
+ const titleId = React19.useId();
3111
+ const [shouldRender, setShouldRender] = React19.useState(open);
2872
3112
  useFocusTrap(panelRef, open);
2873
- React20.useEffect(() => {
2874
- if (open) setShouldRender(true);
2875
- }, [open]);
3113
+ if (open && !shouldRender) setShouldRender(true);
2876
3114
  const overlayStyle = useSpring({
2877
3115
  opacity: open ? 1 : 0,
2878
3116
  config: { tension: 280, friction: 28, clamp: !open },
@@ -2885,7 +3123,7 @@ var Modal = ({
2885
3123
  transform: open ? "scale(1) translateY(0px)" : "scale(0.96) translateY(-4px)",
2886
3124
  config: { tension: 280, friction: 28, clamp: !open }
2887
3125
  });
2888
- React20.useEffect(() => {
3126
+ React19.useEffect(() => {
2889
3127
  if (!open) return;
2890
3128
  const body = document.body;
2891
3129
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -2906,7 +3144,7 @@ var Modal = ({
2906
3144
  }
2907
3145
  };
2908
3146
  }, [open]);
2909
- if (!shouldRender) return null;
3147
+ if (!open && !shouldRender) return null;
2910
3148
  const hasTitle = !!title;
2911
3149
  return /* @__PURE__ */ jsx(
2912
3150
  animated.div,
@@ -2944,7 +3182,7 @@ var Modal = ({
2944
3182
  className: "modal_close",
2945
3183
  onClick: onClose,
2946
3184
  "aria-label": closeLabel,
2947
- children: /* @__PURE__ */ jsx(X, { size: 18, "aria-hidden": "true" })
3185
+ children: /* @__PURE__ */ jsx(X, { size: iconSize.md, "aria-hidden": "true" })
2948
3186
  }
2949
3187
  ),
2950
3188
  title && /* @__PURE__ */ jsx("h2", { id: titleId, className: "modal_title", children: title }),
@@ -2969,12 +3207,12 @@ var OtpInput = ({
2969
3207
  ariaLabel = "OTP \uC785\uB825",
2970
3208
  className
2971
3209
  }) => {
2972
- const inputsRef = React20.useRef([]);
2973
- const isTypingRef = React20.useRef(false);
2974
- React20.useEffect(() => {
3210
+ const inputsRef = React19.useRef([]);
3211
+ const isTypingRef = React19.useRef(false);
3212
+ React19.useEffect(() => {
2975
3213
  if (autoFocus) inputsRef.current[0]?.focus();
2976
3214
  }, [autoFocus]);
2977
- const digits = React20.useMemo(() => {
3215
+ const digits = React19.useMemo(() => {
2978
3216
  const chars = value.split("").slice(0, length);
2979
3217
  while (chars.length < length) chars.push("");
2980
3218
  return chars;
@@ -3115,7 +3353,7 @@ var Pagination = ({
3115
3353
  const emit = onPageChange ?? onChange;
3116
3354
  const prevDisabled = page <= 1;
3117
3355
  const nextDisabled = page >= totalPages;
3118
- const items = React20.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
3356
+ const items = React19.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
3119
3357
  return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
3120
3358
  /* @__PURE__ */ jsx(
3121
3359
  "button",
@@ -3160,9 +3398,9 @@ var Pagination = ({
3160
3398
  )
3161
3399
  ] });
3162
3400
  };
3163
- var RadioGroupContext = React20.createContext(null);
3401
+ var RadioGroupContext = React19.createContext(null);
3164
3402
  function useRadioGroupContext() {
3165
- return React20.useContext(RadioGroupContext);
3403
+ return React19.useContext(RadioGroupContext);
3166
3404
  }
3167
3405
  var RadioGroup = ({
3168
3406
  value: controlledValue,
@@ -3180,21 +3418,21 @@ var RadioGroup = ({
3180
3418
  ...props
3181
3419
  }) => {
3182
3420
  const isControlled = controlledValue !== void 0;
3183
- const [internalValue, setInternalValue] = React20.useState(defaultValue);
3421
+ const [internalValue, setInternalValue] = React19.useState(defaultValue);
3184
3422
  const value = isControlled ? controlledValue : internalValue;
3185
- const generatedName = React20.useId();
3423
+ const generatedName = React19.useId();
3186
3424
  const name = nameProp ?? generatedName;
3187
- const idPrefix = React20.useId();
3425
+ const idPrefix = React19.useId();
3188
3426
  const labelId = label ? `${idPrefix}-label` : void 0;
3189
3427
  const helperId = supportingText ? `${idPrefix}-help` : void 0;
3190
- const onChange = React20.useCallback(
3428
+ const onChange = React19.useCallback(
3191
3429
  (next) => {
3192
3430
  if (!isControlled) setInternalValue(next);
3193
3431
  onValueChange?.(next);
3194
3432
  },
3195
3433
  [isControlled, onValueChange]
3196
3434
  );
3197
- const ctx = React20.useMemo(
3435
+ const ctx = React19.useMemo(
3198
3436
  () => ({ name, value, onChange, size, disabled }),
3199
3437
  [name, value, onChange, size, disabled]
3200
3438
  );
@@ -3323,7 +3561,15 @@ var Table = ({
3323
3561
  stickyHeader = false,
3324
3562
  ariaLabel,
3325
3563
  className,
3326
- onRowClick
3564
+ onRowClick,
3565
+ sort,
3566
+ onSortChange,
3567
+ selectAllAriaLabel = "\uC804\uCCB4 \uC120\uD0DD",
3568
+ selectRowAriaLabel = (index) => `${index + 1}\uBC88\uC9F8 \uD589 \uC120\uD0DD`,
3569
+ selectable = false,
3570
+ rowKey,
3571
+ selectedKeys,
3572
+ onSelectionChange
3327
3573
  }) => {
3328
3574
  const wrapperClassName = cn(
3329
3575
  "table_wrapper",
@@ -3332,69 +3578,171 @@ var Table = ({
3332
3578
  className
3333
3579
  );
3334
3580
  const isEmpty = !isLoading && data.length === 0;
3581
+ const getRowKey = (item, index) => rowKey?.(item) ?? String(index);
3582
+ const allRowKeys = selectable ? data.map((item, index) => getRowKey(item, index)) : [];
3583
+ const selectedSet = React19.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
3584
+ const selectedCount = allRowKeys.filter((key) => selectedSet.has(key)).length;
3585
+ const isAllSelected = allRowKeys.length > 0 && selectedCount === allRowKeys.length;
3586
+ const isSomeSelected = selectedCount > 0 && !isAllSelected;
3587
+ const handleToggleAll = () => {
3588
+ const pageKeys = new Set(allRowKeys);
3589
+ const offPage = (selectedKeys ?? []).filter((k) => !pageKeys.has(k));
3590
+ onSelectionChange?.(isAllSelected ? offPage : [...offPage, ...allRowKeys]);
3591
+ };
3592
+ const handleToggleRow = (key) => {
3593
+ const current = selectedKeys ?? [];
3594
+ const next = current.includes(key) ? current.filter((k) => k !== key) : [...current, key];
3595
+ onSelectionChange?.(next);
3596
+ };
3597
+ const handleSortClick = (key) => {
3598
+ const next = sort?.key !== key ? { key, direction: "asc" } : sort.direction === "asc" ? { key, direction: "desc" } : void 0;
3599
+ onSortChange?.(next);
3600
+ };
3335
3601
  return /* @__PURE__ */ jsxs("div", { className: wrapperClassName, children: [
3336
3602
  /* @__PURE__ */ jsxs("table", { className: "table", "aria-label": ariaLabel, children: [
3337
- /* @__PURE__ */ jsx("thead", { className: "table_thead", children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx(
3338
- "th",
3339
- {
3340
- scope: "col",
3341
- className: cn("table_th", col.align && `table_align_${col.align}`),
3342
- style: { width: col.width },
3343
- children: col.header
3344
- },
3345
- col.key
3346
- )) }) }),
3603
+ /* @__PURE__ */ jsx("thead", { className: "table_thead", children: /* @__PURE__ */ jsxs("tr", { children: [
3604
+ selectable && /* @__PURE__ */ jsx("th", { scope: "col", className: "table_th table_th_checkbox", children: /* @__PURE__ */ jsx(
3605
+ Checkbox,
3606
+ {
3607
+ "aria-label": selectAllAriaLabel,
3608
+ checked: isAllSelected,
3609
+ indeterminate: isSomeSelected,
3610
+ disabled: isLoading || allRowKeys.length === 0,
3611
+ onChange: handleToggleAll
3612
+ }
3613
+ ) }),
3614
+ columns.map((col) => {
3615
+ const isSortActive = Boolean(col.sortable) && sort?.key === col.key;
3616
+ const direction = isSortActive ? sort?.direction : void 0;
3617
+ const ariaSort = col.sortable ? direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none" : void 0;
3618
+ return /* @__PURE__ */ jsx(
3619
+ "th",
3620
+ {
3621
+ scope: "col",
3622
+ className: cn(
3623
+ "table_th",
3624
+ col.align && `table_align_${col.align}`,
3625
+ col.sortable && "table_th_sortable"
3626
+ ),
3627
+ style: { width: col.width },
3628
+ "aria-sort": ariaSort,
3629
+ children: col.sortable ? /* @__PURE__ */ jsxs(
3630
+ "button",
3631
+ {
3632
+ type: "button",
3633
+ className: "table_sort_button",
3634
+ onClick: () => handleSortClick(col.key),
3635
+ disabled: isLoading,
3636
+ children: [
3637
+ /* @__PURE__ */ jsx("span", { className: "table_sort_label", children: col.header }),
3638
+ direction === "asc" ? /* @__PURE__ */ jsx(
3639
+ ArrowUp,
3640
+ {
3641
+ size: iconSize.sm,
3642
+ className: "table_sort_icon table_sort_icon_active",
3643
+ "aria-hidden": "true"
3644
+ }
3645
+ ) : direction === "desc" ? /* @__PURE__ */ jsx(
3646
+ ArrowDown,
3647
+ {
3648
+ size: iconSize.sm,
3649
+ className: "table_sort_icon table_sort_icon_active",
3650
+ "aria-hidden": "true"
3651
+ }
3652
+ ) : /* @__PURE__ */ jsx(
3653
+ ArrowUpDown,
3654
+ {
3655
+ size: iconSize.sm,
3656
+ className: "table_sort_icon",
3657
+ "aria-hidden": "true"
3658
+ }
3659
+ )
3660
+ ]
3661
+ }
3662
+ ) : col.header
3663
+ },
3664
+ col.key
3665
+ );
3666
+ })
3667
+ ] }) }),
3347
3668
  /* @__PURE__ */ jsx("tbody", { className: "table_tbody", children: isLoading ? Array.from({ length: skeletonRows }).map((_, rowIndex) => (
3348
3669
  // biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows have no stable identity
3349
- /* @__PURE__ */ jsx("tr", { className: "table_row table_row_skeleton", children: columns.map((col) => /* @__PURE__ */ jsx(
3350
- "td",
3351
- {
3352
- className: cn("table_td", col.align && `table_align_${col.align}`),
3353
- style: { width: col.width },
3354
- children: /* @__PURE__ */ jsx(Skeleton, { variant: "text" })
3355
- },
3356
- col.key
3357
- )) }, rowIndex)
3358
- )) : data.map((item, rowIndex) => /* @__PURE__ */ jsx(
3359
- "tr",
3360
- {
3361
- className: cn(
3362
- "table_row",
3363
- hoverable && "table_row_hoverable",
3364
- onRowClick && "table_row_clickable"
3365
- ),
3366
- role: onRowClick ? "button" : void 0,
3367
- tabIndex: onRowClick ? 0 : void 0,
3368
- onClick: onRowClick ? () => onRowClick(item, rowIndex) : void 0,
3369
- onKeyDown: onRowClick ? (e) => {
3370
- if (e.key === "Enter" || e.key === " ") {
3371
- e.preventDefault();
3372
- onRowClick(item, rowIndex);
3373
- }
3374
- } : void 0,
3375
- children: columns.map((col) => /* @__PURE__ */ jsx(
3670
+ /* @__PURE__ */ jsxs("tr", { className: "table_row table_row_skeleton", children: [
3671
+ selectable && /* @__PURE__ */ jsx("td", { className: "table_td table_td_checkbox" }),
3672
+ columns.map((col) => /* @__PURE__ */ jsx(
3376
3673
  "td",
3377
3674
  {
3378
3675
  className: cn("table_td", col.align && `table_align_${col.align}`),
3379
3676
  style: { width: col.width },
3380
- children: (() => {
3381
- if (col.render) return col.render(item, rowIndex);
3382
- const value = item[col.key];
3383
- return value === null || value === void 0 || value === "" ? "-" : String(value);
3384
- })()
3677
+ children: /* @__PURE__ */ jsx(Skeleton, { variant: "text" })
3385
3678
  },
3386
3679
  col.key
3387
3680
  ))
3388
- },
3389
- keyExtractor(item, rowIndex)
3390
- )) })
3681
+ ] }, rowIndex)
3682
+ )) : data.map((item, rowIndex) => {
3683
+ const rowIdentity = selectable ? getRowKey(item, rowIndex) : void 0;
3684
+ const isSelected = rowIdentity !== void 0 && selectedSet.has(rowIdentity);
3685
+ return /* @__PURE__ */ jsxs(
3686
+ "tr",
3687
+ {
3688
+ className: cn(
3689
+ "table_row",
3690
+ hoverable && "table_row_hoverable",
3691
+ onRowClick && "table_row_clickable",
3692
+ isSelected && "table_row_selected"
3693
+ ),
3694
+ "aria-selected": isSelected ? "true" : void 0,
3695
+ role: onRowClick && !selectable ? "button" : void 0,
3696
+ tabIndex: onRowClick ? 0 : void 0,
3697
+ onClick: onRowClick ? () => onRowClick(item, rowIndex) : void 0,
3698
+ onKeyDown: onRowClick ? (e) => {
3699
+ if (e.key === "Enter" || e.key === " ") {
3700
+ e.preventDefault();
3701
+ onRowClick(item, rowIndex);
3702
+ }
3703
+ } : void 0,
3704
+ children: [
3705
+ selectable && rowIdentity !== void 0 && /* @__PURE__ */ jsx(
3706
+ "td",
3707
+ {
3708
+ className: "table_td table_td_checkbox",
3709
+ onClick: (e) => e.stopPropagation(),
3710
+ onKeyDown: (e) => e.stopPropagation(),
3711
+ children: /* @__PURE__ */ jsx(
3712
+ Checkbox,
3713
+ {
3714
+ "aria-label": selectRowAriaLabel(rowIndex),
3715
+ checked: isSelected,
3716
+ onChange: () => handleToggleRow(rowIdentity)
3717
+ }
3718
+ )
3719
+ }
3720
+ ),
3721
+ columns.map((col) => /* @__PURE__ */ jsx(
3722
+ "td",
3723
+ {
3724
+ className: cn("table_td", col.align && `table_align_${col.align}`),
3725
+ style: { width: col.width },
3726
+ children: (() => {
3727
+ if (col.render) return col.render(item, rowIndex);
3728
+ const value = item[col.key];
3729
+ return value === null || value === void 0 || value === "" ? "-" : String(value);
3730
+ })()
3731
+ },
3732
+ col.key
3733
+ ))
3734
+ ]
3735
+ },
3736
+ keyExtractor(item, rowIndex)
3737
+ );
3738
+ }) })
3391
3739
  ] }),
3392
3740
  isEmpty && /* @__PURE__ */ jsx("div", { className: "table_empty", role: "status", children: emptyMessage })
3393
3741
  ] });
3394
3742
  };
3395
- var ThemeContext = React20.createContext(null);
3743
+ var ThemeContext = React19.createContext(null);
3396
3744
  var useTheme = () => {
3397
- const ctx = React20.useContext(ThemeContext);
3745
+ const ctx = React19.useContext(ThemeContext);
3398
3746
  if (!ctx) {
3399
3747
  throw new Error(
3400
3748
  '[Bigtablet DS] useTheme\uB294 <ThemeProvider> \uC548\uC5D0\uC11C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5\uD569\uB2C8\uB2E4.\n\n\uC571 \uCD5C\uC0C1\uB2E8\uC5D0 <ThemeProvider>\uB85C \uAC10\uC2F8\uC8FC\uC138\uC694:\n <ThemeProvider mode="system">\n <YourApp />\n </ThemeProvider>'
@@ -3418,15 +3766,15 @@ var ThemeProvider = ({
3418
3766
  targetSelector,
3419
3767
  children
3420
3768
  }) => {
3421
- const [mode, setModeState] = React20.useState(defaultMode);
3422
- const [systemDark, setSystemDark] = React20.useState(false);
3423
- React20.useEffect(() => {
3769
+ const [mode, setModeState] = React19.useState(defaultMode);
3770
+ const [systemDark, setSystemDark] = React19.useState(false);
3771
+ React19.useEffect(() => {
3424
3772
  setModeState(readStored(storageKey) ?? defaultMode);
3425
3773
  if (isClient) {
3426
3774
  setSystemDark(window.matchMedia("(prefers-color-scheme: dark)").matches);
3427
3775
  }
3428
3776
  }, [storageKey, defaultMode]);
3429
- React20.useEffect(() => {
3777
+ React19.useEffect(() => {
3430
3778
  if (!isClient) return;
3431
3779
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
3432
3780
  const handler = (e) => setSystemDark(e.matches);
@@ -3434,7 +3782,7 @@ var ThemeProvider = ({
3434
3782
  return () => mq.removeEventListener("change", handler);
3435
3783
  }, []);
3436
3784
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
3437
- React20.useEffect(() => {
3785
+ React19.useEffect(() => {
3438
3786
  if (!isClient) return;
3439
3787
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
3440
3788
  if (!target) return;
@@ -3444,7 +3792,7 @@ var ThemeProvider = ({
3444
3792
  target.setAttribute("data-theme", mode);
3445
3793
  }
3446
3794
  }, [mode, targetSelector]);
3447
- const setMode = React20.useCallback(
3795
+ const setMode = React19.useCallback(
3448
3796
  (next) => {
3449
3797
  setModeState(next);
3450
3798
  if (storageKey && isClient) {
@@ -3456,13 +3804,13 @@ var ThemeProvider = ({
3456
3804
  },
3457
3805
  [storageKey]
3458
3806
  );
3459
- const value = React20.useMemo(
3807
+ const value = React19.useMemo(
3460
3808
  () => ({ mode, resolved, setMode }),
3461
3809
  [mode, resolved, setMode]
3462
3810
  );
3463
3811
  return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
3464
3812
  };
3465
- var ClearIcon = () => /* @__PURE__ */ jsx(X, { size: 20, "aria-hidden": "true" });
3813
+ var ClearIcon = () => /* @__PURE__ */ jsx(X, { size: iconSize.lg, "aria-hidden": "true" });
3466
3814
  var TextField = ({
3467
3815
  id,
3468
3816
  label,
@@ -3704,24 +4052,24 @@ var Textarea = ({
3704
4052
  ] });
3705
4053
  };
3706
4054
  Textarea.displayName = "Textarea";
3707
- var ToastContext = React20.createContext(null);
4055
+ var ToastContext = React19.createContext(null);
3708
4056
  var VARIANT_ICONS = {
3709
- success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
3710
- error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
3711
- warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 18 }),
3712
- info: /* @__PURE__ */ jsx(Info, { size: 18 }),
3713
- default: /* @__PURE__ */ jsx(Bell, { size: 18 })
4057
+ success: /* @__PURE__ */ jsx(CheckCircle2, { size: iconSize.md }),
4058
+ error: /* @__PURE__ */ jsx(XCircle, { size: iconSize.md }),
4059
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { size: iconSize.md }),
4060
+ info: /* @__PURE__ */ jsx(Info, { size: iconSize.md }),
4061
+ default: /* @__PURE__ */ jsx(Bell, { size: iconSize.md })
3714
4062
  };
3715
4063
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3716
- const [visible, setVisible] = React20.useState(true);
3717
- const closingRef = React20.useRef(false);
4064
+ const [visible, setVisible] = React19.useState(true);
4065
+ const closingRef = React19.useRef(false);
3718
4066
  const style = useSpringPresence({
3719
4067
  visible,
3720
4068
  from: "translateX(20px)",
3721
4069
  // 우측에서 슬라이드 인
3722
4070
  onExitComplete: () => onRemove(item.id)
3723
4071
  });
3724
- const close = React20.useCallback(() => {
4072
+ const close = React19.useCallback(() => {
3725
4073
  if (closingRef.current) return;
3726
4074
  closingRef.current = true;
3727
4075
  setVisible(false);
@@ -3735,7 +4083,7 @@ var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3735
4083
  children: [
3736
4084
  /* @__PURE__ */ jsx("span", { className: `toast_icon toast_icon_${item.variant}`, "aria-hidden": "true", children: VARIANT_ICONS[item.variant] }),
3737
4085
  /* @__PURE__ */ jsx("span", { className: "toast_message", children: item.message }),
3738
- /* @__PURE__ */ jsx("button", { type: "button", className: "toast_close", onClick: close, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsx(X, { size: 14 }) }),
4086
+ /* @__PURE__ */ jsx("button", { type: "button", className: "toast_close", onClick: close, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsx(X, { size: iconSize.xs }) }),
3739
4087
  /* @__PURE__ */ jsx(
3740
4088
  "div",
3741
4089
  {
@@ -3754,22 +4102,22 @@ var ToastProvider = ({
3754
4102
  maxCount = 5,
3755
4103
  closeAriaLabel = "Close"
3756
4104
  }) => {
3757
- const [toasts, setToasts] = React20.useState([]);
3758
- const [isMounted, setIsMounted] = React20.useState(false);
3759
- React20.useEffect(() => {
4105
+ const [toasts, setToasts] = React19.useState([]);
4106
+ const [isMounted, setIsMounted] = React19.useState(false);
4107
+ React19.useEffect(() => {
3760
4108
  setIsMounted(true);
3761
4109
  }, []);
3762
- const addToast = React20.useCallback(
4110
+ const addToast = React19.useCallback(
3763
4111
  (message, variant, duration = 3e3) => {
3764
4112
  const id = crypto.randomUUID();
3765
4113
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
3766
4114
  },
3767
4115
  [maxCount]
3768
4116
  );
3769
- const removeToast = React20.useCallback((id) => {
4117
+ const removeToast = React19.useCallback((id) => {
3770
4118
  setToasts((prev) => prev.filter((t) => t.id !== id));
3771
4119
  }, []);
3772
- const contextValue = React20.useMemo(() => ({ addToast }), [addToast]);
4120
+ const contextValue = React19.useMemo(() => ({ addToast }), [addToast]);
3773
4121
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
3774
4122
  children,
3775
4123
  isMounted && createPortal(
@@ -3830,7 +4178,7 @@ var Toggle = ({
3830
4178
  ...props
3831
4179
  }) => {
3832
4180
  const isControlled = checked !== void 0;
3833
- const [innerChecked, setInnerChecked] = React20.useState(!!defaultChecked);
4181
+ const [innerChecked, setInnerChecked] = React19.useState(!!defaultChecked);
3834
4182
  const isOn = isControlled ? !!checked : innerChecked;
3835
4183
  const handleToggle = () => {
3836
4184
  if (disabled) return;
@@ -4000,4 +4348,4 @@ var Grid = ({
4000
4348
  );
4001
4349
  };
4002
4350
 
4003
- export { Accordion, AlertProvider, Avatar, Badge, BottomNav, BottomNavItem, BottomNavSpacer, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Dropdown, EmptyState, ErrorState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Popover, Radio, RadioGroup, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, Textarea, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useReducedMotion, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
4351
+ export { Accordion, AlertProvider, Avatar, Badge, BottomNav, BottomNavItem, BottomNavSpacer, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Drawer, Dropdown, EmptyState, ErrorState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Popover, Radio, RadioGroup, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, Textarea, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, iconSize, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useReducedMotion, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };