@bigtablet/design-system 3.7.0 → 3.9.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 React15 from 'react';
3
+ import * as React16 from 'react';
4
4
  import { createContext, useRef, useState, useId, useEffect, useContext, useCallback, useMemo, Fragment, useImperativeHandle, useLayoutEffect } from 'react';
5
5
  import { useSpring, animated } from '@react-spring/web';
6
6
  import { ChevronDown, ChevronRight, Globe, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, XCircle, AlertTriangle, CheckCircle2, Info, Bell, Search, Check, Image, X, TriangleAlert } from 'lucide-react';
@@ -58,15 +58,137 @@ function registerOverlay(onEscape) {
58
58
  };
59
59
  }
60
60
  function useOverlayEscape(active, onEscape) {
61
- const handlerRef = React15.useRef(onEscape);
62
- React15.useEffect(() => {
61
+ const handlerRef = React16.useRef(onEscape);
62
+ React16.useEffect(() => {
63
63
  handlerRef.current = onEscape;
64
64
  });
65
- React15.useEffect(() => {
65
+ React16.useEffect(() => {
66
66
  if (!active) return;
67
67
  return registerOverlay(() => handlerRef.current());
68
68
  }, [active]);
69
69
  }
70
+ var useSafeLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
71
+
72
+ // src/utils/use-anchored-position.ts
73
+ var OPPOSITE = {
74
+ top: "bottom",
75
+ bottom: "top",
76
+ left: "right",
77
+ right: "left"
78
+ };
79
+ var isVertical = (side) => side === "top" || side === "bottom";
80
+ var clamp = (value, min, max) => (
81
+ // max < min(가용 공간이 플로팅보다 작을 때)이면 min(가장자리 여백)에 고정.
82
+ Math.max(min, Math.min(max, value))
83
+ );
84
+ var mainAxisStart = (side, anchor, floating, gap) => {
85
+ switch (side) {
86
+ case "top":
87
+ return anchor.top - gap - floating.height;
88
+ case "bottom":
89
+ return anchor.top + anchor.height + gap;
90
+ case "left":
91
+ return anchor.left - gap - floating.width;
92
+ case "right":
93
+ return anchor.left + anchor.width + gap;
94
+ }
95
+ };
96
+ var fitsMainAxis = (side, anchor, floating, viewport, gap, padding) => {
97
+ const start = mainAxisStart(side, anchor, floating, gap);
98
+ switch (side) {
99
+ case "top":
100
+ return start >= padding;
101
+ case "bottom":
102
+ return start + floating.height <= viewport.height - padding;
103
+ case "left":
104
+ return start >= padding;
105
+ case "right":
106
+ return start + floating.width <= viewport.width - padding;
107
+ }
108
+ };
109
+ function computeAnchoredPosition(anchor, floating, viewport, options) {
110
+ const gap = options.gap ?? 8;
111
+ const padding = options.padding;
112
+ const available = viewport.width - padding * 2;
113
+ const maxWidth = available;
114
+ const sized = {
115
+ width: Math.min(floating.width, available),
116
+ height: floating.height
117
+ };
118
+ let side = options.placement;
119
+ if (!fitsMainAxis(side, anchor, sized, viewport, gap, padding) && fitsMainAxis(OPPOSITE[side], anchor, sized, viewport, gap, padding)) {
120
+ side = OPPOSITE[side];
121
+ }
122
+ let x;
123
+ let y;
124
+ if (isVertical(side)) {
125
+ y = mainAxisStart(side, anchor, sized, gap);
126
+ x = anchor.left + anchor.width / 2 - sized.width / 2;
127
+ x = clamp(x, padding, viewport.width - padding - sized.width);
128
+ } else {
129
+ x = mainAxisStart(side, anchor, sized, gap);
130
+ y = anchor.top + anchor.height / 2 - sized.height / 2;
131
+ y = clamp(y, padding, viewport.height - padding - sized.height);
132
+ }
133
+ return { x, y, placement: side, maxWidth };
134
+ }
135
+ function useAnchoredPosition({
136
+ open,
137
+ anchorRef,
138
+ floatingRef,
139
+ placement,
140
+ gap,
141
+ padding
142
+ }) {
143
+ const [state, setState] = React16.useState({
144
+ x: 0,
145
+ y: 0,
146
+ placement,
147
+ maxWidth: 0,
148
+ ready: false
149
+ });
150
+ useSafeLayoutEffect(() => {
151
+ if (!open) {
152
+ setState((prev) => prev.ready ? { ...prev, ready: false } : prev);
153
+ return;
154
+ }
155
+ const anchor = anchorRef.current;
156
+ const floating = floatingRef.current;
157
+ if (!anchor || !floating) return;
158
+ const update = () => {
159
+ const a = anchor.getBoundingClientRect();
160
+ const f = floating.getBoundingClientRect();
161
+ const result = computeAnchoredPosition(
162
+ { top: a.top, left: a.left, width: a.width, height: a.height },
163
+ { width: f.width, height: f.height },
164
+ { width: window.innerWidth, height: window.innerHeight },
165
+ { placement, gap, padding }
166
+ );
167
+ setState({ ...result, ready: true });
168
+ };
169
+ let frame = 0;
170
+ const schedule = () => {
171
+ if (frame) return;
172
+ frame = requestAnimationFrame(() => {
173
+ frame = 0;
174
+ update();
175
+ });
176
+ };
177
+ update();
178
+ window.addEventListener("scroll", schedule, true);
179
+ window.addEventListener("resize", schedule);
180
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(schedule) : null;
181
+ observer?.observe(floating);
182
+ observer?.observe(anchor);
183
+ return () => {
184
+ if (frame) cancelAnimationFrame(frame);
185
+ window.removeEventListener("scroll", schedule, true);
186
+ window.removeEventListener("resize", schedule);
187
+ observer?.disconnect();
188
+ };
189
+ }, [open, placement, gap, padding, anchorRef, floatingRef]);
190
+ return state;
191
+ }
70
192
  var FOCUSABLE_SELECTORS = [
71
193
  "a[href]",
72
194
  "button:not([disabled])",
@@ -76,8 +198,8 @@ var FOCUSABLE_SELECTORS = [
76
198
  '[tabindex]:not([tabindex="-1"])'
77
199
  ].join(", ");
78
200
  function useFocusTrap(containerRef, isActive) {
79
- const previousActiveElement = React15.useRef(null);
80
- React15.useEffect(() => {
201
+ const previousActiveElement = React16.useRef(null);
202
+ React16.useEffect(() => {
81
203
  if (!isActive) return;
82
204
  const container = containerRef.current;
83
205
  if (!container) return;
@@ -126,8 +248,8 @@ function useFocusTrap(containerRef, isActive) {
126
248
  }, [isActive, containerRef]);
127
249
  }
128
250
  function useIsMounted() {
129
- const [mounted, setMounted] = React15.useState(false);
130
- React15.useEffect(() => {
251
+ const [mounted, setMounted] = React16.useState(false);
252
+ React16.useEffect(() => {
131
253
  setMounted(true);
132
254
  }, []);
133
255
  return mounted;
@@ -161,14 +283,13 @@ function getServerSnapshot() {
161
283
  return false;
162
284
  }
163
285
  function useReducedMotion() {
164
- return React15.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
286
+ return React16.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
165
287
  }
166
- var useSafeLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
167
288
  function useSpringHover({
168
289
  scale = 1.02,
169
290
  lift = -2
170
291
  } = {}) {
171
- const [hovered, setHovered] = React15.useState(false);
292
+ const [hovered, setHovered] = React16.useState(false);
172
293
  const reduced = useReducedMotion();
173
294
  const style = useSpring({
174
295
  transform: hovered ? `translateY(${lift}px) scale(${scale})` : "translateY(0px) scale(1)",
@@ -232,9 +353,9 @@ var Accordion = ({
232
353
  ...props
233
354
  }) => {
234
355
  const isControlled = controlledKeys !== void 0;
235
- const [internalKeys, setInternalKeys] = React15.useState(defaultOpenKeys);
356
+ const [internalKeys, setInternalKeys] = React16.useState(defaultOpenKeys);
236
357
  const open = isControlled ? controlledKeys ?? [] : internalKeys;
237
- const idPrefix = React15.useId();
358
+ const idPrefix = React16.useId();
238
359
  const toggle = (key) => {
239
360
  const isOpen = open.includes(key);
240
361
  const next = isOpen ? open.filter((k) => k !== key) : multiple ? [...open, key] : [key];
@@ -300,7 +421,7 @@ var Avatar = ({
300
421
  style,
301
422
  ...props
302
423
  }) => {
303
- const [imgFailed, setImgFailed] = React15.useState(false);
424
+ const [imgFailed, setImgFailed] = React16.useState(false);
304
425
  const showImage = src && !imgFailed;
305
426
  const initials = getInitials(name);
306
427
  return /* @__PURE__ */ jsx(
@@ -519,12 +640,12 @@ var Breadcrumb = ({
519
640
  }) }) });
520
641
  };
521
642
  var Menu = ({ items, trigger, align = "start" }) => {
522
- const [open, setOpen] = React15.useState(false);
523
- const wrapperRef = React15.useRef(null);
524
- const itemRefs = React15.useRef([]);
525
- const menuId = React15.useId();
643
+ const [open, setOpen] = React16.useState(false);
644
+ const wrapperRef = React16.useRef(null);
645
+ const itemRefs = React16.useRef([]);
646
+ const menuId = React16.useId();
526
647
  const style = useSpringPresence({ visible: open, from: "translateY(-4px)" });
527
- React15.useEffect(() => {
648
+ React16.useEffect(() => {
528
649
  if (!open) return;
529
650
  const handleClick = (e) => {
530
651
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -539,7 +660,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
539
660
  document.removeEventListener("keydown", handleEsc);
540
661
  };
541
662
  }, [open]);
542
- React15.useEffect(() => {
663
+ React16.useEffect(() => {
543
664
  const active = document.activeElement;
544
665
  if (!open || active && itemRefs.current.includes(active)) return;
545
666
  const first = items.findIndex((it) => !it.disabled);
@@ -585,7 +706,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
585
706
  break;
586
707
  }
587
708
  };
588
- const triggerWithProps = React15.cloneElement(
709
+ const triggerWithProps = React16.cloneElement(
589
710
  trigger,
590
711
  {
591
712
  onClick: () => setOpen((o) => !o),
@@ -727,6 +848,7 @@ var LocaleSwitcher = ({ locale }) => {
727
848
  const itemRefs = useRef([]);
728
849
  const menuId = useId();
729
850
  const currentOption = locale.options.find((o) => o.value === locale.current);
851
+ const currentLabel = currentOption?.label ?? locale.current.toUpperCase();
730
852
  useEffect(() => {
731
853
  if (!open) return;
732
854
  const handler = (e) => {
@@ -800,10 +922,11 @@ var LocaleSwitcher = ({ locale }) => {
800
922
  "aria-haspopup": "menu",
801
923
  "aria-expanded": open,
802
924
  "aria-controls": menuId,
925
+ "aria-label": locale.ariaLabel ?? currentLabel,
803
926
  onClick: () => setOpen((p) => !p),
804
927
  children: [
805
928
  /* @__PURE__ */ jsx(Globe, { size: iconSize.sm, "aria-hidden": "true" }),
806
- !locale.hideLabel && /* @__PURE__ */ jsx("span", { className: "nav_bar_locale_label", children: currentOption?.label ?? locale.current.toUpperCase() }),
929
+ !locale.hideLabel && /* @__PURE__ */ jsx("span", { className: "nav_bar_locale_label", children: currentLabel }),
807
930
  /* @__PURE__ */ jsx(
808
931
  ChevronDown,
809
932
  {
@@ -876,9 +999,9 @@ var Sidebar = ({
876
999
  ...props
877
1000
  }) => {
878
1001
  const isControlled = collapsedProp !== void 0;
879
- const [internalCollapsed, setInternalCollapsed] = React15.useState(defaultCollapsed);
1002
+ const [internalCollapsed, setInternalCollapsed] = React16.useState(defaultCollapsed);
880
1003
  const collapsed = isControlled ? collapsedProp : internalCollapsed;
881
- const toggle = React15.useCallback(() => {
1004
+ const toggle = React16.useCallback(() => {
882
1005
  const next = !collapsed;
883
1006
  if (!isControlled) setInternalCollapsed(next);
884
1007
  onCollapsedChange?.(next);
@@ -951,9 +1074,9 @@ var SidebarSection = ({ label, className, children, ...props }) => {
951
1074
  children
952
1075
  ] });
953
1076
  };
954
- var TabsContext = React15.createContext(null);
1077
+ var TabsContext = React16.createContext(null);
955
1078
  function useTabsContext() {
956
- const ctx = React15.useContext(TabsContext);
1079
+ const ctx = React16.useContext(TabsContext);
957
1080
  if (!ctx) {
958
1081
  throw new Error(
959
1082
  '[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>'
@@ -972,18 +1095,18 @@ var Tabs = ({
972
1095
  ...props
973
1096
  }) => {
974
1097
  const isControlled = controlledValue !== void 0;
975
- const [internalValue, setInternalValue] = React15.useState(defaultValue);
1098
+ const [internalValue, setInternalValue] = React16.useState(defaultValue);
976
1099
  const value = isControlled ? controlledValue : internalValue;
977
- const setValue = React15.useCallback(
1100
+ const setValue = React16.useCallback(
978
1101
  (v) => {
979
1102
  if (!isControlled) setInternalValue(v);
980
1103
  onValueChange?.(v);
981
1104
  },
982
1105
  [isControlled, onValueChange]
983
1106
  );
984
- const orderRef = React15.useRef([]);
985
- const [firstTabValue, setFirstTabValue] = React15.useState();
986
- const registerTab = React15.useCallback((v) => {
1107
+ const orderRef = React16.useRef([]);
1108
+ const [firstTabValue, setFirstTabValue] = React16.useState();
1109
+ const registerTab = React16.useCallback((v) => {
987
1110
  if (!orderRef.current.includes(v)) {
988
1111
  orderRef.current = [...orderRef.current, v];
989
1112
  setFirstTabValue(orderRef.current[0]);
@@ -993,8 +1116,8 @@ var Tabs = ({
993
1116
  setFirstTabValue(orderRef.current[0]);
994
1117
  };
995
1118
  }, []);
996
- const idPrefix = React15.useId();
997
- const ctx = React15.useMemo(
1119
+ const idPrefix = React16.useId();
1120
+ const ctx = React16.useMemo(
998
1121
  () => ({ value, setValue, variant, size, idPrefix, registerTab, firstTabValue }),
999
1122
  [value, setValue, variant, size, idPrefix, registerTab, firstTabValue]
1000
1123
  );
@@ -1002,9 +1125,9 @@ var Tabs = ({
1002
1125
  };
1003
1126
  var TabList = ({ ariaLabel, className, children, ...props }) => {
1004
1127
  const { variant } = useTabsContext();
1005
- const listRef = React15.useRef(null);
1006
- const [indicator, setIndicator] = React15.useState(null);
1007
- const [hasMounted, setHasMounted] = React15.useState(false);
1128
+ const listRef = React16.useRef(null);
1129
+ const [indicator, setIndicator] = React16.useState(null);
1130
+ const [hasMounted, setHasMounted] = React16.useState(false);
1008
1131
  useSafeLayoutEffect(() => {
1009
1132
  const list = listRef.current;
1010
1133
  if (!list) return;
@@ -1067,7 +1190,7 @@ var Tab = ({ value, className, children, onClick, onKeyDown, ...props }) => {
1067
1190
  const panelId = `${ctx.idPrefix}-panel-${value}`;
1068
1191
  const tabId = `${ctx.idPrefix}-tab-${value}`;
1069
1192
  const { registerTab } = ctx;
1070
- React15.useEffect(() => registerTab(value), [registerTab, value]);
1193
+ React16.useEffect(() => registerTab(value), [registerTab, value]);
1071
1194
  const handleClick = (e) => {
1072
1195
  ctx.setValue(value);
1073
1196
  onClick?.(e);
@@ -1137,14 +1260,7 @@ var TabPanel = ({
1137
1260
  }
1138
1261
  );
1139
1262
  };
1140
- var FOCUSABLE_SELECTORS2 = [
1141
- "a[href]",
1142
- "button:not([disabled])",
1143
- "input:not([disabled])",
1144
- "select:not([disabled])",
1145
- "textarea:not([disabled])",
1146
- '[tabindex]:not([tabindex="-1"])'
1147
- ].join(", ");
1263
+ var POPOVER_GAP = 8;
1148
1264
  var Popover = ({
1149
1265
  trigger,
1150
1266
  content,
@@ -1157,23 +1273,31 @@ var Popover = ({
1157
1273
  className
1158
1274
  }) => {
1159
1275
  const isControlled = openProp !== void 0;
1160
- const [internalOpen, setInternalOpen] = React15.useState(defaultOpen);
1276
+ const [internalOpen, setInternalOpen] = React16.useState(defaultOpen);
1161
1277
  const open = isControlled ? openProp : internalOpen;
1162
- const [shouldRender, setShouldRender] = React15.useState(open ?? false);
1278
+ const [shouldRender, setShouldRender] = React16.useState(open ?? false);
1163
1279
  if (open && !shouldRender) setShouldRender(true);
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(
1280
+ const wrapperRef = React16.useRef(null);
1281
+ const positionRef = React16.useRef(null);
1282
+ const popoverRef = React16.useRef(null);
1283
+ const popoverId = React16.useId();
1284
+ const setOpen = React16.useCallback(
1169
1285
  (next) => {
1170
1286
  if (!isControlled) setInternalOpen(next);
1171
1287
  onOpenChange?.(next);
1172
1288
  },
1173
1289
  [isControlled, onOpenChange]
1174
1290
  );
1291
+ const pos = useAnchoredPosition({
1292
+ open: shouldRender,
1293
+ anchorRef: wrapperRef,
1294
+ floatingRef: positionRef,
1295
+ placement,
1296
+ gap: POPOVER_GAP,
1297
+ padding: 8
1298
+ });
1175
1299
  const fromTransform = (() => {
1176
- switch (placement) {
1300
+ switch (pos.placement) {
1177
1301
  case "top":
1178
1302
  return "translateY(4px)";
1179
1303
  case "bottom":
@@ -1184,28 +1308,27 @@ var Popover = ({
1184
1308
  return "translateX(-4px)";
1185
1309
  }
1186
1310
  })();
1187
- const style = useSpringPresence({ visible: open, from: fromTransform, onExitComplete: () => setShouldRender(false) });
1188
- React15.useEffect(() => {
1189
- if (!open) return;
1190
- previousFocusRef.current = document.activeElement;
1191
- const node = popoverRef.current;
1192
- if (!node) return;
1193
- const focusable = node.querySelector(FOCUSABLE_SELECTORS2);
1194
- (focusable ?? node).focus();
1195
- }, [open]);
1196
- React15.useEffect(() => {
1311
+ const style = useSpringPresence({
1312
+ visible: open,
1313
+ from: fromTransform,
1314
+ onExitComplete: () => setShouldRender(false)
1315
+ });
1316
+ useFocusTrap(popoverRef, open);
1317
+ React16.useEffect(() => {
1197
1318
  if (!open) return;
1198
1319
  const handleClick = (e) => {
1199
- if (!wrapperRef.current?.contains(e.target)) setOpen(false);
1320
+ const target = e.target;
1321
+ if (!wrapperRef.current?.contains(target) && !popoverRef.current?.contains(target)) {
1322
+ setOpen(false);
1323
+ }
1200
1324
  };
1201
1325
  document.addEventListener("mousedown", handleClick);
1202
1326
  return () => document.removeEventListener("mousedown", handleClick);
1203
1327
  }, [open, setOpen]);
1204
1328
  useOverlayEscape(open, () => {
1205
1329
  setOpen(false);
1206
- previousFocusRef.current?.focus();
1207
1330
  });
1208
- const triggerWithProps = React15.cloneElement(
1331
+ const triggerWithProps = React16.cloneElement(
1209
1332
  trigger,
1210
1333
  {
1211
1334
  onClick: (e) => {
@@ -1220,23 +1343,43 @@ var Popover = ({
1220
1343
  );
1221
1344
  return /* @__PURE__ */ jsxs("div", { className: "popover_wrapper", ref: wrapperRef, children: [
1222
1345
  triggerWithProps,
1223
- shouldRender && /* @__PURE__ */ jsx("div", { className: cn("popover_position", `popover_placement_${placement}`), children: /* @__PURE__ */ jsx(
1224
- animated.div,
1225
- {
1226
- id: popoverId,
1227
- ref: popoverRef,
1228
- role: "dialog",
1229
- tabIndex: -1,
1230
- "aria-label": ariaLabel,
1231
- "aria-labelledby": ariaLabelledby,
1232
- style,
1233
- className: cn("popover", className),
1234
- children: content
1235
- }
1236
- ) })
1346
+ shouldRender && typeof document !== "undefined" && createPortal(
1347
+ /* @__PURE__ */ jsx(
1348
+ "div",
1349
+ {
1350
+ ref: positionRef,
1351
+ className: "popover_position",
1352
+ style: {
1353
+ position: "fixed",
1354
+ left: pos.x,
1355
+ top: pos.y,
1356
+ // 최초 측정 전(ready=false)에는 maxWidth(=0)를 걸지 않는다 - 걸면 자연 폭 대신
1357
+ // 0px 로 측정돼 첫 프레임 좌표가 어긋난다. ready 후에만 상한 적용.
1358
+ maxWidth: pos.ready ? pos.maxWidth : void 0,
1359
+ visibility: pos.ready ? void 0 : "hidden"
1360
+ },
1361
+ children: /* @__PURE__ */ jsx(
1362
+ animated.div,
1363
+ {
1364
+ id: popoverId,
1365
+ ref: popoverRef,
1366
+ role: "dialog",
1367
+ tabIndex: -1,
1368
+ "aria-label": ariaLabelledby ? ariaLabel : ariaLabel ?? "Dialog",
1369
+ "aria-labelledby": ariaLabelledby,
1370
+ style,
1371
+ className: cn("popover", className),
1372
+ children: content
1373
+ }
1374
+ )
1375
+ }
1376
+ ),
1377
+ document.body
1378
+ )
1237
1379
  ] });
1238
1380
  };
1239
1381
  Popover.displayName = "Popover";
1382
+ var TOOLTIP_GAP = 6;
1240
1383
  var Tooltip = ({
1241
1384
  content,
1242
1385
  placement = "top",
@@ -1244,33 +1387,45 @@ var Tooltip = ({
1244
1387
  disabled = false,
1245
1388
  children
1246
1389
  }) => {
1247
- const [open, setOpen] = React15.useState(false);
1248
- const timerRef = React15.useRef(null);
1249
- const tooltipId = React15.useId();
1390
+ const [open, setOpen] = React16.useState(false);
1391
+ const [shouldRender, setShouldRender] = React16.useState(false);
1392
+ if (open && !shouldRender) setShouldRender(true);
1393
+ const timerRef = React16.useRef(null);
1394
+ const tooltipId = React16.useId();
1395
+ const wrapperRef = React16.useRef(null);
1396
+ const positionRef = React16.useRef(null);
1250
1397
  const HIDE_DELAY = 120;
1251
- const show = React15.useCallback(() => {
1398
+ const show = React16.useCallback(() => {
1252
1399
  if (timerRef.current) clearTimeout(timerRef.current);
1253
1400
  timerRef.current = setTimeout(() => setOpen(true), delay);
1254
1401
  }, [delay]);
1255
- const hideNow = React15.useCallback(() => {
1402
+ const hideNow = React16.useCallback(() => {
1256
1403
  if (timerRef.current) clearTimeout(timerRef.current);
1257
1404
  setOpen(false);
1258
1405
  }, []);
1259
- const hide = React15.useCallback(() => {
1406
+ const hide = React16.useCallback(() => {
1260
1407
  if (timerRef.current) clearTimeout(timerRef.current);
1261
1408
  timerRef.current = setTimeout(() => setOpen(false), HIDE_DELAY);
1262
1409
  }, []);
1263
- const cancelHide = React15.useCallback(() => {
1410
+ const cancelHide = React16.useCallback(() => {
1264
1411
  if (timerRef.current) clearTimeout(timerRef.current);
1265
1412
  }, []);
1266
- React15.useEffect(() => {
1413
+ React16.useEffect(() => {
1267
1414
  return () => {
1268
1415
  if (timerRef.current) clearTimeout(timerRef.current);
1269
1416
  };
1270
1417
  }, []);
1271
1418
  useOverlayEscape(open, hideNow);
1419
+ const pos = useAnchoredPosition({
1420
+ open: shouldRender,
1421
+ anchorRef: wrapperRef,
1422
+ floatingRef: positionRef,
1423
+ placement,
1424
+ gap: TOOLTIP_GAP,
1425
+ padding: 8
1426
+ });
1272
1427
  const fromTransform = (() => {
1273
- switch (placement) {
1428
+ switch (pos.placement) {
1274
1429
  case "top":
1275
1430
  return "translateY(4px)";
1276
1431
  case "bottom":
@@ -1281,10 +1436,14 @@ var Tooltip = ({
1281
1436
  return "translateX(-4px)";
1282
1437
  }
1283
1438
  })();
1284
- const style = useSpringPresence({ visible: open, from: fromTransform });
1439
+ const style = useSpringPresence({
1440
+ visible: open,
1441
+ from: fromTransform,
1442
+ onExitComplete: () => setShouldRender(false)
1443
+ });
1285
1444
  const child = children;
1286
1445
  const childProps = child.props;
1287
- const trigger = React15.cloneElement(child, {
1446
+ const trigger = React16.cloneElement(child, {
1288
1447
  onMouseEnter: (e) => {
1289
1448
  childProps.onMouseEnter?.(e);
1290
1449
  show();
@@ -1305,25 +1464,29 @@ var Tooltip = ({
1305
1464
  "aria-describedby": open ? [childProps["aria-describedby"], tooltipId].filter(Boolean).join(" ") : childProps["aria-describedby"]
1306
1465
  });
1307
1466
  if (disabled) return children;
1308
- return /* @__PURE__ */ jsxs("span", { className: "tooltip_wrapper", children: [
1467
+ return /* @__PURE__ */ jsxs("span", { className: "tooltip_wrapper", ref: wrapperRef, children: [
1309
1468
  trigger,
1310
- open && /* @__PURE__ */ jsx(
1311
- "span",
1312
- {
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
- )
1326
- }
1469
+ shouldRender && typeof document !== "undefined" && createPortal(
1470
+ /* @__PURE__ */ jsx(
1471
+ "span",
1472
+ {
1473
+ ref: positionRef,
1474
+ className: "tooltip_position",
1475
+ style: {
1476
+ position: "fixed",
1477
+ left: pos.x,
1478
+ top: pos.y,
1479
+ // 최초 측정 전(ready=false)에는 maxWidth(=0)를 걸지 않는다 - 걸면 자연 폭 대신
1480
+ // 0px 로 측정돼 첫 프레임 좌표가 어긋난다. ready 후에만 상한 적용.
1481
+ maxWidth: pos.ready ? pos.maxWidth : void 0,
1482
+ visibility: pos.ready ? void 0 : "hidden"
1483
+ },
1484
+ onMouseEnter: cancelHide,
1485
+ onMouseLeave: hide,
1486
+ children: /* @__PURE__ */ jsx(animated.span, { id: tooltipId, role: "tooltip", style, className: "tooltip", children: content })
1487
+ }
1488
+ ),
1489
+ document.body
1327
1490
  )
1328
1491
  ] });
1329
1492
  };
@@ -2251,12 +2414,12 @@ var Checkbox = ({
2251
2414
  ref,
2252
2415
  ...props
2253
2416
  }) => {
2254
- const inputRef = React15.useRef(null);
2255
- React15.useImperativeHandle(
2417
+ const inputRef = React16.useRef(null);
2418
+ React16.useImperativeHandle(
2256
2419
  ref,
2257
2420
  () => inputRef.current
2258
2421
  );
2259
- React15.useEffect(() => {
2422
+ React16.useEffect(() => {
2260
2423
  if (!inputRef.current) return;
2261
2424
  inputRef.current.indeterminate = Boolean(indeterminate);
2262
2425
  }, [indeterminate]);
@@ -2314,8 +2477,8 @@ var Table = ({
2314
2477
  const isEmpty = !isLoading && data.length === 0;
2315
2478
  const getRowKey = (item, index) => rowKey?.(item) ?? String(index);
2316
2479
  const allRowKeys = selectable ? data.map((item, index) => getRowKey(item, index)) : [];
2317
- const rowClickHintId = React15.useId();
2318
- const selectedSet = React15.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
2480
+ const rowClickHintId = React16.useId();
2481
+ const selectedSet = React16.useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
2319
2482
  const selectedCount = allRowKeys.filter((key) => selectedSet.has(key)).length;
2320
2483
  const isAllSelected = allRowKeys.length > 0 && selectedCount === allRowKeys.length;
2321
2484
  const isSomeSelected = selectedCount > 0 && !isAllSelected;
@@ -2538,9 +2701,9 @@ var AlertModal = ({
2538
2701
  onClose
2539
2702
  }) => {
2540
2703
  const dismiss = onCancel ?? onClose;
2541
- const panelRef = React15.useRef(null);
2542
- const titleId = React15.useId();
2543
- const messageId = React15.useId();
2704
+ const panelRef = React16.useRef(null);
2705
+ const titleId = React16.useId();
2706
+ const messageId = React16.useId();
2544
2707
  const [shouldRender, setShouldRender] = useState(isOpen);
2545
2708
  useFocusTrap(panelRef, isOpen);
2546
2709
  useOverlayEscape(isOpen, () => dismiss());
@@ -2560,7 +2723,7 @@ var AlertModal = ({
2560
2723
  immediate: reduced,
2561
2724
  config: { tension: 280, friction: 28, clamp: !isOpen }
2562
2725
  });
2563
- React15.useEffect(() => {
2726
+ React16.useEffect(() => {
2564
2727
  if (!shouldRender) return;
2565
2728
  const body = document.body;
2566
2729
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -2687,7 +2850,7 @@ var Spinner = ({ size = 24, ariaLabel = "Loading" }) => {
2687
2850
  }
2688
2851
  );
2689
2852
  };
2690
- var ToastContext = React15.createContext(null);
2853
+ var ToastContext = React16.createContext(null);
2691
2854
  var VARIANT_ICONS = {
2692
2855
  success: /* @__PURE__ */ jsx(CheckCircle2, { size: iconSize.md }),
2693
2856
  error: /* @__PURE__ */ jsx(XCircle, { size: iconSize.md }),
@@ -2696,9 +2859,9 @@ var VARIANT_ICONS = {
2696
2859
  default: /* @__PURE__ */ jsx(Bell, { size: iconSize.md })
2697
2860
  };
2698
2861
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
2699
- const [visible, setVisible] = React15.useState(true);
2700
- const closingRef = React15.useRef(false);
2701
- const rootRef = React15.useRef(null);
2862
+ const [visible, setVisible] = React16.useState(true);
2863
+ const closingRef = React16.useRef(false);
2864
+ const rootRef = React16.useRef(null);
2702
2865
  const style = useSpringPresence({
2703
2866
  visible,
2704
2867
  from: "translateX(20px)",
@@ -2718,7 +2881,7 @@ var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
2718
2881
  onRemove(item.id);
2719
2882
  }
2720
2883
  });
2721
- const close = React15.useCallback(() => {
2884
+ const close = React16.useCallback(() => {
2722
2885
  if (closingRef.current) return;
2723
2886
  closingRef.current = true;
2724
2887
  setVisible(false);
@@ -2752,19 +2915,19 @@ var ToastProvider = ({
2752
2915
  maxCount = 5,
2753
2916
  closeAriaLabel = "Close"
2754
2917
  }) => {
2755
- const [toasts, setToasts] = React15.useState([]);
2918
+ const [toasts, setToasts] = React16.useState([]);
2756
2919
  const isMounted = useIsMounted();
2757
- const addToast = React15.useCallback(
2920
+ const addToast = React16.useCallback(
2758
2921
  (message, variant, duration = 3e3) => {
2759
2922
  const id = crypto.randomUUID();
2760
2923
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
2761
2924
  },
2762
2925
  [maxCount]
2763
2926
  );
2764
- const removeToast = React15.useCallback((id) => {
2927
+ const removeToast = React16.useCallback((id) => {
2765
2928
  setToasts((prev) => prev.filter((t) => t.id !== id));
2766
2929
  }, []);
2767
- const contextValue = React15.useMemo(() => ({ addToast }), [addToast]);
2930
+ const contextValue = React16.useMemo(() => ({ addToast }), [addToast]);
2768
2931
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
2769
2932
  children,
2770
2933
  isMounted && createPortal(
@@ -2853,6 +3016,7 @@ var Dropdown = (props) => {
2853
3016
  options,
2854
3017
  disabled,
2855
3018
  size = "md",
3019
+ variant = "outline",
2856
3020
  className,
2857
3021
  searchable = false,
2858
3022
  searchPlaceholder = "\uAC80\uC0C9\u2026",
@@ -3065,7 +3229,12 @@ var Dropdown = (props) => {
3065
3229
  const hasSelection = selectedValues.length > 0;
3066
3230
  const showValue = multiple ? hasSelection : currentOption != null;
3067
3231
  const controlText = multiple ? hasSelection ? selectedSummary(selectedValues.length) : placeholder : currentOption ? currentOption.label : placeholder;
3068
- const rootClassName = cn("dropdown", `dropdown_size_${size}`, className);
3232
+ const rootClassName = cn(
3233
+ "dropdown",
3234
+ `dropdown_size_${size}`,
3235
+ `dropdown_variant_${variant}`,
3236
+ className
3237
+ );
3069
3238
  const fieldsetClassName = cn("dropdown_fieldset", { is_open: isOpen, is_disabled: disabled });
3070
3239
  const listClassName = cn("dropdown_list", { dropdown_list_up: dropUp });
3071
3240
  const listStyle = useSpringPresence({
@@ -3198,9 +3367,9 @@ var DatePicker = ({
3198
3367
  minDateSrFormat = "Minimum date: {date}",
3199
3368
  selectableRangeUntilTodaySrText = "Selectable up to today"
3200
3369
  }) => {
3201
- const groupId = React15.useId();
3202
- const constraintId = React15.useId();
3203
- const { todayYear, todayMonth, todayDay } = React15.useMemo(() => {
3370
+ const groupId = React16.useId();
3371
+ const constraintId = React16.useId();
3372
+ const { todayYear, todayMonth, todayDay } = React16.useMemo(() => {
3204
3373
  const now = /* @__PURE__ */ new Date();
3205
3374
  return {
3206
3375
  todayYear: now.getFullYear(),
@@ -3209,7 +3378,7 @@ var DatePicker = ({
3209
3378
  };
3210
3379
  }, []);
3211
3380
  const endYear = endYearProp ?? todayYear + 10;
3212
- const parsed = React15.useMemo(() => {
3381
+ const parsed = React16.useMemo(() => {
3213
3382
  if (!value) return { year: 0, month: 0, day: 0 };
3214
3383
  const [y, m, d] = value.split("-").map(Number);
3215
3384
  return {
@@ -3218,7 +3387,7 @@ var DatePicker = ({
3218
3387
  day: d || 0
3219
3388
  };
3220
3389
  }, [value]);
3221
- const min = React15.useMemo(() => {
3390
+ const min = React16.useMemo(() => {
3222
3391
  if (!minDate) return { year: 0, month: 0, day: 0 };
3223
3392
  const [y, m, d] = minDate.split("-").map(Number);
3224
3393
  return {
@@ -3238,7 +3407,7 @@ var DatePicker = ({
3238
3407
  min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1
3239
3408
  )
3240
3409
  );
3241
- const maxDay = React15.useMemo(() => {
3410
+ const maxDay = React16.useMemo(() => {
3242
3411
  if (!year || !month) return 31;
3243
3412
  const daysInMonth = getDaysInMonth(year, month);
3244
3413
  if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
@@ -3246,28 +3415,28 @@ var DatePicker = ({
3246
3415
  }
3247
3416
  return daysInMonth;
3248
3417
  }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
3249
- const yearOptions = React15.useMemo(
3418
+ const yearOptions = React16.useMemo(
3250
3419
  () => range(startYear, maxYear).map((y) => ({
3251
3420
  value: String(y),
3252
3421
  label: String(y)
3253
3422
  })),
3254
3423
  [startYear, maxYear]
3255
3424
  );
3256
- const monthOptions = React15.useMemo(
3425
+ const monthOptions = React16.useMemo(
3257
3426
  () => range(minMonth, Math.max(minMonth, maxMonth)).map((m) => ({
3258
3427
  value: String(m),
3259
3428
  label: pad(m)
3260
3429
  })),
3261
3430
  [minMonth, maxMonth]
3262
3431
  );
3263
- const dayOptions = React15.useMemo(
3432
+ const dayOptions = React16.useMemo(
3264
3433
  () => range(minDay, Math.max(minDay, maxDay)).map((d) => ({
3265
3434
  value: String(d),
3266
3435
  label: pad(d)
3267
3436
  })),
3268
3437
  [minDay, maxDay]
3269
3438
  );
3270
- const emit = React15.useCallback(
3439
+ const emit = React16.useCallback(
3271
3440
  (yy, mm, dd) => {
3272
3441
  const cb = onValueChange ?? onChange;
3273
3442
  if (mode === "year-month") {
@@ -3279,7 +3448,7 @@ var DatePicker = ({
3279
3448
  },
3280
3449
  [mode, onValueChange, onChange]
3281
3450
  );
3282
- const handleYearChange = React15.useCallback(
3451
+ const handleYearChange = React16.useCallback(
3283
3452
  (raw) => {
3284
3453
  if (!raw) return;
3285
3454
  const newYear = Number(raw);
@@ -3296,14 +3465,14 @@ var DatePicker = ({
3296
3465
  },
3297
3466
  [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
3298
3467
  );
3299
- const handleMonthChange = React15.useCallback(
3468
+ const handleMonthChange = React16.useCallback(
3300
3469
  (raw) => {
3301
3470
  if (!raw || !year) return;
3302
3471
  emit(year, Number(raw), day || void 0);
3303
3472
  },
3304
3473
  [year, day, emit]
3305
3474
  );
3306
- const handleDayChange = React15.useCallback(
3475
+ const handleDayChange = React16.useCallback(
3307
3476
  (raw) => {
3308
3477
  if (!raw || !year || !month) return;
3309
3478
  emit(year, month, Number(raw));
@@ -3387,11 +3556,11 @@ var FileInput = ({
3387
3556
  onChange,
3388
3557
  ...props
3389
3558
  }) => {
3390
- const inputId = React15.useId();
3391
- const helperId = React15.useId();
3392
- const inputRef = React15.useRef(null);
3393
- const [previewUrls, setPreviewUrls] = React15.useState([]);
3394
- const previewUrlsRef = React15.useRef([]);
3559
+ const inputId = React16.useId();
3560
+ const helperId = React16.useId();
3561
+ const inputRef = React16.useRef(null);
3562
+ const [previewUrls, setPreviewUrls] = React16.useState([]);
3563
+ const previewUrlsRef = React16.useRef([]);
3395
3564
  const isPreviewVariant = variant === "preview";
3396
3565
  const showPreview = isPreviewVariant || preview;
3397
3566
  const handleChange = (e) => {
@@ -3432,7 +3601,7 @@ var FileInput = ({
3432
3601
  }
3433
3602
  onFiles?.(null);
3434
3603
  };
3435
- React15.useEffect(() => {
3604
+ React16.useEffect(() => {
3436
3605
  return () => {
3437
3606
  for (const url of previewUrlsRef.current) {
3438
3607
  URL.revokeObjectURL(url);
@@ -3841,12 +4010,12 @@ var OtpInput = ({
3841
4010
  ariaLabel = "OTP \uC785\uB825",
3842
4011
  className
3843
4012
  }) => {
3844
- const inputsRef = React15.useRef([]);
3845
- const isTypingRef = React15.useRef(false);
3846
- React15.useEffect(() => {
4013
+ const inputsRef = React16.useRef([]);
4014
+ const isTypingRef = React16.useRef(false);
4015
+ React16.useEffect(() => {
3847
4016
  if (autoFocus) inputsRef.current[0]?.focus();
3848
4017
  }, [autoFocus]);
3849
- const digits = React15.useMemo(() => {
4018
+ const digits = React16.useMemo(() => {
3850
4019
  const chars = value.split("").slice(0, length);
3851
4020
  while (chars.length < length) chars.push("");
3852
4021
  return chars;
@@ -3923,7 +4092,7 @@ var OtpInput = ({
3923
4092
  focusInput(nextIndex);
3924
4093
  };
3925
4094
  const rootClassName = cn("otp_input", className);
3926
- const supportingId = React15.useId();
4095
+ const supportingId = React16.useId();
3927
4096
  return (
3928
4097
  // biome-ignore lint/a11y/useSemanticElements: <fieldset> would force border/legend styles; role=group is the WAI-ARIA equivalent for OTP grouping
3929
4098
  /* @__PURE__ */ jsxs("div", { className: rootClassName, role: "group", "aria-label": ariaLabel, children: [
@@ -3967,9 +4136,9 @@ var OtpInput = ({
3967
4136
  );
3968
4137
  };
3969
4138
  OtpInput.displayName = "OtpInput";
3970
- var RadioGroupContext = React15.createContext(null);
4139
+ var RadioGroupContext = React16.createContext(null);
3971
4140
  function useRadioGroupContext() {
3972
- return React15.useContext(RadioGroupContext);
4141
+ return React16.useContext(RadioGroupContext);
3973
4142
  }
3974
4143
  var RadioGroup = ({
3975
4144
  value: controlledValue,
@@ -3987,21 +4156,21 @@ var RadioGroup = ({
3987
4156
  ...props
3988
4157
  }) => {
3989
4158
  const isControlled = controlledValue !== void 0;
3990
- const [internalValue, setInternalValue] = React15.useState(defaultValue);
4159
+ const [internalValue, setInternalValue] = React16.useState(defaultValue);
3991
4160
  const value = isControlled ? controlledValue : internalValue;
3992
- const generatedName = React15.useId();
4161
+ const generatedName = React16.useId();
3993
4162
  const name = nameProp ?? generatedName;
3994
- const idPrefix = React15.useId();
4163
+ const idPrefix = React16.useId();
3995
4164
  const labelId = label ? `${idPrefix}-label` : void 0;
3996
4165
  const helperId = supportingText ? `${idPrefix}-help` : void 0;
3997
- const onChange = React15.useCallback(
4166
+ const onChange = React16.useCallback(
3998
4167
  (next) => {
3999
4168
  if (!isControlled) setInternalValue(next);
4000
4169
  onValueChange?.(next);
4001
4170
  },
4002
4171
  [isControlled, onValueChange]
4003
4172
  );
4004
- const ctx = React15.useMemo(
4173
+ const ctx = React16.useMemo(
4005
4174
  () => ({ name, value, onChange, size, disabled }),
4006
4175
  [name, value, onChange, size, disabled]
4007
4176
  );
@@ -4212,11 +4381,13 @@ var TextField = ({
4212
4381
  showLabel = true,
4213
4382
  supportingText,
4214
4383
  error,
4384
+ success,
4215
4385
  leadingIcon,
4216
4386
  trailingIcon,
4217
4387
  clearable,
4218
4388
  fullWidth,
4219
4389
  size = "md",
4390
+ variant = "outline",
4220
4391
  className,
4221
4392
  onValueChange,
4222
4393
  onChangeAction,
@@ -4257,12 +4428,16 @@ var TextField = ({
4257
4428
  const handleClear = useCallback(() => {
4258
4429
  emit("");
4259
4430
  }, [emit]);
4431
+ const isError = !!error;
4432
+ const isSuccess = !!success && !isError;
4260
4433
  const rootClassName = cn(
4261
4434
  "text_field",
4435
+ `text_field_variant_${variant}`,
4262
4436
  size === "sm" && "text_field_size_sm",
4263
4437
  size === "lg" && "text_field_size_lg",
4264
4438
  fullWidth && "text_field_full_width",
4265
- error && "text_field_error",
4439
+ isError && "text_field_error",
4440
+ isSuccess && "text_field_success",
4266
4441
  props.disabled && "text_field_disabled",
4267
4442
  className
4268
4443
  );
@@ -4284,7 +4459,7 @@ var TextField = ({
4284
4459
  id: inputId,
4285
4460
  ref,
4286
4461
  className: "text_field_input",
4287
- "aria-invalid": !!error,
4462
+ "aria-invalid": isError,
4288
4463
  "aria-describedby": helperId,
4289
4464
  "aria-label": !showLabel ? label : void 0,
4290
4465
  ...props,
@@ -4331,7 +4506,7 @@ var Toggle = ({
4331
4506
  ...props
4332
4507
  }) => {
4333
4508
  const isControlled = checked !== void 0;
4334
- const [innerChecked, setInnerChecked] = React15.useState(!!defaultChecked);
4509
+ const [innerChecked, setInnerChecked] = React16.useState(!!defaultChecked);
4335
4510
  const isOn = isControlled ? !!checked : innerChecked;
4336
4511
  const handleToggle = (e) => {
4337
4512
  props.onClick?.(e);
@@ -4420,7 +4595,7 @@ var Pagination = ({
4420
4595
  const emit = onPageChange ?? onChange;
4421
4596
  const prevDisabled = page <= 1;
4422
4597
  const nextDisabled = page >= totalPages;
4423
- const items = React15.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
4598
+ const items = React16.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
4424
4599
  return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
4425
4600
  /* @__PURE__ */ jsx(
4426
4601
  "button",
@@ -4485,9 +4660,9 @@ var Drawer = ({
4485
4660
  className,
4486
4661
  ...props
4487
4662
  }) => {
4488
- const panelRef = React15.useRef(null);
4489
- const titleId = React15.useId();
4490
- const [shouldRender, setShouldRender] = React15.useState(open);
4663
+ const panelRef = React16.useRef(null);
4664
+ const titleId = React16.useId();
4665
+ const [shouldRender, setShouldRender] = React16.useState(open);
4491
4666
  const reduced = useReducedMotion();
4492
4667
  const isMounted = useIsMounted();
4493
4668
  useFocusTrap(panelRef, open && isMounted);
@@ -4506,7 +4681,7 @@ var Drawer = ({
4506
4681
  immediate: reduced,
4507
4682
  config: { tension: 280, friction: 28, clamp: !open }
4508
4683
  });
4509
- React15.useEffect(() => {
4684
+ React16.useEffect(() => {
4510
4685
  if (!open) return;
4511
4686
  const body = document.body;
4512
4687
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -4594,9 +4769,9 @@ var Modal = ({
4594
4769
  ariaLabel,
4595
4770
  ...props
4596
4771
  }) => {
4597
- const panelRef = React15.useRef(null);
4598
- const titleId = React15.useId();
4599
- const [shouldRender, setShouldRender] = React15.useState(open);
4772
+ const panelRef = React16.useRef(null);
4773
+ const titleId = React16.useId();
4774
+ const [shouldRender, setShouldRender] = React16.useState(open);
4600
4775
  const isMounted = useIsMounted();
4601
4776
  useFocusTrap(panelRef, open && isMounted);
4602
4777
  useOverlayEscape(open && isMounted, () => onClose?.());
@@ -4616,7 +4791,7 @@ var Modal = ({
4616
4791
  immediate: reduced,
4617
4792
  config: { tension: 280, friction: 28, clamp: !open }
4618
4793
  });
4619
- React15.useEffect(() => {
4794
+ React16.useEffect(() => {
4620
4795
  if (!open) return;
4621
4796
  const body = document.body;
4622
4797
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -4686,9 +4861,9 @@ var Modal = ({
4686
4861
  document.body
4687
4862
  );
4688
4863
  };
4689
- var ThemeContext = React15.createContext(null);
4864
+ var ThemeContext = React16.createContext(null);
4690
4865
  var useTheme = () => {
4691
- const ctx = React15.useContext(ThemeContext);
4866
+ const ctx = React16.useContext(ThemeContext);
4692
4867
  if (!ctx) {
4693
4868
  throw new Error(
4694
4869
  '[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>'
@@ -4712,15 +4887,15 @@ var ThemeProvider = ({
4712
4887
  targetSelector,
4713
4888
  children
4714
4889
  }) => {
4715
- const [mode, setModeState] = React15.useState(defaultMode);
4716
- const [systemDark, setSystemDark] = React15.useState(false);
4717
- React15.useEffect(() => {
4890
+ const [mode, setModeState] = React16.useState(defaultMode);
4891
+ const [systemDark, setSystemDark] = React16.useState(false);
4892
+ React16.useEffect(() => {
4718
4893
  setModeState(readStored(storageKey) ?? defaultMode);
4719
4894
  if (isClient) {
4720
4895
  setSystemDark(window.matchMedia("(prefers-color-scheme: dark)").matches);
4721
4896
  }
4722
4897
  }, [storageKey, defaultMode]);
4723
- React15.useEffect(() => {
4898
+ React16.useEffect(() => {
4724
4899
  if (!isClient) return;
4725
4900
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
4726
4901
  const handler = (e) => setSystemDark(e.matches);
@@ -4728,7 +4903,7 @@ var ThemeProvider = ({
4728
4903
  return () => mq.removeEventListener("change", handler);
4729
4904
  }, []);
4730
4905
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
4731
- React15.useEffect(() => {
4906
+ React16.useEffect(() => {
4732
4907
  if (!isClient) return;
4733
4908
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
4734
4909
  if (!target) return;
@@ -4738,7 +4913,7 @@ var ThemeProvider = ({
4738
4913
  target.setAttribute("data-theme", mode);
4739
4914
  }
4740
4915
  }, [mode, targetSelector]);
4741
- const setMode = React15.useCallback(
4916
+ const setMode = React16.useCallback(
4742
4917
  (next) => {
4743
4918
  setModeState(next);
4744
4919
  if (storageKey && isClient) {
@@ -4750,7 +4925,7 @@ var ThemeProvider = ({
4750
4925
  },
4751
4926
  [storageKey]
4752
4927
  );
4753
- const value = React15.useMemo(
4928
+ const value = React16.useMemo(
4754
4929
  () => ({ mode, resolved, setMode }),
4755
4930
  [mode, resolved, setMode]
4756
4931
  );
@@ -4863,4 +5038,4 @@ var Stack = ({
4863
5038
  );
4864
5039
  };
4865
5040
 
4866
- 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, ImageCropper, 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 };
5041
+ 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, ImageCropper, 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, useRadioGroupContext, useReducedMotion, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };