@bigtablet/design-system 3.3.0 → 3.4.1

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
+ ...props,
2724
+ className: cn("drawer_panel", className),
2725
+ style: { ...props.style, ...panelStyle, ...panelSizeStyle },
2726
+ role: "document",
2727
+ onClick: (e) => e.stopPropagation(),
2728
+ onKeyDown: (e) => {
2729
+ if (e.key !== "Escape") e.stopPropagation();
2730
+ },
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,
@@ -2928,14 +3166,14 @@ var Modal = ({
2928
3166
  animated.div,
2929
3167
  {
2930
3168
  ref: panelRef,
3169
+ ...props,
2931
3170
  className: cn("modal_panel", className),
2932
- style: { ...panelStyle, width },
3171
+ style: { ...props.style, ...panelStyle, width },
2933
3172
  role: "document",
2934
3173
  onClick: (e) => e.stopPropagation(),
2935
3174
  onKeyDown: (e) => {
2936
3175
  if (e.key !== "Escape") e.stopPropagation();
2937
3176
  },
2938
- ...props,
2939
3177
  children: [
2940
3178
  showCloseIcon && onClose && /* @__PURE__ */ jsx(
2941
3179
  "button",
@@ -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,16 @@ var Table = ({
3323
3561
  stickyHeader = false,
3324
3562
  ariaLabel,
3325
3563
  className,
3326
- onRowClick
3564
+ onRowClick,
3565
+ rowClickHint = "\uD074\uB9AD \uAC00\uB2A5\uD55C \uD589",
3566
+ sort,
3567
+ onSortChange,
3568
+ selectAllAriaLabel = "\uC804\uCCB4 \uC120\uD0DD",
3569
+ selectRowAriaLabel = (index) => `${index + 1}\uBC88\uC9F8 \uD589 \uC120\uD0DD`,
3570
+ selectable = false,
3571
+ rowKey,
3572
+ selectedKeys,
3573
+ onSelectionChange
3327
3574
  }) => {
3328
3575
  const wrapperClassName = cn(
3329
3576
  "table_wrapper",
@@ -3332,69 +3579,173 @@ var Table = ({
3332
3579
  className
3333
3580
  );
3334
3581
  const isEmpty = !isLoading && data.length === 0;
3582
+ const getRowKey = (item, index) => rowKey?.(item) ?? String(index);
3583
+ const allRowKeys = selectable ? data.map((item, index) => getRowKey(item, index)) : [];
3584
+ const rowClickHintId = React19.useId();
3585
+ const selectedSet = React19.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
3586
+ const selectedCount = allRowKeys.filter((key) => selectedSet.has(key)).length;
3587
+ const isAllSelected = allRowKeys.length > 0 && selectedCount === allRowKeys.length;
3588
+ const isSomeSelected = selectedCount > 0 && !isAllSelected;
3589
+ const handleToggleAll = () => {
3590
+ const pageKeys = new Set(allRowKeys);
3591
+ const offPage = (selectedKeys ?? []).filter((k) => !pageKeys.has(k));
3592
+ onSelectionChange?.(isAllSelected ? offPage : [...offPage, ...allRowKeys]);
3593
+ };
3594
+ const handleToggleRow = (key) => {
3595
+ const current = selectedKeys ?? [];
3596
+ const next = current.includes(key) ? current.filter((k) => k !== key) : [...current, key];
3597
+ onSelectionChange?.(next);
3598
+ };
3599
+ const handleSortClick = (key) => {
3600
+ const next = sort?.key !== key ? { key, direction: "asc" } : sort.direction === "asc" ? { key, direction: "desc" } : void 0;
3601
+ onSortChange?.(next);
3602
+ };
3335
3603
  return /* @__PURE__ */ jsxs("div", { className: wrapperClassName, children: [
3336
3604
  /* @__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
- )) }) }),
3605
+ /* @__PURE__ */ jsx("thead", { className: "table_thead", children: /* @__PURE__ */ jsxs("tr", { children: [
3606
+ selectable && /* @__PURE__ */ jsx("th", { scope: "col", className: "table_th table_th_checkbox", children: /* @__PURE__ */ jsx(
3607
+ Checkbox,
3608
+ {
3609
+ "aria-label": selectAllAriaLabel,
3610
+ checked: isAllSelected,
3611
+ indeterminate: isSomeSelected,
3612
+ disabled: isLoading || allRowKeys.length === 0,
3613
+ onChange: handleToggleAll
3614
+ }
3615
+ ) }),
3616
+ columns.map((col) => {
3617
+ const isSortActive = Boolean(col.sortable) && sort?.key === col.key;
3618
+ const direction = isSortActive ? sort?.direction : void 0;
3619
+ const ariaSort = col.sortable ? direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none" : void 0;
3620
+ return /* @__PURE__ */ jsx(
3621
+ "th",
3622
+ {
3623
+ scope: "col",
3624
+ className: cn(
3625
+ "table_th",
3626
+ col.align && `table_align_${col.align}`,
3627
+ col.sortable && "table_th_sortable"
3628
+ ),
3629
+ style: { width: col.width },
3630
+ "aria-sort": ariaSort,
3631
+ children: col.sortable ? /* @__PURE__ */ jsxs(
3632
+ "button",
3633
+ {
3634
+ type: "button",
3635
+ className: "table_sort_button",
3636
+ onClick: () => handleSortClick(col.key),
3637
+ disabled: isLoading,
3638
+ children: [
3639
+ /* @__PURE__ */ jsx("span", { className: "table_sort_label", children: col.header }),
3640
+ direction === "asc" ? /* @__PURE__ */ jsx(
3641
+ ArrowUp,
3642
+ {
3643
+ size: iconSize.sm,
3644
+ className: "table_sort_icon table_sort_icon_active",
3645
+ "aria-hidden": "true"
3646
+ }
3647
+ ) : direction === "desc" ? /* @__PURE__ */ jsx(
3648
+ ArrowDown,
3649
+ {
3650
+ size: iconSize.sm,
3651
+ className: "table_sort_icon table_sort_icon_active",
3652
+ "aria-hidden": "true"
3653
+ }
3654
+ ) : /* @__PURE__ */ jsx(
3655
+ ArrowUpDown,
3656
+ {
3657
+ size: iconSize.sm,
3658
+ className: "table_sort_icon",
3659
+ "aria-hidden": "true"
3660
+ }
3661
+ )
3662
+ ]
3663
+ }
3664
+ ) : col.header
3665
+ },
3666
+ col.key
3667
+ );
3668
+ })
3669
+ ] }) }),
3347
3670
  /* @__PURE__ */ jsx("tbody", { className: "table_tbody", children: isLoading ? Array.from({ length: skeletonRows }).map((_, rowIndex) => (
3348
3671
  // 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(
3672
+ /* @__PURE__ */ jsxs("tr", { className: "table_row table_row_skeleton", children: [
3673
+ selectable && /* @__PURE__ */ jsx("td", { className: "table_td table_td_checkbox" }),
3674
+ columns.map((col) => /* @__PURE__ */ jsx(
3376
3675
  "td",
3377
3676
  {
3378
3677
  className: cn("table_td", col.align && `table_align_${col.align}`),
3379
3678
  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
- })()
3679
+ children: /* @__PURE__ */ jsx(Skeleton, { variant: "text" })
3385
3680
  },
3386
3681
  col.key
3387
3682
  ))
3388
- },
3389
- keyExtractor(item, rowIndex)
3390
- )) })
3683
+ ] }, rowIndex)
3684
+ )) : data.map((item, rowIndex) => {
3685
+ const rowIdentity = selectable ? getRowKey(item, rowIndex) : void 0;
3686
+ const isSelected = rowIdentity !== void 0 && selectedSet.has(rowIdentity);
3687
+ return /* @__PURE__ */ jsxs(
3688
+ "tr",
3689
+ {
3690
+ className: cn(
3691
+ "table_row",
3692
+ hoverable && "table_row_hoverable",
3693
+ onRowClick && "table_row_clickable",
3694
+ isSelected && "table_row_selected"
3695
+ ),
3696
+ "aria-selected": isSelected ? "true" : void 0,
3697
+ "aria-describedby": onRowClick && rowClickHint ? rowClickHintId : void 0,
3698
+ tabIndex: onRowClick ? 0 : void 0,
3699
+ onClick: onRowClick ? () => onRowClick(item, rowIndex) : void 0,
3700
+ onKeyDown: onRowClick ? (e) => {
3701
+ if (e.key === "Enter" || e.key === " ") {
3702
+ e.preventDefault();
3703
+ onRowClick(item, rowIndex);
3704
+ }
3705
+ } : void 0,
3706
+ children: [
3707
+ selectable && rowIdentity !== void 0 && /* @__PURE__ */ jsx(
3708
+ "td",
3709
+ {
3710
+ className: "table_td table_td_checkbox",
3711
+ onClick: (e) => e.stopPropagation(),
3712
+ onKeyDown: (e) => e.stopPropagation(),
3713
+ children: /* @__PURE__ */ jsx(
3714
+ Checkbox,
3715
+ {
3716
+ "aria-label": selectRowAriaLabel(rowIndex),
3717
+ checked: isSelected,
3718
+ onChange: () => handleToggleRow(rowIdentity)
3719
+ }
3720
+ )
3721
+ }
3722
+ ),
3723
+ columns.map((col) => /* @__PURE__ */ jsx(
3724
+ "td",
3725
+ {
3726
+ className: cn("table_td", col.align && `table_align_${col.align}`),
3727
+ style: { width: col.width },
3728
+ children: (() => {
3729
+ if (col.render) return col.render(item, rowIndex);
3730
+ const value = item[col.key];
3731
+ return value === null || value === void 0 || value === "" ? "-" : String(value);
3732
+ })()
3733
+ },
3734
+ col.key
3735
+ ))
3736
+ ]
3737
+ },
3738
+ keyExtractor(item, rowIndex)
3739
+ );
3740
+ }) })
3391
3741
  ] }),
3742
+ onRowClick && rowClickHint && /* @__PURE__ */ jsx("span", { id: rowClickHintId, className: "table_sr_only", children: rowClickHint }),
3392
3743
  isEmpty && /* @__PURE__ */ jsx("div", { className: "table_empty", role: "status", children: emptyMessage })
3393
3744
  ] });
3394
3745
  };
3395
- var ThemeContext = React20.createContext(null);
3746
+ var ThemeContext = React19.createContext(null);
3396
3747
  var useTheme = () => {
3397
- const ctx = React20.useContext(ThemeContext);
3748
+ const ctx = React19.useContext(ThemeContext);
3398
3749
  if (!ctx) {
3399
3750
  throw new Error(
3400
3751
  '[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 +3769,15 @@ var ThemeProvider = ({
3418
3769
  targetSelector,
3419
3770
  children
3420
3771
  }) => {
3421
- const [mode, setModeState] = React20.useState(defaultMode);
3422
- const [systemDark, setSystemDark] = React20.useState(false);
3423
- React20.useEffect(() => {
3772
+ const [mode, setModeState] = React19.useState(defaultMode);
3773
+ const [systemDark, setSystemDark] = React19.useState(false);
3774
+ React19.useEffect(() => {
3424
3775
  setModeState(readStored(storageKey) ?? defaultMode);
3425
3776
  if (isClient) {
3426
3777
  setSystemDark(window.matchMedia("(prefers-color-scheme: dark)").matches);
3427
3778
  }
3428
3779
  }, [storageKey, defaultMode]);
3429
- React20.useEffect(() => {
3780
+ React19.useEffect(() => {
3430
3781
  if (!isClient) return;
3431
3782
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
3432
3783
  const handler = (e) => setSystemDark(e.matches);
@@ -3434,7 +3785,7 @@ var ThemeProvider = ({
3434
3785
  return () => mq.removeEventListener("change", handler);
3435
3786
  }, []);
3436
3787
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
3437
- React20.useEffect(() => {
3788
+ React19.useEffect(() => {
3438
3789
  if (!isClient) return;
3439
3790
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
3440
3791
  if (!target) return;
@@ -3444,7 +3795,7 @@ var ThemeProvider = ({
3444
3795
  target.setAttribute("data-theme", mode);
3445
3796
  }
3446
3797
  }, [mode, targetSelector]);
3447
- const setMode = React20.useCallback(
3798
+ const setMode = React19.useCallback(
3448
3799
  (next) => {
3449
3800
  setModeState(next);
3450
3801
  if (storageKey && isClient) {
@@ -3456,13 +3807,13 @@ var ThemeProvider = ({
3456
3807
  },
3457
3808
  [storageKey]
3458
3809
  );
3459
- const value = React20.useMemo(
3810
+ const value = React19.useMemo(
3460
3811
  () => ({ mode, resolved, setMode }),
3461
3812
  [mode, resolved, setMode]
3462
3813
  );
3463
3814
  return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
3464
3815
  };
3465
- var ClearIcon = () => /* @__PURE__ */ jsx(X, { size: 20, "aria-hidden": "true" });
3816
+ var ClearIcon = () => /* @__PURE__ */ jsx(X, { size: iconSize.lg, "aria-hidden": "true" });
3466
3817
  var TextField = ({
3467
3818
  id,
3468
3819
  label,
@@ -3704,24 +4055,24 @@ var Textarea = ({
3704
4055
  ] });
3705
4056
  };
3706
4057
  Textarea.displayName = "Textarea";
3707
- var ToastContext = React20.createContext(null);
4058
+ var ToastContext = React19.createContext(null);
3708
4059
  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 })
4060
+ success: /* @__PURE__ */ jsx(CheckCircle2, { size: iconSize.md }),
4061
+ error: /* @__PURE__ */ jsx(XCircle, { size: iconSize.md }),
4062
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { size: iconSize.md }),
4063
+ info: /* @__PURE__ */ jsx(Info, { size: iconSize.md }),
4064
+ default: /* @__PURE__ */ jsx(Bell, { size: iconSize.md })
3714
4065
  };
3715
4066
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3716
- const [visible, setVisible] = React20.useState(true);
3717
- const closingRef = React20.useRef(false);
4067
+ const [visible, setVisible] = React19.useState(true);
4068
+ const closingRef = React19.useRef(false);
3718
4069
  const style = useSpringPresence({
3719
4070
  visible,
3720
4071
  from: "translateX(20px)",
3721
4072
  // 우측에서 슬라이드 인
3722
4073
  onExitComplete: () => onRemove(item.id)
3723
4074
  });
3724
- const close = React20.useCallback(() => {
4075
+ const close = React19.useCallback(() => {
3725
4076
  if (closingRef.current) return;
3726
4077
  closingRef.current = true;
3727
4078
  setVisible(false);
@@ -3735,7 +4086,7 @@ var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3735
4086
  children: [
3736
4087
  /* @__PURE__ */ jsx("span", { className: `toast_icon toast_icon_${item.variant}`, "aria-hidden": "true", children: VARIANT_ICONS[item.variant] }),
3737
4088
  /* @__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 }) }),
4089
+ /* @__PURE__ */ jsx("button", { type: "button", className: "toast_close", onClick: close, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsx(X, { size: iconSize.xs }) }),
3739
4090
  /* @__PURE__ */ jsx(
3740
4091
  "div",
3741
4092
  {
@@ -3754,22 +4105,22 @@ var ToastProvider = ({
3754
4105
  maxCount = 5,
3755
4106
  closeAriaLabel = "Close"
3756
4107
  }) => {
3757
- const [toasts, setToasts] = React20.useState([]);
3758
- const [isMounted, setIsMounted] = React20.useState(false);
3759
- React20.useEffect(() => {
4108
+ const [toasts, setToasts] = React19.useState([]);
4109
+ const [isMounted, setIsMounted] = React19.useState(false);
4110
+ React19.useEffect(() => {
3760
4111
  setIsMounted(true);
3761
4112
  }, []);
3762
- const addToast = React20.useCallback(
4113
+ const addToast = React19.useCallback(
3763
4114
  (message, variant, duration = 3e3) => {
3764
4115
  const id = crypto.randomUUID();
3765
4116
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
3766
4117
  },
3767
4118
  [maxCount]
3768
4119
  );
3769
- const removeToast = React20.useCallback((id) => {
4120
+ const removeToast = React19.useCallback((id) => {
3770
4121
  setToasts((prev) => prev.filter((t) => t.id !== id));
3771
4122
  }, []);
3772
- const contextValue = React20.useMemo(() => ({ addToast }), [addToast]);
4123
+ const contextValue = React19.useMemo(() => ({ addToast }), [addToast]);
3773
4124
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
3774
4125
  children,
3775
4126
  isMounted && createPortal(
@@ -3830,7 +4181,7 @@ var Toggle = ({
3830
4181
  ...props
3831
4182
  }) => {
3832
4183
  const isControlled = checked !== void 0;
3833
- const [innerChecked, setInnerChecked] = React20.useState(!!defaultChecked);
4184
+ const [innerChecked, setInnerChecked] = React19.useState(!!defaultChecked);
3834
4185
  const isOn = isControlled ? !!checked : innerChecked;
3835
4186
  const handleToggle = () => {
3836
4187
  if (disabled) return;
@@ -4000,4 +4351,4 @@ var Grid = ({
4000
4351
  );
4001
4352
  };
4002
4353
 
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 };
4354
+ 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 };