@bigtablet/design-system 3.1.1 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import './index.css';
3
- import * as React17 from 'react';
3
+ import * as React19 from 'react';
4
4
  import { createContext, useRef, useState, useLayoutEffect, useId, useEffect, useContext, useCallback, useMemo, Fragment } from 'react';
5
5
  import { useSpring, animated } from '@react-spring/web';
6
- import { ChevronDown, Globe, ChevronRight, ChevronLeft, Check, Image, X, XCircle, AlertTriangle, CheckCircle2, Info, Bell } from 'lucide-react';
6
+ import { ChevronDown, Globe, ChevronRight, ChevronLeft, Check, Image, X, TriangleAlert, XCircle, AlertTriangle, CheckCircle2, Info, Bell } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
8
8
  import { createPortal } from 'react-dom';
9
9
 
@@ -38,8 +38,8 @@ var FOCUSABLE_SELECTORS = [
38
38
  '[tabindex]:not([tabindex="-1"])'
39
39
  ].join(", ");
40
40
  function useFocusTrap(containerRef, isActive) {
41
- const previousActiveElement = React17.useRef(null);
42
- React17.useEffect(() => {
41
+ const previousActiveElement = React19.useRef(null);
42
+ React19.useEffect(() => {
43
43
  if (!isActive) return;
44
44
  const container = containerRef.current;
45
45
  if (!container) return;
@@ -87,11 +87,12 @@ function useFocusTrap(containerRef, isActive) {
87
87
  };
88
88
  }, [isActive, containerRef]);
89
89
  }
90
+ var useSafeLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
90
91
  function useSpringHover({
91
92
  scale = 1.02,
92
93
  lift = -2
93
94
  } = {}) {
94
- const [hovered, setHovered] = React17.useState(false);
95
+ const [hovered, setHovered] = React19.useState(false);
95
96
  const style = useSpring({
96
97
  transform: hovered ? `translateY(${lift}px) scale(${scale})` : "translateY(0px) scale(1)",
97
98
  config: { tension: 320, friction: 22 }
@@ -139,7 +140,7 @@ var Accordion = ({
139
140
  ...props
140
141
  }) => {
141
142
  const isControlled = controlledKeys !== void 0;
142
- const [internalKeys, setInternalKeys] = React17.useState(defaultOpenKeys);
143
+ const [internalKeys, setInternalKeys] = React19.useState(defaultOpenKeys);
143
144
  const open = isControlled ? controlledKeys ?? [] : internalKeys;
144
145
  const toggle = (key) => {
145
146
  const isOpen = open.includes(key);
@@ -205,7 +206,7 @@ var Avatar = ({
205
206
  style,
206
207
  ...props
207
208
  }) => {
208
- const [imgFailed, setImgFailed] = React17.useState(false);
209
+ const [imgFailed, setImgFailed] = React19.useState(false);
209
210
  const showImage = src && !imgFailed;
210
211
  const initials = getInitials(name);
211
212
  return /* @__PURE__ */ jsx(
@@ -217,7 +218,7 @@ var Avatar = ({
217
218
  "aria-label": showImage ? void 0 : name || void 0,
218
219
  ...props,
219
220
  children: showImage ? (
220
- // biome-ignore lint/performance/noImgElement: DS is framework-agnostic consumers wrap with next/image
221
+ // biome-ignore lint/performance/noImgElement: DS is framework-agnostic - consumers wrap with next/image
221
222
  /* @__PURE__ */ jsx(
222
223
  "img",
223
224
  {
@@ -235,6 +236,7 @@ var Badge = ({
235
236
  variant = "accent",
236
237
  shape = "label",
237
238
  size = "md",
239
+ appearance = "solid",
238
240
  count,
239
241
  max = 99,
240
242
  className,
@@ -256,6 +258,7 @@ var Badge = ({
256
258
  `badge_variant_${variant}`,
257
259
  `badge_shape_${shape}`,
258
260
  `badge_size_${size}`,
261
+ `badge_appearance_${appearance}`,
259
262
  className
260
263
  ),
261
264
  ...props,
@@ -391,12 +394,41 @@ var EmptyState = ({
391
394
  action && /* @__PURE__ */ jsx("div", { className: "empty_state_action", children: action })
392
395
  ] });
393
396
  };
397
+ var DEFAULT_ICON_SIZE = {
398
+ page: 48,
399
+ widget: 28
400
+ };
401
+ var ErrorState = ({
402
+ title = "\uBB38\uC81C\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4",
403
+ description,
404
+ icon,
405
+ action,
406
+ variant = "page",
407
+ className,
408
+ ...props
409
+ }) => {
410
+ const resolvedIcon = icon === null ? null : icon ?? /* @__PURE__ */ jsx(TriangleAlert, { size: DEFAULT_ICON_SIZE[variant], strokeWidth: 1.5 });
411
+ return /* @__PURE__ */ jsxs(
412
+ "div",
413
+ {
414
+ className: cn("error_state", `error_state_variant_${variant}`, className),
415
+ role: "alert",
416
+ ...props,
417
+ children: [
418
+ resolvedIcon && /* @__PURE__ */ jsx("div", { className: "error_state_icon", "aria-hidden": "true", children: resolvedIcon }),
419
+ title && /* @__PURE__ */ jsx("h3", { className: "error_state_title", children: title }),
420
+ description && /* @__PURE__ */ jsx("p", { className: "error_state_description", children: description }),
421
+ action && /* @__PURE__ */ jsx("div", { className: "error_state_action", children: action })
422
+ ]
423
+ }
424
+ );
425
+ };
394
426
  var Menu = ({ items, trigger, align = "start" }) => {
395
- const [open, setOpen] = React17.useState(false);
396
- const wrapperRef = React17.useRef(null);
397
- const menuId = React17.useId();
427
+ const [open, setOpen] = React19.useState(false);
428
+ const wrapperRef = React19.useRef(null);
429
+ const menuId = React19.useId();
398
430
  const style = useSpringPresence({ visible: open, from: "translateY(-4px)" });
399
- React17.useEffect(() => {
431
+ React19.useEffect(() => {
400
432
  if (!open) return;
401
433
  const handleClick = (e) => {
402
434
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -411,7 +443,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
411
443
  document.removeEventListener("keydown", handleEsc);
412
444
  };
413
445
  }, [open]);
414
- const triggerWithProps = React17.cloneElement(
446
+ const triggerWithProps = React19.cloneElement(
415
447
  trigger,
416
448
  {
417
449
  onClick: () => setOpen((o) => !o),
@@ -630,9 +662,9 @@ var Sidebar = ({
630
662
  ...props
631
663
  }) => {
632
664
  const isControlled = collapsedProp !== void 0;
633
- const [internalCollapsed, setInternalCollapsed] = React17.useState(defaultCollapsed);
665
+ const [internalCollapsed, setInternalCollapsed] = React19.useState(defaultCollapsed);
634
666
  const collapsed = isControlled ? collapsedProp : internalCollapsed;
635
- const toggle = React17.useCallback(() => {
667
+ const toggle = React19.useCallback(() => {
636
668
  const next = !collapsed;
637
669
  if (!isControlled) setInternalCollapsed(next);
638
670
  onCollapsedChange?.(next);
@@ -705,9 +737,9 @@ var SidebarSection = ({ label, className, children, ...props }) => {
705
737
  children
706
738
  ] });
707
739
  };
708
- var TabsContext = React17.createContext(null);
740
+ var TabsContext = React19.createContext(null);
709
741
  function useTabsContext() {
710
- const ctx = React17.useContext(TabsContext);
742
+ const ctx = React19.useContext(TabsContext);
711
743
  if (!ctx) {
712
744
  throw new Error(
713
745
  '[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>'
@@ -726,17 +758,17 @@ var Tabs = ({
726
758
  ...props
727
759
  }) => {
728
760
  const isControlled = controlledValue !== void 0;
729
- const [internalValue, setInternalValue] = React17.useState(defaultValue);
761
+ const [internalValue, setInternalValue] = React19.useState(defaultValue);
730
762
  const value = isControlled ? controlledValue : internalValue;
731
- const setValue = React17.useCallback(
763
+ const setValue = React19.useCallback(
732
764
  (v) => {
733
765
  if (!isControlled) setInternalValue(v);
734
766
  onValueChange?.(v);
735
767
  },
736
768
  [isControlled, onValueChange]
737
769
  );
738
- const idPrefix = React17.useId();
739
- const ctx = React17.useMemo(
770
+ const idPrefix = React19.useId();
771
+ const ctx = React19.useMemo(
740
772
  () => ({ value, setValue, variant, size, idPrefix }),
741
773
  [value, setValue, variant, size, idPrefix]
742
774
  );
@@ -744,10 +776,10 @@ var Tabs = ({
744
776
  };
745
777
  var TabList = ({ ariaLabel, className, children, ...props }) => {
746
778
  const { variant } = useTabsContext();
747
- const listRef = React17.useRef(null);
748
- const [indicator, setIndicator] = React17.useState(null);
749
- const [hasMounted, setHasMounted] = React17.useState(false);
750
- React17.useLayoutEffect(() => {
779
+ const listRef = React19.useRef(null);
780
+ const [indicator, setIndicator] = React19.useState(null);
781
+ const [hasMounted, setHasMounted] = React19.useState(false);
782
+ React19.useLayoutEffect(() => {
751
783
  const list = listRef.current;
752
784
  if (!list) return;
753
785
  const update = () => {
@@ -875,6 +907,112 @@ var TabPanel = ({
875
907
  }
876
908
  );
877
909
  };
910
+ var FOCUSABLE_SELECTORS2 = [
911
+ "a[href]",
912
+ "button:not([disabled])",
913
+ "input:not([disabled])",
914
+ "select:not([disabled])",
915
+ "textarea:not([disabled])",
916
+ '[tabindex]:not([tabindex="-1"])'
917
+ ].join(", ");
918
+ var Popover = ({
919
+ trigger,
920
+ content,
921
+ placement = "bottom",
922
+ open: openProp,
923
+ defaultOpen = false,
924
+ onOpenChange,
925
+ "aria-label": ariaLabel,
926
+ "aria-labelledby": ariaLabelledby,
927
+ className
928
+ }) => {
929
+ const isControlled = openProp !== void 0;
930
+ const [internalOpen, setInternalOpen] = React19.useState(defaultOpen);
931
+ const open = isControlled ? openProp : internalOpen;
932
+ const [shouldRender, setShouldRender] = React19.useState(open ?? false);
933
+ if (open && !shouldRender) setShouldRender(true);
934
+ const wrapperRef = React19.useRef(null);
935
+ const popoverRef = React19.useRef(null);
936
+ const previousFocusRef = React19.useRef(null);
937
+ const popoverId = React19.useId();
938
+ const setOpen = React19.useCallback(
939
+ (next) => {
940
+ if (!isControlled) setInternalOpen(next);
941
+ onOpenChange?.(next);
942
+ },
943
+ [isControlled, onOpenChange]
944
+ );
945
+ const fromTransform = (() => {
946
+ switch (placement) {
947
+ case "top":
948
+ return "translateY(4px)";
949
+ case "bottom":
950
+ return "translateY(-4px)";
951
+ case "left":
952
+ return "translateX(4px)";
953
+ case "right":
954
+ return "translateX(-4px)";
955
+ }
956
+ })();
957
+ const style = useSpringPresence({ visible: open, from: fromTransform, onExitComplete: () => setShouldRender(false) });
958
+ React19.useEffect(() => {
959
+ if (!open) return;
960
+ previousFocusRef.current = document.activeElement;
961
+ const node = popoverRef.current;
962
+ if (!node) return;
963
+ const focusable = node.querySelector(FOCUSABLE_SELECTORS2);
964
+ (focusable ?? node).focus();
965
+ }, [open]);
966
+ React19.useEffect(() => {
967
+ if (!open) return;
968
+ const handleClick = (e) => {
969
+ if (!wrapperRef.current?.contains(e.target)) setOpen(false);
970
+ };
971
+ const handleKeyDown = (e) => {
972
+ if (e.key === "Escape") {
973
+ setOpen(false);
974
+ previousFocusRef.current?.focus();
975
+ }
976
+ };
977
+ document.addEventListener("mousedown", handleClick);
978
+ document.addEventListener("keydown", handleKeyDown);
979
+ return () => {
980
+ document.removeEventListener("mousedown", handleClick);
981
+ document.removeEventListener("keydown", handleKeyDown);
982
+ };
983
+ }, [open, setOpen]);
984
+ const triggerWithProps = React19.cloneElement(
985
+ trigger,
986
+ {
987
+ onClick: (e) => {
988
+ trigger.props.onClick?.(e);
989
+ if (e.defaultPrevented) return;
990
+ setOpen(!open);
991
+ },
992
+ "aria-haspopup": "dialog",
993
+ "aria-expanded": open,
994
+ "aria-controls": open ? popoverId : void 0
995
+ }
996
+ );
997
+ return /* @__PURE__ */ jsxs("div", { className: "popover_wrapper", ref: wrapperRef, children: [
998
+ triggerWithProps,
999
+ shouldRender && /* @__PURE__ */ jsx("div", { className: cn("popover_position", `popover_placement_${placement}`), children: /* @__PURE__ */ jsx(
1000
+ animated.div,
1001
+ {
1002
+ id: popoverId,
1003
+ ref: popoverRef,
1004
+ role: "dialog",
1005
+ tabIndex: -1,
1006
+ "aria-label": ariaLabel,
1007
+ "aria-labelledby": ariaLabelledby,
1008
+ style,
1009
+ className: cn("popover", className),
1010
+ children: content
1011
+ }
1012
+ ) })
1013
+ ] });
1014
+ };
1015
+ Popover.displayName = "Popover";
878
1016
  var Tooltip = ({
879
1017
  content,
880
1018
  placement = "top",
@@ -882,18 +1020,18 @@ var Tooltip = ({
882
1020
  disabled = false,
883
1021
  children
884
1022
  }) => {
885
- const [open, setOpen] = React17.useState(false);
886
- const timerRef = React17.useRef(null);
887
- const tooltipId = React17.useId();
888
- const show = React17.useCallback(() => {
1023
+ const [open, setOpen] = React19.useState(false);
1024
+ const timerRef = React19.useRef(null);
1025
+ const tooltipId = React19.useId();
1026
+ const show = React19.useCallback(() => {
889
1027
  if (timerRef.current) clearTimeout(timerRef.current);
890
1028
  timerRef.current = setTimeout(() => setOpen(true), delay);
891
1029
  }, [delay]);
892
- const hide = React17.useCallback(() => {
1030
+ const hide = React19.useCallback(() => {
893
1031
  if (timerRef.current) clearTimeout(timerRef.current);
894
1032
  setOpen(false);
895
1033
  }, []);
896
- React17.useEffect(() => {
1034
+ React19.useEffect(() => {
897
1035
  return () => {
898
1036
  if (timerRef.current) clearTimeout(timerRef.current);
899
1037
  };
@@ -911,7 +1049,7 @@ var Tooltip = ({
911
1049
  }
912
1050
  })();
913
1051
  const style = useSpringPresence({ visible: open, from: fromTransform });
914
- const trigger = React17.cloneElement(children, {
1052
+ const trigger = React19.cloneElement(children, {
915
1053
  onMouseEnter: show,
916
1054
  onMouseLeave: hide,
917
1055
  onFocus: show,
@@ -988,21 +1126,12 @@ var baseColors = {
988
1126
  neutral700: "#666666",
989
1127
  neutral800: "#333333",
990
1128
  neutral900: "#121212",
1129
+ neutral925: "#141414",
1130
+ neutral950: "#0A0A0A",
991
1131
  statusError: "#EF4444",
992
1132
  statusSuccess: "#10B981",
993
1133
  statusWarning: "#F59E0B",
994
1134
  statusInfo: "#3B82F6",
995
- // Navy — Bigtablet 초기 홈페이지 컬러. #47555E 메인 navy + sky blue 패밀리.
996
- navy50: "#F2F5F8",
997
- navy100: "#DDE3E9",
998
- navy200: "#B4C2CD",
999
- navy300: "#7AA5D2",
1000
- navy400: "#5A8DCB",
1001
- navy500: "#4C7CB6",
1002
- navy600: "#47555E",
1003
- navy700: "#3D4852",
1004
- navy800: "#303841",
1005
- navy900: "#1F2630",
1006
1135
  alphaBlack5: "rgba(0, 0, 0, 0.05)",
1007
1136
  alphaBlack8: "rgba(0, 0, 0, 0.08)",
1008
1137
  alphaBlack12: "rgba(26, 26, 26, 0.12)",
@@ -1052,13 +1181,13 @@ var colors = {
1052
1181
  warning: baseColors.statusWarning,
1053
1182
  info: baseColors.statusInfo
1054
1183
  },
1055
- // Accent (Bigtablet navy) 메인 brand 검정과 함께 B2C/마케팅 강조에 사용.
1184
+ // Accent (brand 검정 기반 - light=검정, dark=흰색 반전)
1056
1185
  accent: {
1057
- subtle: baseColors.navy50,
1058
- muted: baseColors.navy100,
1059
- light: baseColors.navy300,
1060
- default: baseColors.navy600,
1061
- strong: baseColors.navy800,
1186
+ subtle: baseColors.alphaBlack5,
1187
+ muted: baseColors.alphaBlack12,
1188
+ light: baseColors.neutral400,
1189
+ default: baseColors.brandPrimary,
1190
+ strong: baseColors.neutral900,
1062
1191
  onSurface: baseColors.neutral0
1063
1192
  }
1064
1193
  };
@@ -1505,12 +1634,12 @@ var AlertModal = ({
1505
1634
  onCancel,
1506
1635
  onClose
1507
1636
  }) => {
1508
- const panelRef = React17.useRef(null);
1509
- const titleId = React17.useId();
1510
- const messageId = React17.useId();
1637
+ const panelRef = React19.useRef(null);
1638
+ const titleId = React19.useId();
1639
+ const messageId = React19.useId();
1511
1640
  const [shouldRender, setShouldRender] = useState(isOpen);
1512
1641
  useFocusTrap(panelRef, isOpen);
1513
- React17.useEffect(() => {
1642
+ React19.useEffect(() => {
1514
1643
  if (isOpen) setShouldRender(true);
1515
1644
  }, [isOpen]);
1516
1645
  const overlayStyle = useSpring({
@@ -1588,6 +1717,9 @@ var Card = ({
1588
1717
  padding = "md",
1589
1718
  bordered = false,
1590
1719
  variant = "default",
1720
+ interactive = false,
1721
+ footer,
1722
+ footerAlign = "end",
1591
1723
  className,
1592
1724
  children,
1593
1725
  ...props
@@ -1597,12 +1729,13 @@ var Card = ({
1597
1729
  `card_shadow_${shadow}`,
1598
1730
  `card_p_${padding}`,
1599
1731
  variant !== "default" && `card_variant_${variant}`,
1600
- { card_bordered: bordered },
1732
+ { card_bordered: bordered, card_interactive: interactive },
1601
1733
  className
1602
1734
  );
1603
1735
  return /* @__PURE__ */ jsxs("div", { className: cardClassName, ...props, children: [
1604
1736
  heading ? /* @__PURE__ */ jsx(HeadingTag, { className: "card_title", children: heading }) : null,
1605
- /* @__PURE__ */ jsx("div", { className: "card_body", children })
1737
+ /* @__PURE__ */ jsx("div", { className: "card_body", children }),
1738
+ footer ? /* @__PURE__ */ jsx("div", { className: cn("card_footer", `card_footer_${footerAlign}`), children: footer }) : null
1606
1739
  ] });
1607
1740
  };
1608
1741
  var Checkbox = ({
@@ -1613,12 +1746,12 @@ var Checkbox = ({
1613
1746
  ref,
1614
1747
  ...props
1615
1748
  }) => {
1616
- const inputRef = React17.useRef(null);
1617
- React17.useImperativeHandle(
1749
+ const inputRef = React19.useRef(null);
1750
+ React19.useImperativeHandle(
1618
1751
  ref,
1619
1752
  () => inputRef.current
1620
1753
  );
1621
- React17.useEffect(() => {
1754
+ React19.useEffect(() => {
1622
1755
  if (!inputRef.current) return;
1623
1756
  inputRef.current.indeterminate = Boolean(indeterminate);
1624
1757
  }, [indeterminate]);
@@ -2023,9 +2156,9 @@ var DatePicker = ({
2023
2156
  minDateSrFormat = "Minimum date: {date}",
2024
2157
  selectableRangeUntilTodaySrText = "Selectable up to today"
2025
2158
  }) => {
2026
- const groupId = React17.useId();
2027
- const constraintId = React17.useId();
2028
- const { todayYear, todayMonth, todayDay } = React17.useMemo(() => {
2159
+ const groupId = React19.useId();
2160
+ const constraintId = React19.useId();
2161
+ const { todayYear, todayMonth, todayDay } = React19.useMemo(() => {
2029
2162
  const now = /* @__PURE__ */ new Date();
2030
2163
  return {
2031
2164
  todayYear: now.getFullYear(),
@@ -2034,7 +2167,7 @@ var DatePicker = ({
2034
2167
  };
2035
2168
  }, []);
2036
2169
  const endYear = endYearProp ?? todayYear + 10;
2037
- const parsed = React17.useMemo(() => {
2170
+ const parsed = React19.useMemo(() => {
2038
2171
  if (!value) return { year: 0, month: 0, day: 0 };
2039
2172
  const [y, m, d] = value.split("-").map(Number);
2040
2173
  return {
@@ -2043,7 +2176,7 @@ var DatePicker = ({
2043
2176
  day: d || 0
2044
2177
  };
2045
2178
  }, [value]);
2046
- const min = React17.useMemo(() => {
2179
+ const min = React19.useMemo(() => {
2047
2180
  if (!minDate) return { year: 0, month: 0, day: 0 };
2048
2181
  const [y, m, d] = minDate.split("-").map(Number);
2049
2182
  return {
@@ -2063,7 +2196,7 @@ var DatePicker = ({
2063
2196
  min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1
2064
2197
  )
2065
2198
  );
2066
- const maxDay = React17.useMemo(() => {
2199
+ const maxDay = React19.useMemo(() => {
2067
2200
  if (!year || !month) return 31;
2068
2201
  const daysInMonth = getDaysInMonth(year, month);
2069
2202
  if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
@@ -2071,28 +2204,28 @@ var DatePicker = ({
2071
2204
  }
2072
2205
  return daysInMonth;
2073
2206
  }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
2074
- const yearOptions = React17.useMemo(
2207
+ const yearOptions = React19.useMemo(
2075
2208
  () => range(startYear, maxYear).map((y) => ({
2076
2209
  value: String(y),
2077
2210
  label: String(y)
2078
2211
  })),
2079
2212
  [startYear, maxYear]
2080
2213
  );
2081
- const monthOptions = React17.useMemo(
2214
+ const monthOptions = React19.useMemo(
2082
2215
  () => range(minMonth, Math.max(minMonth, maxMonth)).map((m) => ({
2083
2216
  value: String(m),
2084
2217
  label: pad(m)
2085
2218
  })),
2086
2219
  [minMonth, maxMonth]
2087
2220
  );
2088
- const dayOptions = React17.useMemo(
2221
+ const dayOptions = React19.useMemo(
2089
2222
  () => range(minDay, Math.max(minDay, maxDay)).map((d) => ({
2090
2223
  value: String(d),
2091
2224
  label: pad(d)
2092
2225
  })),
2093
2226
  [minDay, maxDay]
2094
2227
  );
2095
- const emit = React17.useCallback(
2228
+ const emit = React19.useCallback(
2096
2229
  (yy, mm, dd) => {
2097
2230
  if (mode === "year-month") {
2098
2231
  onChange(`${yy}-${pad(mm)}`);
@@ -2103,7 +2236,7 @@ var DatePicker = ({
2103
2236
  },
2104
2237
  [mode, onChange]
2105
2238
  );
2106
- const handleYearChange = React17.useCallback(
2239
+ const handleYearChange = React19.useCallback(
2107
2240
  (raw) => {
2108
2241
  if (!raw) return;
2109
2242
  const newYear = Number(raw);
@@ -2120,14 +2253,14 @@ var DatePicker = ({
2120
2253
  },
2121
2254
  [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
2122
2255
  );
2123
- const handleMonthChange = React17.useCallback(
2256
+ const handleMonthChange = React19.useCallback(
2124
2257
  (raw) => {
2125
2258
  if (!raw || !year) return;
2126
2259
  emit(year, Number(raw), day || void 0);
2127
2260
  },
2128
2261
  [year, day, emit]
2129
2262
  );
2130
- const handleDayChange = React17.useCallback(
2263
+ const handleDayChange = React19.useCallback(
2131
2264
  (raw) => {
2132
2265
  if (!raw || !year || !month) return;
2133
2266
  emit(year, month, Number(raw));
@@ -2215,10 +2348,10 @@ var FileInput = ({
2215
2348
  onChange,
2216
2349
  ...props
2217
2350
  }) => {
2218
- const inputId = React17.useId();
2219
- const helperId = React17.useId();
2220
- const inputRef = React17.useRef(null);
2221
- const [previewUrls, setPreviewUrls] = React17.useState([]);
2351
+ const inputId = React19.useId();
2352
+ const helperId = React19.useId();
2353
+ const inputRef = React19.useRef(null);
2354
+ const [previewUrls, setPreviewUrls] = React19.useState([]);
2222
2355
  const isPreviewVariant = variant === "preview";
2223
2356
  const showPreview = isPreviewVariant || preview;
2224
2357
  const handleChange = (e) => {
@@ -2256,7 +2389,7 @@ var FileInput = ({
2256
2389
  }
2257
2390
  onFiles?.(null);
2258
2391
  };
2259
- React17.useEffect(() => {
2392
+ React19.useEffect(() => {
2260
2393
  return () => {
2261
2394
  for (const url of previewUrls) {
2262
2395
  URL.revokeObjectURL(url);
@@ -2293,7 +2426,7 @@ var FileInput = ({
2293
2426
  className: "file_input_label",
2294
2427
  style: { width: previewSize, height: previewSize },
2295
2428
  children: hasImage ? (
2296
- // biome-ignore lint/performance/noImgElement: framework-agnostic DS host app should swap with next/image if needed
2429
+ // biome-ignore lint/performance/noImgElement: framework-agnostic DS - host app should swap with next/image if needed
2297
2430
  /* @__PURE__ */ jsx(
2298
2431
  "img",
2299
2432
  {
@@ -2320,7 +2453,7 @@ var FileInput = ({
2320
2453
  ),
2321
2454
  supportingText && /* @__PURE__ */ jsx("span", { id: helperId, className: "file_input_helper", children: supportingText }),
2322
2455
  !isPreviewVariant && previewUrls.length > 0 && /* @__PURE__ */ jsx("div", { className: "file_input_preview", children: previewUrls.map((url) => (
2323
- // biome-ignore lint/performance/noImgElement: framework-agnostic DS host app should swap with next/image if needed
2456
+ // biome-ignore lint/performance/noImgElement: framework-agnostic DS - host app should swap with next/image if needed
2324
2457
  /* @__PURE__ */ jsx(
2325
2458
  "img",
2326
2459
  {
@@ -2351,7 +2484,7 @@ var Hero = ({
2351
2484
  ...props
2352
2485
  }) => {
2353
2486
  const resolvedOverlay = overlay === true ? "dark" : overlay;
2354
- const isDarkOverlay = resolvedOverlay === "dark" || resolvedOverlay === "navy";
2487
+ const isDarkOverlay = resolvedOverlay === "dark";
2355
2488
  const resolvedTextColor = textColor === "auto" ? isDarkOverlay || backgroundImage && !resolvedOverlay ? "inverse" : "default" : textColor;
2356
2489
  const heroClassName = cn(
2357
2490
  "hero",
@@ -2466,7 +2599,7 @@ var ListItem = ({
2466
2599
  className
2467
2600
  );
2468
2601
  return (
2469
- // biome-ignore lint/a11y/noStaticElementInteractions: optional interactive list item role=button + tabIndex set conditionally based on onClick
2602
+ // biome-ignore lint/a11y/noStaticElementInteractions: optional interactive list item - role=button + tabIndex set conditionally based on onClick
2470
2603
  /* @__PURE__ */ jsx(
2471
2604
  "div",
2472
2605
  {
@@ -2487,10 +2620,10 @@ var ListItem = ({
2487
2620
  children: /* @__PURE__ */ jsxs("div", { className: "list_item_state_layer", children: [
2488
2621
  leadingElement && /* @__PURE__ */ jsx("div", { className: "list_item_leading", children: leadingElement }),
2489
2622
  /* @__PURE__ */ jsxs("div", { className: "list_item_content", children: [
2490
- overline && /* @__PURE__ */ jsx("span", { className: "list_item_overline", children: overline }),
2491
- /* @__PURE__ */ jsx("span", { className: "list_item_label", children: label }),
2492
- supportingText && /* @__PURE__ */ jsx("span", { className: "list_item_supporting", children: supportingText }),
2493
- metadata && /* @__PURE__ */ jsx("span", { className: "list_item_metadata", children: metadata })
2623
+ overline && /* @__PURE__ */ jsx("div", { className: "list_item_overline", children: overline }),
2624
+ /* @__PURE__ */ jsx("div", { className: "list_item_label", children: label }),
2625
+ supportingText && /* @__PURE__ */ jsx("div", { className: "list_item_supporting", children: supportingText }),
2626
+ metadata && /* @__PURE__ */ jsx("div", { className: "list_item_metadata", children: metadata })
2494
2627
  ] }),
2495
2628
  trailingElement && /* @__PURE__ */ jsx("div", { className: "list_item_trailing", children: trailingElement })
2496
2629
  ] })
@@ -2553,11 +2686,11 @@ var Modal = ({
2553
2686
  ariaLabel,
2554
2687
  ...props
2555
2688
  }) => {
2556
- const panelRef = React17.useRef(null);
2557
- const titleId = React17.useId();
2558
- const [shouldRender, setShouldRender] = React17.useState(open);
2689
+ const panelRef = React19.useRef(null);
2690
+ const titleId = React19.useId();
2691
+ const [shouldRender, setShouldRender] = React19.useState(open);
2559
2692
  useFocusTrap(panelRef, open);
2560
- React17.useEffect(() => {
2693
+ React19.useEffect(() => {
2561
2694
  if (open) setShouldRender(true);
2562
2695
  }, [open]);
2563
2696
  const overlayStyle = useSpring({
@@ -2572,15 +2705,15 @@ var Modal = ({
2572
2705
  transform: open ? "scale(1) translateY(0px)" : "scale(0.96) translateY(-4px)",
2573
2706
  config: { tension: 280, friction: 28, clamp: !open }
2574
2707
  });
2575
- const handleEscape = React17.useEffectEvent((e) => {
2708
+ const handleEscape = React19.useEffectEvent((e) => {
2576
2709
  if (e.key === "Escape") onClose?.();
2577
2710
  });
2578
- React17.useEffect(() => {
2711
+ React19.useEffect(() => {
2579
2712
  if (!open) return;
2580
2713
  document.addEventListener("keydown", handleEscape);
2581
2714
  return () => document.removeEventListener("keydown", handleEscape);
2582
2715
  }, [open]);
2583
- React17.useEffect(() => {
2716
+ React19.useEffect(() => {
2584
2717
  if (!open) return;
2585
2718
  const body = document.body;
2586
2719
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -2660,12 +2793,12 @@ var OtpInput = ({
2660
2793
  ariaLabel = "OTP \uC785\uB825",
2661
2794
  className
2662
2795
  }) => {
2663
- const inputsRef = React17.useRef([]);
2664
- const isTypingRef = React17.useRef(false);
2665
- React17.useEffect(() => {
2796
+ const inputsRef = React19.useRef([]);
2797
+ const isTypingRef = React19.useRef(false);
2798
+ React19.useEffect(() => {
2666
2799
  if (autoFocus) inputsRef.current[0]?.focus();
2667
2800
  }, [autoFocus]);
2668
- const digits = React17.useMemo(() => {
2801
+ const digits = React19.useMemo(() => {
2669
2802
  const chars = value.split("").slice(0, length);
2670
2803
  while (chars.length < length) chars.push("");
2671
2804
  return chars;
@@ -2804,7 +2937,7 @@ var Pagination = ({
2804
2937
  }) => {
2805
2938
  const prevDisabled = page <= 1;
2806
2939
  const nextDisabled = page >= totalPages;
2807
- const items = React17.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
2940
+ const items = React19.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
2808
2941
  return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
2809
2942
  /* @__PURE__ */ jsx(
2810
2943
  "button",
@@ -2849,15 +2982,110 @@ var Pagination = ({
2849
2982
  )
2850
2983
  ] });
2851
2984
  };
2852
- var Radio = ({ label, size = "md", className, ref, ...props }) => {
2853
- const rootClassName = cn(
2854
- "radio",
2855
- `radio_size_${size}`,
2856
- props.disabled && "radio_disabled",
2857
- className
2985
+ var RadioGroupContext = React19.createContext(null);
2986
+ function useRadioGroupContext() {
2987
+ return React19.useContext(RadioGroupContext);
2988
+ }
2989
+ var RadioGroup = ({
2990
+ value: controlledValue,
2991
+ defaultValue,
2992
+ onValueChange,
2993
+ name: nameProp,
2994
+ label,
2995
+ supportingText,
2996
+ error = false,
2997
+ size = "md",
2998
+ orientation = "vertical",
2999
+ disabled = false,
3000
+ className,
3001
+ children,
3002
+ ...props
3003
+ }) => {
3004
+ const isControlled = controlledValue !== void 0;
3005
+ const [internalValue, setInternalValue] = React19.useState(defaultValue);
3006
+ const value = isControlled ? controlledValue : internalValue;
3007
+ const generatedName = React19.useId();
3008
+ const name = nameProp ?? generatedName;
3009
+ const idPrefix = React19.useId();
3010
+ const labelId = label ? `${idPrefix}-label` : void 0;
3011
+ const helperId = supportingText ? `${idPrefix}-help` : void 0;
3012
+ const onChange = React19.useCallback(
3013
+ (next) => {
3014
+ if (!isControlled) setInternalValue(next);
3015
+ onValueChange?.(next);
3016
+ },
3017
+ [isControlled, onValueChange]
3018
+ );
3019
+ const ctx = React19.useMemo(
3020
+ () => ({ name, value, onChange, size, disabled }),
3021
+ [name, value, onChange, size, disabled]
2858
3022
  );
3023
+ return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
3024
+ "div",
3025
+ {
3026
+ className: cn(
3027
+ "radio_group",
3028
+ `radio_group_orientation_${orientation}`,
3029
+ error && "radio_group_error",
3030
+ className
3031
+ ),
3032
+ ...props,
3033
+ children: [
3034
+ label && /* @__PURE__ */ jsx("span", { id: labelId, className: "radio_group_label", children: label }),
3035
+ /* @__PURE__ */ jsx(
3036
+ "div",
3037
+ {
3038
+ role: "radiogroup",
3039
+ "aria-labelledby": labelId,
3040
+ "aria-describedby": helperId,
3041
+ "aria-invalid": error || void 0,
3042
+ className: "radio_group_options",
3043
+ children
3044
+ }
3045
+ ),
3046
+ supportingText && /* @__PURE__ */ jsx("div", { id: helperId, className: "radio_group_helper", children: supportingText })
3047
+ ]
3048
+ }
3049
+ ) });
3050
+ };
3051
+ RadioGroup.displayName = "RadioGroup";
3052
+ var Radio = ({
3053
+ label,
3054
+ size: sizeProp,
3055
+ className,
3056
+ ref,
3057
+ value,
3058
+ checked,
3059
+ onChange,
3060
+ name: nameProp,
3061
+ disabled: disabledProp,
3062
+ ...props
3063
+ }) => {
3064
+ const group = useRadioGroupContext();
3065
+ const size = sizeProp ?? group?.size ?? "md";
3066
+ const name = nameProp ?? group?.name;
3067
+ const disabled = disabledProp ?? group?.disabled;
3068
+ const resolvedChecked = group ? group.value === value : checked;
3069
+ const handleChange = (event) => {
3070
+ if (group && value !== void 0) group.onChange(String(value));
3071
+ onChange?.(event);
3072
+ };
3073
+ const rootClassName = cn("radio", `radio_size_${size}`, disabled && "radio_disabled", className);
2859
3074
  return /* @__PURE__ */ jsxs("label", { className: rootClassName, children: [
2860
- /* @__PURE__ */ jsx("input", { ref, type: "radio", className: "radio_input", ...props }),
3075
+ /* @__PURE__ */ jsx(
3076
+ "input",
3077
+ {
3078
+ ref,
3079
+ type: "radio",
3080
+ className: "radio_input",
3081
+ value,
3082
+ name,
3083
+ disabled,
3084
+ checked: resolvedChecked,
3085
+ onChange: handleChange,
3086
+ ...props
3087
+ }
3088
+ ),
2861
3089
  /* @__PURE__ */ jsx("span", { className: "radio_state_layer", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "radio_dot" }) }),
2862
3090
  label ? /* @__PURE__ */ jsx("span", { className: "radio_label", children: label }) : null
2863
3091
  ] });
@@ -2986,9 +3214,9 @@ var Table = ({
2986
3214
  isEmpty && /* @__PURE__ */ jsx("div", { className: "table_empty", role: "status", children: emptyMessage })
2987
3215
  ] });
2988
3216
  };
2989
- var ThemeContext = React17.createContext(null);
3217
+ var ThemeContext = React19.createContext(null);
2990
3218
  var useTheme = () => {
2991
- const ctx = React17.useContext(ThemeContext);
3219
+ const ctx = React19.useContext(ThemeContext);
2992
3220
  if (!ctx) {
2993
3221
  throw new Error(
2994
3222
  '[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>'
@@ -3012,14 +3240,14 @@ var ThemeProvider = ({
3012
3240
  targetSelector,
3013
3241
  children
3014
3242
  }) => {
3015
- const [mode, setModeState] = React17.useState(() => {
3243
+ const [mode, setModeState] = React19.useState(() => {
3016
3244
  return readStored(storageKey) ?? defaultMode;
3017
3245
  });
3018
- const [systemDark, setSystemDark] = React17.useState(() => {
3246
+ const [systemDark, setSystemDark] = React19.useState(() => {
3019
3247
  if (!isClient) return false;
3020
3248
  return window.matchMedia("(prefers-color-scheme: dark)").matches;
3021
3249
  });
3022
- React17.useEffect(() => {
3250
+ React19.useEffect(() => {
3023
3251
  if (!isClient) return;
3024
3252
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
3025
3253
  const handler = (e) => setSystemDark(e.matches);
@@ -3027,7 +3255,7 @@ var ThemeProvider = ({
3027
3255
  return () => mq.removeEventListener("change", handler);
3028
3256
  }, []);
3029
3257
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
3030
- React17.useEffect(() => {
3258
+ React19.useEffect(() => {
3031
3259
  if (!isClient) return;
3032
3260
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
3033
3261
  if (!target) return;
@@ -3037,7 +3265,7 @@ var ThemeProvider = ({
3037
3265
  target.setAttribute("data-theme", mode);
3038
3266
  }
3039
3267
  }, [mode, targetSelector]);
3040
- const setMode = React17.useCallback(
3268
+ const setMode = React19.useCallback(
3041
3269
  (next) => {
3042
3270
  setModeState(next);
3043
3271
  if (storageKey && isClient) {
@@ -3049,7 +3277,7 @@ var ThemeProvider = ({
3049
3277
  },
3050
3278
  [storageKey]
3051
3279
  );
3052
- const value = React17.useMemo(
3280
+ const value = React19.useMemo(
3053
3281
  () => ({ mode, resolved, setMode }),
3054
3282
  [mode, resolved, setMode]
3055
3283
  );
@@ -3069,6 +3297,7 @@ var TextField = ({
3069
3297
  size = "md",
3070
3298
  className,
3071
3299
  onChangeAction,
3300
+ imeStrategy = "delayed",
3072
3301
  value,
3073
3302
  defaultValue,
3074
3303
  transformValue,
@@ -3084,15 +3313,27 @@ var TextField = ({
3084
3313
  () => applyTransform(value ?? defaultValue ?? "")
3085
3314
  );
3086
3315
  const isComposingRef = useRef(false);
3087
- useEffect(() => {
3088
- if (!isControlled) return;
3089
- const nextValue = value ?? "";
3090
- setInnerValue(transformValue ? transformValue(nextValue) : nextValue);
3091
- }, [isControlled, value, transformValue]);
3316
+ const lastEmittedValueRef = useRef(innerValue);
3317
+ const [prevValue, setPrevValue] = useState(value);
3318
+ if (isControlled && value !== prevValue && !isComposingRef.current) {
3319
+ setPrevValue(value);
3320
+ const nextValue = applyTransform(value ?? "");
3321
+ setInnerValue(nextValue);
3322
+ lastEmittedValueRef.current = nextValue;
3323
+ }
3324
+ const emit = useCallback(
3325
+ (nextValue) => {
3326
+ setInnerValue(nextValue);
3327
+ if (nextValue !== lastEmittedValueRef.current) {
3328
+ lastEmittedValueRef.current = nextValue;
3329
+ onChangeAction?.(nextValue);
3330
+ }
3331
+ },
3332
+ [onChangeAction]
3333
+ );
3092
3334
  const handleClear = useCallback(() => {
3093
- setInnerValue("");
3094
- onChangeAction?.("");
3095
- }, [onChangeAction]);
3335
+ emit("");
3336
+ }, [emit]);
3096
3337
  const rootClassName = cn(
3097
3338
  "text_field",
3098
3339
  size === "sm" && "text_field_size_sm",
@@ -3130,20 +3371,19 @@ var TextField = ({
3130
3371
  },
3131
3372
  onCompositionEnd: (event) => {
3132
3373
  isComposingRef.current = false;
3133
- const rawValue = event.currentTarget.value;
3134
- const nextValue = applyTransform(rawValue);
3135
- setInnerValue(nextValue);
3136
- onChangeAction?.(nextValue);
3374
+ emit(applyTransform(event.currentTarget.value));
3137
3375
  },
3138
3376
  onChange: (event) => {
3139
3377
  const rawValue = event.target.value;
3140
3378
  if (isComposingRef.current) {
3141
3379
  setInnerValue(rawValue);
3380
+ if (imeStrategy === "immediate" && rawValue !== lastEmittedValueRef.current) {
3381
+ lastEmittedValueRef.current = rawValue;
3382
+ onChangeAction?.(rawValue);
3383
+ }
3142
3384
  return;
3143
3385
  }
3144
- const nextValue = applyTransform(rawValue);
3145
- setInnerValue(nextValue);
3146
- onChangeAction?.(nextValue);
3386
+ emit(applyTransform(rawValue));
3147
3387
  }
3148
3388
  }
3149
3389
  )
@@ -3155,7 +3395,135 @@ var TextField = ({
3155
3395
  ] });
3156
3396
  };
3157
3397
  TextField.displayName = "TextField";
3158
- var ToastContext = React17.createContext(null);
3398
+ var LINE_HEIGHT_PX = {
3399
+ sm: 20,
3400
+ md: 20,
3401
+ lg: 24
3402
+ };
3403
+ var Textarea = ({
3404
+ id,
3405
+ label,
3406
+ showLabel = true,
3407
+ supportingText,
3408
+ error,
3409
+ fullWidth,
3410
+ size = "md",
3411
+ className,
3412
+ onChangeAction,
3413
+ imeStrategy = "delayed",
3414
+ value,
3415
+ defaultValue,
3416
+ transformValue,
3417
+ rows = 3,
3418
+ minRows,
3419
+ maxRows,
3420
+ showCounter,
3421
+ resize = "vertical",
3422
+ maxLength,
3423
+ ref,
3424
+ ...props
3425
+ }) => {
3426
+ const generatedId = useId();
3427
+ const inputId = id ?? generatedId;
3428
+ const helperId = supportingText ? `${inputId}-help` : void 0;
3429
+ const isControlled = value !== void 0;
3430
+ const applyTransform = (nextValue) => transformValue ? transformValue(nextValue) : nextValue;
3431
+ const [innerValue, setInnerValue] = useState(
3432
+ () => applyTransform(value ?? defaultValue ?? "")
3433
+ );
3434
+ const isComposingRef = useRef(false);
3435
+ const innerRef = useRef(null);
3436
+ const lastEmittedValueRef = useRef(innerValue);
3437
+ const autoGrow = minRows !== void 0 || maxRows !== void 0;
3438
+ const [prevValue, setPrevValue] = useState(value);
3439
+ if (isControlled && value !== prevValue && !isComposingRef.current) {
3440
+ setPrevValue(value);
3441
+ const nextValue = applyTransform(value ?? "");
3442
+ setInnerValue(nextValue);
3443
+ lastEmittedValueRef.current = nextValue;
3444
+ }
3445
+ const setRefs = useCallback(
3446
+ (node) => {
3447
+ innerRef.current = node;
3448
+ if (typeof ref === "function") ref(node);
3449
+ else if (ref) ref.current = node;
3450
+ },
3451
+ [ref]
3452
+ );
3453
+ useSafeLayoutEffect(() => {
3454
+ if (!autoGrow) return;
3455
+ const el = innerRef.current;
3456
+ if (!el) return;
3457
+ const lh2 = LINE_HEIGHT_PX[size];
3458
+ const minH = minRows ? minRows * lh2 : 0;
3459
+ const maxH = maxRows ? maxRows * lh2 : Number.POSITIVE_INFINITY;
3460
+ el.style.height = "auto";
3461
+ const next = Math.min(Math.max(el.scrollHeight, minH), maxH);
3462
+ el.style.height = `${next}px`;
3463
+ el.style.overflowY = el.scrollHeight > maxH ? "auto" : "hidden";
3464
+ }, [innerValue, autoGrow, size, minRows, maxRows]);
3465
+ const rootClassName = cn(
3466
+ "textarea",
3467
+ size === "sm" && "textarea_size_sm",
3468
+ size === "lg" && "textarea_size_lg",
3469
+ fullWidth && "textarea_full_width",
3470
+ error && "textarea_error",
3471
+ props.disabled && "textarea_disabled",
3472
+ className
3473
+ );
3474
+ const counterText = showCounter && maxLength !== void 0 ? `${innerValue.length}/${maxLength}` : showCounter ? String(innerValue.length) : null;
3475
+ const emit = (nextValue) => {
3476
+ setInnerValue(nextValue);
3477
+ if (nextValue !== lastEmittedValueRef.current) {
3478
+ lastEmittedValueRef.current = nextValue;
3479
+ onChangeAction?.(nextValue);
3480
+ }
3481
+ };
3482
+ return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
3483
+ label && showLabel && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "textarea_label", children: label }),
3484
+ /* @__PURE__ */ jsx("div", { className: "textarea_container", children: /* @__PURE__ */ jsx("div", { className: "textarea_input_wrap", children: /* @__PURE__ */ jsx(
3485
+ "textarea",
3486
+ {
3487
+ id: inputId,
3488
+ ref: setRefs,
3489
+ className: "textarea_input",
3490
+ style: { resize: autoGrow ? "none" : resize },
3491
+ rows: autoGrow ? minRows ?? rows : rows,
3492
+ maxLength,
3493
+ "aria-invalid": !!error,
3494
+ "aria-describedby": helperId,
3495
+ "aria-label": !showLabel ? label : void 0,
3496
+ ...props,
3497
+ value: innerValue,
3498
+ onCompositionStart: () => {
3499
+ isComposingRef.current = true;
3500
+ },
3501
+ onCompositionEnd: (event) => {
3502
+ isComposingRef.current = false;
3503
+ emit(applyTransform(event.currentTarget.value));
3504
+ },
3505
+ onChange: (event) => {
3506
+ const rawValue = event.target.value;
3507
+ if (isComposingRef.current) {
3508
+ setInnerValue(rawValue);
3509
+ if (imeStrategy === "immediate" && rawValue !== lastEmittedValueRef.current) {
3510
+ lastEmittedValueRef.current = rawValue;
3511
+ onChangeAction?.(rawValue);
3512
+ }
3513
+ return;
3514
+ }
3515
+ emit(applyTransform(rawValue));
3516
+ }
3517
+ }
3518
+ ) }) }),
3519
+ (supportingText || counterText) && /* @__PURE__ */ jsxs("div", { className: "textarea_footer", children: [
3520
+ supportingText ? /* @__PURE__ */ jsx("div", { id: helperId, className: "textarea_helper", children: supportingText }) : /* @__PURE__ */ jsx("span", {}),
3521
+ counterText && /* @__PURE__ */ jsx("div", { className: "textarea_counter", "aria-hidden": "true", children: counterText })
3522
+ ] })
3523
+ ] });
3524
+ };
3525
+ Textarea.displayName = "Textarea";
3526
+ var ToastContext = React19.createContext(null);
3159
3527
  var VARIANT_ICONS = {
3160
3528
  success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
3161
3529
  error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
@@ -3164,15 +3532,15 @@ var VARIANT_ICONS = {
3164
3532
  default: /* @__PURE__ */ jsx(Bell, { size: 18 })
3165
3533
  };
3166
3534
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3167
- const [visible, setVisible] = React17.useState(true);
3168
- const closingRef = React17.useRef(false);
3535
+ const [visible, setVisible] = React19.useState(true);
3536
+ const closingRef = React19.useRef(false);
3169
3537
  const style = useSpringPresence({
3170
3538
  visible,
3171
3539
  from: "translateX(20px)",
3172
3540
  // 우측에서 슬라이드 인
3173
3541
  onExitComplete: () => onRemove(item.id)
3174
3542
  });
3175
- const close = React17.useCallback(() => {
3543
+ const close = React19.useCallback(() => {
3176
3544
  if (closingRef.current) return;
3177
3545
  closingRef.current = true;
3178
3546
  setVisible(false);
@@ -3205,19 +3573,19 @@ var ToastProvider = ({
3205
3573
  maxCount = 5,
3206
3574
  closeAriaLabel = "Close"
3207
3575
  }) => {
3208
- const [toasts, setToasts] = React17.useState([]);
3209
- const [isMounted, setIsMounted] = React17.useState(false);
3210
- React17.useEffect(() => {
3576
+ const [toasts, setToasts] = React19.useState([]);
3577
+ const [isMounted, setIsMounted] = React19.useState(false);
3578
+ React19.useEffect(() => {
3211
3579
  setIsMounted(true);
3212
3580
  }, []);
3213
- const addToast = React17.useCallback(
3581
+ const addToast = React19.useCallback(
3214
3582
  (message, variant, duration = 3e3) => {
3215
3583
  const id = crypto.randomUUID();
3216
3584
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
3217
3585
  },
3218
3586
  [maxCount]
3219
3587
  );
3220
- const removeToast = React17.useCallback((id) => {
3588
+ const removeToast = React19.useCallback((id) => {
3221
3589
  setToasts((prev) => prev.filter((t) => t.id !== id));
3222
3590
  }, []);
3223
3591
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { addToast }, children: [
@@ -3279,7 +3647,7 @@ var Toggle = ({
3279
3647
  ...props
3280
3648
  }) => {
3281
3649
  const isControlled = checked !== void 0;
3282
- const [innerChecked, setInnerChecked] = React17.useState(!!defaultChecked);
3650
+ const [innerChecked, setInnerChecked] = React19.useState(!!defaultChecked);
3283
3651
  const isOn = isControlled ? !!checked : innerChecked;
3284
3652
  const handleToggle = () => {
3285
3653
  if (disabled) return;
@@ -3441,4 +3809,4 @@ var Grid = ({
3441
3809
  );
3442
3810
  };
3443
3811
 
3444
- export { Accordion, AlertProvider, Avatar, Badge, BottomNav, BottomNavItem, BottomNavSpacer, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Dropdown, EmptyState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Radio, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
3812
+ export { Accordion, AlertProvider, Avatar, Badge, BottomNav, BottomNavItem, BottomNavSpacer, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Dropdown, EmptyState, ErrorState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Popover, Radio, RadioGroup, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, Textarea, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };