@bigtablet/design-system 3.4.0 → 3.5.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,6 +1,6 @@
1
1
  "use client";
2
2
  import './index.css';
3
- import * as React19 from 'react';
3
+ import * as React15 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
6
  import { ChevronDown, ChevronRight, Globe, ChevronLeft, XCircle, AlertTriangle, CheckCircle2, Info, Search, Check, X, Image, ArrowUp, ArrowDown, ArrowUpDown, Bell, TriangleAlert } from 'lucide-react';
@@ -29,6 +29,44 @@ var cn = (...classes) => {
29
29
  }
30
30
  return classNames.join(" ");
31
31
  };
32
+ var stack = [];
33
+ var handleKeyDown = (e) => {
34
+ if (e.key !== "Escape") return;
35
+ const top = stack[stack.length - 1];
36
+ if (!top) return;
37
+ e.stopImmediatePropagation();
38
+ top();
39
+ };
40
+ function registerOverlay(onEscape) {
41
+ if (typeof document === "undefined") {
42
+ return () => {
43
+ };
44
+ }
45
+ if (stack.length === 0) {
46
+ document.addEventListener("keydown", handleKeyDown);
47
+ }
48
+ stack.push(onEscape);
49
+ let done = false;
50
+ return () => {
51
+ if (done) return;
52
+ done = true;
53
+ const index = stack.lastIndexOf(onEscape);
54
+ if (index !== -1) stack.splice(index, 1);
55
+ if (stack.length === 0) {
56
+ document.removeEventListener("keydown", handleKeyDown);
57
+ }
58
+ };
59
+ }
60
+ function useOverlayEscape(active, onEscape) {
61
+ const handlerRef = React15.useRef(onEscape);
62
+ React15.useEffect(() => {
63
+ handlerRef.current = onEscape;
64
+ });
65
+ React15.useEffect(() => {
66
+ if (!active) return;
67
+ return registerOverlay(() => handlerRef.current());
68
+ }, [active]);
69
+ }
32
70
  var FOCUSABLE_SELECTORS = [
33
71
  "a[href]",
34
72
  "button:not([disabled])",
@@ -38,8 +76,8 @@ var FOCUSABLE_SELECTORS = [
38
76
  '[tabindex]:not([tabindex="-1"])'
39
77
  ].join(", ");
40
78
  function useFocusTrap(containerRef, isActive) {
41
- const previousActiveElement = React19.useRef(null);
42
- React19.useEffect(() => {
79
+ const previousActiveElement = React15.useRef(null);
80
+ React15.useEffect(() => {
43
81
  if (!isActive) return;
44
82
  const container = containerRef.current;
45
83
  if (!container) return;
@@ -56,7 +94,7 @@ function useFocusTrap(containerRef, isActive) {
56
94
  wasTabIndexAdded = true;
57
95
  container.focus();
58
96
  }
59
- const handleKeyDown = (e) => {
97
+ const handleKeyDown2 = (e) => {
60
98
  if (e.key !== "Tab") return;
61
99
  const focusableElements2 = getFocusableElements();
62
100
  if (focusableElements2.length === 0) {
@@ -77,9 +115,9 @@ function useFocusTrap(containerRef, isActive) {
77
115
  }
78
116
  }
79
117
  };
80
- document.addEventListener("keydown", handleKeyDown);
118
+ document.addEventListener("keydown", handleKeyDown2);
81
119
  return () => {
82
- document.removeEventListener("keydown", handleKeyDown);
120
+ document.removeEventListener("keydown", handleKeyDown2);
83
121
  if (wasTabIndexAdded) {
84
122
  container.removeAttribute("tabindex");
85
123
  }
@@ -87,6 +125,13 @@ function useFocusTrap(containerRef, isActive) {
87
125
  };
88
126
  }, [isActive, containerRef]);
89
127
  }
128
+ function useIsMounted() {
129
+ const [mounted, setMounted] = React15.useState(false);
130
+ React15.useEffect(() => {
131
+ setMounted(true);
132
+ }, []);
133
+ return mounted;
134
+ }
90
135
  var QUERY = "(prefers-reduced-motion: reduce)";
91
136
  var mqCache = null;
92
137
  var cachedFor = null;
@@ -116,14 +161,14 @@ function getServerSnapshot() {
116
161
  return false;
117
162
  }
118
163
  function useReducedMotion() {
119
- return React19.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
164
+ return React15.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
120
165
  }
121
166
  var useSafeLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
122
167
  function useSpringHover({
123
168
  scale = 1.02,
124
169
  lift = -2
125
170
  } = {}) {
126
- const [hovered, setHovered] = React19.useState(false);
171
+ const [hovered, setHovered] = React15.useState(false);
127
172
  const reduced = useReducedMotion();
128
173
  const style = useSpring({
129
174
  transform: hovered ? `translateY(${lift}px) scale(${scale})` : "translateY(0px) scale(1)",
@@ -187,8 +232,9 @@ var Accordion = ({
187
232
  ...props
188
233
  }) => {
189
234
  const isControlled = controlledKeys !== void 0;
190
- const [internalKeys, setInternalKeys] = React19.useState(defaultOpenKeys);
235
+ const [internalKeys, setInternalKeys] = React15.useState(defaultOpenKeys);
191
236
  const open = isControlled ? controlledKeys ?? [] : internalKeys;
237
+ const idPrefix = React15.useId();
192
238
  const toggle = (key) => {
193
239
  const isOpen = open.includes(key);
194
240
  const next = isOpen ? open.filter((k) => k !== key) : multiple ? [...open, key] : [key];
@@ -197,8 +243,8 @@ var Accordion = ({
197
243
  };
198
244
  return /* @__PURE__ */ jsx("div", { className: cn("accordion", className), ...props, children: items.map((item) => {
199
245
  const isOpen = open.includes(item.key);
200
- const headerId = `${item.key}-header`;
201
- const panelId = `${item.key}-panel`;
246
+ const headerId = `${idPrefix}-${item.key}-header`;
247
+ const panelId = `${idPrefix}-${item.key}-panel`;
202
248
  return /* @__PURE__ */ jsxs("div", { className: cn("accordion_item", isOpen && "accordion_item_open"), children: [
203
249
  /* @__PURE__ */ jsx("h3", { className: "accordion_header", children: /* @__PURE__ */ jsxs(
204
250
  "button",
@@ -254,7 +300,7 @@ var Avatar = ({
254
300
  style,
255
301
  ...props
256
302
  }) => {
257
- const [imgFailed, setImgFailed] = React19.useState(false);
303
+ const [imgFailed, setImgFailed] = React15.useState(false);
258
304
  const showImage = src && !imgFailed;
259
305
  const initials = getInitials(name);
260
306
  return /* @__PURE__ */ jsx(
@@ -262,8 +308,9 @@ var Avatar = ({
262
308
  {
263
309
  className: cn("avatar", `avatar_size_${size}`, `avatar_shape_${shape}`, className),
264
310
  style: { background: !showImage ? bgColor : void 0, ...style },
265
- role: showImage ? void 0 : "img",
311
+ role: showImage || !name ? void 0 : "img",
266
312
  "aria-label": showImage ? void 0 : name || void 0,
313
+ "aria-hidden": !showImage && !name ? true : void 0,
267
314
  ...props,
268
315
  children: showImage ? (
269
316
  // biome-ignore lint/performance/noImgElement: DS is framework-agnostic - consumers wrap with next/image
@@ -472,12 +519,12 @@ var ErrorState = ({
472
519
  );
473
520
  };
474
521
  var Menu = ({ items, trigger, align = "start" }) => {
475
- const [open, setOpen] = React19.useState(false);
476
- const wrapperRef = React19.useRef(null);
477
- const itemRefs = React19.useRef([]);
478
- const menuId = React19.useId();
522
+ const [open, setOpen] = React15.useState(false);
523
+ const wrapperRef = React15.useRef(null);
524
+ const itemRefs = React15.useRef([]);
525
+ const menuId = React15.useId();
479
526
  const style = useSpringPresence({ visible: open, from: "translateY(-4px)" });
480
- React19.useEffect(() => {
527
+ React15.useEffect(() => {
481
528
  if (!open) return;
482
529
  const handleClick = (e) => {
483
530
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -492,7 +539,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
492
539
  document.removeEventListener("keydown", handleEsc);
493
540
  };
494
541
  }, [open]);
495
- React19.useEffect(() => {
542
+ React15.useEffect(() => {
496
543
  const active = document.activeElement;
497
544
  if (!open || active && itemRefs.current.includes(active)) return;
498
545
  const first = items.findIndex((it) => !it.disabled);
@@ -538,7 +585,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
538
585
  break;
539
586
  }
540
587
  };
541
- const triggerWithProps = React19.cloneElement(
588
+ const triggerWithProps = React15.cloneElement(
542
589
  trigger,
543
590
  {
544
591
  onClick: () => setOpen((o) => !o),
@@ -782,7 +829,8 @@ var LocaleSwitcher = ({ locale }) => {
782
829
  itemRefs.current[index] = el;
783
830
  },
784
831
  type: "button",
785
- role: "menuitem",
832
+ role: "menuitemradio",
833
+ "aria-checked": opt.value === locale.current,
786
834
  tabIndex: -1,
787
835
  className: cn(
788
836
  "nav_bar_locale_item",
@@ -828,9 +876,9 @@ var Sidebar = ({
828
876
  ...props
829
877
  }) => {
830
878
  const isControlled = collapsedProp !== void 0;
831
- const [internalCollapsed, setInternalCollapsed] = React19.useState(defaultCollapsed);
879
+ const [internalCollapsed, setInternalCollapsed] = React15.useState(defaultCollapsed);
832
880
  const collapsed = isControlled ? collapsedProp : internalCollapsed;
833
- const toggle = React19.useCallback(() => {
881
+ const toggle = React15.useCallback(() => {
834
882
  const next = !collapsed;
835
883
  if (!isControlled) setInternalCollapsed(next);
836
884
  onCollapsedChange?.(next);
@@ -903,9 +951,9 @@ var SidebarSection = ({ label, className, children, ...props }) => {
903
951
  children
904
952
  ] });
905
953
  };
906
- var TabsContext = React19.createContext(null);
954
+ var TabsContext = React15.createContext(null);
907
955
  function useTabsContext() {
908
- const ctx = React19.useContext(TabsContext);
956
+ const ctx = React15.useContext(TabsContext);
909
957
  if (!ctx) {
910
958
  throw new Error(
911
959
  '[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>'
@@ -924,27 +972,39 @@ var Tabs = ({
924
972
  ...props
925
973
  }) => {
926
974
  const isControlled = controlledValue !== void 0;
927
- const [internalValue, setInternalValue] = React19.useState(defaultValue);
975
+ const [internalValue, setInternalValue] = React15.useState(defaultValue);
928
976
  const value = isControlled ? controlledValue : internalValue;
929
- const setValue = React19.useCallback(
977
+ const setValue = React15.useCallback(
930
978
  (v) => {
931
979
  if (!isControlled) setInternalValue(v);
932
980
  onValueChange?.(v);
933
981
  },
934
982
  [isControlled, onValueChange]
935
983
  );
936
- const idPrefix = React19.useId();
937
- const ctx = React19.useMemo(
938
- () => ({ value, setValue, variant, size, idPrefix }),
939
- [value, setValue, variant, size, idPrefix]
984
+ const orderRef = React15.useRef([]);
985
+ const [firstTabValue, setFirstTabValue] = React15.useState();
986
+ const registerTab = React15.useCallback((v) => {
987
+ if (!orderRef.current.includes(v)) {
988
+ orderRef.current = [...orderRef.current, v];
989
+ setFirstTabValue(orderRef.current[0]);
990
+ }
991
+ return () => {
992
+ orderRef.current = orderRef.current.filter((x) => x !== v);
993
+ setFirstTabValue(orderRef.current[0]);
994
+ };
995
+ }, []);
996
+ const idPrefix = React15.useId();
997
+ const ctx = React15.useMemo(
998
+ () => ({ value, setValue, variant, size, idPrefix, registerTab, firstTabValue }),
999
+ [value, setValue, variant, size, idPrefix, registerTab, firstTabValue]
940
1000
  );
941
1001
  return /* @__PURE__ */ jsx(TabsContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx("div", { className: cn("tabs", `tabs_variant_${variant}`, `tabs_size_${size}`, className), ...props, children }) });
942
1002
  };
943
1003
  var TabList = ({ ariaLabel, className, children, ...props }) => {
944
1004
  const { variant } = useTabsContext();
945
- const listRef = React19.useRef(null);
946
- const [indicator, setIndicator] = React19.useState(null);
947
- const [hasMounted, setHasMounted] = React19.useState(false);
1005
+ const listRef = React15.useRef(null);
1006
+ const [indicator, setIndicator] = React15.useState(null);
1007
+ const [hasMounted, setHasMounted] = React15.useState(false);
948
1008
  useSafeLayoutEffect(() => {
949
1009
  const list = listRef.current;
950
1010
  if (!list) return;
@@ -1001,16 +1061,20 @@ var TabList = ({ ariaLabel, className, children, ...props }) => {
1001
1061
  }
1002
1062
  );
1003
1063
  };
1004
- var Tab = ({ value, className, children, onClick, ...props }) => {
1064
+ var Tab = ({ value, className, children, onClick, onKeyDown, ...props }) => {
1005
1065
  const ctx = useTabsContext();
1006
1066
  const isActive = ctx.value === value;
1007
1067
  const panelId = `${ctx.idPrefix}-panel-${value}`;
1008
1068
  const tabId = `${ctx.idPrefix}-tab-${value}`;
1069
+ const { registerTab } = ctx;
1070
+ React15.useEffect(() => registerTab(value), [registerTab, value]);
1009
1071
  const handleClick = (e) => {
1010
1072
  ctx.setValue(value);
1011
1073
  onClick?.(e);
1012
1074
  };
1013
- const handleKeyDown = (e) => {
1075
+ const handleKeyDown2 = (e) => {
1076
+ onKeyDown?.(e);
1077
+ if (e.defaultPrevented) return;
1014
1078
  if (e.key !== "ArrowLeft" && e.key !== "ArrowRight" && e.key !== "Home" && e.key !== "End") return;
1015
1079
  const list = e.currentTarget.closest('[role="tablist"]');
1016
1080
  if (!list) return;
@@ -1032,17 +1096,17 @@ var Tab = ({ value, className, children, onClick, ...props }) => {
1032
1096
  return /* @__PURE__ */ jsx(
1033
1097
  "button",
1034
1098
  {
1099
+ ...props,
1035
1100
  type: "button",
1036
1101
  role: "tab",
1037
1102
  id: tabId,
1038
1103
  "data-value": value,
1039
1104
  "aria-selected": isActive,
1040
1105
  "aria-controls": panelId,
1041
- tabIndex: isActive ? 0 : -1,
1106
+ tabIndex: isActive || !ctx.value && value === ctx.firstTabValue ? 0 : -1,
1042
1107
  className: cn("tabs_tab", isActive && "tabs_tab_active", className),
1043
1108
  onClick: handleClick,
1044
- onKeyDown: handleKeyDown,
1045
- ...props,
1109
+ onKeyDown: handleKeyDown2,
1046
1110
  children
1047
1111
  }
1048
1112
  );
@@ -1093,15 +1157,15 @@ var Popover = ({
1093
1157
  className
1094
1158
  }) => {
1095
1159
  const isControlled = openProp !== void 0;
1096
- const [internalOpen, setInternalOpen] = React19.useState(defaultOpen);
1160
+ const [internalOpen, setInternalOpen] = React15.useState(defaultOpen);
1097
1161
  const open = isControlled ? openProp : internalOpen;
1098
- const [shouldRender, setShouldRender] = React19.useState(open ?? false);
1162
+ const [shouldRender, setShouldRender] = React15.useState(open ?? false);
1099
1163
  if (open && !shouldRender) setShouldRender(true);
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(
1164
+ const wrapperRef = React15.useRef(null);
1165
+ const popoverRef = React15.useRef(null);
1166
+ const previousFocusRef = React15.useRef(null);
1167
+ const popoverId = React15.useId();
1168
+ const setOpen = React15.useCallback(
1105
1169
  (next) => {
1106
1170
  if (!isControlled) setInternalOpen(next);
1107
1171
  onOpenChange?.(next);
@@ -1121,7 +1185,7 @@ var Popover = ({
1121
1185
  }
1122
1186
  })();
1123
1187
  const style = useSpringPresence({ visible: open, from: fromTransform, onExitComplete: () => setShouldRender(false) });
1124
- React19.useEffect(() => {
1188
+ React15.useEffect(() => {
1125
1189
  if (!open) return;
1126
1190
  previousFocusRef.current = document.activeElement;
1127
1191
  const node = popoverRef.current;
@@ -1129,25 +1193,19 @@ var Popover = ({
1129
1193
  const focusable = node.querySelector(FOCUSABLE_SELECTORS2);
1130
1194
  (focusable ?? node).focus();
1131
1195
  }, [open]);
1132
- React19.useEffect(() => {
1196
+ React15.useEffect(() => {
1133
1197
  if (!open) return;
1134
1198
  const handleClick = (e) => {
1135
1199
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
1136
1200
  };
1137
- const handleKeyDown = (e) => {
1138
- if (e.key === "Escape") {
1139
- setOpen(false);
1140
- previousFocusRef.current?.focus();
1141
- }
1142
- };
1143
1201
  document.addEventListener("mousedown", handleClick);
1144
- document.addEventListener("keydown", handleKeyDown);
1145
- return () => {
1146
- document.removeEventListener("mousedown", handleClick);
1147
- document.removeEventListener("keydown", handleKeyDown);
1148
- };
1202
+ return () => document.removeEventListener("mousedown", handleClick);
1149
1203
  }, [open, setOpen]);
1150
- const triggerWithProps = React19.cloneElement(
1204
+ useOverlayEscape(open, () => {
1205
+ setOpen(false);
1206
+ previousFocusRef.current?.focus();
1207
+ });
1208
+ const triggerWithProps = React15.cloneElement(
1151
1209
  trigger,
1152
1210
  {
1153
1211
  onClick: (e) => {
@@ -1186,22 +1244,31 @@ var Tooltip = ({
1186
1244
  disabled = false,
1187
1245
  children
1188
1246
  }) => {
1189
- const [open, setOpen] = React19.useState(false);
1190
- const timerRef = React19.useRef(null);
1191
- const tooltipId = React19.useId();
1192
- const show = React19.useCallback(() => {
1247
+ const [open, setOpen] = React15.useState(false);
1248
+ const timerRef = React15.useRef(null);
1249
+ const tooltipId = React15.useId();
1250
+ const HIDE_DELAY = 120;
1251
+ const show = React15.useCallback(() => {
1193
1252
  if (timerRef.current) clearTimeout(timerRef.current);
1194
1253
  timerRef.current = setTimeout(() => setOpen(true), delay);
1195
1254
  }, [delay]);
1196
- const hide = React19.useCallback(() => {
1255
+ const hideNow = React15.useCallback(() => {
1197
1256
  if (timerRef.current) clearTimeout(timerRef.current);
1198
1257
  setOpen(false);
1199
1258
  }, []);
1200
- React19.useEffect(() => {
1259
+ const hide = React15.useCallback(() => {
1260
+ if (timerRef.current) clearTimeout(timerRef.current);
1261
+ timerRef.current = setTimeout(() => setOpen(false), HIDE_DELAY);
1262
+ }, []);
1263
+ const cancelHide = React15.useCallback(() => {
1264
+ if (timerRef.current) clearTimeout(timerRef.current);
1265
+ }, []);
1266
+ React15.useEffect(() => {
1201
1267
  return () => {
1202
1268
  if (timerRef.current) clearTimeout(timerRef.current);
1203
1269
  };
1204
1270
  }, []);
1271
+ useOverlayEscape(open, hideNow);
1205
1272
  const fromTransform = (() => {
1206
1273
  switch (placement) {
1207
1274
  case "top":
@@ -1217,7 +1284,7 @@ var Tooltip = ({
1217
1284
  const style = useSpringPresence({ visible: open, from: fromTransform });
1218
1285
  const child = children;
1219
1286
  const childProps = child.props;
1220
- const trigger = React19.cloneElement(child, {
1287
+ const trigger = React15.cloneElement(child, {
1221
1288
  onMouseEnter: (e) => {
1222
1289
  childProps.onMouseEnter?.(e);
1223
1290
  show();
@@ -1232,7 +1299,7 @@ var Tooltip = ({
1232
1299
  },
1233
1300
  onBlur: (e) => {
1234
1301
  childProps.onBlur?.(e);
1235
- hide();
1302
+ hideNow();
1236
1303
  },
1237
1304
  // 자식의 기존 aria-describedby 보존 + tooltip id 합성 (폼 설명/에러 연결 끊김 방지)
1238
1305
  "aria-describedby": open ? [childProps["aria-describedby"], tooltipId].filter(Boolean).join(" ") : childProps["aria-describedby"]
@@ -1240,16 +1307,24 @@ var Tooltip = ({
1240
1307
  if (disabled) return children;
1241
1308
  return /* @__PURE__ */ jsxs("span", { className: "tooltip_wrapper", children: [
1242
1309
  trigger,
1243
- open && /* @__PURE__ */ jsx("span", { className: cn("tooltip_position", `tooltip_placement_${placement}`), children: /* @__PURE__ */ jsx(
1244
- animated.span,
1310
+ open && /* @__PURE__ */ jsx(
1311
+ "span",
1245
1312
  {
1246
- id: tooltipId,
1247
- role: "tooltip",
1248
- style,
1249
- className: cn("tooltip", `tooltip_placement_${placement}`),
1250
- children: content
1313
+ className: cn("tooltip_position", `tooltip_placement_${placement}`),
1314
+ onMouseEnter: cancelHide,
1315
+ onMouseLeave: hide,
1316
+ children: /* @__PURE__ */ jsx(
1317
+ animated.span,
1318
+ {
1319
+ id: tooltipId,
1320
+ role: "tooltip",
1321
+ style,
1322
+ className: cn("tooltip", `tooltip_placement_${placement}`),
1323
+ children: content
1324
+ }
1325
+ )
1251
1326
  }
1252
- ) })
1327
+ )
1253
1328
  ] });
1254
1329
  };
1255
1330
 
@@ -1731,20 +1806,22 @@ var zIndex = {
1731
1806
  level4: 500,
1732
1807
  level5: 1e3
1733
1808
  };
1734
- var Button = ({
1735
- variant = "filled",
1736
- size = "md",
1737
- leadingIcon,
1738
- trailingIcon,
1739
- fullWidth = false,
1740
- radius: radius2,
1741
- danger = false,
1742
- type = "button",
1743
- ref,
1744
- className,
1745
- children,
1746
- ...props
1747
- }) => {
1809
+ var Button = (props) => {
1810
+ const {
1811
+ variant = "filled",
1812
+ size = "md",
1813
+ leadingIcon,
1814
+ trailingIcon,
1815
+ fullWidth = false,
1816
+ radius: radius2,
1817
+ danger = false,
1818
+ disabled = false,
1819
+ as,
1820
+ className,
1821
+ children,
1822
+ ref,
1823
+ ...rest
1824
+ } = props;
1748
1825
  const buttonClassName = cn(
1749
1826
  "button",
1750
1827
  `button_variant_${variant}`,
@@ -1752,13 +1829,44 @@ var Button = ({
1752
1829
  fullWidth && "button_full_width",
1753
1830
  radius2 && `button_radius_${radius2}`,
1754
1831
  danger && "button_danger",
1832
+ // anchor 엔 native :disabled 가 안 먹으므로 클래스로 비활성 스타일 적용 (button 도 무해)
1833
+ disabled && "button_disabled",
1755
1834
  className
1756
1835
  );
1757
- return /* @__PURE__ */ jsxs("button", { ref, type, className: buttonClassName, ...props, children: [
1836
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
1758
1837
  leadingIcon && /* @__PURE__ */ jsx("span", { className: "button_icon", "aria-hidden": "true", children: leadingIcon }),
1759
1838
  children && /* @__PURE__ */ jsx("span", { className: "button_label", children }),
1760
1839
  trailingIcon && /* @__PURE__ */ jsx("span", { className: "button_icon", "aria-hidden": "true", children: trailingIcon })
1761
1840
  ] });
1841
+ const anchorRest = rest;
1842
+ const renderAnchor = as === "a" || as === void 0 && anchorRest.href != null;
1843
+ if (renderAnchor) {
1844
+ const { onClick, tabIndex, ...anchorProps } = anchorRest;
1845
+ return /* @__PURE__ */ jsx(
1846
+ "a",
1847
+ {
1848
+ ...anchorProps,
1849
+ ref,
1850
+ className: buttonClassName,
1851
+ "aria-disabled": disabled || void 0,
1852
+ tabIndex: disabled ? -1 : tabIndex,
1853
+ onClick: disabled ? (e) => e.preventDefault() : onClick,
1854
+ children: content
1855
+ }
1856
+ );
1857
+ }
1858
+ const { type = "button", ...buttonRest } = rest;
1859
+ return /* @__PURE__ */ jsx(
1860
+ "button",
1861
+ {
1862
+ ref,
1863
+ type,
1864
+ disabled,
1865
+ className: buttonClassName,
1866
+ ...buttonRest,
1867
+ children: content
1868
+ }
1869
+ );
1762
1870
  };
1763
1871
  var ICONS = {
1764
1872
  info: /* @__PURE__ */ jsx(Info, { size: iconSize.lg, "aria-hidden": "true" }),
@@ -1816,20 +1924,23 @@ var AlertModal = ({
1816
1924
  destructive = false,
1817
1925
  actionsAlign = "right",
1818
1926
  showIcon = false,
1927
+ closeOnOverlay = true,
1819
1928
  onConfirm,
1820
1929
  onCancel,
1821
1930
  onClose
1822
1931
  }) => {
1823
- const panelRef = React19.useRef(null);
1824
- const titleId = React19.useId();
1825
- const messageId = React19.useId();
1932
+ const dismiss = onCancel ?? onClose;
1933
+ const panelRef = React15.useRef(null);
1934
+ const titleId = React15.useId();
1935
+ const messageId = React15.useId();
1826
1936
  const [shouldRender, setShouldRender] = useState(isOpen);
1827
1937
  useFocusTrap(panelRef, isOpen);
1828
- React19.useEffect(() => {
1829
- if (isOpen) setShouldRender(true);
1830
- }, [isOpen]);
1938
+ useOverlayEscape(isOpen, () => dismiss());
1939
+ if (isOpen && !shouldRender) setShouldRender(true);
1940
+ const reduced = useReducedMotion();
1831
1941
  const overlayStyle = useSpring({
1832
1942
  opacity: isOpen ? 1 : 0,
1943
+ immediate: reduced,
1833
1944
  config: { tension: 280, friction: 28, clamp: !isOpen },
1834
1945
  onRest: (result) => {
1835
1946
  if (!isOpen && result.finished) setShouldRender(false);
@@ -1838,9 +1949,31 @@ var AlertModal = ({
1838
1949
  const panelStyle = useSpring({
1839
1950
  opacity: isOpen ? 1 : 0,
1840
1951
  transform: isOpen ? "scale(1) translateY(0px)" : "scale(0.96) translateY(-4px)",
1952
+ immediate: reduced,
1841
1953
  config: { tension: 280, friction: 28, clamp: !isOpen }
1842
1954
  });
1843
- if (!shouldRender) return null;
1955
+ React15.useEffect(() => {
1956
+ if (!shouldRender) return;
1957
+ const body = document.body;
1958
+ const openModals = parseInt(body.dataset.openModals || "0", 10);
1959
+ if (openModals === 0) {
1960
+ body.dataset.originalOverflow = window.getComputedStyle(body).overflow;
1961
+ body.style.overflow = "hidden";
1962
+ }
1963
+ body.dataset.openModals = String(openModals + 1);
1964
+ return () => {
1965
+ const currentOpenModals = parseInt(body.dataset.openModals || "1", 10);
1966
+ const nextOpenModals = currentOpenModals - 1;
1967
+ if (nextOpenModals === 0) {
1968
+ body.style.overflow = body.dataset.originalOverflow || "";
1969
+ delete body.dataset.openModals;
1970
+ delete body.dataset.originalOverflow;
1971
+ } else {
1972
+ body.dataset.openModals = String(nextOpenModals);
1973
+ }
1974
+ };
1975
+ }, [shouldRender]);
1976
+ if (!isOpen && !shouldRender) return null;
1844
1977
  const confirmVariant = "filled";
1845
1978
  const cancelVariant = "outline";
1846
1979
  const modalClassName = cn("alert_modal", `alert_variant_${variant}`);
@@ -1852,8 +1985,7 @@ var AlertModal = ({
1852
1985
  className: "alert_overlay",
1853
1986
  style: overlayStyle,
1854
1987
  role: "presentation",
1855
- onClick: onClose,
1856
- onKeyDown: (e) => e.key === "Escape" && onClose(),
1988
+ onClick: () => closeOnOverlay && dismiss(),
1857
1989
  children: /* @__PURE__ */ jsxs(
1858
1990
  animated.div,
1859
1991
  {
@@ -1933,12 +2065,12 @@ var Checkbox = ({
1933
2065
  ref,
1934
2066
  ...props
1935
2067
  }) => {
1936
- const inputRef = React19.useRef(null);
1937
- React19.useImperativeHandle(
2068
+ const inputRef = React15.useRef(null);
2069
+ React15.useImperativeHandle(
1938
2070
  ref,
1939
2071
  () => inputRef.current
1940
2072
  );
1941
- React19.useEffect(() => {
2073
+ React15.useEffect(() => {
1942
2074
  if (!inputRef.current) return;
1943
2075
  inputRef.current.indeterminate = Boolean(indeterminate);
1944
2076
  }, [indeterminate]);
@@ -1949,7 +2081,16 @@ var Checkbox = ({
1949
2081
  className
1950
2082
  );
1951
2083
  return /* @__PURE__ */ jsxs("label", { className: rootClassName, children: [
1952
- /* @__PURE__ */ jsx("input", { ref: inputRef, type: "checkbox", className: "checkbox_input", ...props }),
2084
+ /* @__PURE__ */ jsx(
2085
+ "input",
2086
+ {
2087
+ ...props,
2088
+ ref: inputRef,
2089
+ type: "checkbox",
2090
+ className: "checkbox_input",
2091
+ "aria-invalid": error || void 0
2092
+ }
2093
+ ),
1953
2094
  /* @__PURE__ */ jsx("span", { className: "checkbox_state_layer", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "checkbox_icon" }) }),
1954
2095
  label ? /* @__PURE__ */ jsx("span", { className: "checkbox_label", children: label }) : null
1955
2096
  ] });
@@ -2012,9 +2153,11 @@ var Chip = ({
2012
2153
  leadingIcon,
2013
2154
  onClick,
2014
2155
  onRemove,
2156
+ removeLabel,
2015
2157
  className,
2016
2158
  ...props
2017
2159
  }) => {
2160
+ const removeAriaLabel = removeLabel ?? `${label} \uC81C\uAC70`;
2018
2161
  const [iconHovered, setIconHovered] = useState(false);
2019
2162
  const isStatic = type === "static";
2020
2163
  const hasLeading = !isStatic && selected;
@@ -2048,7 +2191,7 @@ var Chip = ({
2048
2191
  className: "chip_trailing",
2049
2192
  disabled,
2050
2193
  onClick: onRemove,
2051
- "aria-label": "Remove",
2194
+ "aria-label": removeAriaLabel,
2052
2195
  children: /* @__PURE__ */ jsx("span", { className: "chip_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
2053
2196
  }
2054
2197
  )
@@ -2062,6 +2205,7 @@ var Chip = ({
2062
2205
  className: "chip_content",
2063
2206
  disabled,
2064
2207
  onClick,
2208
+ "aria-pressed": type !== "filter" && selected !== void 0 ? selected : void 0,
2065
2209
  ...type === "filter" ? { "aria-haspopup": "listbox", "aria-expanded": !!open } : {},
2066
2210
  children: [
2067
2211
  hasLeading && /* @__PURE__ */ jsx(
@@ -2095,7 +2239,7 @@ var Chip = ({
2095
2239
  className: "chip_trailing",
2096
2240
  disabled,
2097
2241
  onClick: onRemove,
2098
- "aria-label": "Remove",
2242
+ "aria-label": removeAriaLabel,
2099
2243
  children: /* @__PURE__ */ jsx(
2100
2244
  "span",
2101
2245
  {
@@ -2123,7 +2267,8 @@ var Dropdown = (props) => {
2123
2267
  searchable = false,
2124
2268
  searchPlaceholder = "\uAC80\uC0C9\u2026",
2125
2269
  emptyText = "\uACB0\uACFC \uC5C6\uC74C",
2126
- selectedSummary = (count) => `${count}\uAC1C \uC120\uD0DD`
2270
+ selectedSummary = (count) => `${count}\uAC1C \uC120\uD0DD`,
2271
+ name
2127
2272
  } = props;
2128
2273
  const multiple = props.multiple === true;
2129
2274
  const internalId = useId();
@@ -2267,6 +2412,9 @@ var Dropdown = (props) => {
2267
2412
  e.preventDefault();
2268
2413
  setIsOpen(false);
2269
2414
  break;
2415
+ case "Tab":
2416
+ setIsOpen(false);
2417
+ break;
2270
2418
  }
2271
2419
  };
2272
2420
  const onSearchKeyDown = (e) => {
@@ -2288,6 +2436,9 @@ var Dropdown = (props) => {
2288
2436
  e.preventDefault();
2289
2437
  closePanel();
2290
2438
  break;
2439
+ case "Tab":
2440
+ closePanel();
2441
+ break;
2291
2442
  }
2292
2443
  };
2293
2444
  useEffect(() => {
@@ -2342,7 +2493,7 @@ var Dropdown = (props) => {
2342
2493
  className: cn("dropdown_control", { is_disabled: disabled }),
2343
2494
  "aria-haspopup": "listbox",
2344
2495
  "aria-expanded": isOpen,
2345
- "aria-controls": `${dropdownId}_listbox`,
2496
+ "aria-controls": isOpen ? `${dropdownId}_listbox` : void 0,
2346
2497
  onClick: () => !disabled && setIsOpen((o) => !o),
2347
2498
  onKeyDown: onControlKeyDown,
2348
2499
  disabled,
@@ -2352,6 +2503,7 @@ var Dropdown = (props) => {
2352
2503
  ]
2353
2504
  }
2354
2505
  ) }),
2506
+ name && (multiple ? selectedValues.map((v) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v, disabled }, v)) : /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selectedValues[0] ?? "", disabled })),
2355
2507
  isOpen && /* @__PURE__ */ jsxs(animated.div, { className: listClassName, style: listStyle, children: [
2356
2508
  searchable && /* @__PURE__ */ jsxs("div", { className: "dropdown_search", children: [
2357
2509
  /* @__PURE__ */ jsx("span", { className: "dropdown_search_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Search, { size: iconSize.sm }) }),
@@ -2456,9 +2608,9 @@ var DatePicker = ({
2456
2608
  minDateSrFormat = "Minimum date: {date}",
2457
2609
  selectableRangeUntilTodaySrText = "Selectable up to today"
2458
2610
  }) => {
2459
- const groupId = React19.useId();
2460
- const constraintId = React19.useId();
2461
- const { todayYear, todayMonth, todayDay } = React19.useMemo(() => {
2611
+ const groupId = React15.useId();
2612
+ const constraintId = React15.useId();
2613
+ const { todayYear, todayMonth, todayDay } = React15.useMemo(() => {
2462
2614
  const now = /* @__PURE__ */ new Date();
2463
2615
  return {
2464
2616
  todayYear: now.getFullYear(),
@@ -2467,7 +2619,7 @@ var DatePicker = ({
2467
2619
  };
2468
2620
  }, []);
2469
2621
  const endYear = endYearProp ?? todayYear + 10;
2470
- const parsed = React19.useMemo(() => {
2622
+ const parsed = React15.useMemo(() => {
2471
2623
  if (!value) return { year: 0, month: 0, day: 0 };
2472
2624
  const [y, m, d] = value.split("-").map(Number);
2473
2625
  return {
@@ -2476,7 +2628,7 @@ var DatePicker = ({
2476
2628
  day: d || 0
2477
2629
  };
2478
2630
  }, [value]);
2479
- const min = React19.useMemo(() => {
2631
+ const min = React15.useMemo(() => {
2480
2632
  if (!minDate) return { year: 0, month: 0, day: 0 };
2481
2633
  const [y, m, d] = minDate.split("-").map(Number);
2482
2634
  return {
@@ -2496,7 +2648,7 @@ var DatePicker = ({
2496
2648
  min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1
2497
2649
  )
2498
2650
  );
2499
- const maxDay = React19.useMemo(() => {
2651
+ const maxDay = React15.useMemo(() => {
2500
2652
  if (!year || !month) return 31;
2501
2653
  const daysInMonth = getDaysInMonth(year, month);
2502
2654
  if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
@@ -2504,28 +2656,28 @@ var DatePicker = ({
2504
2656
  }
2505
2657
  return daysInMonth;
2506
2658
  }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
2507
- const yearOptions = React19.useMemo(
2659
+ const yearOptions = React15.useMemo(
2508
2660
  () => range(startYear, maxYear).map((y) => ({
2509
2661
  value: String(y),
2510
2662
  label: String(y)
2511
2663
  })),
2512
2664
  [startYear, maxYear]
2513
2665
  );
2514
- const monthOptions = React19.useMemo(
2666
+ const monthOptions = React15.useMemo(
2515
2667
  () => range(minMonth, Math.max(minMonth, maxMonth)).map((m) => ({
2516
2668
  value: String(m),
2517
2669
  label: pad(m)
2518
2670
  })),
2519
2671
  [minMonth, maxMonth]
2520
2672
  );
2521
- const dayOptions = React19.useMemo(
2673
+ const dayOptions = React15.useMemo(
2522
2674
  () => range(minDay, Math.max(minDay, maxDay)).map((d) => ({
2523
2675
  value: String(d),
2524
2676
  label: pad(d)
2525
2677
  })),
2526
2678
  [minDay, maxDay]
2527
2679
  );
2528
- const emit = React19.useCallback(
2680
+ const emit = React15.useCallback(
2529
2681
  (yy, mm, dd) => {
2530
2682
  const cb = onValueChange ?? onChange;
2531
2683
  if (mode === "year-month") {
@@ -2537,7 +2689,7 @@ var DatePicker = ({
2537
2689
  },
2538
2690
  [mode, onValueChange, onChange]
2539
2691
  );
2540
- const handleYearChange = React19.useCallback(
2692
+ const handleYearChange = React15.useCallback(
2541
2693
  (raw) => {
2542
2694
  if (!raw) return;
2543
2695
  const newYear = Number(raw);
@@ -2554,14 +2706,14 @@ var DatePicker = ({
2554
2706
  },
2555
2707
  [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
2556
2708
  );
2557
- const handleMonthChange = React19.useCallback(
2709
+ const handleMonthChange = React15.useCallback(
2558
2710
  (raw) => {
2559
2711
  if (!raw || !year) return;
2560
2712
  emit(year, Number(raw), day || void 0);
2561
2713
  },
2562
2714
  [year, day, emit]
2563
2715
  );
2564
- const handleDayChange = React19.useCallback(
2716
+ const handleDayChange = React15.useCallback(
2565
2717
  (raw) => {
2566
2718
  if (!raw || !year || !month) return;
2567
2719
  emit(year, month, Number(raw));
@@ -2656,11 +2808,13 @@ var Drawer = ({
2656
2808
  className,
2657
2809
  ...props
2658
2810
  }) => {
2659
- const panelRef = React19.useRef(null);
2660
- const titleId = React19.useId();
2661
- const [shouldRender, setShouldRender] = React19.useState(open);
2811
+ const panelRef = React15.useRef(null);
2812
+ const titleId = React15.useId();
2813
+ const [shouldRender, setShouldRender] = React15.useState(open);
2662
2814
  const reduced = useReducedMotion();
2663
- useFocusTrap(panelRef, open);
2815
+ const isMounted = useIsMounted();
2816
+ useFocusTrap(panelRef, open && isMounted);
2817
+ useOverlayEscape(open && isMounted, () => onClose?.());
2664
2818
  if (open && !shouldRender) setShouldRender(true);
2665
2819
  const overlayStyle = useSpringPresence({
2666
2820
  visible: open,
@@ -2675,7 +2829,7 @@ var Drawer = ({
2675
2829
  immediate: reduced,
2676
2830
  config: { tension: 280, friction: 28, clamp: !open }
2677
2831
  });
2678
- React19.useEffect(() => {
2832
+ React15.useEffect(() => {
2679
2833
  if (!open) return;
2680
2834
  const body = document.body;
2681
2835
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -2697,55 +2851,53 @@ var Drawer = ({
2697
2851
  };
2698
2852
  }, [open]);
2699
2853
  if (!open && !shouldRender) return null;
2854
+ if (typeof document === "undefined" || !isMounted) return null;
2700
2855
  const hasTitle = !!title;
2701
2856
  const sizeValue = typeof size === "number" ? `${size}px` : size;
2702
2857
  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
- }
2858
+ return createPortal(
2859
+ /* @__PURE__ */ jsx(
2860
+ animated.div,
2861
+ {
2862
+ className: cn("drawer", `drawer_placement_${placement}`),
2863
+ style: overlayStyle,
2864
+ role: "dialog",
2865
+ "aria-modal": "true",
2866
+ "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
2867
+ "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
2868
+ onClick: () => closeOnOverlay && onClose?.(),
2869
+ children: /* @__PURE__ */ jsxs(
2870
+ animated.div,
2871
+ {
2872
+ ref: panelRef,
2873
+ ...props,
2874
+ className: cn("drawer_panel", className),
2875
+ style: { ...props.style, ...panelStyle, ...panelSizeStyle },
2876
+ role: "document",
2877
+ onClick: (e) => e.stopPropagation(),
2878
+ onKeyDown: (e) => {
2879
+ if (e.key !== "Escape") e.stopPropagation();
2880
+ },
2881
+ children: [
2882
+ showCloseIcon && onClose && /* @__PURE__ */ jsx(
2883
+ "button",
2884
+ {
2885
+ type: "button",
2886
+ className: "drawer_close",
2887
+ onClick: onClose,
2888
+ "aria-label": closeLabel,
2889
+ children: /* @__PURE__ */ jsx(X, { size: iconSize.md, "aria-hidden": "true" })
2890
+ }
2891
+ ),
2892
+ title && /* @__PURE__ */ jsx("div", { className: "drawer_header", children: /* @__PURE__ */ jsx("h2", { id: titleId, className: "drawer_title", children: title }) }),
2893
+ children && /* @__PURE__ */ jsx("div", { className: "drawer_body", children }),
2894
+ footer && /* @__PURE__ */ jsx("div", { className: "drawer_footer", children: footer })
2895
+ ]
2896
+ }
2897
+ )
2898
+ }
2899
+ ),
2900
+ document.body
2749
2901
  );
2750
2902
  };
2751
2903
  Drawer.displayName = "Drawer";
@@ -2762,11 +2914,11 @@ var FileInput = ({
2762
2914
  onChange,
2763
2915
  ...props
2764
2916
  }) => {
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([]);
2917
+ const inputId = React15.useId();
2918
+ const helperId = React15.useId();
2919
+ const inputRef = React15.useRef(null);
2920
+ const [previewUrls, setPreviewUrls] = React15.useState([]);
2921
+ const previewUrlsRef = React15.useRef([]);
2770
2922
  const isPreviewVariant = variant === "preview";
2771
2923
  const showPreview = isPreviewVariant || preview;
2772
2924
  const handleChange = (e) => {
@@ -2807,7 +2959,7 @@ var FileInput = ({
2807
2959
  }
2808
2960
  onFiles?.(null);
2809
2961
  };
2810
- React19.useEffect(() => {
2962
+ React15.useEffect(() => {
2811
2963
  return () => {
2812
2964
  for (const url of previewUrlsRef.current) {
2813
2965
  URL.revokeObjectURL(url);
@@ -2884,6 +3036,10 @@ var FileInput = ({
2884
3036
  )) })
2885
3037
  ] });
2886
3038
  };
3039
+ var HeroActionButton = ({
3040
+ action,
3041
+ variant
3042
+ }) => action.href ? /* @__PURE__ */ jsx(Button, { as: "a", href: action.href, size: "lg", variant, onClick: action.onClick, children: action.label }) : /* @__PURE__ */ jsx(Button, { size: "lg", variant, onClick: action.onClick, children: action.label });
2887
3043
  var Hero = ({
2888
3044
  height = "md",
2889
3045
  align = "left",
@@ -2922,8 +3078,8 @@ var Hero = ({
2922
3078
  title && /* @__PURE__ */ jsx("h1", { className: "hero_title", children: title }),
2923
3079
  subtitle && /* @__PURE__ */ jsx("p", { className: "hero_subtitle", children: subtitle }),
2924
3080
  (primaryAction || secondaryAction || children) && /* @__PURE__ */ jsxs("div", { className: "hero_actions", children: [
2925
- primaryAction && /* @__PURE__ */ jsx(Button, { size: "lg", variant: "filled", onClick: primaryAction.onClick, children: primaryAction.label }),
2926
- secondaryAction && /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", onClick: secondaryAction.onClick, children: secondaryAction.label }),
3081
+ primaryAction && /* @__PURE__ */ jsx(HeroActionButton, { action: primaryAction, variant: "filled" }),
3082
+ secondaryAction && /* @__PURE__ */ jsx(HeroActionButton, { action: secondaryAction, variant: "outline" }),
2927
3083
  children
2928
3084
  ] })
2929
3085
  ] })
@@ -3035,7 +3191,7 @@ var ListItem = ({
3035
3191
  role: onClick ? "button" : void 0,
3036
3192
  tabIndex: onClick && !disabled ? 0 : void 0,
3037
3193
  "aria-disabled": disabled || void 0,
3038
- "aria-selected": selected || void 0,
3194
+ "aria-pressed": onClick && selected !== void 0 ? selected : void 0,
3039
3195
  ...props,
3040
3196
  children: /* @__PURE__ */ jsxs("div", { className: "list_item_state_layer", children: [
3041
3197
  leadingElement && /* @__PURE__ */ jsx("div", { className: "list_item_leading", children: leadingElement }),
@@ -3106,13 +3262,17 @@ var Modal = ({
3106
3262
  ariaLabel,
3107
3263
  ...props
3108
3264
  }) => {
3109
- const panelRef = React19.useRef(null);
3110
- const titleId = React19.useId();
3111
- const [shouldRender, setShouldRender] = React19.useState(open);
3112
- useFocusTrap(panelRef, open);
3265
+ const panelRef = React15.useRef(null);
3266
+ const titleId = React15.useId();
3267
+ const [shouldRender, setShouldRender] = React15.useState(open);
3268
+ const isMounted = useIsMounted();
3269
+ useFocusTrap(panelRef, open && isMounted);
3270
+ useOverlayEscape(open && isMounted, () => onClose?.());
3113
3271
  if (open && !shouldRender) setShouldRender(true);
3272
+ const reduced = useReducedMotion();
3114
3273
  const overlayStyle = useSpring({
3115
3274
  opacity: open ? 1 : 0,
3275
+ immediate: reduced,
3116
3276
  config: { tension: 280, friction: 28, clamp: !open },
3117
3277
  onRest: (result) => {
3118
3278
  if (!open && result.finished) setShouldRender(false);
@@ -3121,9 +3281,10 @@ var Modal = ({
3121
3281
  const panelStyle = useSpring({
3122
3282
  opacity: open ? 1 : 0,
3123
3283
  transform: open ? "scale(1) translateY(0px)" : "scale(0.96) translateY(-4px)",
3284
+ immediate: reduced,
3124
3285
  config: { tension: 280, friction: 28, clamp: !open }
3125
3286
  });
3126
- React19.useEffect(() => {
3287
+ React15.useEffect(() => {
3127
3288
  if (!open) return;
3128
3289
  const body = document.body;
3129
3290
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -3145,54 +3306,52 @@ var Modal = ({
3145
3306
  };
3146
3307
  }, [open]);
3147
3308
  if (!open && !shouldRender) return null;
3309
+ if (typeof document === "undefined" || !isMounted) return null;
3148
3310
  const hasTitle = !!title;
3149
- return /* @__PURE__ */ jsx(
3150
- animated.div,
3151
- {
3152
- className: "modal",
3153
- style: overlayStyle,
3154
- role: "dialog",
3155
- "aria-modal": "true",
3156
- "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
3157
- "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
3158
- onClick: () => closeOnOverlay && onClose?.(),
3159
- onKeyDown: (e) => {
3160
- if (e.key === "Escape") {
3161
- e.stopPropagation();
3162
- onClose?.();
3163
- }
3164
- },
3165
- children: /* @__PURE__ */ jsxs(
3166
- animated.div,
3167
- {
3168
- ref: panelRef,
3169
- className: cn("modal_panel", className),
3170
- style: { ...panelStyle, width },
3171
- role: "document",
3172
- onClick: (e) => e.stopPropagation(),
3173
- onKeyDown: (e) => {
3174
- if (e.key !== "Escape") e.stopPropagation();
3175
- },
3176
- ...props,
3177
- children: [
3178
- showCloseIcon && onClose && /* @__PURE__ */ jsx(
3179
- "button",
3180
- {
3181
- type: "button",
3182
- className: "modal_close",
3183
- onClick: onClose,
3184
- "aria-label": closeLabel,
3185
- children: /* @__PURE__ */ jsx(X, { size: iconSize.md, "aria-hidden": "true" })
3186
- }
3187
- ),
3188
- title && /* @__PURE__ */ jsx("h2", { id: titleId, className: "modal_title", children: title }),
3189
- description && /* @__PURE__ */ jsx("div", { className: "modal_description", children: description }),
3190
- children && /* @__PURE__ */ jsx("div", { className: "modal_body", children }),
3191
- footer && /* @__PURE__ */ jsx("div", { className: cn("modal_footer", `modal_footer_${footerAlign}`), children: footer })
3192
- ]
3193
- }
3194
- )
3195
- }
3311
+ return createPortal(
3312
+ /* @__PURE__ */ jsx(
3313
+ animated.div,
3314
+ {
3315
+ className: "modal",
3316
+ style: overlayStyle,
3317
+ role: "dialog",
3318
+ "aria-modal": "true",
3319
+ "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
3320
+ "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
3321
+ onClick: () => closeOnOverlay && onClose?.(),
3322
+ children: /* @__PURE__ */ jsxs(
3323
+ animated.div,
3324
+ {
3325
+ ref: panelRef,
3326
+ ...props,
3327
+ className: cn("modal_panel", className),
3328
+ style: { ...props.style, ...panelStyle, width },
3329
+ role: "document",
3330
+ onClick: (e) => e.stopPropagation(),
3331
+ onKeyDown: (e) => {
3332
+ if (e.key !== "Escape") e.stopPropagation();
3333
+ },
3334
+ children: [
3335
+ showCloseIcon && onClose && /* @__PURE__ */ jsx(
3336
+ "button",
3337
+ {
3338
+ type: "button",
3339
+ className: "modal_close",
3340
+ onClick: onClose,
3341
+ "aria-label": closeLabel,
3342
+ children: /* @__PURE__ */ jsx(X, { size: iconSize.md, "aria-hidden": "true" })
3343
+ }
3344
+ ),
3345
+ title && /* @__PURE__ */ jsx("h2", { id: titleId, className: "modal_title", children: title }),
3346
+ description && /* @__PURE__ */ jsx("div", { className: "modal_description", children: description }),
3347
+ children && /* @__PURE__ */ jsx("div", { className: "modal_body", children }),
3348
+ footer && /* @__PURE__ */ jsx("div", { className: cn("modal_footer", `modal_footer_${footerAlign}`), children: footer })
3349
+ ]
3350
+ }
3351
+ )
3352
+ }
3353
+ ),
3354
+ document.body
3196
3355
  );
3197
3356
  };
3198
3357
  var OtpInput = ({
@@ -3207,12 +3366,12 @@ var OtpInput = ({
3207
3366
  ariaLabel = "OTP \uC785\uB825",
3208
3367
  className
3209
3368
  }) => {
3210
- const inputsRef = React19.useRef([]);
3211
- const isTypingRef = React19.useRef(false);
3212
- React19.useEffect(() => {
3369
+ const inputsRef = React15.useRef([]);
3370
+ const isTypingRef = React15.useRef(false);
3371
+ React15.useEffect(() => {
3213
3372
  if (autoFocus) inputsRef.current[0]?.focus();
3214
3373
  }, [autoFocus]);
3215
- const digits = React19.useMemo(() => {
3374
+ const digits = React15.useMemo(() => {
3216
3375
  const chars = value.split("").slice(0, length);
3217
3376
  while (chars.length < length) chars.push("");
3218
3377
  return chars;
@@ -3225,6 +3384,15 @@ var OtpInput = ({
3225
3384
  };
3226
3385
  const handleChange = (index, e) => {
3227
3386
  const nextDigit = e.target.value;
3387
+ if (index === 0 && nextDigit.length > 1) {
3388
+ const filled = nextDigit.replace(/\D/g, "").slice(0, length);
3389
+ if (!filled) return;
3390
+ const newDigits2 = [...digits];
3391
+ for (let i = 0; i < filled.length; i++) newDigits2[i] = filled[i];
3392
+ updateValue(newDigits2);
3393
+ focusInput(Math.min(filled.length, length - 1));
3394
+ return;
3395
+ }
3228
3396
  if (nextDigit && !/^\d$/.test(nextDigit)) return;
3229
3397
  const newDigits = [...digits];
3230
3398
  newDigits[index] = nextDigit;
@@ -3244,7 +3412,7 @@ var OtpInput = ({
3244
3412
  focusInput(firstEmpty);
3245
3413
  }
3246
3414
  };
3247
- const handleKeyDown = (index, e) => {
3415
+ const handleKeyDown2 = (index, e) => {
3248
3416
  if (e.key === "Backspace") {
3249
3417
  if (digits[index]) {
3250
3418
  const newDigits = [...digits];
@@ -3280,6 +3448,7 @@ var OtpInput = ({
3280
3448
  focusInput(nextIndex);
3281
3449
  };
3282
3450
  const rootClassName = cn("otp_input", className);
3451
+ const supportingId = React15.useId();
3283
3452
  return (
3284
3453
  // biome-ignore lint/a11y/useSemanticElements: <fieldset> would force border/legend styles; role=group is the WAI-ARIA equivalent for OTP grouping
3285
3454
  /* @__PURE__ */ jsxs("div", { className: rootClassName, role: "group", "aria-label": ariaLabel, children: [
@@ -3293,13 +3462,16 @@ var OtpInput = ({
3293
3462
  inputMode: "numeric",
3294
3463
  pattern: "\\d*",
3295
3464
  maxLength: 1,
3465
+ autoComplete: i === 0 ? "one-time-code" : "off",
3296
3466
  value: digit,
3297
3467
  onChange: (e) => handleChange(i, e),
3298
3468
  onFocus: () => handleFocus(i),
3299
- onKeyDown: (e) => handleKeyDown(i, e),
3469
+ onKeyDown: (e) => handleKeyDown2(i, e),
3300
3470
  onPaste: handlePaste,
3301
3471
  disabled,
3302
3472
  "aria-label": `${i + 1}\uBC88\uC9F8 \uC790\uB9AC`,
3473
+ "aria-invalid": error || void 0,
3474
+ "aria-describedby": supportingText ? supportingId : void 0,
3303
3475
  className: cn(
3304
3476
  "otp_input_box",
3305
3477
  error && "otp_input_box_error",
@@ -3308,7 +3480,14 @@ var OtpInput = ({
3308
3480
  },
3309
3481
  i
3310
3482
  )) }),
3311
- supportingText && /* @__PURE__ */ jsx("span", { className: cn("otp_input_supporting", error && "otp_input_supporting_error"), children: supportingText })
3483
+ supportingText && /* @__PURE__ */ jsx(
3484
+ "span",
3485
+ {
3486
+ id: supportingId,
3487
+ className: cn("otp_input_supporting", error && "otp_input_supporting_error"),
3488
+ children: supportingText
3489
+ }
3490
+ )
3312
3491
  ] })
3313
3492
  );
3314
3493
  };
@@ -3353,7 +3532,7 @@ var Pagination = ({
3353
3532
  const emit = onPageChange ?? onChange;
3354
3533
  const prevDisabled = page <= 1;
3355
3534
  const nextDisabled = page >= totalPages;
3356
- const items = React19.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
3535
+ const items = React15.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
3357
3536
  return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
3358
3537
  /* @__PURE__ */ jsx(
3359
3538
  "button",
@@ -3398,9 +3577,9 @@ var Pagination = ({
3398
3577
  )
3399
3578
  ] });
3400
3579
  };
3401
- var RadioGroupContext = React19.createContext(null);
3580
+ var RadioGroupContext = React15.createContext(null);
3402
3581
  function useRadioGroupContext() {
3403
- return React19.useContext(RadioGroupContext);
3582
+ return React15.useContext(RadioGroupContext);
3404
3583
  }
3405
3584
  var RadioGroup = ({
3406
3585
  value: controlledValue,
@@ -3418,21 +3597,21 @@ var RadioGroup = ({
3418
3597
  ...props
3419
3598
  }) => {
3420
3599
  const isControlled = controlledValue !== void 0;
3421
- const [internalValue, setInternalValue] = React19.useState(defaultValue);
3600
+ const [internalValue, setInternalValue] = React15.useState(defaultValue);
3422
3601
  const value = isControlled ? controlledValue : internalValue;
3423
- const generatedName = React19.useId();
3602
+ const generatedName = React15.useId();
3424
3603
  const name = nameProp ?? generatedName;
3425
- const idPrefix = React19.useId();
3604
+ const idPrefix = React15.useId();
3426
3605
  const labelId = label ? `${idPrefix}-label` : void 0;
3427
3606
  const helperId = supportingText ? `${idPrefix}-help` : void 0;
3428
- const onChange = React19.useCallback(
3607
+ const onChange = React15.useCallback(
3429
3608
  (next) => {
3430
3609
  if (!isControlled) setInternalValue(next);
3431
3610
  onValueChange?.(next);
3432
3611
  },
3433
3612
  [isControlled, onValueChange]
3434
3613
  );
3435
- const ctx = React19.useMemo(
3614
+ const ctx = React15.useMemo(
3436
3615
  () => ({ name, value, onChange, size, disabled }),
3437
3616
  [name, value, onChange, size, disabled]
3438
3617
  );
@@ -3481,7 +3660,7 @@ var Radio = ({
3481
3660
  const size = sizeProp ?? group?.size ?? "md";
3482
3661
  const name = nameProp ?? group?.name;
3483
3662
  const disabled = disabledProp ?? group?.disabled;
3484
- const resolvedChecked = group ? group.value === value : checked;
3663
+ const resolvedChecked = group ? value !== void 0 && group.value != null && String(group.value) === String(value) : checked;
3485
3664
  const handleChange = (event) => {
3486
3665
  if (group && value !== void 0) group.onChange(String(value));
3487
3666
  onChange?.(event);
@@ -3562,6 +3741,7 @@ var Table = ({
3562
3741
  ariaLabel,
3563
3742
  className,
3564
3743
  onRowClick,
3744
+ rowClickHint = "\uD074\uB9AD \uAC00\uB2A5\uD55C \uD589",
3565
3745
  sort,
3566
3746
  onSortChange,
3567
3747
  selectAllAriaLabel = "\uC804\uCCB4 \uC120\uD0DD",
@@ -3580,7 +3760,8 @@ var Table = ({
3580
3760
  const isEmpty = !isLoading && data.length === 0;
3581
3761
  const getRowKey = (item, index) => rowKey?.(item) ?? String(index);
3582
3762
  const allRowKeys = selectable ? data.map((item, index) => getRowKey(item, index)) : [];
3583
- const selectedSet = React19.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
3763
+ const rowClickHintId = React15.useId();
3764
+ const selectedSet = React15.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
3584
3765
  const selectedCount = allRowKeys.filter((key) => selectedSet.has(key)).length;
3585
3766
  const isAllSelected = allRowKeys.length > 0 && selectedCount === allRowKeys.length;
3586
3767
  const isSomeSelected = selectedCount > 0 && !isAllSelected;
@@ -3599,7 +3780,7 @@ var Table = ({
3599
3780
  onSortChange?.(next);
3600
3781
  };
3601
3782
  return /* @__PURE__ */ jsxs("div", { className: wrapperClassName, children: [
3602
- /* @__PURE__ */ jsxs("table", { className: "table", "aria-label": ariaLabel, children: [
3783
+ /* @__PURE__ */ jsxs("table", { className: "table", "aria-label": ariaLabel, "aria-busy": isLoading || void 0, children: [
3603
3784
  /* @__PURE__ */ jsx("thead", { className: "table_thead", children: /* @__PURE__ */ jsxs("tr", { children: [
3604
3785
  selectable && /* @__PURE__ */ jsx("th", { scope: "col", className: "table_th table_th_checkbox", children: /* @__PURE__ */ jsx(
3605
3786
  Checkbox,
@@ -3692,7 +3873,7 @@ var Table = ({
3692
3873
  isSelected && "table_row_selected"
3693
3874
  ),
3694
3875
  "aria-selected": isSelected ? "true" : void 0,
3695
- role: onRowClick && !selectable ? "button" : void 0,
3876
+ "aria-describedby": onRowClick && rowClickHint ? rowClickHintId : void 0,
3696
3877
  tabIndex: onRowClick ? 0 : void 0,
3697
3878
  onClick: onRowClick ? () => onRowClick(item, rowIndex) : void 0,
3698
3879
  onKeyDown: onRowClick ? (e) => {
@@ -3737,12 +3918,13 @@ var Table = ({
3737
3918
  );
3738
3919
  }) })
3739
3920
  ] }),
3921
+ onRowClick && rowClickHint && /* @__PURE__ */ jsx("span", { id: rowClickHintId, className: "table_sr_only", children: rowClickHint }),
3740
3922
  isEmpty && /* @__PURE__ */ jsx("div", { className: "table_empty", role: "status", children: emptyMessage })
3741
3923
  ] });
3742
3924
  };
3743
- var ThemeContext = React19.createContext(null);
3925
+ var ThemeContext = React15.createContext(null);
3744
3926
  var useTheme = () => {
3745
- const ctx = React19.useContext(ThemeContext);
3927
+ const ctx = React15.useContext(ThemeContext);
3746
3928
  if (!ctx) {
3747
3929
  throw new Error(
3748
3930
  '[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>'
@@ -3766,15 +3948,15 @@ var ThemeProvider = ({
3766
3948
  targetSelector,
3767
3949
  children
3768
3950
  }) => {
3769
- const [mode, setModeState] = React19.useState(defaultMode);
3770
- const [systemDark, setSystemDark] = React19.useState(false);
3771
- React19.useEffect(() => {
3951
+ const [mode, setModeState] = React15.useState(defaultMode);
3952
+ const [systemDark, setSystemDark] = React15.useState(false);
3953
+ React15.useEffect(() => {
3772
3954
  setModeState(readStored(storageKey) ?? defaultMode);
3773
3955
  if (isClient) {
3774
3956
  setSystemDark(window.matchMedia("(prefers-color-scheme: dark)").matches);
3775
3957
  }
3776
3958
  }, [storageKey, defaultMode]);
3777
- React19.useEffect(() => {
3959
+ React15.useEffect(() => {
3778
3960
  if (!isClient) return;
3779
3961
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
3780
3962
  const handler = (e) => setSystemDark(e.matches);
@@ -3782,7 +3964,7 @@ var ThemeProvider = ({
3782
3964
  return () => mq.removeEventListener("change", handler);
3783
3965
  }, []);
3784
3966
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
3785
- React19.useEffect(() => {
3967
+ React15.useEffect(() => {
3786
3968
  if (!isClient) return;
3787
3969
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
3788
3970
  if (!target) return;
@@ -3792,7 +3974,7 @@ var ThemeProvider = ({
3792
3974
  target.setAttribute("data-theme", mode);
3793
3975
  }
3794
3976
  }, [mode, targetSelector]);
3795
- const setMode = React19.useCallback(
3977
+ const setMode = React15.useCallback(
3796
3978
  (next) => {
3797
3979
  setModeState(next);
3798
3980
  if (storageKey && isClient) {
@@ -3804,7 +3986,7 @@ var ThemeProvider = ({
3804
3986
  },
3805
3987
  [storageKey]
3806
3988
  );
3807
- const value = React19.useMemo(
3989
+ const value = React15.useMemo(
3808
3990
  () => ({ mode, resolved, setMode }),
3809
3991
  [mode, resolved, setMode]
3810
3992
  );
@@ -4052,7 +4234,7 @@ var Textarea = ({
4052
4234
  ] });
4053
4235
  };
4054
4236
  Textarea.displayName = "Textarea";
4055
- var ToastContext = React19.createContext(null);
4237
+ var ToastContext = React15.createContext(null);
4056
4238
  var VARIANT_ICONS = {
4057
4239
  success: /* @__PURE__ */ jsx(CheckCircle2, { size: iconSize.md }),
4058
4240
  error: /* @__PURE__ */ jsx(XCircle, { size: iconSize.md }),
@@ -4061,15 +4243,29 @@ var VARIANT_ICONS = {
4061
4243
  default: /* @__PURE__ */ jsx(Bell, { size: iconSize.md })
4062
4244
  };
4063
4245
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
4064
- const [visible, setVisible] = React19.useState(true);
4065
- const closingRef = React19.useRef(false);
4246
+ const [visible, setVisible] = React15.useState(true);
4247
+ const closingRef = React15.useRef(false);
4248
+ const rootRef = React15.useRef(null);
4066
4249
  const style = useSpringPresence({
4067
4250
  visible,
4068
4251
  from: "translateX(20px)",
4069
4252
  // 우측에서 슬라이드 인
4070
- onExitComplete: () => onRemove(item.id)
4253
+ onExitComplete: () => {
4254
+ const hadFocus = rootRef.current?.contains(document.activeElement) ?? false;
4255
+ if (hadFocus) {
4256
+ const closeButtons = Array.from(
4257
+ document.querySelectorAll(".toast_item .toast_close")
4258
+ );
4259
+ const currentIndex = closeButtons.findIndex((el) => rootRef.current?.contains(el));
4260
+ if (currentIndex !== -1) {
4261
+ const next = closeButtons[currentIndex + 1] || closeButtons[currentIndex - 1];
4262
+ next?.focus();
4263
+ }
4264
+ }
4265
+ onRemove(item.id);
4266
+ }
4071
4267
  });
4072
- const close = React19.useCallback(() => {
4268
+ const close = React15.useCallback(() => {
4073
4269
  if (closingRef.current) return;
4074
4270
  closingRef.current = true;
4075
4271
  setVisible(false);
@@ -4077,6 +4273,7 @@ var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
4077
4273
  return /* @__PURE__ */ jsxs(
4078
4274
  animated.div,
4079
4275
  {
4276
+ ref: rootRef,
4080
4277
  className: "toast_item",
4081
4278
  style,
4082
4279
  role: item.variant === "error" ? "alert" : "status",
@@ -4102,22 +4299,19 @@ var ToastProvider = ({
4102
4299
  maxCount = 5,
4103
4300
  closeAriaLabel = "Close"
4104
4301
  }) => {
4105
- const [toasts, setToasts] = React19.useState([]);
4106
- const [isMounted, setIsMounted] = React19.useState(false);
4107
- React19.useEffect(() => {
4108
- setIsMounted(true);
4109
- }, []);
4110
- const addToast = React19.useCallback(
4302
+ const [toasts, setToasts] = React15.useState([]);
4303
+ const isMounted = useIsMounted();
4304
+ const addToast = React15.useCallback(
4111
4305
  (message, variant, duration = 3e3) => {
4112
4306
  const id = crypto.randomUUID();
4113
4307
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
4114
4308
  },
4115
4309
  [maxCount]
4116
4310
  );
4117
- const removeToast = React19.useCallback((id) => {
4311
+ const removeToast = React15.useCallback((id) => {
4118
4312
  setToasts((prev) => prev.filter((t) => t.id !== id));
4119
4313
  }, []);
4120
- const contextValue = React19.useMemo(() => ({ addToast }), [addToast]);
4314
+ const contextValue = React15.useMemo(() => ({ addToast }), [addToast]);
4121
4315
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
4122
4316
  children,
4123
4317
  isMounted && createPortal(
@@ -4178,10 +4372,11 @@ var Toggle = ({
4178
4372
  ...props
4179
4373
  }) => {
4180
4374
  const isControlled = checked !== void 0;
4181
- const [innerChecked, setInnerChecked] = React19.useState(!!defaultChecked);
4375
+ const [innerChecked, setInnerChecked] = React15.useState(!!defaultChecked);
4182
4376
  const isOn = isControlled ? !!checked : innerChecked;
4183
- const handleToggle = () => {
4184
- if (disabled) return;
4377
+ const handleToggle = (e) => {
4378
+ props.onClick?.(e);
4379
+ if (disabled || e.defaultPrevented) return;
4185
4380
  const next = !isOn;
4186
4381
  if (!isControlled) setInnerChecked(next);
4187
4382
  (onCheckedChange ?? onChange)?.(next);
@@ -4195,6 +4390,7 @@ var Toggle = ({
4195
4390
  return /* @__PURE__ */ jsx(
4196
4391
  "button",
4197
4392
  {
4393
+ ...props,
4198
4394
  ref,
4199
4395
  type: "button",
4200
4396
  role: "switch",
@@ -4203,7 +4399,6 @@ var Toggle = ({
4203
4399
  disabled,
4204
4400
  onClick: handleToggle,
4205
4401
  className: rootClassName,
4206
- ...props,
4207
4402
  children: /* @__PURE__ */ jsx("span", { className: "toggle_thumb" })
4208
4403
  }
4209
4404
  );