@x-plat/design-system 0.10.0 → 0.12.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.
Files changed (115) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +14 -5
  2. package/dist/components/AutoResizeTextArea/index.css +15 -0
  3. package/dist/components/AutoResizeTextArea/index.d.cts +4 -0
  4. package/dist/components/AutoResizeTextArea/index.d.ts +4 -0
  5. package/dist/components/AutoResizeTextArea/index.js +14 -5
  6. package/dist/components/Badge/index.d.cts +1 -1
  7. package/dist/components/Badge/index.d.ts +1 -1
  8. package/dist/components/ChatInput/index.cjs +20 -7
  9. package/dist/components/ChatInput/index.css +15 -0
  10. package/dist/components/ChatInput/index.d.cts +4 -0
  11. package/dist/components/ChatInput/index.d.ts +4 -0
  12. package/dist/components/ChatInput/index.js +20 -7
  13. package/dist/components/CheckBox/index.cjs +8 -3
  14. package/dist/components/CheckBox/index.css +15 -0
  15. package/dist/components/CheckBox/index.d.cts +1 -1
  16. package/dist/components/CheckBox/index.d.ts +1 -1
  17. package/dist/components/CheckBox/index.js +8 -3
  18. package/dist/components/DatePicker/index.cjs +179 -64
  19. package/dist/components/DatePicker/index.css +19 -0
  20. package/dist/components/DatePicker/index.js +179 -64
  21. package/dist/components/Drawer/index.cjs +83 -4
  22. package/dist/components/Drawer/index.js +83 -4
  23. package/dist/components/Dropdown/index.cjs +57 -16
  24. package/dist/components/Dropdown/index.css +3 -0
  25. package/dist/components/Dropdown/index.js +57 -16
  26. package/dist/components/Editor/index.cjs +779 -50
  27. package/dist/components/Editor/index.css +100 -0
  28. package/dist/components/Editor/index.d.cts +1 -1
  29. package/dist/components/Editor/index.d.ts +1 -1
  30. package/dist/components/Editor/index.js +779 -50
  31. package/dist/components/FieldMessage/index.cjs +8 -3
  32. package/dist/components/FieldMessage/index.css +15 -0
  33. package/dist/components/FieldMessage/index.d.cts +11 -4
  34. package/dist/components/FieldMessage/index.d.ts +11 -4
  35. package/dist/components/FieldMessage/index.js +8 -3
  36. package/dist/components/FileUpload/index.cjs +8 -3
  37. package/dist/components/FileUpload/index.css +15 -0
  38. package/dist/components/FileUpload/index.js +8 -3
  39. package/dist/components/ImageSelector/index.cjs +8 -3
  40. package/dist/components/ImageSelector/index.css +15 -0
  41. package/dist/components/ImageSelector/index.js +8 -3
  42. package/dist/components/Input/index.cjs +15 -5
  43. package/dist/components/Input/index.css +15 -0
  44. package/dist/components/Input/index.d.cts +4 -0
  45. package/dist/components/Input/index.d.ts +4 -0
  46. package/dist/components/Input/index.js +15 -5
  47. package/dist/components/Modal/index.cjs +84 -5
  48. package/dist/components/Modal/index.js +84 -5
  49. package/dist/components/NotificationBadge/index.d.cts +1 -1
  50. package/dist/components/NotificationBadge/index.d.ts +1 -1
  51. package/dist/components/Pagination/index.d.cts +1 -1
  52. package/dist/components/Pagination/index.d.ts +1 -1
  53. package/dist/components/PopOver/index.cjs +55 -13
  54. package/dist/components/PopOver/index.css +3 -0
  55. package/dist/components/PopOver/index.js +55 -13
  56. package/dist/components/Progress/index.d.cts +1 -1
  57. package/dist/components/Progress/index.d.ts +1 -1
  58. package/dist/components/Radio/index.cjs +8 -3
  59. package/dist/components/Radio/index.css +15 -0
  60. package/dist/components/Radio/index.d.cts +1 -1
  61. package/dist/components/Radio/index.d.ts +1 -1
  62. package/dist/components/Radio/index.js +8 -3
  63. package/dist/components/Select/index.cjs +120 -39
  64. package/dist/components/Select/index.css +57 -0
  65. package/dist/components/Select/index.d.cts +11 -0
  66. package/dist/components/Select/index.d.ts +11 -0
  67. package/dist/components/Select/index.js +120 -39
  68. package/dist/components/Spinner/index.d.cts +1 -1
  69. package/dist/components/Spinner/index.d.ts +1 -1
  70. package/dist/components/Stat/index.cjs +84 -0
  71. package/dist/components/Stat/index.css +96 -0
  72. package/dist/components/Stat/index.d.cts +37 -0
  73. package/dist/components/Stat/index.d.ts +37 -0
  74. package/dist/components/Stat/index.js +57 -0
  75. package/dist/components/Steps/index.d.cts +1 -1
  76. package/dist/components/Steps/index.d.ts +1 -1
  77. package/dist/components/Switch/index.cjs +14 -5
  78. package/dist/components/Switch/index.css +15 -0
  79. package/dist/components/Switch/index.d.cts +6 -1
  80. package/dist/components/Switch/index.d.ts +6 -1
  81. package/dist/components/Switch/index.js +14 -5
  82. package/dist/components/Tab/index.cjs +49 -25
  83. package/dist/components/Tab/index.css +4 -0
  84. package/dist/components/Tab/index.js +49 -25
  85. package/dist/components/TextArea/index.cjs +14 -4
  86. package/dist/components/TextArea/index.css +15 -0
  87. package/dist/components/TextArea/index.d.cts +4 -0
  88. package/dist/components/TextArea/index.d.ts +4 -0
  89. package/dist/components/TextArea/index.js +14 -4
  90. package/dist/components/TimePicker/index.cjs +461 -0
  91. package/dist/components/TimePicker/index.css +175 -0
  92. package/dist/components/TimePicker/index.d.cts +42 -0
  93. package/dist/components/TimePicker/index.d.ts +42 -0
  94. package/dist/components/TimePicker/index.js +424 -0
  95. package/dist/components/index.cjs +1500 -398
  96. package/dist/components/index.css +385 -0
  97. package/dist/components/index.d.cts +3 -1
  98. package/dist/components/index.d.ts +3 -1
  99. package/dist/components/index.js +1498 -398
  100. package/dist/index.cjs +1524 -422
  101. package/dist/index.css +385 -0
  102. package/dist/index.d.cts +3 -1
  103. package/dist/index.d.ts +3 -1
  104. package/dist/index.js +1522 -422
  105. package/dist/status-njWqQWFV.d.cts +20 -0
  106. package/dist/status-njWqQWFV.d.ts +20 -0
  107. package/guidelines/AGENT_PROMPT.md +3 -3
  108. package/guidelines/Guidelines.md +3 -0
  109. package/guidelines/components/editor.md +141 -0
  110. package/guidelines/components/form.md +19 -0
  111. package/guidelines/components/stat.md +31 -0
  112. package/guidelines/components/time-picker.md +49 -0
  113. package/package.json +1 -1
  114. package/dist/status-g_KP3qT6.d.cts +0 -8
  115. package/dist/status-g_KP3qT6.d.ts +0 -8
@@ -1532,11 +1532,16 @@ var FieldMessage = (props) => {
1532
1532
  FieldMessage.displayName = "FieldMessage";
1533
1533
  var FieldMessage_default = FieldMessage;
1534
1534
  var Field = (props) => {
1535
- const { validations, children } = props;
1536
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx297(Fragment, { children });
1535
+ const { label, required = false, htmlFor, validations, children } = props;
1536
+ const hasMessages = !!validations && validations.length > 0;
1537
+ if (!label && !hasMessages) return /* @__PURE__ */ jsx297(Fragment, { children });
1537
1538
  return /* @__PURE__ */ jsxs191("div", { className: "lib-xplat-field", children: [
1539
+ label && /* @__PURE__ */ jsxs191("label", { className: "lib-xplat-field-label", htmlFor, children: [
1540
+ label,
1541
+ required && /* @__PURE__ */ jsx297("span", { className: "required", "aria-hidden": "true", children: "*" })
1542
+ ] }),
1538
1543
  children,
1539
- /* @__PURE__ */ jsx297(FieldMessage, { validations })
1544
+ hasMessages && /* @__PURE__ */ jsx297(FieldMessage, { validations })
1540
1545
  ] });
1541
1546
  };
1542
1547
  Field.displayName = "Field";
@@ -1547,7 +1552,9 @@ import { jsx as jsx298 } from "react/jsx-runtime";
1547
1552
  var MAX_HEIGHT = 400;
1548
1553
  var AutoResizeTextArea = React2.forwardRef(
1549
1554
  (props, ref) => {
1550
- const { value, className, onChange, disabled, bare = false, validations, ...textareaProps } = props;
1555
+ const { value, className, onChange, disabled, bare = false, validations, label, required, ...textareaProps } = props;
1556
+ const autoId = React2.useId();
1557
+ const fieldId = props.id ?? autoId;
1551
1558
  const localRef = React2.useRef(null);
1552
1559
  const setRefs = (el) => {
1553
1560
  localRef.current = el;
@@ -1581,6 +1588,7 @@ var AutoResizeTextArea = React2.forwardRef(
1581
1588
  "aria-invalid": hasError(validations) || void 0,
1582
1589
  ...textareaProps,
1583
1590
  ref: setRefs,
1591
+ id: fieldId,
1584
1592
  className,
1585
1593
  disabled,
1586
1594
  value,
@@ -1594,12 +1602,13 @@ var AutoResizeTextArea = React2.forwardRef(
1594
1602
  "aria-invalid": hasError(validations) || void 0,
1595
1603
  ...textareaProps,
1596
1604
  ref: setRefs,
1605
+ id: fieldId,
1597
1606
  disabled,
1598
1607
  value,
1599
1608
  onChange: handleChange
1600
1609
  }
1601
1610
  ) }) });
1602
- return /* @__PURE__ */ jsx298(Field, { validations, children: control });
1611
+ return /* @__PURE__ */ jsx298(Field, { validations, label, required, htmlFor: fieldId, children: control });
1603
1612
  }
1604
1613
  );
1605
1614
  AutoResizeTextArea.displayName = "AutoResizeTextArea";
@@ -2817,8 +2826,11 @@ var ChatInput = React7.forwardRef(
2817
2826
  buttonType = "primary",
2818
2827
  onSubmit,
2819
2828
  onChange,
2820
- validations
2829
+ validations,
2830
+ label,
2831
+ required
2821
2832
  } = props;
2833
+ const fieldId = React7.useId();
2822
2834
  const isControlled = valueProp !== void 0;
2823
2835
  const [internalValue, setInternalValue] = React7.useState("");
2824
2836
  const value = isControlled ? valueProp : internalValue;
@@ -2843,6 +2855,7 @@ var ChatInput = React7.forwardRef(
2843
2855
  /* @__PURE__ */ jsx308(
2844
2856
  AutoResizeTextArea_default,
2845
2857
  {
2858
+ id: fieldId,
2846
2859
  ref,
2847
2860
  bare: true,
2848
2861
  className: "chat-input-textarea",
@@ -2867,7 +2880,7 @@ var ChatInput = React7.forwardRef(
2867
2880
  }
2868
2881
  )
2869
2882
  ] });
2870
- return /* @__PURE__ */ jsx308(Field, { validations, children: control });
2883
+ return /* @__PURE__ */ jsx308(Field, { validations, label, required, htmlFor: fieldId, children: control });
2871
2884
  }
2872
2885
  );
2873
2886
  ChatInput.displayName = "ChatInput";
@@ -2938,7 +2951,7 @@ Chip.displayName = "Chip";
2938
2951
  var Chip_default = Chip;
2939
2952
 
2940
2953
  // src/components/DatePicker/InputDatePicker/index.tsx
2941
- import React12 from "react";
2954
+ import React13 from "react";
2942
2955
 
2943
2956
  // src/components/Input/Input.tsx
2944
2957
  import React8 from "react";
@@ -3036,8 +3049,12 @@ var Input = React8.forwardRef((props, ref) => {
3036
3049
  suffix,
3037
3050
  disabled,
3038
3051
  validations,
3052
+ label,
3053
+ required,
3039
3054
  ...inputProps
3040
3055
  } = props;
3056
+ const autoId = React8.useId();
3057
+ const fieldId = inputProps.id ?? autoId;
3041
3058
  const localValue = formatValue(type, value);
3042
3059
  const handleChange = (e) => {
3043
3060
  let val = e.target.value;
@@ -3053,7 +3070,7 @@ var Input = React8.forwardRef((props, ref) => {
3053
3070
  onChange(event);
3054
3071
  }
3055
3072
  };
3056
- return /* @__PURE__ */ jsxs199("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
3073
+ return /* @__PURE__ */ jsx311(Field, { label, required, htmlFor: fieldId, children: /* @__PURE__ */ jsxs199("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
3057
3074
  /* @__PURE__ */ jsxs199(
3058
3075
  "div",
3059
3076
  {
@@ -3064,6 +3081,7 @@ var Input = React8.forwardRef((props, ref) => {
3064
3081
  {
3065
3082
  ...inputProps,
3066
3083
  ref,
3084
+ id: fieldId,
3067
3085
  disabled,
3068
3086
  type: type === "number" ? "text" : type,
3069
3087
  value: localValue,
@@ -3076,7 +3094,7 @@ var Input = React8.forwardRef((props, ref) => {
3076
3094
  }
3077
3095
  ),
3078
3096
  /* @__PURE__ */ jsx311(FieldMessage_default, { validations })
3079
- ] });
3097
+ ] }) });
3080
3098
  });
3081
3099
  Input.displayName = "Input";
3082
3100
  var Input_default = Input;
@@ -3106,16 +3124,96 @@ PasswordInput.displayName = "PasswordInput";
3106
3124
  var PasswordInput_default = PasswordInput;
3107
3125
 
3108
3126
  // src/components/Modal/Modal.tsx
3109
- import React10 from "react";
3127
+ import React11 from "react";
3110
3128
  import { createPortal } from "react-dom";
3129
+
3130
+ // src/tokens/hooks/useOverlayKeyboard.ts
3131
+ import React10 from "react";
3132
+ var FOCUSABLE = [
3133
+ "a[href]",
3134
+ "button:not([disabled])",
3135
+ "input:not([disabled]):not([type='hidden'])",
3136
+ "select:not([disabled])",
3137
+ "textarea:not([disabled])",
3138
+ "[tabindex]:not([tabindex='-1'])"
3139
+ ].join(",");
3140
+ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
3141
+ // 화면에 없는 것은 제외 (display:none 등)
3142
+ (n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
3143
+ );
3144
+ var useEscapeKey = (enabled, onEscape) => {
3145
+ React10.useEffect(() => {
3146
+ if (!enabled) return;
3147
+ const onKey = (e) => {
3148
+ if (e.key !== "Escape") return;
3149
+ if (e.defaultPrevented) return;
3150
+ e.preventDefault();
3151
+ onEscape();
3152
+ };
3153
+ document.addEventListener("keydown", onKey);
3154
+ return () => document.removeEventListener("keydown", onKey);
3155
+ }, [enabled, onEscape]);
3156
+ };
3157
+ var useFocusTrap = (containerRef, enabled, options = {}) => {
3158
+ const { trap = false, initialFocus } = options;
3159
+ const restoreRef = React10.useRef(null);
3160
+ React10.useEffect(() => {
3161
+ if (!enabled) return;
3162
+ restoreRef.current = document.activeElement;
3163
+ const raf = requestAnimationFrame(() => {
3164
+ const el = containerRef.current;
3165
+ if (!el) return;
3166
+ const target = initialFocus?.current ?? focusableIn(el)[0] ?? el;
3167
+ if (!target.hasAttribute("tabindex") && !focusableIn(el).includes(target)) {
3168
+ target.setAttribute("tabindex", "-1");
3169
+ }
3170
+ target.focus({ preventScroll: true });
3171
+ });
3172
+ return () => {
3173
+ cancelAnimationFrame(raf);
3174
+ const prev = restoreRef.current;
3175
+ if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
3176
+ };
3177
+ }, [enabled, containerRef, initialFocus]);
3178
+ React10.useEffect(() => {
3179
+ if (!enabled || !trap) return;
3180
+ const onKey = (e) => {
3181
+ if (e.key !== "Tab") return;
3182
+ const el = containerRef.current;
3183
+ if (!el) return;
3184
+ const items = focusableIn(el);
3185
+ if (items.length === 0) {
3186
+ e.preventDefault();
3187
+ return;
3188
+ }
3189
+ const first = items[0];
3190
+ const last = items[items.length - 1];
3191
+ const active = document.activeElement;
3192
+ if (e.shiftKey && (active === first || !el.contains(active))) {
3193
+ e.preventDefault();
3194
+ last.focus();
3195
+ } else if (!e.shiftKey && (active === last || !el.contains(active))) {
3196
+ e.preventDefault();
3197
+ first.focus();
3198
+ }
3199
+ };
3200
+ document.addEventListener("keydown", onKey);
3201
+ return () => document.removeEventListener("keydown", onKey);
3202
+ }, [enabled, trap, containerRef]);
3203
+ };
3204
+
3205
+ // src/components/Modal/Modal.tsx
3111
3206
  import { jsx as jsx313 } from "react/jsx-runtime";
3112
3207
  var ANIMATION_DURATION_MS = 200;
3113
3208
  var Modal = (props) => {
3114
3209
  const { isOpen, onClose, children } = props;
3115
- const [mounted, setMounted] = React10.useState(false);
3116
- const [visible, setVisible] = React10.useState(false);
3117
- const [settled, setSettled] = React10.useState(false);
3118
- React10.useEffect(() => {
3210
+ const [mounted, setMounted] = React11.useState(false);
3211
+ const [visible, setVisible] = React11.useState(false);
3212
+ const [settled, setSettled] = React11.useState(false);
3213
+ const boxRef = React11.useRef(null);
3214
+ useEscapeKey(isOpen, onClose);
3215
+ useFocusTrap(boxRef, isOpen && mounted, { trap: true });
3216
+ React11.useEffect(() => {
3119
3217
  if (isOpen) {
3120
3218
  setMounted(true);
3121
3219
  const show = setTimeout(() => setVisible(true), 1);
@@ -3142,6 +3240,7 @@ var Modal = (props) => {
3142
3240
  children: /* @__PURE__ */ jsx313(
3143
3241
  "div",
3144
3242
  {
3243
+ ref: boxRef,
3145
3244
  className: clsx_default("lib-xplat-modal", "modal-box", stateClass, settled && "settled"),
3146
3245
  role: "dialog",
3147
3246
  "aria-modal": "true",
@@ -3158,9 +3257,9 @@ Modal.displayName = "Modal";
3158
3257
  var Modal_default = Modal;
3159
3258
 
3160
3259
  // src/components/DatePicker/SingleDatePicker/index.tsx
3161
- import React11 from "react";
3260
+ import React12 from "react";
3162
3261
  import { Fragment as Fragment4, jsx as jsx314, jsxs as jsxs200 } from "react/jsx-runtime";
3163
- var DayCell2 = React11.memo(
3262
+ var DayCell2 = React12.memo(
3164
3263
  ({
3165
3264
  day,
3166
3265
  disabled,
@@ -3212,26 +3311,26 @@ var SingleDatePicker = (props) => {
3212
3311
  initialYear,
3213
3312
  initialMonth
3214
3313
  );
3215
- const [pickerMode, setPickerMode] = React11.useState("days");
3216
- const [yearRangeStart, setYearRangeStart] = React11.useState(
3314
+ const [pickerMode, setPickerMode] = React12.useState("days");
3315
+ const [yearRangeStart, setYearRangeStart] = React12.useState(
3217
3316
  Math.floor((initialYear ?? (/* @__PURE__ */ new Date()).getFullYear()) / 12) * 12
3218
3317
  );
3219
- const minTime = React11.useMemo(
3318
+ const minTime = React12.useMemo(
3220
3319
  () => minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()).getTime() : -Infinity,
3221
3320
  [minDate]
3222
3321
  );
3223
- const maxTime = React11.useMemo(
3322
+ const maxTime = React12.useMemo(
3224
3323
  () => maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()).getTime() : Infinity,
3225
3324
  [maxDate]
3226
3325
  );
3227
- const highlightSet = React11.useMemo(() => {
3326
+ const highlightSet = React12.useMemo(() => {
3228
3327
  const set = /* @__PURE__ */ new Set();
3229
3328
  for (const h of highlightDates) {
3230
3329
  set.add(`${h.getFullYear()}-${h.getMonth()}-${h.getDate()}`);
3231
3330
  }
3232
3331
  return set;
3233
3332
  }, [highlightDates]);
3234
- const handleSelect = React11.useCallback(
3333
+ const handleSelect = React12.useCallback(
3235
3334
  (date) => {
3236
3335
  onChange?.(date);
3237
3336
  },
@@ -3360,8 +3459,8 @@ var formatDate = (date) => {
3360
3459
  };
3361
3460
  var InputDatePicker = (props) => {
3362
3461
  const { value, onChange, minDate, maxDate, disabled, locale = "ko", placeholder } = props;
3363
- const [isOpen, setIsOpen] = React12.useState(false);
3364
- const [tempDate, setTempDate] = React12.useState(value ?? /* @__PURE__ */ new Date());
3462
+ const [isOpen, setIsOpen] = React13.useState(false);
3463
+ const [tempDate, setTempDate] = React13.useState(value ?? /* @__PURE__ */ new Date());
3365
3464
  const handleOpen = () => {
3366
3465
  if (disabled) return;
3367
3466
  setTempDate(value ?? /* @__PURE__ */ new Date());
@@ -3410,23 +3509,27 @@ InputDatePicker.displayName = "InputDatePicker";
3410
3509
  var InputDatePicker_default = InputDatePicker;
3411
3510
 
3412
3511
  // src/components/DatePicker/PopupPicker/index.tsx
3413
- import React16 from "react";
3512
+ import React17 from "react";
3414
3513
 
3415
3514
  // src/components/DatePicker/RangePicker/index.tsx
3416
- import React15 from "react";
3515
+ import React16 from "react";
3417
3516
 
3418
3517
  // src/components/Tab/Tab.tsx
3419
- import React14 from "react";
3518
+ import React15 from "react";
3420
3519
 
3421
3520
  // src/components/Tab/TabItem.tsx
3422
- import React13 from "react";
3521
+ import React14 from "react";
3423
3522
  import { jsx as jsx316 } from "react/jsx-runtime";
3424
- var TabItem = React13.forwardRef((props, ref) => {
3523
+ var TabItem = React14.forwardRef((props, ref) => {
3425
3524
  const { isActive: isActive2, title, onClick } = props;
3426
3525
  return /* @__PURE__ */ jsx316(
3427
- "div",
3526
+ "button",
3428
3527
  {
3429
3528
  ref,
3529
+ type: "button",
3530
+ role: "tab",
3531
+ "aria-selected": isActive2,
3532
+ tabIndex: isActive2 ? 0 : -1,
3430
3533
  className: clsx_default("tab-item", isActive2 ? "active" : null),
3431
3534
  onClick,
3432
3535
  children: title
@@ -3441,44 +3544,64 @@ import { jsx as jsx317, jsxs as jsxs202 } from "react/jsx-runtime";
3441
3544
  var Tab = (props) => {
3442
3545
  const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
3443
3546
  const shape = variant ?? type ?? "default";
3444
- const [underlineStyle, setUnderlineStyle] = React14.useState({
3547
+ const [underlineStyle, setUnderlineStyle] = React15.useState({
3445
3548
  left: 0,
3446
3549
  width: 0
3447
3550
  });
3448
- const itemRefs = React14.useRef([]);
3551
+ const itemRefs = React15.useRef([]);
3552
+ const handleKeyDown = (e) => {
3553
+ const last = tabs.length - 1;
3554
+ let next = null;
3555
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
3556
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
3557
+ else if (e.key === "Home") next = 0;
3558
+ else if (e.key === "End") next = last;
3559
+ if (next === null) return;
3560
+ e.preventDefault();
3561
+ onChange(tabs[next], next);
3562
+ itemRefs.current[next]?.focus();
3563
+ };
3449
3564
  const handleChangeActiveTab = (tabItem, tabIdx) => {
3450
3565
  onChange(tabItem, tabIdx);
3451
3566
  };
3452
- React14.useEffect(() => {
3567
+ React15.useEffect(() => {
3453
3568
  const el = itemRefs.current[activeIndex];
3454
3569
  if (el) {
3455
3570
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
3456
3571
  }
3457
3572
  }, [activeIndex, tabs.length]);
3458
- return /* @__PURE__ */ jsxs202("div", { className: clsx_default("lib-xplat-tab", `type-${shape}`, size), children: [
3459
- tabs.map((tab, idx) => /* @__PURE__ */ jsx317(
3460
- TabItem_default,
3461
- {
3462
- onClick: () => handleChangeActiveTab(tab, idx),
3463
- isActive: activeIndex === idx,
3464
- ref: (el) => {
3465
- itemRefs.current[idx] = el;
3466
- },
3467
- title: tab.title
3468
- },
3469
- `${tab.value}_${idx}`
3470
- )),
3471
- shape === "toggle" && /* @__PURE__ */ jsx317(
3472
- "div",
3473
- {
3474
- className: "tab-toggle-underline",
3475
- style: {
3476
- left: underlineStyle.left,
3477
- width: underlineStyle.width
3478
- }
3479
- }
3480
- )
3481
- ] });
3573
+ return /* @__PURE__ */ jsxs202(
3574
+ "div",
3575
+ {
3576
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
3577
+ role: "tablist",
3578
+ onKeyDown: handleKeyDown,
3579
+ children: [
3580
+ tabs.map((tab, idx) => /* @__PURE__ */ jsx317(
3581
+ TabItem_default,
3582
+ {
3583
+ onClick: () => handleChangeActiveTab(tab, idx),
3584
+ isActive: activeIndex === idx,
3585
+ ref: (el) => {
3586
+ itemRefs.current[idx] = el;
3587
+ },
3588
+ title: tab.title
3589
+ },
3590
+ `${tab.value}_${idx}`
3591
+ )),
3592
+ shape === "toggle" && /* @__PURE__ */ jsx317(
3593
+ "div",
3594
+ {
3595
+ className: "tab-toggle-underline",
3596
+ style: {
3597
+ left: underlineStyle.left,
3598
+ width: underlineStyle.width
3599
+ }
3600
+ }
3601
+ )
3602
+ ]
3603
+ }
3604
+ );
3482
3605
  };
3483
3606
  Tab.displayName = "Tab";
3484
3607
  var Tab_default = Tab;
@@ -3494,7 +3617,7 @@ var RangePicker = (props) => {
3494
3617
  maxDate,
3495
3618
  locale = "ko"
3496
3619
  } = props;
3497
- const [activeTab, setActiveTab] = React15.useState("start");
3620
+ const [activeTab, setActiveTab] = React16.useState("start");
3498
3621
  const handleStartChange = (date) => {
3499
3622
  if (!date) return;
3500
3623
  const newStart = date > endDate ? endDate : date;
@@ -3579,7 +3702,7 @@ var RangePicker_default = RangePicker;
3579
3702
  import { jsx as jsx319, jsxs as jsxs204 } from "react/jsx-runtime";
3580
3703
  var PopupPicker = (props) => {
3581
3704
  const { component, type, locale } = props;
3582
- const [isOpen, setIsOpen] = React16.useState(false);
3705
+ const [isOpen, setIsOpen] = React17.useState(false);
3583
3706
  const handleClick = () => setIsOpen(true);
3584
3707
  const handleClose = () => setIsOpen(false);
3585
3708
  const handleSingleChange = (date) => {
@@ -3588,7 +3711,7 @@ var PopupPicker = (props) => {
3588
3711
  handleClose();
3589
3712
  };
3590
3713
  return /* @__PURE__ */ jsxs204("div", { className: "lib-xplat-popup-datepicker", children: [
3591
- React16.cloneElement(component, { onClick: handleClick }),
3714
+ React17.cloneElement(component, { onClick: handleClick }),
3592
3715
  /* @__PURE__ */ jsx319(Modal_default, { isOpen, onClose: handleClose, children: /* @__PURE__ */ jsxs204("div", { className: clsx_default("lib-xplat-popup-datepicker-card", type === "range" && "range-mode"), children: [
3593
3716
  /* @__PURE__ */ jsxs204("div", { className: "popup-datepicker-content", children: [
3594
3717
  type === "single" && /* @__PURE__ */ jsx319(
@@ -3647,15 +3770,18 @@ Divider.displayName = "Divider";
3647
3770
  var Divider_default = Divider;
3648
3771
 
3649
3772
  // src/components/Drawer/Drawer.tsx
3650
- import React17 from "react";
3773
+ import React18 from "react";
3651
3774
  import { createPortal as createPortal2 } from "react-dom";
3652
3775
  import { jsx as jsx321, jsxs as jsxs205 } from "react/jsx-runtime";
3653
3776
  var ANIMATION_DURATION_MS2 = 250;
3654
3777
  var Drawer = (props) => {
3655
3778
  const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
3656
- const [mounted, setMounted] = React17.useState(false);
3657
- const [visible, setVisible] = React17.useState(false);
3658
- React17.useEffect(() => {
3779
+ const [mounted, setMounted] = React18.useState(false);
3780
+ const [visible, setVisible] = React18.useState(false);
3781
+ const panelRef = React18.useRef(null);
3782
+ useEscapeKey(isOpen, onClose);
3783
+ useFocusTrap(panelRef, isOpen && mounted, { trap: true });
3784
+ React18.useEffect(() => {
3659
3785
  if (isOpen) {
3660
3786
  setMounted(true);
3661
3787
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -3677,6 +3803,7 @@ var Drawer = (props) => {
3677
3803
  children: /* @__PURE__ */ jsxs205(
3678
3804
  "div",
3679
3805
  {
3806
+ ref: panelRef,
3680
3807
  className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
3681
3808
  role: "dialog",
3682
3809
  "aria-modal": "true",
@@ -3699,7 +3826,7 @@ Drawer.displayName = "Drawer";
3699
3826
  var Drawer_default = Drawer;
3700
3827
 
3701
3828
  // src/components/Dropdown/Dropdown.tsx
3702
- import React20 from "react";
3829
+ import React21 from "react";
3703
3830
 
3704
3831
  // src/tokens/hooks/Portal.tsx
3705
3832
  import ReactDOM from "react-dom";
@@ -3711,13 +3838,15 @@ Portal.displayName = "Portal";
3711
3838
  var Portal_default = Portal;
3712
3839
 
3713
3840
  // src/tokens/hooks/useAutoPosition.ts
3714
- import React18 from "react";
3841
+ import React19 from "react";
3715
3842
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3716
- const [position, setPosition] = React18.useState({
3843
+ const [position, setPosition] = React19.useState({
3717
3844
  position: {},
3718
- direction: "bottom"
3845
+ direction: "bottom",
3846
+ repositioning: false
3719
3847
  });
3720
- const calculatePosition = React18.useCallback(() => {
3848
+ const placedRef = React19.useRef(false);
3849
+ const calculatePosition = React19.useCallback(() => {
3721
3850
  if (!triggerRef.current || !popRef.current) return;
3722
3851
  const triggerRect = triggerRef.current.getBoundingClientRect();
3723
3852
  const popW = popRef.current.offsetWidth;
@@ -3741,16 +3870,21 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3741
3870
  if (left < 8) left = 8;
3742
3871
  setPosition({
3743
3872
  position: { top, left },
3744
- direction
3873
+ direction,
3874
+ repositioning: placedRef.current
3745
3875
  });
3876
+ placedRef.current = true;
3746
3877
  }, [triggerRef, popRef]);
3747
- React18.useLayoutEffect(() => {
3748
- if (!enabled) return;
3878
+ React19.useLayoutEffect(() => {
3879
+ if (!enabled) {
3880
+ placedRef.current = false;
3881
+ return;
3882
+ }
3749
3883
  calculatePosition();
3750
3884
  const raf = requestAnimationFrame(calculatePosition);
3751
3885
  return () => cancelAnimationFrame(raf);
3752
3886
  }, [calculatePosition, enabled]);
3753
- React18.useEffect(() => {
3887
+ React19.useEffect(() => {
3754
3888
  if (!enabled || !popRef.current) return;
3755
3889
  const observer = new ResizeObserver(() => {
3756
3890
  calculatePosition();
@@ -3758,13 +3892,19 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3758
3892
  observer.observe(popRef.current);
3759
3893
  return () => observer.disconnect();
3760
3894
  }, [calculatePosition, enabled, popRef]);
3761
- React18.useEffect(() => {
3895
+ React19.useEffect(() => {
3762
3896
  if (!enabled) return;
3763
- window.addEventListener("resize", calculatePosition);
3764
- window.addEventListener("scroll", calculatePosition, true);
3897
+ let raf = 0;
3898
+ const onScrollOrResize = () => {
3899
+ cancelAnimationFrame(raf);
3900
+ raf = requestAnimationFrame(calculatePosition);
3901
+ };
3902
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
3903
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
3765
3904
  return () => {
3766
- window.removeEventListener("resize", calculatePosition);
3767
- window.removeEventListener("scroll", calculatePosition, true);
3905
+ cancelAnimationFrame(raf);
3906
+ window.removeEventListener("resize", onScrollOrResize);
3907
+ window.removeEventListener("scroll", onScrollOrResize, true);
3768
3908
  };
3769
3909
  }, [calculatePosition, enabled]);
3770
3910
  return position;
@@ -3772,9 +3912,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3772
3912
  var useAutoPosition_default = useAutoPosition;
3773
3913
 
3774
3914
  // src/tokens/hooks/useClickOutside.ts
3775
- import React19 from "react";
3915
+ import React20 from "react";
3776
3916
  var useClickOutside = (refs, handler, enabled = true) => {
3777
- React19.useEffect(() => {
3917
+ React20.useEffect(() => {
3778
3918
  if (!enabled) return;
3779
3919
  const refArray = Array.isArray(refs) ? refs : [refs];
3780
3920
  const listener = (event) => {
@@ -3800,14 +3940,18 @@ var useClickOutside_default = useClickOutside;
3800
3940
  import { jsx as jsx322, jsxs as jsxs206 } from "react/jsx-runtime";
3801
3941
  var Dropdown = (props) => {
3802
3942
  const { items, children } = props;
3803
- const [isOpen, setIsOpen] = React20.useState(false);
3804
- const [mounted, setMounted] = React20.useState(false);
3805
- const [visible, setVisible] = React20.useState(false);
3806
- const triggerRef = React20.useRef(null);
3807
- const menuRef = React20.useRef(null);
3808
- const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
3943
+ const [isOpen, setIsOpen] = React21.useState(false);
3944
+ const [mounted, setMounted] = React21.useState(false);
3945
+ const [visible, setVisible] = React21.useState(false);
3946
+ const triggerRef = React21.useRef(null);
3947
+ const menuRef = React21.useRef(null);
3948
+ const { position, direction, repositioning } = useAutoPosition_default(triggerRef, menuRef, mounted);
3809
3949
  useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
3810
- React20.useEffect(() => {
3950
+ useEscapeKey(isOpen, () => {
3951
+ setIsOpen(false);
3952
+ triggerRef.current?.focus({ preventScroll: true });
3953
+ });
3954
+ React21.useEffect(() => {
3811
3955
  if (isOpen) {
3812
3956
  setMounted(true);
3813
3957
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -3836,7 +3980,7 @@ var Dropdown = (props) => {
3836
3980
  "div",
3837
3981
  {
3838
3982
  ref: menuRef,
3839
- className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }),
3983
+ className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }, repositioning && "repositioning"),
3840
3984
  style: { top: position.top, left: position.left },
3841
3985
  role: "menu",
3842
3986
  children: items.map((item) => /* @__PURE__ */ jsx322(
@@ -3861,7 +4005,112 @@ Dropdown.displayName = "Dropdown";
3861
4005
  var Dropdown_default = Dropdown;
3862
4006
 
3863
4007
  // src/components/Editor/Editor.tsx
3864
- import React21 from "react";
4008
+ import React22 from "react";
4009
+
4010
+ // src/components/Editor/highlight.ts
4011
+ var CODE_LANGUAGES = [
4012
+ { value: "plain", label: "\uC5C6\uC74C" },
4013
+ { value: "javascript", label: "JavaScript" },
4014
+ { value: "typescript", label: "TypeScript" },
4015
+ { value: "json", label: "JSON" },
4016
+ { value: "html", label: "HTML" },
4017
+ { value: "css", label: "CSS" },
4018
+ { value: "python", label: "Python" },
4019
+ { value: "sql", label: "SQL" },
4020
+ { value: "bash", label: "Bash" }
4021
+ ];
4022
+ var KEYWORDS = {
4023
+ plain: [],
4024
+ javascript: "const let var function return if else for while class new this import export from default async await try catch throw typeof instanceof null undefined true false break continue switch case do delete in of yield".split(" "),
4025
+ typescript: "const let var function return if else for while class new this import export from default async await try catch throw typeof instanceof null undefined true false break continue switch case do delete in of yield interface type enum implements extends public private protected readonly as satisfies keyof infer never unknown any".split(" "),
4026
+ json: ["true", "false", "null"],
4027
+ html: [],
4028
+ css: [],
4029
+ python: "def class return if elif else for while import from as try except finally raise with lambda None True False and or not in is pass break continue global nonlocal yield async await".split(" "),
4030
+ sql: "SELECT FROM WHERE INSERT INTO VALUES UPDATE SET DELETE CREATE TABLE ALTER DROP JOIN LEFT RIGHT INNER OUTER ON GROUP BY ORDER HAVING LIMIT OFFSET AS AND OR NOT NULL DISTINCT UNION INDEX PRIMARY KEY FOREIGN REFERENCES DEFAULT".split(" "),
4031
+ bash: "if then else fi for while do done case esac function return export local echo cd ls rm cp mv mkdir sudo source set unset exit".split(" ")
4032
+ };
4033
+ var LINE_COMMENT = {
4034
+ javascript: "//",
4035
+ typescript: "//",
4036
+ css: "",
4037
+ python: "#",
4038
+ sql: "--",
4039
+ bash: "#"
4040
+ };
4041
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4042
+ var wrap = (cls, text) => `<span class="tok-${cls}">${escape(text)}</span>`;
4043
+ var highlight = (code, lang) => {
4044
+ if (lang === "plain" || !code) return escape(code);
4045
+ const keywords = new Set(KEYWORDS[lang]);
4046
+ const lineComment = LINE_COMMENT[lang];
4047
+ const caseInsensitive = lang === "sql";
4048
+ let out = "";
4049
+ let i = 0;
4050
+ while (i < code.length) {
4051
+ const rest = code.slice(i);
4052
+ if ((lang === "javascript" || lang === "typescript" || lang === "css") && rest.startsWith("/*")) {
4053
+ const end = code.indexOf("*/", i + 2);
4054
+ const stop = end === -1 ? code.length : end + 2;
4055
+ out += wrap("comment", code.slice(i, stop));
4056
+ i = stop;
4057
+ continue;
4058
+ }
4059
+ if (lineComment && rest.startsWith(lineComment)) {
4060
+ const nl = code.indexOf("\n", i);
4061
+ const stop = nl === -1 ? code.length : nl;
4062
+ out += wrap("comment", code.slice(i, stop));
4063
+ i = stop;
4064
+ continue;
4065
+ }
4066
+ const q = rest[0];
4067
+ if (q === '"' || q === "'" || q === "`") {
4068
+ let j = 1;
4069
+ while (j < rest.length) {
4070
+ if (rest[j] === "\\") {
4071
+ j += 2;
4072
+ continue;
4073
+ }
4074
+ if (rest[j] === q) {
4075
+ j += 1;
4076
+ break;
4077
+ }
4078
+ if (rest[j] === "\n" && q !== "`") break;
4079
+ j += 1;
4080
+ }
4081
+ out += wrap("string", rest.slice(0, j));
4082
+ i += j;
4083
+ continue;
4084
+ }
4085
+ const num = /^\d+(\.\d+)?/.exec(rest);
4086
+ if (num && !/[\w$]/.test(code[i - 1] ?? "")) {
4087
+ out += wrap("number", num[0]);
4088
+ i += num[0].length;
4089
+ continue;
4090
+ }
4091
+ const word = /^[A-Za-z_$][\w$]*/.exec(rest);
4092
+ if (word) {
4093
+ const w = word[0];
4094
+ const hit = caseInsensitive ? keywords.has(w.toUpperCase()) : keywords.has(w);
4095
+ out += hit ? wrap("keyword", w) : escape(w);
4096
+ i += w.length;
4097
+ continue;
4098
+ }
4099
+ if (lang === "html" && rest.startsWith("<")) {
4100
+ const tag = /^<\/?[A-Za-z][\w-]*/.exec(rest);
4101
+ if (tag) {
4102
+ out += wrap("keyword", tag[0]);
4103
+ i += tag[0].length;
4104
+ continue;
4105
+ }
4106
+ }
4107
+ out += escape(code[i]);
4108
+ i += 1;
4109
+ }
4110
+ return out;
4111
+ };
4112
+
4113
+ // src/components/Editor/Editor.tsx
3865
4114
  import { jsx as jsx323, jsxs as jsxs207 } from "react/jsx-runtime";
3866
4115
  var DEFAULT_TOOLBAR = [
3867
4116
  "bold",
@@ -3869,14 +4118,27 @@ var DEFAULT_TOOLBAR = [
3869
4118
  "underline",
3870
4119
  "strikethrough",
3871
4120
  "code",
3872
- "heading",
4121
+ "heading-1",
4122
+ "heading-2",
4123
+ "heading-3",
3873
4124
  "list",
3874
4125
  "ordered-list",
3875
4126
  "blockquote",
3876
4127
  "code-block",
3877
4128
  "link",
3878
4129
  "image",
3879
- "divider"
4130
+ "divider",
4131
+ "checklist",
4132
+ "table",
4133
+ "align-left",
4134
+ "align-center",
4135
+ "align-right",
4136
+ "highlight",
4137
+ "superscript",
4138
+ "subscript",
4139
+ "clear-format",
4140
+ "undo",
4141
+ "redo"
3880
4142
  ];
3881
4143
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3882
4144
  "p",
@@ -3905,12 +4167,51 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3905
4167
  "img",
3906
4168
  "hr",
3907
4169
  "span",
3908
- "div"
4170
+ "div",
4171
+ "sub",
4172
+ "sup",
4173
+ "mark",
4174
+ "table",
4175
+ "thead",
4176
+ "tbody",
4177
+ "tr",
4178
+ "th",
4179
+ "td"
3909
4180
  ]);
3910
4181
  var ALLOWED_ATTRS = {
3911
4182
  a: ["href", "target", "rel"],
3912
- img: ["src", "alt"]
4183
+ img: ["src", "alt", "width"],
4184
+ // 정렬·체크리스트는 클래스로만 표현한다. style 을 열면 붙여넣기로 아무 CSS 나 들어온다
4185
+ p: ["class"],
4186
+ h1: ["class"],
4187
+ h2: ["class"],
4188
+ h3: ["class"],
4189
+ h4: ["class"],
4190
+ h5: ["class"],
4191
+ h6: ["class"],
4192
+ ul: ["class"],
4193
+ ol: ["class"],
4194
+ li: ["class", "data-checked"],
4195
+ blockquote: ["class"],
4196
+ div: ["class"],
4197
+ span: ["class"],
4198
+ pre: ["class"],
4199
+ code: ["class"],
4200
+ mark: ["class"],
4201
+ table: ["class"],
4202
+ td: ["class"],
4203
+ th: ["class"],
4204
+ tr: ["class"]
3913
4205
  };
4206
+ var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
4207
+ "script",
4208
+ "style",
4209
+ "noscript",
4210
+ "iframe",
4211
+ "object",
4212
+ "embed",
4213
+ "template"
4214
+ ]);
3914
4215
  var sanitizeHtml = (input) => {
3915
4216
  const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
3916
4217
  const root = doc.body.firstElementChild;
@@ -3919,6 +4220,10 @@ var sanitizeHtml = (input) => {
3919
4220
  const children = Array.from(node.children);
3920
4221
  for (const child of children) {
3921
4222
  const tag = child.tagName.toLowerCase();
4223
+ if (DROP_WITH_CONTENT.has(tag)) {
4224
+ node.removeChild(child);
4225
+ continue;
4226
+ }
3922
4227
  if (!ALLOWED_TAGS.has(tag)) {
3923
4228
  while (child.firstChild) node.insertBefore(child.firstChild, child);
3924
4229
  node.removeChild(child);
@@ -3949,7 +4254,9 @@ var escapeHtml = (text) => {
3949
4254
  };
3950
4255
  var SLASH_ITEMS = [
3951
4256
  { key: "paragraph", label: "\uB2E8\uB77D", hint: "\uAE30\uBCF8 \uD14D\uC2A4\uD2B8" },
3952
- { key: "heading", label: "\uC81C\uBAA9 1", hint: "\uD070 \uC81C\uBAA9" },
4257
+ { key: "heading-1", label: "\uC81C\uBAA9 1", hint: "\uAC00\uC7A5 \uD070 \uC81C\uBAA9" },
4258
+ { key: "heading-2", label: "\uC81C\uBAA9 2", hint: "" },
4259
+ { key: "heading-3", label: "\uC81C\uBAA9 3", hint: "" },
3953
4260
  { key: "list", label: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D", hint: "\u2022 \uD56D\uBAA9" },
3954
4261
  { key: "ordered-list", label: "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D", hint: "1. \uD56D\uBAA9" },
3955
4262
  { key: "blockquote", label: "\uC778\uC6A9", hint: "" },
@@ -3969,26 +4276,32 @@ var Editor = (props) => {
3969
4276
  onImageUpload,
3970
4277
  minHeight = 200
3971
4278
  } = props;
3972
- const editorRef = React21.useRef(null);
3973
- const lastRangeRef = React21.useRef(null);
3974
- const composingRef = React21.useRef(false);
3975
- const [isFocused, setIsFocused] = React21.useState(false);
3976
- const [isEmpty, setIsEmpty] = React21.useState(!value);
3977
- const [activeFormats, setActiveFormats] = React21.useState(/* @__PURE__ */ new Set());
3978
- const [slashOpen, setSlashOpen] = React21.useState(false);
3979
- const [slashFilter, setSlashFilter] = React21.useState("");
3980
- const [slashIdx, setSlashIdx] = React21.useState(0);
3981
- const [slashPos, setSlashPos] = React21.useState({ top: 0, left: 0 });
3982
- const slashStartRangeRef = React21.useRef(null);
3983
- const [linkOpen, setLinkOpen] = React21.useState(false);
3984
- const [linkValue, setLinkValue] = React21.useState("");
3985
- const [linkPos, setLinkPos] = React21.useState({ top: 0, left: 0 });
3986
- React21.useEffect(() => {
3987
- if (!editorRef.current) return;
3988
- if (editorRef.current.innerHTML !== value) {
3989
- editorRef.current.innerHTML = sanitizeHtml(value || "");
3990
- updateEmpty();
3991
- }
4279
+ const editorRef = React22.useRef(null);
4280
+ const lastRangeRef = React22.useRef(null);
4281
+ const composingRef = React22.useRef(false);
4282
+ const [isFocused, setIsFocused] = React22.useState(false);
4283
+ const [isEmpty, setIsEmpty] = React22.useState(!value);
4284
+ const [activeFormats, setActiveFormats] = React22.useState(/* @__PURE__ */ new Set());
4285
+ const [slashOpen, setSlashOpen] = React22.useState(false);
4286
+ const [slashFilter, setSlashFilter] = React22.useState("");
4287
+ const [slashIdx, setSlashIdx] = React22.useState(0);
4288
+ const [slashPos, setSlashPos] = React22.useState({ top: 0, left: 0 });
4289
+ const slashStartRangeRef = React22.useRef(null);
4290
+ const [linkOpen, setLinkOpen] = React22.useState(false);
4291
+ const [linkValue, setLinkValue] = React22.useState("");
4292
+ const [linkPos, setLinkPos] = React22.useState({ top: 0, left: 0 });
4293
+ const lastEmittedRef = React22.useRef(null);
4294
+ React22.useEffect(() => {
4295
+ const el = editorRef.current;
4296
+ if (!el) return;
4297
+ if (value === lastEmittedRef.current) return;
4298
+ if (el.innerHTML === value) return;
4299
+ el.innerHTML = sanitizeHtml(value || "");
4300
+ if (!el.firstChild) el.innerHTML = "<p><br></p>";
4301
+ lastEmittedRef.current = null;
4302
+ historyRef.current = [{ html: el.innerHTML, caret: 0 }];
4303
+ historyIdxRef.current = 0;
4304
+ updateEmpty();
3992
4305
  }, [value]);
3993
4306
  const updateEmpty = () => {
3994
4307
  const el = editorRef.current;
@@ -4004,10 +4317,19 @@ var Editor = (props) => {
4004
4317
  }
4005
4318
  };
4006
4319
  const restoreSelection = () => {
4007
- if (!lastRangeRef.current) return;
4320
+ const range = lastRangeRef.current;
4321
+ if (!range) return;
4322
+ if (!editorRef.current?.contains(range.startContainer)) {
4323
+ lastRangeRef.current = null;
4324
+ return;
4325
+ }
4008
4326
  const sel = window.getSelection();
4009
- sel?.removeAllRanges();
4010
- sel?.addRange(lastRangeRef.current);
4327
+ try {
4328
+ sel?.removeAllRanges();
4329
+ sel?.addRange(range);
4330
+ } catch {
4331
+ lastRangeRef.current = null;
4332
+ }
4011
4333
  };
4012
4334
  const updateActiveFormats = () => {
4013
4335
  if (!editorRef.current) return;
@@ -4023,6 +4345,17 @@ var Editor = (props) => {
4023
4345
  if (document.queryCommandState("insertOrderedList")) next.add("ordered-list");
4024
4346
  const block = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
4025
4347
  if (block) next.add(`block:${block}`);
4348
+ if (document.queryCommandState("superscript")) next.add("superscript");
4349
+ if (document.queryCommandState("subscript")) next.add("subscript");
4350
+ } catch {
4351
+ }
4352
+ try {
4353
+ const blockEl = currentBlock();
4354
+ if (blockEl?.classList.contains("align-center")) next.add("align:center");
4355
+ else if (blockEl?.classList.contains("align-right")) next.add("align:right");
4356
+ else if (blockEl) next.add("align:left");
4357
+ if (blockEl?.closest("mark")) next.add("highlight");
4358
+ if (blockEl?.closest("ul.checklist")) next.add("checklist");
4026
4359
  } catch {
4027
4360
  }
4028
4361
  setActiveFormats(next);
@@ -4036,7 +4369,374 @@ var Editor = (props) => {
4036
4369
  const setBlock = (tag) => {
4037
4370
  exec("formatBlock", tag);
4038
4371
  };
4372
+ const toggleBlock = (tag) => {
4373
+ const current = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
4374
+ setBlock(current === tag ? "p" : tag);
4375
+ };
4376
+ const historyRef = React22.useRef([]);
4377
+ const historyIdxRef = React22.useRef(-1);
4378
+ const lastPushAtRef = React22.useRef(0);
4379
+ const isRestoringRef = React22.useRef(false);
4380
+ const caretOffset = (rootEl) => {
4381
+ const el = rootEl ?? editorRef.current;
4382
+ const sel = window.getSelection();
4383
+ if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return 0;
4384
+ const range = sel.getRangeAt(0).cloneRange();
4385
+ range.selectNodeContents(el);
4386
+ range.setEnd(sel.getRangeAt(0).endContainer, sel.getRangeAt(0).endOffset);
4387
+ return range.toString().length;
4388
+ };
4389
+ const setCaretOffset = (offset, rootEl) => {
4390
+ const el = rootEl ?? editorRef.current;
4391
+ if (!el) return;
4392
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
4393
+ let remaining = offset;
4394
+ let node = null;
4395
+ while (node = walker.nextNode()) {
4396
+ const len = node.textContent?.length ?? 0;
4397
+ if (remaining <= len) {
4398
+ const range = document.createRange();
4399
+ range.setStart(node, remaining);
4400
+ range.collapse(true);
4401
+ const sel = window.getSelection();
4402
+ sel?.removeAllRanges();
4403
+ sel?.addRange(range);
4404
+ return;
4405
+ }
4406
+ remaining -= len;
4407
+ }
4408
+ placeCaretAtEnd(el);
4409
+ };
4410
+ const pushHistory = (force = false) => {
4411
+ const el = editorRef.current;
4412
+ if (!el || isRestoringRef.current) return;
4413
+ const html = el.innerHTML;
4414
+ const hist = historyRef.current;
4415
+ const cur = hist[historyIdxRef.current];
4416
+ if (cur && cur.html === html) return;
4417
+ const now = Date.now();
4418
+ const coalesce = !force && cur && now - lastPushAtRef.current < 500;
4419
+ lastPushAtRef.current = now;
4420
+ if (coalesce) {
4421
+ hist[historyIdxRef.current] = { html, caret: caretOffset() };
4422
+ return;
4423
+ }
4424
+ hist.length = historyIdxRef.current + 1;
4425
+ hist.push({ html, caret: caretOffset() });
4426
+ if (hist.length > 200) hist.shift();
4427
+ historyIdxRef.current = hist.length - 1;
4428
+ };
4429
+ const applySnapshot = (snap) => {
4430
+ const el = editorRef.current;
4431
+ if (!el) return;
4432
+ isRestoringRef.current = true;
4433
+ el.innerHTML = snap.html;
4434
+ setCaretOffset(snap.caret);
4435
+ isRestoringRef.current = false;
4436
+ lastEmittedRef.current = el.innerHTML;
4437
+ onChange?.(el.innerHTML);
4438
+ updateEmpty();
4439
+ updateActiveFormats();
4440
+ };
4441
+ const undo = () => {
4442
+ pushHistory(true);
4443
+ if (historyIdxRef.current <= 0) return;
4444
+ historyIdxRef.current -= 1;
4445
+ applySnapshot(historyRef.current[historyIdxRef.current]);
4446
+ };
4447
+ const redo = () => {
4448
+ if (historyIdxRef.current >= historyRef.current.length - 1) return;
4449
+ historyIdxRef.current += 1;
4450
+ applySnapshot(historyRef.current[historyIdxRef.current]);
4451
+ };
4452
+ const langOf = (pre) => {
4453
+ const cls = Array.from(pre.classList).find((c) => c.startsWith("lang-"));
4454
+ return cls?.slice(5) ?? "plain";
4455
+ };
4456
+ const [codeLang, setCodeLang] = React22.useState("plain");
4457
+ const highlightRafRef = React22.useRef(0);
4458
+ const rehighlight = (pre) => {
4459
+ if (composingRef.current) return;
4460
+ const code = pre.querySelector("code");
4461
+ if (!code) return;
4462
+ const lang = langOf(pre);
4463
+ const text = code.textContent || "";
4464
+ const next = highlight(text, lang);
4465
+ if (code.innerHTML === next) return;
4466
+ const caret = caretOffset(code);
4467
+ isRestoringRef.current = true;
4468
+ code.innerHTML = next;
4469
+ setCaretOffset(caret, code);
4470
+ isRestoringRef.current = false;
4471
+ };
4472
+ const scheduleHighlight = () => {
4473
+ const pre = closestPre();
4474
+ if (!pre) return;
4475
+ cancelAnimationFrame(highlightRafRef.current);
4476
+ highlightRafRef.current = requestAnimationFrame(() => rehighlight(pre));
4477
+ };
4478
+ const changeCodeLang = (lang) => {
4479
+ const pre = closestPre();
4480
+ if (!pre) return;
4481
+ pushHistory(true);
4482
+ Array.from(pre.classList).filter((c) => c.startsWith("lang-")).forEach((c) => pre.classList.remove(c));
4483
+ if (lang !== "plain") pre.classList.add(`lang-${lang}`);
4484
+ setCodeLang(lang);
4485
+ rehighlight(pre);
4486
+ handleInput();
4487
+ };
4488
+ const closestPre = () => {
4489
+ const sel = window.getSelection();
4490
+ const node = sel?.anchorNode;
4491
+ if (!node || !editorRef.current?.contains(node)) return null;
4492
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4493
+ return el?.closest("pre") ?? null;
4494
+ };
4495
+ const insertCodeBlock = () => {
4496
+ pushHistory(true);
4497
+ const el = editorRef.current;
4498
+ const sel = window.getSelection();
4499
+ if (!el || !sel || sel.rangeCount === 0) return;
4500
+ const existing = closestPre();
4501
+ if (existing) {
4502
+ const p = document.createElement("p");
4503
+ p.textContent = existing.textContent || "";
4504
+ if (!p.textContent) p.innerHTML = "<br>";
4505
+ existing.replaceWith(p);
4506
+ placeCaretAtEnd(p);
4507
+ handleInput();
4508
+ return;
4509
+ }
4510
+ const range = sel.getRangeAt(0);
4511
+ const frag = range.cloneContents();
4512
+ const holder = document.createElement("div");
4513
+ holder.appendChild(frag);
4514
+ const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
4515
+ const linesOf = (node) => {
4516
+ const out = [];
4517
+ let cur = "";
4518
+ for (const child of Array.from(node.childNodes)) {
4519
+ if (child.nodeType === Node.TEXT_NODE) {
4520
+ cur += child.textContent || "";
4521
+ } else if (child.nodeName === "BR") {
4522
+ out.push(cur);
4523
+ cur = "";
4524
+ } else if (BLOCK.test(child.nodeName)) {
4525
+ if (cur) {
4526
+ out.push(cur);
4527
+ cur = "";
4528
+ }
4529
+ const sub = linesOf(child);
4530
+ out.push(...sub.length > 0 ? sub : [""]);
4531
+ } else {
4532
+ cur += linesOf(child).join("\n");
4533
+ }
4534
+ }
4535
+ if (cur) out.push(cur);
4536
+ return out;
4537
+ };
4538
+ const text = linesOf(holder).join("\n").replace(/\n+$/, "");
4539
+ const pre = document.createElement("pre");
4540
+ const code = document.createElement("code");
4541
+ code.textContent = text || "";
4542
+ pre.appendChild(code);
4543
+ range.deleteContents();
4544
+ range.insertNode(pre);
4545
+ ensureParagraphAfter(pre);
4546
+ placeCaretAtEnd(code);
4547
+ handleInput();
4548
+ };
4549
+ const insertTextAtCaret = (text) => {
4550
+ const sel = window.getSelection();
4551
+ if (!sel || sel.rangeCount === 0) return;
4552
+ const range = sel.getRangeAt(0);
4553
+ range.deleteContents();
4554
+ const node = document.createTextNode(text);
4555
+ range.insertNode(node);
4556
+ range.setStartAfter(node);
4557
+ range.collapse(true);
4558
+ sel.removeAllRanges();
4559
+ sel.addRange(range);
4560
+ handleInput();
4561
+ };
4562
+ const ensureParagraphAfter = (node) => {
4563
+ const next = node.nextElementSibling;
4564
+ if (next && next.tagName === "P") return;
4565
+ const p = document.createElement("p");
4566
+ p.innerHTML = "<br>";
4567
+ node.after(p);
4568
+ };
4569
+ const placeCaretAtEnd = (node) => {
4570
+ const sel = window.getSelection();
4571
+ if (!sel) return;
4572
+ const r2 = document.createRange();
4573
+ r2.selectNodeContents(node);
4574
+ r2.collapse(false);
4575
+ sel.removeAllRanges();
4576
+ sel.addRange(r2);
4577
+ editorRef.current?.focus();
4578
+ };
4579
+ const clearFormat = () => {
4580
+ exec("removeFormat");
4581
+ exec("unlink");
4582
+ setBlock("p");
4583
+ };
4584
+ const indentList = (outdent) => {
4585
+ exec(outdent ? "outdent" : "indent");
4586
+ };
4587
+ const inListItem = () => {
4588
+ const sel = window.getSelection();
4589
+ const node = sel?.anchorNode;
4590
+ if (!node || !editorRef.current?.contains(node)) return false;
4591
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4592
+ return !!el?.closest("li");
4593
+ };
4594
+ const currentBlock = () => {
4595
+ const sel = window.getSelection();
4596
+ const node = sel?.anchorNode;
4597
+ const root = editorRef.current;
4598
+ if (!node || !root?.contains(node)) return null;
4599
+ let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4600
+ while (el && el !== root && !/^(P|H[1-6]|LI|BLOCKQUOTE|PRE|DIV|TD|TH)$/.test(el.tagName)) {
4601
+ el = el.parentElement;
4602
+ }
4603
+ return el && el !== root ? el : null;
4604
+ };
4605
+ const setAlign = (align) => {
4606
+ const block = currentBlock();
4607
+ if (!block) return;
4608
+ pushHistory(true);
4609
+ block.classList.remove("align-left", "align-center", "align-right");
4610
+ if (align !== "left") block.classList.add(`align-${align}`);
4611
+ handleInput();
4612
+ };
4613
+ const toggleChecklist = () => {
4614
+ pushHistory(true);
4615
+ const block = currentBlock();
4616
+ const list = block?.closest("ul");
4617
+ if (list?.classList.contains("checklist")) {
4618
+ list.classList.remove("checklist");
4619
+ list.querySelectorAll("li").forEach((li) => li.removeAttribute("data-checked"));
4620
+ handleInput();
4621
+ return;
4622
+ }
4623
+ if (list) {
4624
+ list.classList.add("checklist");
4625
+ list.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
4626
+ handleInput();
4627
+ return;
4628
+ }
4629
+ exec("insertUnorderedList");
4630
+ const made = currentBlock()?.closest("ul");
4631
+ if (made) {
4632
+ made.classList.add("checklist");
4633
+ made.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
4634
+ }
4635
+ handleInput();
4636
+ };
4637
+ const handleEditorClick = (e) => {
4638
+ if (readOnly) return;
4639
+ const target = e.target;
4640
+ const li = target.closest?.("li");
4641
+ if (!li || !li.closest("ul.checklist")) return;
4642
+ const rect = li.getBoundingClientRect();
4643
+ if (e.clientX - rect.left > 20) return;
4644
+ e.preventDefault();
4645
+ pushHistory(true);
4646
+ li.setAttribute("data-checked", li.getAttribute("data-checked") === "true" ? "false" : "true");
4647
+ handleInput();
4648
+ };
4649
+ const toggleHighlight = () => {
4650
+ const sel = window.getSelection();
4651
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
4652
+ pushHistory(true);
4653
+ const range = sel.getRangeAt(0);
4654
+ const node = sel.anchorNode;
4655
+ const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
4656
+ const existing = el?.closest("mark");
4657
+ if (existing) {
4658
+ const parent = existing.parentNode;
4659
+ while (existing.firstChild) parent?.insertBefore(existing.firstChild, existing);
4660
+ parent?.removeChild(existing);
4661
+ handleInput();
4662
+ return;
4663
+ }
4664
+ const mark = document.createElement("mark");
4665
+ try {
4666
+ range.surroundContents(mark);
4667
+ } catch {
4668
+ const frag = range.extractContents();
4669
+ mark.appendChild(frag);
4670
+ range.insertNode(mark);
4671
+ }
4672
+ handleInput();
4673
+ };
4674
+ const insertTable = (cols = 3, rows = 2) => {
4675
+ const sel = window.getSelection();
4676
+ if (!sel || sel.rangeCount === 0) return;
4677
+ pushHistory(true);
4678
+ const table = document.createElement("table");
4679
+ const thead = document.createElement("thead");
4680
+ const headRow = document.createElement("tr");
4681
+ for (let c = 0; c < cols; c++) {
4682
+ const th = document.createElement("th");
4683
+ th.innerHTML = "<br>";
4684
+ headRow.appendChild(th);
4685
+ }
4686
+ thead.appendChild(headRow);
4687
+ table.appendChild(thead);
4688
+ const tbody = document.createElement("tbody");
4689
+ for (let r2 = 0; r2 < rows; r2++) {
4690
+ const tr = document.createElement("tr");
4691
+ for (let c = 0; c < cols; c++) {
4692
+ const td = document.createElement("td");
4693
+ td.innerHTML = "<br>";
4694
+ tr.appendChild(td);
4695
+ }
4696
+ tbody.appendChild(tr);
4697
+ }
4698
+ table.appendChild(tbody);
4699
+ const range = sel.getRangeAt(0);
4700
+ range.deleteContents();
4701
+ range.insertNode(table);
4702
+ ensureParagraphAfter(table);
4703
+ const first = table.querySelector("th");
4704
+ if (first) placeCaretAtEnd(first);
4705
+ handleInput();
4706
+ };
4707
+ const moveTableCell = (backwards) => {
4708
+ const block = currentBlock();
4709
+ const cell = block?.closest("td, th");
4710
+ if (!cell) return false;
4711
+ const table = cell.closest("table");
4712
+ if (!table) return false;
4713
+ const cells = Array.from(table.querySelectorAll("td, th"));
4714
+ const i = cells.indexOf(cell);
4715
+ const next = cells[i + (backwards ? -1 : 1)];
4716
+ if (next) {
4717
+ placeCaretAtEnd(next);
4718
+ return true;
4719
+ }
4720
+ if (!backwards) {
4721
+ pushHistory(true);
4722
+ const lastRow = table.querySelector("tbody")?.lastElementChild;
4723
+ if (lastRow) {
4724
+ const tr = document.createElement("tr");
4725
+ for (let c = 0; c < lastRow.children.length; c++) {
4726
+ const td = document.createElement("td");
4727
+ td.innerHTML = "<br>";
4728
+ tr.appendChild(td);
4729
+ }
4730
+ lastRow.after(tr);
4731
+ placeCaretAtEnd(tr.firstElementChild);
4732
+ handleInput();
4733
+ return true;
4734
+ }
4735
+ }
4736
+ return false;
4737
+ };
4039
4738
  const insertDivider = () => {
4739
+ pushHistory(true);
4040
4740
  exec("insertHorizontalRule");
4041
4741
  const el = editorRef.current;
4042
4742
  if (el && el.lastElementChild?.tagName === "HR") {
@@ -4082,7 +4782,9 @@ var Editor = (props) => {
4082
4782
  const range = sel.getRangeAt(0);
4083
4783
  slashStartRangeRef.current = range.cloneRange();
4084
4784
  const rect = range.getBoundingClientRect();
4085
- const editorRect = editorRef.current.getBoundingClientRect();
4785
+ const editorEl = editorRef.current;
4786
+ if (!editorEl) return;
4787
+ const editorRect = editorEl.getBoundingClientRect();
4086
4788
  setSlashPos({
4087
4789
  top: rect.bottom - editorRect.top + 4,
4088
4790
  left: rect.left - editorRect.left
@@ -4097,7 +4799,7 @@ var Editor = (props) => {
4097
4799
  setSlashIdx(0);
4098
4800
  slashStartRangeRef.current = null;
4099
4801
  };
4100
- const filteredSlashItems = React21.useMemo(() => {
4802
+ const filteredSlashItems = React22.useMemo(() => {
4101
4803
  if (!slashFilter) return SLASH_ITEMS;
4102
4804
  const f = slashFilter.toLowerCase();
4103
4805
  return SLASH_ITEMS.filter((it) => it.label.toLowerCase().includes(f) || it.key.includes(f));
@@ -4127,8 +4829,14 @@ var Editor = (props) => {
4127
4829
  case "paragraph":
4128
4830
  setBlock("p");
4129
4831
  break;
4130
- case "heading":
4131
- setBlock("h2");
4832
+ case "heading-1":
4833
+ toggleBlock("h1");
4834
+ break;
4835
+ case "heading-2":
4836
+ toggleBlock("h2");
4837
+ break;
4838
+ case "heading-3":
4839
+ toggleBlock("h3");
4132
4840
  break;
4133
4841
  case "list":
4134
4842
  exec("insertUnorderedList");
@@ -4140,7 +4848,7 @@ var Editor = (props) => {
4140
4848
  setBlock("blockquote");
4141
4849
  break;
4142
4850
  case "code-block":
4143
- setBlock("pre");
4851
+ insertCodeBlock();
4144
4852
  break;
4145
4853
  case "divider":
4146
4854
  insertDivider();
@@ -4158,7 +4866,9 @@ var Editor = (props) => {
4158
4866
  if (!sel || sel.rangeCount === 0) return;
4159
4867
  const range = sel.getRangeAt(0);
4160
4868
  const rect = range.getBoundingClientRect();
4161
- const editorRect = editorRef.current.getBoundingClientRect();
4869
+ const editorEl2 = editorRef.current;
4870
+ if (!editorEl2) return;
4871
+ const editorRect = editorEl2.getBoundingClientRect();
4162
4872
  setLinkPos({
4163
4873
  top: rect.bottom - editorRect.top + 4,
4164
4874
  left: rect.left - editorRect.left
@@ -4191,13 +4901,13 @@ var Editor = (props) => {
4191
4901
  if (node.nodeType !== Node.TEXT_NODE) return false;
4192
4902
  const text = (node.textContent || "").slice(0, range.startOffset);
4193
4903
  const patterns = [
4194
- [/^###\s$/, () => setBlock("h3")],
4195
- [/^##\s$/, () => setBlock("h2")],
4196
- [/^#\s$/, () => setBlock("h1")],
4197
- [/^-\s$/, () => exec("insertUnorderedList")],
4198
- [/^\*\s$/, () => exec("insertUnorderedList")],
4199
- [/^1\.\s$/, () => exec("insertOrderedList")],
4200
- [/^>\s$/, () => setBlock("blockquote")]
4904
+ [/^###$/, () => setBlock("h3")],
4905
+ [/^##$/, () => setBlock("h2")],
4906
+ [/^#$/, () => setBlock("h1")],
4907
+ [/^-$/, () => exec("insertUnorderedList")],
4908
+ [/^\*$/, () => exec("insertUnorderedList")],
4909
+ [/^1\.$/, () => exec("insertOrderedList")],
4910
+ [/^>$/, () => setBlock("blockquote")]
4201
4911
  ];
4202
4912
  for (const [re, fn] of patterns) {
4203
4913
  if (re.test(text)) {
@@ -4217,7 +4927,11 @@ var Editor = (props) => {
4217
4927
  if (composingRef.current) return;
4218
4928
  const el = editorRef.current;
4219
4929
  if (!el) return;
4220
- onChange?.(el.innerHTML);
4930
+ const html = el.innerHTML;
4931
+ lastEmittedRef.current = html;
4932
+ pushHistory();
4933
+ scheduleHighlight();
4934
+ onChange?.(html);
4221
4935
  updateEmpty();
4222
4936
  updateActiveFormats();
4223
4937
  if (slashOpen) {
@@ -4245,6 +4959,40 @@ var Editor = (props) => {
4245
4959
  };
4246
4960
  const handleKeyDown = (e) => {
4247
4961
  if (readOnly) return;
4962
+ if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
4963
+ const pre = closestPre();
4964
+ if (pre && !slashOpen) {
4965
+ if (e.key === "Enter" && !e.shiftKey) {
4966
+ const text = pre.textContent || "";
4967
+ if (text.endsWith("\n\n")) {
4968
+ e.preventDefault();
4969
+ const code = pre.querySelector("code") ?? pre;
4970
+ code.textContent = text.replace(/\n\n$/, "");
4971
+ ensureParagraphAfter(pre);
4972
+ const next = pre.nextElementSibling;
4973
+ if (next) placeCaretAtEnd(next);
4974
+ handleInput();
4975
+ return;
4976
+ }
4977
+ e.preventDefault();
4978
+ insertTextAtCaret("\n");
4979
+ return;
4980
+ }
4981
+ if (e.key === "Tab") {
4982
+ e.preventDefault();
4983
+ insertTextAtCaret(" ");
4984
+ return;
4985
+ }
4986
+ }
4987
+ if (e.key === "Tab" && moveTableCell(e.shiftKey)) {
4988
+ e.preventDefault();
4989
+ return;
4990
+ }
4991
+ if (e.key === "Tab" && inListItem()) {
4992
+ e.preventDefault();
4993
+ indentList(e.shiftKey);
4994
+ return;
4995
+ }
4248
4996
  if (slashOpen) {
4249
4997
  if (e.key === "ArrowDown") {
4250
4998
  e.preventDefault();
@@ -4268,6 +5016,21 @@ var Editor = (props) => {
4268
5016
  return;
4269
5017
  }
4270
5018
  }
5019
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
5020
+ e.preventDefault();
5021
+ if (e.shiftKey) redo();
5022
+ else undo();
5023
+ return;
5024
+ }
5025
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "v") {
5026
+ plainPasteRef.current = true;
5027
+ return;
5028
+ }
5029
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "y") {
5030
+ e.preventDefault();
5031
+ redo();
5032
+ return;
5033
+ }
4271
5034
  if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
4272
5035
  const k = e.key.toLowerCase();
4273
5036
  if (k === "b") {
@@ -4291,6 +5054,7 @@ var Editor = (props) => {
4291
5054
  return;
4292
5055
  }
4293
5056
  }
5057
+ if (pre) return;
4294
5058
  if (enableSlashCommand && e.key === "/") {
4295
5059
  setTimeout(openSlashMenu, 0);
4296
5060
  }
@@ -4300,11 +5064,21 @@ var Editor = (props) => {
4300
5064
  }
4301
5065
  }
4302
5066
  };
5067
+ const plainPasteRef = React22.useRef(false);
4303
5068
  const handlePaste = (e) => {
4304
5069
  if (readOnly) return;
4305
5070
  e.preventDefault();
4306
- const html = e.clipboardData.getData("text/html");
4307
5071
  const text = e.clipboardData.getData("text/plain");
5072
+ if (plainPasteRef.current) {
5073
+ plainPasteRef.current = false;
5074
+ insertTextAtCaret(text);
5075
+ return;
5076
+ }
5077
+ if (closestPre()) {
5078
+ insertTextAtCaret(text);
5079
+ return;
5080
+ }
5081
+ const html = e.clipboardData.getData("text/html");
4308
5082
  const clean = html ? sanitizeHtml(html) : escapeHtml(text);
4309
5083
  document.execCommand("insertHTML", false, clean);
4310
5084
  handleInput();
@@ -4326,8 +5100,11 @@ var Editor = (props) => {
4326
5100
  };
4327
5101
  const handleSelectionUpdate = () => {
4328
5102
  updateActiveFormats();
5103
+ const pre = closestPre();
5104
+ setCodeLang(pre ? langOf(pre) : "plain");
4329
5105
  };
4330
5106
  const onToolbarAction = (item) => {
5107
+ if (closestPre() && !["code-block", "undo", "redo"].includes(item)) return;
4331
5108
  switch (item) {
4332
5109
  case "bold":
4333
5110
  exec("bold");
@@ -4356,8 +5133,14 @@ var Editor = (props) => {
4356
5133
  handleInput();
4357
5134
  break;
4358
5135
  }
4359
- case "heading":
4360
- setBlock("h2");
5136
+ case "heading-1":
5137
+ toggleBlock("h1");
5138
+ break;
5139
+ case "heading-2":
5140
+ toggleBlock("h2");
5141
+ break;
5142
+ case "heading-3":
5143
+ toggleBlock("h3");
4361
5144
  break;
4362
5145
  case "list":
4363
5146
  exec("insertUnorderedList");
@@ -4369,7 +5152,7 @@ var Editor = (props) => {
4369
5152
  setBlock("blockquote");
4370
5153
  break;
4371
5154
  case "code-block":
4372
- setBlock("pre");
5155
+ insertCodeBlock();
4373
5156
  break;
4374
5157
  case "link":
4375
5158
  openLinkEditor();
@@ -4380,6 +5163,39 @@ var Editor = (props) => {
4380
5163
  case "divider":
4381
5164
  insertDivider();
4382
5165
  break;
5166
+ case "checklist":
5167
+ toggleChecklist();
5168
+ break;
5169
+ case "align-left":
5170
+ setAlign("left");
5171
+ break;
5172
+ case "align-center":
5173
+ setAlign("center");
5174
+ break;
5175
+ case "align-right":
5176
+ setAlign("right");
5177
+ break;
5178
+ case "highlight":
5179
+ toggleHighlight();
5180
+ break;
5181
+ case "superscript":
5182
+ exec("superscript");
5183
+ break;
5184
+ case "subscript":
5185
+ exec("subscript");
5186
+ break;
5187
+ case "table":
5188
+ insertTable();
5189
+ break;
5190
+ case "clear-format":
5191
+ clearFormat();
5192
+ break;
5193
+ case "undo":
5194
+ undo();
5195
+ break;
5196
+ case "redo":
5197
+ redo();
5198
+ break;
4383
5199
  }
4384
5200
  };
4385
5201
  return /* @__PURE__ */ jsxs207("div", { className: clsx_default("lib-xplat-editor", isFocused && "focused", readOnly && "readonly"), children: [
@@ -4388,7 +5204,10 @@ var Editor = (props) => {
4388
5204
  {
4389
5205
  items: toolbar,
4390
5206
  active: activeFormats,
4391
- onAction: onToolbarAction
5207
+ onAction: onToolbarAction,
5208
+ codeLang,
5209
+ inCodeBlock: activeFormats.has("block:pre"),
5210
+ onCodeLangChange: changeCodeLang
4392
5211
  }
4393
5212
  ),
4394
5213
  /* @__PURE__ */ jsxs207("div", { className: "lib-xplat-editor__content", style: { minHeight }, children: [
@@ -4399,9 +5218,14 @@ var Editor = (props) => {
4399
5218
  ref: editorRef,
4400
5219
  contentEditable: !readOnly,
4401
5220
  suppressContentEditableWarning: true,
5221
+ role: "textbox",
5222
+ "aria-multiline": "true",
5223
+ "aria-readonly": readOnly || void 0,
5224
+ "aria-label": placeholder,
4402
5225
  className: "lib-xplat-editor__editable",
4403
5226
  onInput: handleInput,
4404
5227
  onKeyDown: handleKeyDown,
5228
+ onClick: handleEditorClick,
4405
5229
  onKeyUp: handleSelectionUpdate,
4406
5230
  onMouseUp: handleSelectionUpdate,
4407
5231
  onPaste: handlePaste,
@@ -4443,14 +5267,27 @@ var TOOLBAR_LABEL = {
4443
5267
  underline: "U",
4444
5268
  strikethrough: "S",
4445
5269
  code: "<>",
4446
- heading: "H",
5270
+ "heading-1": "H1",
5271
+ "heading-2": "H2",
5272
+ "heading-3": "H3",
4447
5273
  list: "\u2022",
4448
5274
  "ordered-list": "1.",
4449
5275
  blockquote: "\u275D",
4450
5276
  "code-block": "[ ]",
4451
5277
  link: "\u{1F517}",
4452
5278
  image: "\u{1F5BC}",
4453
- divider: "\u2014"
5279
+ divider: "\u2014",
5280
+ checklist: "\u2611",
5281
+ "align-left": "\u21E4",
5282
+ "align-center": "\u2194",
5283
+ "align-right": "\u21E5",
5284
+ highlight: "\u{1F58D}",
5285
+ superscript: "x\xB2",
5286
+ subscript: "x\u2082",
5287
+ table: "\u25A6",
5288
+ "clear-format": "\u232B",
5289
+ undo: "\u21B6",
5290
+ redo: "\u21B7"
4454
5291
  };
4455
5292
  var TOOLBAR_TITLE = {
4456
5293
  bold: "\uAD75\uAC8C (\u2318B)",
@@ -4458,39 +5295,77 @@ var TOOLBAR_TITLE = {
4458
5295
  underline: "\uBC11\uC904 (\u2318U)",
4459
5296
  strikethrough: "\uCDE8\uC18C\uC120",
4460
5297
  code: "\uC778\uB77C\uC778 \uCF54\uB4DC",
4461
- heading: "\uC81C\uBAA9",
5298
+ "heading-1": "\uC81C\uBAA9 1",
5299
+ "heading-2": "\uC81C\uBAA9 2",
5300
+ "heading-3": "\uC81C\uBAA9 3",
4462
5301
  list: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D",
4463
5302
  "ordered-list": "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D",
4464
5303
  blockquote: "\uC778\uC6A9",
4465
5304
  "code-block": "\uCF54\uB4DC \uBE14\uB85D",
4466
5305
  link: "\uB9C1\uD06C (\u2318K)",
4467
5306
  image: "\uC774\uBBF8\uC9C0",
4468
- divider: "\uAD6C\uBD84\uC120"
5307
+ divider: "\uAD6C\uBD84\uC120",
5308
+ checklist: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
5309
+ "align-left": "\uC67C\uCABD \uC815\uB82C",
5310
+ "align-center": "\uAC00\uC6B4\uB370 \uC815\uB82C",
5311
+ "align-right": "\uC624\uB978\uCABD \uC815\uB82C",
5312
+ highlight: "\uD615\uAD11\uD39C",
5313
+ superscript: "\uC704 \uCCA8\uC790",
5314
+ subscript: "\uC544\uB798 \uCCA8\uC790",
5315
+ table: "\uD45C \uC0BD\uC785",
5316
+ "clear-format": "\uC11C\uC2DD \uC9C0\uC6B0\uAE30",
5317
+ undo: "\uB418\uB3CC\uB9AC\uAE30 (\u2318Z)",
5318
+ redo: "\uB2E4\uC2DC\uD558\uAE30 (\u2318\u21E7Z)"
4469
5319
  };
4470
5320
  var isActive = (item, active) => {
4471
- if (item === "heading") return active.has("block:h1") || active.has("block:h2") || active.has("block:h3");
5321
+ if (item === "heading-1") return active.has("block:h1");
5322
+ if (item === "heading-2") return active.has("block:h2");
5323
+ if (item === "heading-3") return active.has("block:h3");
4472
5324
  if (item === "blockquote") return active.has("block:blockquote");
4473
5325
  if (item === "code-block") return active.has("block:pre");
5326
+ if (item === "align-left") return active.has("align:left");
5327
+ if (item === "align-center") return active.has("align:center");
5328
+ if (item === "align-right") return active.has("align:right");
4474
5329
  return active.has(item);
4475
5330
  };
4476
- var EditorToolbar = ({ items, active, onAction }) => {
4477
- return /* @__PURE__ */ jsx323("div", { className: "lib-xplat-editor__toolbar", children: items.map((item) => /* @__PURE__ */ jsx323(
4478
- "button",
4479
- {
4480
- type: "button",
4481
- className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
4482
- title: TOOLBAR_TITLE[item],
4483
- onMouseDown: (e) => {
4484
- e.preventDefault();
4485
- onAction(item);
5331
+ var EditorToolbar = ({
5332
+ items,
5333
+ active,
5334
+ onAction,
5335
+ codeLang,
5336
+ inCodeBlock,
5337
+ onCodeLangChange
5338
+ }) => {
5339
+ return /* @__PURE__ */ jsxs207("div", { className: "lib-xplat-editor__toolbar", children: [
5340
+ items.map((item) => /* @__PURE__ */ jsx323(
5341
+ "button",
5342
+ {
5343
+ type: "button",
5344
+ className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
5345
+ title: TOOLBAR_TITLE[item],
5346
+ onMouseDown: (e) => {
5347
+ e.preventDefault();
5348
+ onAction(item);
5349
+ },
5350
+ children: TOOLBAR_LABEL[item]
4486
5351
  },
4487
- children: TOOLBAR_LABEL[item]
4488
- },
4489
- item
4490
- )) });
5352
+ item
5353
+ )),
5354
+ inCodeBlock && /* @__PURE__ */ jsx323(
5355
+ "select",
5356
+ {
5357
+ className: "lib-xplat-editor__lang",
5358
+ value: codeLang,
5359
+ "aria-label": "\uCF54\uB4DC \uC5B8\uC5B4",
5360
+ onMouseDown: (e) => e.stopPropagation(),
5361
+ onChange: (e) => onCodeLangChange(e.target.value),
5362
+ children: CODE_LANGUAGES.map((l) => /* @__PURE__ */ jsx323("option", { value: l.value, children: l.label }, l.value))
5363
+ }
5364
+ )
5365
+ ] });
4491
5366
  };
4492
5367
  var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
4493
- React21.useEffect(() => {
5368
+ React22.useEffect(() => {
4494
5369
  const handleClickOutside = (e) => {
4495
5370
  const target = e.target;
4496
5371
  const menu = document.querySelector(".lib-xplat-editor__slash-menu");
@@ -4524,8 +5399,8 @@ var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
4524
5399
  );
4525
5400
  };
4526
5401
  var LinkPopover = ({ position, value, onChange, onConfirm, onClose }) => {
4527
- const inputRef = React21.useRef(null);
4528
- React21.useEffect(() => {
5402
+ const inputRef = React22.useRef(null);
5403
+ React22.useEffect(() => {
4529
5404
  inputRef.current?.focus();
4530
5405
  }, []);
4531
5406
  return /* @__PURE__ */ jsxs207(
@@ -4583,7 +5458,7 @@ EmptyState.displayName = "EmptyState";
4583
5458
  var EmptyState_default = EmptyState;
4584
5459
 
4585
5460
  // src/components/FileUpload/FileUpload.tsx
4586
- import React22 from "react";
5461
+ import React23 from "react";
4587
5462
  import { jsx as jsx325, jsxs as jsxs209 } from "react/jsx-runtime";
4588
5463
  var FileUpload = (props) => {
4589
5464
  const {
@@ -4595,8 +5470,8 @@ var FileUpload = (props) => {
4595
5470
  description,
4596
5471
  validations
4597
5472
  } = props;
4598
- const [isDragOver, setIsDragOver] = React22.useState(false);
4599
- const inputRef = React22.useRef(null);
5473
+ const [isDragOver, setIsDragOver] = React23.useState(false);
5474
+ const inputRef = React23.useRef(null);
4600
5475
  const handleFiles = (fileList) => {
4601
5476
  let files = Array.from(fileList);
4602
5477
  if (maxSize) {
@@ -4658,10 +5533,10 @@ FileUpload.displayName = "FileUpload";
4658
5533
  var FileUpload_default = FileUpload;
4659
5534
 
4660
5535
  // src/components/HtmlTypeWriter/HtmlTypeWriter.tsx
4661
- import React24 from "react";
5536
+ import React25 from "react";
4662
5537
 
4663
5538
  // src/components/HtmlTypeWriter/utils.ts
4664
- import React23 from "react";
5539
+ import React24 from "react";
4665
5540
  var voidTags = /* @__PURE__ */ new Set([
4666
5541
  "br",
4667
5542
  "img",
@@ -4729,15 +5604,15 @@ var convertNodeToReactWithRange = (node, typedLen, rangeMap) => {
4729
5604
  props[attr.name] = attr.value;
4730
5605
  });
4731
5606
  if (voidTags.has(tag)) {
4732
- return React23.createElement(tag, props);
5607
+ return React24.createElement(tag, props);
4733
5608
  }
4734
5609
  const children = Array.from(element.childNodes).map((child) => convertNodeToReactWithRange(child, typedLen, rangeMap)).filter((n) => n != null);
4735
- return React23.createElement(tag, props, ...children);
5610
+ return React24.createElement(tag, props, ...children);
4736
5611
  };
4737
5612
  var htmlToReactProgressive = (root, typedLen, rangeMap) => {
4738
5613
  const nodes = Array.from(root.childNodes).map((child, idx) => {
4739
5614
  const node = convertNodeToReactWithRange(child, typedLen, rangeMap);
4740
- return node == null ? null : React23.createElement(React23.Fragment, { key: idx }, node);
5615
+ return node == null ? null : React24.createElement(React24.Fragment, { key: idx }, node);
4741
5616
  }).filter(Boolean);
4742
5617
  return nodes.length === 0 ? null : nodes;
4743
5618
  };
@@ -4750,20 +5625,20 @@ var HtmlTypeWriter = ({
4750
5625
  onDone,
4751
5626
  onChange
4752
5627
  }) => {
4753
- const [typedLen, setTypedLen] = React24.useState(0);
4754
- const doneCalledRef = React24.useRef(false);
4755
- const { doc, rangeMap, totalLength } = React24.useMemo(() => {
5628
+ const [typedLen, setTypedLen] = React25.useState(0);
5629
+ const doneCalledRef = React25.useRef(false);
5630
+ const { doc, rangeMap, totalLength } = React25.useMemo(() => {
4756
5631
  if (typeof window === "undefined") return { doc: null, rangeMap: /* @__PURE__ */ new Map(), totalLength: 0 };
4757
5632
  const decoded = decodeHtmlEntities(html);
4758
5633
  const doc2 = new DOMParser().parseFromString(decoded, "text/html");
4759
5634
  const { rangeMap: rangeMap2, totalLength: totalLength2 } = buildRangeMap(doc2.body);
4760
5635
  return { doc: doc2, rangeMap: rangeMap2, totalLength: totalLength2 };
4761
5636
  }, [html]);
4762
- React24.useEffect(() => {
5637
+ React25.useEffect(() => {
4763
5638
  setTypedLen(0);
4764
5639
  doneCalledRef.current = false;
4765
5640
  }, [html]);
4766
- React24.useEffect(() => {
5641
+ React25.useEffect(() => {
4767
5642
  if (!totalLength) return;
4768
5643
  if (typedLen >= totalLength) return;
4769
5644
  const timer = window.setInterval(() => {
@@ -4771,18 +5646,18 @@ var HtmlTypeWriter = ({
4771
5646
  }, duration);
4772
5647
  return () => window.clearInterval(timer);
4773
5648
  }, [typedLen, totalLength, duration]);
4774
- React24.useEffect(() => {
5649
+ React25.useEffect(() => {
4775
5650
  if (typedLen > 0 && typedLen < totalLength) {
4776
5651
  onChange?.();
4777
5652
  }
4778
5653
  }, [typedLen, totalLength, onChange]);
4779
- React24.useEffect(() => {
5654
+ React25.useEffect(() => {
4780
5655
  if (typedLen === totalLength && totalLength > 0 && !doneCalledRef.current) {
4781
5656
  doneCalledRef.current = true;
4782
5657
  onDone?.();
4783
5658
  }
4784
5659
  }, [typedLen, totalLength, onDone]);
4785
- const parsed = React24.useMemo(
5660
+ const parsed = React25.useMemo(
4786
5661
  () => doc ? htmlToReactProgressive(doc.body, typedLen, rangeMap) : null,
4787
5662
  [doc, typedLen, rangeMap]
4788
5663
  );
@@ -4792,12 +5667,12 @@ HtmlTypeWriter.displayName = "HtmlTypeWriter";
4792
5667
  var HtmlTypeWriter_default = HtmlTypeWriter;
4793
5668
 
4794
5669
  // src/components/ImageSelector/ImageSelector.tsx
4795
- import React25 from "react";
5670
+ import React26 from "react";
4796
5671
  import { jsx as jsx327, jsxs as jsxs210 } from "react/jsx-runtime";
4797
5672
  var ImageSelector = (props) => {
4798
5673
  const { value, label, onChange, validations } = props;
4799
- const [previewUrl, setPreviewUrl] = React25.useState();
4800
- React25.useEffect(() => {
5674
+ const [previewUrl, setPreviewUrl] = React26.useState();
5675
+ React26.useEffect(() => {
4801
5676
  if (!value) {
4802
5677
  setPreviewUrl(void 0);
4803
5678
  return;
@@ -4806,7 +5681,7 @@ var ImageSelector = (props) => {
4806
5681
  setPreviewUrl(url);
4807
5682
  return () => URL.revokeObjectURL(url);
4808
5683
  }, [value]);
4809
- const inputRef = React25.useRef(null);
5684
+ const inputRef = React26.useRef(null);
4810
5685
  const handleFileChange = (e) => {
4811
5686
  const selectedFile = e.target.files?.[0];
4812
5687
  if (selectedFile) {
@@ -4989,17 +5864,21 @@ Pagination.displayName = "Pagination";
4989
5864
  var Pagination_default = Pagination;
4990
5865
 
4991
5866
  // src/components/PopOver/PopOver.tsx
4992
- import React26 from "react";
5867
+ import React27 from "react";
4993
5868
  import { jsx as jsx332, jsxs as jsxs213 } from "react/jsx-runtime";
4994
5869
  var PopOver = (props) => {
4995
5870
  const { children, isOpen, onClose, PopOverEl } = props;
4996
- const popRef = React26.useRef(null);
4997
- const triggerRef = React26.useRef(null);
4998
- const [localOpen, setLocalOpen] = React26.useState(false);
4999
- const [eventTrigger, setEventTrigger] = React26.useState(false);
5871
+ const popRef = React27.useRef(null);
5872
+ const triggerRef = React27.useRef(null);
5873
+ const [localOpen, setLocalOpen] = React27.useState(false);
5874
+ const [eventTrigger, setEventTrigger] = React27.useState(false);
5000
5875
  useClickOutside_default([popRef, triggerRef], onClose, isOpen);
5876
+ useEscapeKey(isOpen, () => {
5877
+ onClose();
5878
+ triggerRef.current?.focus({ preventScroll: true });
5879
+ });
5001
5880
  const position = useAutoPosition_default(triggerRef, popRef, localOpen);
5002
- React26.useEffect(() => {
5881
+ React27.useEffect(() => {
5003
5882
  if (isOpen) {
5004
5883
  setLocalOpen(isOpen);
5005
5884
  setTimeout(() => {
@@ -5028,6 +5907,7 @@ var PopOver = (props) => {
5028
5907
  className: clsx_default(
5029
5908
  "lib-xplat-pop-over-content",
5030
5909
  position.direction,
5910
+ position.repositioning && "repositioning",
5031
5911
  eventTrigger && "visible"
5032
5912
  ),
5033
5913
  ref: popRef,
@@ -5084,12 +5964,12 @@ Progress.displayName = "Progress";
5084
5964
  var Progress_default = Progress;
5085
5965
 
5086
5966
  // src/components/Radio/RadioGroupContext.tsx
5087
- import React27 from "react";
5088
- var RadioGroupContext = React27.createContext(
5967
+ import React28 from "react";
5968
+ var RadioGroupContext = React28.createContext(
5089
5969
  null
5090
5970
  );
5091
5971
  var useRadioGroupContext = () => {
5092
- return React27.useContext(RadioGroupContext);
5972
+ return React28.useContext(RadioGroupContext);
5093
5973
  };
5094
5974
  var RadioGroupContext_default = RadioGroupContext;
5095
5975
 
@@ -5165,19 +6045,19 @@ RadioGroup.displayName = "RadioGroup";
5165
6045
  var RadioGroup_default = RadioGroup;
5166
6046
 
5167
6047
  // src/components/Select/Select.tsx
5168
- import React30 from "react";
6048
+ import React31 from "react";
5169
6049
 
5170
6050
  // src/components/Select/context.ts
5171
- import React28 from "react";
5172
- var SelectContext = React28.createContext(null);
6051
+ import React29 from "react";
6052
+ var SelectContext = React29.createContext(null);
5173
6053
  var context_default = SelectContext;
5174
6054
 
5175
6055
  // src/components/Select/SelectItem.tsx
5176
- import React29 from "react";
6056
+ import React30 from "react";
5177
6057
  import { jsx as jsx336 } from "react/jsx-runtime";
5178
6058
  var SelectItem = (props) => {
5179
6059
  const { children, value, onClick, disabled = false } = props;
5180
- const ctx = React29.useContext(context_default);
6060
+ const ctx = React30.useContext(context_default);
5181
6061
  const handleClick = (e) => {
5182
6062
  e.preventDefault();
5183
6063
  e.stopPropagation();
@@ -5217,32 +6097,37 @@ var SelectRoot = (props) => {
5217
6097
  onChange,
5218
6098
  children,
5219
6099
  disabled = false,
6100
+ searchable = false,
6101
+ searchPlaceholder = "\uAC80\uC0C9",
5220
6102
  error = false,
5221
6103
  validations,
6104
+ label,
6105
+ required,
5222
6106
  size = "md"
5223
6107
  } = props;
5224
- const itemChildren = React30.Children.toArray(children).filter(
5225
- (child) => React30.isValidElement(child) && child.type === SelectItem_default
6108
+ const fieldId = React31.useId();
6109
+ const itemChildren = React31.Children.toArray(children).filter(
6110
+ (child) => React31.isValidElement(child) && child.type === SelectItem_default
5226
6111
  );
5227
6112
  const isControlled = valueProp !== void 0;
5228
- const [isOpen, setOpen] = React30.useState(false);
5229
- const [uncontrolledLabel, setUncontrolledLabel] = React30.useState(null);
5230
- const [uncontrolledValue, setUncontrolledValue] = React30.useState(void 0);
5231
- const controlledLabel = React30.useMemo(() => {
6113
+ const [isOpen, setOpen] = React31.useState(false);
6114
+ const [uncontrolledLabel, setUncontrolledLabel] = React31.useState(null);
6115
+ const [uncontrolledValue, setUncontrolledValue] = React31.useState(void 0);
6116
+ const controlledLabel = React31.useMemo(() => {
5232
6117
  if (!isControlled) return null;
5233
6118
  const match = itemChildren.find((child) => child.props.value === valueProp);
5234
6119
  return match ? match.props.children : null;
5235
6120
  }, [isControlled, valueProp, itemChildren]);
5236
6121
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
5237
6122
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
5238
- const triggerRef = React30.useRef(null);
5239
- const contentRef = React30.useRef(null);
5240
- const [mounted, setMounted] = React30.useState(false);
5241
- const [visible, setVisible] = React30.useState(false);
5242
- React30.useEffect(() => {
6123
+ const triggerRef = React31.useRef(null);
6124
+ const contentRef = React31.useRef(null);
6125
+ const [mounted, setMounted] = React31.useState(false);
6126
+ const [visible, setVisible] = React31.useState(false);
6127
+ React31.useEffect(() => {
5243
6128
  if (disabled && isOpen) setOpen(false);
5244
6129
  }, [disabled, isOpen]);
5245
- React30.useEffect(() => {
6130
+ React31.useEffect(() => {
5246
6131
  if (isOpen) {
5247
6132
  setMounted(true);
5248
6133
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -5252,22 +6137,42 @@ var SelectRoot = (props) => {
5252
6137
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS3);
5253
6138
  return () => clearTimeout(t);
5254
6139
  }, [isOpen]);
5255
- const open = React30.useCallback(() => setOpen(true), []);
5256
- const close = React30.useCallback(() => setOpen(false), []);
5257
- const toggle = React30.useCallback(() => setOpen((prev) => !prev), []);
6140
+ const [query, setQuery] = React31.useState("");
6141
+ const textOf = (node) => {
6142
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
6143
+ if (typeof node === "string" || typeof node === "number") return String(node);
6144
+ if (Array.isArray(node)) return node.map(textOf).join("");
6145
+ if (React31.isValidElement(node)) return textOf(node.props.children);
6146
+ return "";
6147
+ };
6148
+ const visibleItems = React31.useMemo(() => {
6149
+ if (!searchable || !query.trim()) return itemChildren;
6150
+ const q = query.trim().toLowerCase();
6151
+ return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
6152
+ }, [searchable, query, itemChildren]);
6153
+ const open = React31.useCallback(() => setOpen(true), []);
6154
+ const close = React31.useCallback(() => {
6155
+ setOpen(false);
6156
+ setQuery("");
6157
+ }, []);
6158
+ const toggle = React31.useCallback(() => setOpen((prev) => !prev), []);
5258
6159
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
6160
+ useEscapeKey(isOpen, () => {
6161
+ close();
6162
+ triggerRef.current?.focus({ preventScroll: true });
6163
+ });
5259
6164
  const position = useAutoPosition_default(triggerRef, contentRef, mounted);
5260
- const setSelected = React30.useCallback(
5261
- (label, itemValue) => {
6165
+ const setSelected = React31.useCallback(
6166
+ (label2, itemValue) => {
5262
6167
  if (!isControlled) {
5263
- setUncontrolledLabel(label);
6168
+ setUncontrolledLabel(label2);
5264
6169
  setUncontrolledValue(itemValue);
5265
6170
  }
5266
- onChange?.(itemValue, label);
6171
+ onChange?.(itemValue, label2);
5267
6172
  },
5268
6173
  [isControlled, onChange]
5269
6174
  );
5270
- const ctxValue = React30.useMemo(
6175
+ const ctxValue = React31.useMemo(
5271
6176
  () => ({
5272
6177
  isOpen,
5273
6178
  mounted,
@@ -5303,6 +6208,7 @@ var SelectRoot = (props) => {
5303
6208
  /* @__PURE__ */ jsxs216(
5304
6209
  "div",
5305
6210
  {
6211
+ id: fieldId,
5306
6212
  ref: triggerRef,
5307
6213
  className: clsx_default(
5308
6214
  "select-trigger",
@@ -5345,20 +6251,33 @@ var SelectRoot = (props) => {
5345
6251
  ]
5346
6252
  }
5347
6253
  ),
5348
- mounted && /* @__PURE__ */ jsx337(Portal_default, { children: /* @__PURE__ */ jsx337(
6254
+ mounted && /* @__PURE__ */ jsx337(Portal_default, { children: /* @__PURE__ */ jsxs216(
5349
6255
  "div",
5350
6256
  {
5351
- className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
6257
+ className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass, position.repositioning && "repositioning"),
5352
6258
  ref: contentRef,
5353
6259
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
5354
6260
  role: "listbox",
5355
- children: /* @__PURE__ */ jsx337(context_default.Provider, { value: ctxValue, children: itemChildren })
6261
+ children: [
6262
+ searchable && /* @__PURE__ */ jsx337("div", { className: "select-search", children: /* @__PURE__ */ jsx337(
6263
+ "input",
6264
+ {
6265
+ type: "text",
6266
+ value: query,
6267
+ placeholder: searchPlaceholder,
6268
+ onChange: (e) => setQuery(e.target.value),
6269
+ autoFocus: true,
6270
+ "aria-label": searchPlaceholder
6271
+ }
6272
+ ) }),
6273
+ /* @__PURE__ */ jsx337(context_default.Provider, { value: ctxValue, children: visibleItems.length > 0 ? visibleItems : /* @__PURE__ */ jsx337("div", { className: "select-empty", children: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }) })
6274
+ ]
5356
6275
  }
5357
6276
  ) })
5358
6277
  ]
5359
6278
  }
5360
6279
  ) });
5361
- return /* @__PURE__ */ jsx337(Field, { validations, children: control });
6280
+ return /* @__PURE__ */ jsx337(Field, { validations, label, required, htmlFor: fieldId, children: control });
5362
6281
  };
5363
6282
  SelectRoot.displayName = "Select";
5364
6283
  var Select = Object.assign(SelectRoot, {
@@ -5438,8 +6357,44 @@ var Spinner = (props) => {
5438
6357
  Spinner.displayName = "Spinner";
5439
6358
  var Spinner_default = Spinner;
5440
6359
 
5441
- // src/components/Steps/Steps.tsx
6360
+ // src/components/Stat/Stat.tsx
5442
6361
  import { jsx as jsx340, jsxs as jsxs218 } from "react/jsx-runtime";
6362
+ var ARROW = {
6363
+ up: "\u25B2",
6364
+ down: "\u25BC",
6365
+ flat: "\u2014"
6366
+ };
6367
+ var Stat = (props) => {
6368
+ const {
6369
+ label,
6370
+ value,
6371
+ unit,
6372
+ description,
6373
+ trend,
6374
+ icon,
6375
+ size = "md",
6376
+ type = "primary"
6377
+ } = props;
6378
+ const statusType = normalizeStatus(type);
6379
+ return /* @__PURE__ */ jsxs218("div", { className: clsx_default("lib-xplat-stat", size, statusType), children: [
6380
+ /* @__PURE__ */ jsx340("div", { className: "stat-label", children: label }),
6381
+ /* @__PURE__ */ jsxs218("div", { className: "stat-value-row", children: [
6382
+ icon && /* @__PURE__ */ jsx340("span", { className: "stat-icon", children: icon }),
6383
+ /* @__PURE__ */ jsx340("span", { className: "stat-value", children: value }),
6384
+ unit && /* @__PURE__ */ jsx340("span", { className: "stat-unit", children: unit }),
6385
+ trend && /* @__PURE__ */ jsxs218("span", { className: clsx_default("stat-trend", trend.direction), children: [
6386
+ /* @__PURE__ */ jsx340("span", { "aria-hidden": "true", children: ARROW[trend.direction] }),
6387
+ trend.value
6388
+ ] })
6389
+ ] }),
6390
+ description && /* @__PURE__ */ jsx340("div", { className: "stat-description", children: description })
6391
+ ] });
6392
+ };
6393
+ Stat.displayName = "Stat";
6394
+ var Stat_default = Stat;
6395
+
6396
+ // src/components/Steps/Steps.tsx
6397
+ import { jsx as jsx341, jsxs as jsxs219 } from "react/jsx-runtime";
5443
6398
  var Steps = (props) => {
5444
6399
  const {
5445
6400
  items,
@@ -5447,13 +6402,13 @@ var Steps = (props) => {
5447
6402
  type = "primary"
5448
6403
  } = props;
5449
6404
  const statusType = normalizeStatus(type);
5450
- return /* @__PURE__ */ jsx340("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
6405
+ return /* @__PURE__ */ jsx341("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
5451
6406
  const status = index < current ? "completed" : index === current ? "active" : "pending";
5452
- return /* @__PURE__ */ jsxs218("div", { className: clsx_default("step-item", status), children: [
5453
- /* @__PURE__ */ jsx340("div", { className: "step-circle", children: status === "completed" ? /* @__PURE__ */ jsx340(CheckIcon_default, {}) : /* @__PURE__ */ jsx340("span", { children: index + 1 }) }),
5454
- /* @__PURE__ */ jsxs218("div", { className: "step-content", children: [
5455
- /* @__PURE__ */ jsx340("span", { className: "step-title", children: item.title }),
5456
- item.description && /* @__PURE__ */ jsx340("span", { className: "step-description", children: item.description })
6407
+ return /* @__PURE__ */ jsxs219("div", { className: clsx_default("step-item", status), children: [
6408
+ /* @__PURE__ */ jsx341("div", { className: "step-circle", children: status === "completed" ? /* @__PURE__ */ jsx341(CheckIcon_default, {}) : /* @__PURE__ */ jsx341("span", { children: index + 1 }) }),
6409
+ /* @__PURE__ */ jsxs219("div", { className: "step-content", children: [
6410
+ /* @__PURE__ */ jsx341("span", { className: "step-title", children: item.title }),
6411
+ item.description && /* @__PURE__ */ jsx341("span", { className: "step-description", children: item.description })
5457
6412
  ] })
5458
6413
  ] }, index);
5459
6414
  }) });
@@ -5462,8 +6417,8 @@ Steps.displayName = "Steps";
5462
6417
  var Steps_default = Steps;
5463
6418
 
5464
6419
  // src/components/Swiper/Swiper.tsx
5465
- import React31 from "react";
5466
- import { jsx as jsx341, jsxs as jsxs219 } from "react/jsx-runtime";
6420
+ import React32 from "react";
6421
+ import { jsx as jsx342, jsxs as jsxs220 } from "react/jsx-runtime";
5467
6422
  var Swiper = (props) => {
5468
6423
  const {
5469
6424
  auto = false,
@@ -5487,26 +6442,26 @@ var Swiper = (props) => {
5487
6442
  const maxIndex = Math.max(0, totalSlides - viewItemCount);
5488
6443
  const useLoop = loop && canSlide;
5489
6444
  const cloneCount = useLoop ? totalSlides : 0;
5490
- const extendedItems = React31.useMemo(() => {
6445
+ const extendedItems = React32.useMemo(() => {
5491
6446
  if (!useLoop) return items;
5492
6447
  return [...items, ...items, ...items];
5493
6448
  }, [items, useLoop]);
5494
6449
  const initialIdx = Math.max(0, Math.min(indexProp ?? 0, maxIndex));
5495
- const [innerIndex, setInnerIndex] = React31.useState(
6450
+ const [innerIndex, setInnerIndex] = React32.useState(
5496
6451
  useLoop ? cloneCount + initialIdx : initialIdx
5497
6452
  );
5498
- const [isDragging, setIsDragging] = React31.useState(false);
5499
- const [dragOffset, setDragOffset] = React31.useState(0);
5500
- const [animated, setAnimated] = React31.useState(false);
5501
- const [containerWidth, setContainerWidth] = React31.useState(0);
5502
- const containerRef = React31.useRef(null);
5503
- const startXRef = React31.useRef(0);
5504
- const startTimeRef = React31.useRef(0);
5505
- const autoplayTimerRef = React31.useRef(null);
5506
- const resumeTimeoutRef = React31.useRef(null);
5507
- const [paused, setPaused] = React31.useState(false);
5508
- const initializedRef = React31.useRef(false);
5509
- React31.useEffect(() => {
6453
+ const [isDragging, setIsDragging] = React32.useState(false);
6454
+ const [dragOffset, setDragOffset] = React32.useState(0);
6455
+ const [animated, setAnimated] = React32.useState(false);
6456
+ const [containerWidth, setContainerWidth] = React32.useState(0);
6457
+ const containerRef = React32.useRef(null);
6458
+ const startXRef = React32.useRef(0);
6459
+ const startTimeRef = React32.useRef(0);
6460
+ const autoplayTimerRef = React32.useRef(null);
6461
+ const resumeTimeoutRef = React32.useRef(null);
6462
+ const [paused, setPaused] = React32.useState(false);
6463
+ const initializedRef = React32.useRef(false);
6464
+ React32.useEffect(() => {
5510
6465
  const el = containerRef.current;
5511
6466
  if (!el) return;
5512
6467
  const ro = new ResizeObserver((entries) => {
@@ -5529,7 +6484,7 @@ var Swiper = (props) => {
5529
6484
  return ((inner - cloneCount) % totalSlides + totalSlides) % totalSlides;
5530
6485
  };
5531
6486
  const realIndex = getRealIndex(innerIndex);
5532
- const moveToInner = React31.useCallback(
6487
+ const moveToInner = React32.useCallback(
5533
6488
  (idx, withAnim = true) => {
5534
6489
  if (!useLoop) {
5535
6490
  setAnimated(withAnim);
@@ -5557,7 +6512,7 @@ var Swiper = (props) => {
5557
6512
  },
5558
6513
  [useLoop, cloneCount, totalSlides]
5559
6514
  );
5560
- const handleTransitionEnd = React31.useCallback(() => {
6515
+ const handleTransitionEnd = React32.useCallback(() => {
5561
6516
  if (!useLoop) return;
5562
6517
  const real = getRealIndex(innerIndex);
5563
6518
  const canonical = cloneCount + real;
@@ -5567,7 +6522,7 @@ var Swiper = (props) => {
5567
6522
  }
5568
6523
  onChange?.(Math.min(real, maxIndex));
5569
6524
  }, [useLoop, innerIndex, cloneCount, totalSlides, maxIndex, onChange]);
5570
- const slideTo = React31.useCallback(
6525
+ const slideTo = React32.useCallback(
5571
6526
  (logicalIndex) => {
5572
6527
  if (!canSlide) return;
5573
6528
  const clamped = useLoop ? logicalIndex : Math.max(0, Math.min(logicalIndex, maxIndex));
@@ -5577,7 +6532,7 @@ var Swiper = (props) => {
5577
6532
  },
5578
6533
  [canSlide, useLoop, cloneCount, maxIndex, onChange, moveToInner]
5579
6534
  );
5580
- const slideNext = React31.useCallback(() => {
6535
+ const slideNext = React32.useCallback(() => {
5581
6536
  if (!canSlide) return;
5582
6537
  const nextInner = innerIndex + slideBy;
5583
6538
  if (useLoop) {
@@ -5586,7 +6541,7 @@ var Swiper = (props) => {
5586
6541
  moveToInner(Math.min(nextInner, maxIndex), true);
5587
6542
  }
5588
6543
  }, [canSlide, useLoop, innerIndex, slideBy, maxIndex, moveToInner]);
5589
- const slidePrev = React31.useCallback(() => {
6544
+ const slidePrev = React32.useCallback(() => {
5590
6545
  if (!canSlide) return;
5591
6546
  const prevInner = innerIndex - slideBy;
5592
6547
  if (useLoop) {
@@ -5595,7 +6550,7 @@ var Swiper = (props) => {
5595
6550
  moveToInner(Math.max(prevInner, 0), true);
5596
6551
  }
5597
6552
  }, [canSlide, useLoop, innerIndex, slideBy, moveToInner]);
5598
- React31.useEffect(() => {
6553
+ React32.useEffect(() => {
5599
6554
  if (indexProp === void 0) return;
5600
6555
  const clamped = Math.max(0, Math.min(indexProp, maxIndex));
5601
6556
  const target = useLoop ? cloneCount + clamped : clamped;
@@ -5603,12 +6558,12 @@ var Swiper = (props) => {
5603
6558
  moveToInner(target, true);
5604
6559
  }
5605
6560
  }, [indexProp]);
5606
- React31.useImperativeHandle(swiperRef, () => ({
6561
+ React32.useImperativeHandle(swiperRef, () => ({
5607
6562
  slidePrev,
5608
6563
  slideNext,
5609
6564
  slideTo
5610
6565
  }));
5611
- React31.useEffect(() => {
6566
+ React32.useEffect(() => {
5612
6567
  if (!auto || !canSlide || paused) return;
5613
6568
  autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
5614
6569
  return () => {
@@ -5634,7 +6589,7 @@ var Swiper = (props) => {
5634
6589
  resumeTimeoutRef.current = null;
5635
6590
  }, pauseOnInteraction);
5636
6591
  };
5637
- React31.useEffect(() => {
6592
+ React32.useEffect(() => {
5638
6593
  return () => {
5639
6594
  if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
5640
6595
  };
@@ -5652,7 +6607,7 @@ var Swiper = (props) => {
5652
6607
  startXRef.current = getClientX(e);
5653
6608
  startTimeRef.current = Date.now();
5654
6609
  };
5655
- React31.useEffect(() => {
6610
+ React32.useEffect(() => {
5656
6611
  if (!isDragging) return;
5657
6612
  const handleMove = (e) => {
5658
6613
  setDragOffset(getClientX(e) - startXRef.current);
@@ -5691,8 +6646,8 @@ var Swiper = (props) => {
5691
6646
  }, [isDragging, dragOffset, innerIndex, useLoop, maxIndex, slideStep, moveToInner]);
5692
6647
  const slideWidthPercent = 100 / viewItemCount;
5693
6648
  const gapAdjust = spaceBetween * (viewItemCount - 1) / viewItemCount;
5694
- const slideElements = React31.useMemo(
5695
- () => extendedItems.map((item, idx) => /* @__PURE__ */ jsx341(
6649
+ const slideElements = React32.useMemo(
6650
+ () => extendedItems.map((item, idx) => /* @__PURE__ */ jsx342(
5696
6651
  "div",
5697
6652
  {
5698
6653
  className: "lib-xplat-swiper__slide",
@@ -5711,15 +6666,15 @@ var Swiper = (props) => {
5711
6666
  Math.floor(realIndex / slideBy),
5712
6667
  totalSteps - 1
5713
6668
  );
5714
- return /* @__PURE__ */ jsxs219("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
5715
- /* @__PURE__ */ jsx341(
6669
+ return /* @__PURE__ */ jsxs220("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
6670
+ /* @__PURE__ */ jsx342(
5716
6671
  "div",
5717
6672
  {
5718
6673
  className: "lib-xplat-swiper__viewport",
5719
6674
  onMouseDown: handleDragStart,
5720
6675
  onTouchStart: handleDragStart,
5721
6676
  onDragStart: (e) => e.preventDefault(),
5722
- children: /* @__PURE__ */ jsx341(
6677
+ children: /* @__PURE__ */ jsx342(
5723
6678
  "div",
5724
6679
  {
5725
6680
  className: clsx_default(
@@ -5737,7 +6692,7 @@ var Swiper = (props) => {
5737
6692
  )
5738
6693
  }
5739
6694
  ),
5740
- showProgress && canSlide && /* @__PURE__ */ jsx341("div", { className: "lib-xplat-swiper__progress", children: /* @__PURE__ */ jsx341("div", { className: "lib-xplat-swiper__progress-track", children: /* @__PURE__ */ jsx341(
6695
+ showProgress && canSlide && /* @__PURE__ */ jsx342("div", { className: "lib-xplat-swiper__progress", children: /* @__PURE__ */ jsx342("div", { className: "lib-xplat-swiper__progress-track", children: /* @__PURE__ */ jsx342(
5741
6696
  "span",
5742
6697
  {
5743
6698
  className: "lib-xplat-swiper__progress-fill",
@@ -5747,7 +6702,7 @@ var Swiper = (props) => {
5747
6702
  }
5748
6703
  }
5749
6704
  ) }) }),
5750
- canSlide && /* @__PURE__ */ jsx341("div", { className: "lib-xplat-swiper__dots", children: Array.from({ length: totalSteps }, (_, i) => /* @__PURE__ */ jsx341(
6705
+ canSlide && /* @__PURE__ */ jsx342("div", { className: "lib-xplat-swiper__dots", children: Array.from({ length: totalSteps }, (_, i) => /* @__PURE__ */ jsx342(
5751
6706
  "button",
5752
6707
  {
5753
6708
  className: clsx_default(
@@ -5765,8 +6720,8 @@ Swiper.displayName = "Swiper";
5765
6720
  var Swiper_default = Swiper;
5766
6721
 
5767
6722
  // src/components/Switch/Switch.tsx
5768
- import React32 from "react";
5769
- import { jsx as jsx342 } from "react/jsx-runtime";
6723
+ import React33 from "react";
6724
+ import { jsx as jsx343 } from "react/jsx-runtime";
5770
6725
  var KNOB_TRANSITION_MS = 250;
5771
6726
  var Switch = (props) => {
5772
6727
  const {
@@ -5775,12 +6730,15 @@ var Switch = (props) => {
5775
6730
  type = "primary",
5776
6731
  disabled,
5777
6732
  onChange,
5778
- validations
6733
+ validations,
6734
+ label,
6735
+ required
5779
6736
  } = props;
6737
+ const fieldId = React33.useId();
5780
6738
  const statusType = normalizeStatus(type);
5781
- const [isAnimating, setIsAnimating] = React32.useState(false);
5782
- const timeoutRef = React32.useRef(null);
5783
- React32.useEffect(() => {
6739
+ const [isAnimating, setIsAnimating] = React33.useState(false);
6740
+ const timeoutRef = React33.useRef(null);
6741
+ React33.useEffect(() => {
5784
6742
  return () => {
5785
6743
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5786
6744
  };
@@ -5795,9 +6753,10 @@ var Switch = (props) => {
5795
6753
  timeoutRef.current = null;
5796
6754
  }, KNOB_TRANSITION_MS);
5797
6755
  };
5798
- const control = /* @__PURE__ */ jsx342(
6756
+ const control = /* @__PURE__ */ jsx343(
5799
6757
  "div",
5800
6758
  {
6759
+ id: fieldId,
5801
6760
  className: clsx_default(
5802
6761
  "lib-xplat-switch",
5803
6762
  size,
@@ -5809,26 +6768,26 @@ var Switch = (props) => {
5809
6768
  onClick: handleClick,
5810
6769
  "aria-invalid": hasError(validations) || void 0,
5811
6770
  "aria-disabled": disabled || isAnimating,
5812
- children: /* @__PURE__ */ jsx342("div", { className: clsx_default("knob", value ? "checked" : void 0) })
6771
+ children: /* @__PURE__ */ jsx343("div", { className: clsx_default("knob", value ? "checked" : void 0) })
5813
6772
  }
5814
6773
  );
5815
- return /* @__PURE__ */ jsx342(Field, { validations, children: control });
6774
+ return /* @__PURE__ */ jsx343(Field, { validations, label, required, htmlFor: fieldId, children: control });
5816
6775
  };
5817
6776
  Switch.displayName = "Switch";
5818
6777
  var Switch_default = Switch;
5819
6778
 
5820
6779
  // src/components/Table/TableContext.tsx
5821
- import React33 from "react";
5822
- var TableContext = React33.createContext(null);
6780
+ import React34 from "react";
6781
+ var TableContext = React34.createContext(null);
5823
6782
  var useTableContext = () => {
5824
- const ctx = React33.useContext(TableContext);
6783
+ const ctx = React34.useContext(TableContext);
5825
6784
  if (!ctx) throw new Error("Table components must be used inside <Table>");
5826
6785
  return ctx;
5827
6786
  };
5828
6787
  var TableContext_default = TableContext;
5829
6788
 
5830
6789
  // src/components/Table/Table.tsx
5831
- import { jsx as jsx343 } from "react/jsx-runtime";
6790
+ import { jsx as jsx344 } from "react/jsx-runtime";
5832
6791
  var Table = (props) => {
5833
6792
  const {
5834
6793
  children,
@@ -5838,7 +6797,7 @@ var Table = (props) => {
5838
6797
  headerSticky = false,
5839
6798
  stickyShadow = true
5840
6799
  } = props;
5841
- return /* @__PURE__ */ jsx343("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ jsx343(
6800
+ return /* @__PURE__ */ jsx344("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ jsx344(
5842
6801
  TableContext_default.Provider,
5843
6802
  {
5844
6803
  value: {
@@ -5847,7 +6806,7 @@ var Table = (props) => {
5847
6806
  headerSticky,
5848
6807
  stickyShadow
5849
6808
  },
5850
- children: /* @__PURE__ */ jsx343("table", { className: "lib-xplat-table", children })
6809
+ children: /* @__PURE__ */ jsx344("table", { className: "lib-xplat-table", children })
5851
6810
  }
5852
6811
  ) });
5853
6812
  };
@@ -5855,41 +6814,41 @@ Table.displayName = "Table";
5855
6814
  var Table_default = Table;
5856
6815
 
5857
6816
  // src/components/Table/TableBody.tsx
5858
- import { jsx as jsx344 } from "react/jsx-runtime";
6817
+ import { jsx as jsx345 } from "react/jsx-runtime";
5859
6818
  var TableBody = (props) => {
5860
6819
  const { children } = props;
5861
- return /* @__PURE__ */ jsx344("tbody", { children });
6820
+ return /* @__PURE__ */ jsx345("tbody", { children });
5862
6821
  };
5863
6822
  TableBody.displayName = "TableBody";
5864
6823
  var TableBody_default = TableBody;
5865
6824
 
5866
6825
  // src/components/Table/TableCell.tsx
5867
- import React36 from "react";
6826
+ import React37 from "react";
5868
6827
 
5869
6828
  // src/components/Table/TableHeadContext.tsx
5870
- import React34 from "react";
5871
- var TableHeadContext = React34.createContext(
6829
+ import React35 from "react";
6830
+ var TableHeadContext = React35.createContext(
5872
6831
  null
5873
6832
  );
5874
6833
  var useTableHeadContext = () => {
5875
- const ctx = React34.useContext(TableHeadContext);
6834
+ const ctx = React35.useContext(TableHeadContext);
5876
6835
  return ctx;
5877
6836
  };
5878
6837
  var TableHeadContext_default = TableHeadContext;
5879
6838
 
5880
6839
  // src/components/Table/TableRowContext.tsx
5881
- import React35 from "react";
5882
- var TableRowContext = React35.createContext(null);
6840
+ import React36 from "react";
6841
+ var TableRowContext = React36.createContext(null);
5883
6842
  var useTableRowContext = () => {
5884
- const ctx = React35.useContext(TableRowContext);
6843
+ const ctx = React36.useContext(TableRowContext);
5885
6844
  if (!ctx) throw new Error("Table components must be used inside <Table>");
5886
6845
  return ctx;
5887
6846
  };
5888
6847
  var TableRowContext_default = TableRowContext;
5889
6848
 
5890
6849
  // src/components/Table/TableCell.tsx
5891
- import { jsx as jsx345 } from "react/jsx-runtime";
5892
- var TableCell = React36.memo((props) => {
6850
+ import { jsx as jsx346 } from "react/jsx-runtime";
6851
+ var TableCell = React37.memo((props) => {
5893
6852
  const {
5894
6853
  children,
5895
6854
  align = "center",
@@ -5903,9 +6862,9 @@ var TableCell = React36.memo((props) => {
5903
6862
  const { registerStickyCell, stickyCells } = useTableRowContext();
5904
6863
  const { stickyShadow } = useTableContext();
5905
6864
  const headContext = useTableHeadContext();
5906
- const [left, setLeft] = React36.useState(0);
5907
- const cellRef = React36.useRef(null);
5908
- const calculateLeft = React36.useCallback(() => {
6865
+ const [left, setLeft] = React37.useState(0);
6866
+ const cellRef = React37.useRef(null);
6867
+ const calculateLeft = React37.useCallback(() => {
5909
6868
  if (!cellRef.current) return 0;
5910
6869
  let totalLeft = 0;
5911
6870
  for (const ref of stickyCells) {
@@ -5914,7 +6873,7 @@ var TableCell = React36.memo((props) => {
5914
6873
  }
5915
6874
  return totalLeft;
5916
6875
  }, [stickyCells]);
5917
- React36.useEffect(() => {
6876
+ React37.useEffect(() => {
5918
6877
  if (!isSticky || !cellRef.current) return;
5919
6878
  registerStickyCell(cellRef);
5920
6879
  setLeft(calculateLeft());
@@ -5933,7 +6892,7 @@ var TableCell = React36.memo((props) => {
5933
6892
  const CellTag = isHeaderCell ? "th" : "td";
5934
6893
  const isLastSticky = isSticky && stickyCells[stickyCells.length - 1] === cellRef;
5935
6894
  const enableHover = headContext && headContext.cellHover;
5936
- return /* @__PURE__ */ jsx345(
6895
+ return /* @__PURE__ */ jsx346(
5937
6896
  CellTag,
5938
6897
  {
5939
6898
  ref: cellRef,
@@ -5960,21 +6919,21 @@ TableCell.displayName = "TableCell";
5960
6919
  var TableCell_default = TableCell;
5961
6920
 
5962
6921
  // src/components/Table/TableHead.tsx
5963
- import { jsx as jsx346 } from "react/jsx-runtime";
6922
+ import { jsx as jsx347 } from "react/jsx-runtime";
5964
6923
  var TableHead = ({
5965
6924
  children,
5966
6925
  cellHover = false
5967
6926
  }) => {
5968
6927
  const { headerSticky } = useTableContext();
5969
- return /* @__PURE__ */ jsx346(TableHeadContext_default.Provider, { value: { cellHover }, children: /* @__PURE__ */ jsx346("thead", { className: clsx_default(headerSticky ? "table-sticky" : null), children }) });
6928
+ return /* @__PURE__ */ jsx347(TableHeadContext_default.Provider, { value: { cellHover }, children: /* @__PURE__ */ jsx347("thead", { className: clsx_default(headerSticky ? "table-sticky" : null), children }) });
5970
6929
  };
5971
6930
  TableHead.displayName = "TableHead";
5972
6931
  var TableHead_default = TableHead;
5973
6932
 
5974
6933
  // src/components/Table/TableRow.tsx
5975
- import React37 from "react";
5976
- import { jsx as jsx347 } from "react/jsx-runtime";
5977
- var TableRow = React37.memo((props) => {
6934
+ import React38 from "react";
6935
+ import { jsx as jsx348 } from "react/jsx-runtime";
6936
+ var TableRow = React38.memo((props) => {
5978
6937
  const {
5979
6938
  children,
5980
6939
  type = "secondary",
@@ -5983,14 +6942,14 @@ var TableRow = React37.memo((props) => {
5983
6942
  onClick
5984
6943
  } = props;
5985
6944
  const { rowBorderUse } = useTableContext();
5986
- const [stickyCells, setStickyCells] = React37.useState([]);
6945
+ const [stickyCells, setStickyCells] = React38.useState([]);
5987
6946
  const registerStickyCell = (ref) => {
5988
6947
  setStickyCells((prev) => {
5989
6948
  if (prev.includes(ref)) return prev;
5990
6949
  return [...prev, ref];
5991
6950
  });
5992
6951
  };
5993
- return /* @__PURE__ */ jsx347(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ jsx347(
6952
+ return /* @__PURE__ */ jsx348(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ jsx348(
5994
6953
  "tr",
5995
6954
  {
5996
6955
  className: clsx_default(
@@ -6014,7 +6973,7 @@ TableRow.displayName = "TableRow";
6014
6973
  var TableRow_default = TableRow;
6015
6974
 
6016
6975
  // src/components/Tag/Tag.tsx
6017
- import { jsx as jsx348, jsxs as jsxs220 } from "react/jsx-runtime";
6976
+ import { jsx as jsx349, jsxs as jsxs221 } from "react/jsx-runtime";
6018
6977
  var Tag = (props) => {
6019
6978
  const {
6020
6979
  children,
@@ -6026,7 +6985,7 @@ var Tag = (props) => {
6026
6985
  colorIndex
6027
6986
  } = props;
6028
6987
  const shape = variant ?? type ?? "neutral";
6029
- return /* @__PURE__ */ jsxs220(
6988
+ return /* @__PURE__ */ jsxs221(
6030
6989
  "span",
6031
6990
  {
6032
6991
  className: clsx_default(
@@ -6037,8 +6996,8 @@ var Tag = (props) => {
6037
6996
  disabled && "disabled"
6038
6997
  ),
6039
6998
  children: [
6040
- /* @__PURE__ */ jsx348("span", { className: "tag-label", children }),
6041
- onClose && /* @__PURE__ */ jsx348("button", { className: "tag-close", onClick: onClose, "aria-label": "\uC0AD\uC81C", disabled, children: /* @__PURE__ */ jsx348(XIcon_default, {}) })
6999
+ /* @__PURE__ */ jsx349("span", { className: "tag-label", children }),
7000
+ onClose && /* @__PURE__ */ jsx349("button", { className: "tag-close", onClick: onClose, "aria-label": "\uC0AD\uC81C", disabled, children: /* @__PURE__ */ jsx349(XIcon_default, {}) })
6042
7001
  ]
6043
7002
  }
6044
7003
  );
@@ -6047,24 +7006,29 @@ Tag.displayName = "Tag";
6047
7006
  var Tag_default = Tag;
6048
7007
 
6049
7008
  // src/components/TextArea/TextArea.tsx
6050
- import React38 from "react";
6051
- import { jsx as jsx349 } from "react/jsx-runtime";
6052
- var TextArea = React38.forwardRef(
7009
+ import React39 from "react";
7010
+ import { jsx as jsx350 } from "react/jsx-runtime";
7011
+ var TextArea = React39.forwardRef(
6053
7012
  (props, ref) => {
6054
7013
  const {
6055
7014
  className,
6056
7015
  size = "md",
6057
7016
  validations,
7017
+ label,
7018
+ required,
6058
7019
  resize = "vertical",
6059
7020
  rows = 3,
6060
7021
  disabled,
6061
7022
  ...textareaProps
6062
7023
  } = props;
6063
- const control = /* @__PURE__ */ jsx349("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ jsx349(
7024
+ const autoId = React39.useId();
7025
+ const fieldId = props.id ?? autoId;
7026
+ const control = /* @__PURE__ */ jsx350("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ jsx350(
6064
7027
  "textarea",
6065
7028
  {
6066
7029
  ...textareaProps,
6067
7030
  ref,
7031
+ id: fieldId,
6068
7032
  rows,
6069
7033
  disabled,
6070
7034
  "aria-invalid": hasError(validations) || void 0,
@@ -6072,32 +7036,166 @@ var TextArea = React38.forwardRef(
6072
7036
  style: { resize }
6073
7037
  }
6074
7038
  ) });
6075
- return /* @__PURE__ */ jsx349(Field, { validations, children: control });
7039
+ return /* @__PURE__ */ jsx350(Field, { validations, label, required, htmlFor: fieldId, children: control });
6076
7040
  }
6077
7041
  );
6078
7042
  TextArea.displayName = "TextArea";
6079
7043
  var TextArea_default = TextArea;
6080
7044
 
7045
+ // src/components/TimePicker/TimePicker.tsx
7046
+ import React40 from "react";
7047
+ import { jsx as jsx351, jsxs as jsxs222 } from "react/jsx-runtime";
7048
+ var ANIMATION_DURATION_MS4 = 200;
7049
+ var toMinutes = (hhmm) => {
7050
+ if (!hhmm) return null;
7051
+ const m = /^(\d{1,2}):(\d{2})$/.exec(hhmm.trim());
7052
+ if (!m) return null;
7053
+ const h = Number(m[1]);
7054
+ const min = Number(m[2]);
7055
+ if (h > 23 || min > 59) return null;
7056
+ return h * 60 + min;
7057
+ };
7058
+ var toHHmm = (minutes) => `${String(Math.floor(minutes / 60)).padStart(2, "0")}:${String(minutes % 60).padStart(2, "0")}`;
7059
+ var display = (hhmm, use12Hours) => {
7060
+ if (!use12Hours) return hhmm;
7061
+ const total = toMinutes(hhmm);
7062
+ if (total === null) return hhmm;
7063
+ const h24 = Math.floor(total / 60);
7064
+ const mm = String(total % 60).padStart(2, "0");
7065
+ const period = h24 < 12 ? "\uC624\uC804" : "\uC624\uD6C4";
7066
+ const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
7067
+ return `${period} ${h12}:${mm}`;
7068
+ };
7069
+ var TimePicker = (props) => {
7070
+ const {
7071
+ value,
7072
+ onChange,
7073
+ step = 30,
7074
+ min,
7075
+ max,
7076
+ use12Hours = false,
7077
+ placeholder = "\uC2DC\uAC01 \uC120\uD0DD",
7078
+ disabled = false,
7079
+ size = "md",
7080
+ label,
7081
+ required,
7082
+ validations
7083
+ } = props;
7084
+ const fieldId = React40.useId();
7085
+ const triggerRef = React40.useRef(null);
7086
+ const contentRef = React40.useRef(null);
7087
+ const [isOpen, setOpen] = React40.useState(false);
7088
+ const [mounted, setMounted] = React40.useState(false);
7089
+ const [visible, setVisible] = React40.useState(false);
7090
+ const options = React40.useMemo(() => {
7091
+ const safeStep = step > 0 && step <= 720 ? step : 30;
7092
+ const lo = toMinutes(min) ?? 0;
7093
+ const hi = toMinutes(max) ?? 24 * 60 - 1;
7094
+ const out = [];
7095
+ for (let t = lo; t <= hi; t += safeStep) out.push(toHHmm(t));
7096
+ return out;
7097
+ }, [step, min, max]);
7098
+ const close = React40.useCallback(() => setOpen(false), []);
7099
+ useClickOutside_default([triggerRef, contentRef], close, isOpen);
7100
+ useEscapeKey(isOpen, () => {
7101
+ close();
7102
+ triggerRef.current?.focus({ preventScroll: true });
7103
+ });
7104
+ const position = useAutoPosition_default(triggerRef, contentRef, mounted);
7105
+ React40.useEffect(() => {
7106
+ if (disabled && isOpen) setOpen(false);
7107
+ }, [disabled, isOpen]);
7108
+ React40.useEffect(() => {
7109
+ if (isOpen) {
7110
+ setMounted(true);
7111
+ const t2 = setTimeout(() => setVisible(true), 1);
7112
+ return () => clearTimeout(t2);
7113
+ }
7114
+ setVisible(false);
7115
+ const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS4);
7116
+ return () => clearTimeout(t);
7117
+ }, [isOpen]);
7118
+ const pick = (hhmm) => {
7119
+ onChange?.(hhmm);
7120
+ close();
7121
+ triggerRef.current?.focus({ preventScroll: true });
7122
+ };
7123
+ const control = /* @__PURE__ */ jsxs222("div", { className: clsx_default("lib-xplat-time-picker", size, disabled && "disabled"), children: [
7124
+ /* @__PURE__ */ jsx351(
7125
+ "div",
7126
+ {
7127
+ id: fieldId,
7128
+ ref: triggerRef,
7129
+ className: clsx_default("time-picker-trigger", hasError(validations) && "error"),
7130
+ role: "combobox",
7131
+ "aria-expanded": isOpen,
7132
+ "aria-haspopup": "listbox",
7133
+ "aria-invalid": hasError(validations) || void 0,
7134
+ tabIndex: disabled ? void 0 : 0,
7135
+ onClick: () => !disabled && setOpen((p) => !p),
7136
+ onKeyDown: (e) => {
7137
+ if (disabled) return;
7138
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
7139
+ e.preventDefault();
7140
+ setOpen(true);
7141
+ }
7142
+ },
7143
+ children: /* @__PURE__ */ jsx351("span", { className: clsx_default("time-picker-value", !value && "placeholder"), children: value ? display(value, use12Hours) : placeholder })
7144
+ }
7145
+ ),
7146
+ mounted && /* @__PURE__ */ jsx351(Portal_default, { children: /* @__PURE__ */ jsx351(
7147
+ "div",
7148
+ {
7149
+ ref: contentRef,
7150
+ className: clsx_default("lib-xplat-time-picker-content", size, visible ? "enter" : "exit", position.repositioning && "repositioning"),
7151
+ style: { ...position.position, width: triggerRef.current?.offsetWidth },
7152
+ role: "listbox",
7153
+ children: options.map((t) => /* @__PURE__ */ jsx351(
7154
+ "div",
7155
+ {
7156
+ className: clsx_default("time-picker-item", t === value && "is-selected"),
7157
+ role: "option",
7158
+ "aria-selected": t === value,
7159
+ tabIndex: 0,
7160
+ onClick: () => pick(t),
7161
+ onKeyDown: (e) => {
7162
+ if (e.key === "Enter" || e.key === " ") {
7163
+ e.preventDefault();
7164
+ pick(t);
7165
+ }
7166
+ },
7167
+ children: display(t, use12Hours)
7168
+ },
7169
+ t
7170
+ ))
7171
+ }
7172
+ ) })
7173
+ ] });
7174
+ return /* @__PURE__ */ jsx351(Field, { validations, label, required, htmlFor: fieldId, children: control });
7175
+ };
7176
+ TimePicker.displayName = "TimePicker";
7177
+ var TimePicker_default = TimePicker;
7178
+
6081
7179
  // src/components/Toast/Toast.tsx
6082
- import React39 from "react";
7180
+ import React41 from "react";
6083
7181
  import { createPortal as createPortal3 } from "react-dom";
6084
- import { jsx as jsx350, jsxs as jsxs221 } from "react/jsx-runtime";
6085
- var ToastContext = React39.createContext(null);
7182
+ import { jsx as jsx352, jsxs as jsxs223 } from "react/jsx-runtime";
7183
+ var ToastContext = React41.createContext(null);
6086
7184
  var useToast = () => {
6087
- const ctx = React39.useContext(ToastContext);
7185
+ const ctx = React41.useContext(ToastContext);
6088
7186
  if (!ctx) throw new Error("useToast must be used within ToastProvider");
6089
7187
  return ctx;
6090
7188
  };
6091
7189
  var EXIT_DURATION = 300;
6092
7190
  var ToastItemComponent = ({ item, isExiting, onClose }) => {
6093
- const ref = React39.useRef(null);
6094
- const [height, setHeight] = React39.useState(void 0);
6095
- React39.useEffect(() => {
7191
+ const ref = React41.useRef(null);
7192
+ const [height, setHeight] = React41.useState(void 0);
7193
+ React41.useEffect(() => {
6096
7194
  if (ref.current && !isExiting) {
6097
7195
  setHeight(ref.current.offsetHeight);
6098
7196
  }
6099
7197
  }, [isExiting]);
6100
- return /* @__PURE__ */ jsx350(
7198
+ return /* @__PURE__ */ jsx352(
6101
7199
  "div",
6102
7200
  {
6103
7201
  className: clsx_default("lib-xplat-toast-wrapper", { exit: isExiting }),
@@ -6105,15 +7203,15 @@ var ToastItemComponent = ({ item, isExiting, onClose }) => {
6105
7203
  maxHeight: isExiting ? 0 : height ?? "none",
6106
7204
  marginBottom: isExiting ? 0 : void 0
6107
7205
  },
6108
- children: /* @__PURE__ */ jsxs221(
7206
+ children: /* @__PURE__ */ jsxs223(
6109
7207
  "div",
6110
7208
  {
6111
7209
  ref,
6112
7210
  className: clsx_default("lib-xplat-toast", item.type, { exit: isExiting }),
6113
7211
  role: "alert",
6114
7212
  children: [
6115
- /* @__PURE__ */ jsx350("span", { className: "message", children: item.message }),
6116
- /* @__PURE__ */ jsx350("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
7213
+ /* @__PURE__ */ jsx352("span", { className: "message", children: item.message }),
7214
+ /* @__PURE__ */ jsx352("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
6117
7215
  ]
6118
7216
  }
6119
7217
  )
@@ -6124,13 +7222,13 @@ var ToastProvider = ({
6124
7222
  children,
6125
7223
  position = "top-right"
6126
7224
  }) => {
6127
- const [toasts, setToasts] = React39.useState([]);
6128
- const [removing, setRemoving] = React39.useState(/* @__PURE__ */ new Set());
6129
- const [mounted, setMounted] = React39.useState(false);
6130
- React39.useEffect(() => {
7225
+ const [toasts, setToasts] = React41.useState([]);
7226
+ const [removing, setRemoving] = React41.useState(/* @__PURE__ */ new Set());
7227
+ const [mounted, setMounted] = React41.useState(false);
7228
+ React41.useEffect(() => {
6131
7229
  setMounted(true);
6132
7230
  }, []);
6133
- const remove = React39.useCallback((id) => {
7231
+ const remove = React41.useCallback((id) => {
6134
7232
  setRemoving((prev) => new Set(prev).add(id));
6135
7233
  setTimeout(() => {
6136
7234
  setToasts((prev) => prev.filter((t) => t.id !== id));
@@ -6141,7 +7239,7 @@ var ToastProvider = ({
6141
7239
  });
6142
7240
  }, EXIT_DURATION);
6143
7241
  }, []);
6144
- const toast = React39.useCallback(
7242
+ const toast = React41.useCallback(
6145
7243
  (type, message, duration = 3e3) => {
6146
7244
  const id = `${Date.now()}-${Math.random()}`;
6147
7245
  setToasts((prev) => [...prev, { id, type, message }]);
@@ -6151,10 +7249,10 @@ var ToastProvider = ({
6151
7249
  },
6152
7250
  [remove]
6153
7251
  );
6154
- return /* @__PURE__ */ jsxs221(ToastContext.Provider, { value: { toast }, children: [
7252
+ return /* @__PURE__ */ jsxs223(ToastContext.Provider, { value: { toast }, children: [
6155
7253
  children,
6156
7254
  mounted && createPortal3(
6157
- /* @__PURE__ */ jsx350("div", { className: clsx_default("lib-xplat-toast-container", position), children: toasts.map((t) => /* @__PURE__ */ jsx350(
7255
+ /* @__PURE__ */ jsx352("div", { className: clsx_default("lib-xplat-toast-container", position), children: toasts.map((t) => /* @__PURE__ */ jsx352(
6158
7256
  ToastItemComponent,
6159
7257
  {
6160
7258
  item: t,
@@ -6170,8 +7268,8 @@ var ToastProvider = ({
6170
7268
  ToastProvider.displayName = "ToastProvider";
6171
7269
 
6172
7270
  // src/components/Tooltip/Tooltip.tsx
6173
- import React40 from "react";
6174
- import { Fragment as Fragment6, jsx as jsx351, jsxs as jsxs222 } from "react/jsx-runtime";
7271
+ import React42 from "react";
7272
+ import { Fragment as Fragment6, jsx as jsx353, jsxs as jsxs224 } from "react/jsx-runtime";
6175
7273
  var OFFSET = 12;
6176
7274
  var GAP = 8;
6177
7275
  var SHOW_DELAY = 300;
@@ -6186,14 +7284,14 @@ var Tooltip = (props) => {
6186
7284
  disabled = false
6187
7285
  } = props;
6188
7286
  const shape = variant ?? type ?? "dark";
6189
- const triggerRef = React40.useRef(null);
6190
- const tooltipRef = React40.useRef(null);
6191
- const [visible, setVisible] = React40.useState(false);
6192
- const [pos, setPos] = React40.useState({ left: 0, top: 0 });
6193
- const delayTimer = React40.useRef(0);
6194
- const rafRef = React40.useRef(0);
6195
- const cursor = React40.useRef(null);
6196
- const clamp = React40.useCallback((left, top, w, h) => {
7287
+ const triggerRef = React42.useRef(null);
7288
+ const tooltipRef = React42.useRef(null);
7289
+ const [visible, setVisible] = React42.useState(false);
7290
+ const [pos, setPos] = React42.useState({ left: 0, top: 0 });
7291
+ const delayTimer = React42.useRef(0);
7292
+ const rafRef = React42.useRef(0);
7293
+ const cursor = React42.useRef(null);
7294
+ const clamp = React42.useCallback((left, top, w, h) => {
6197
7295
  const vw = window.innerWidth;
6198
7296
  const vh = window.innerHeight;
6199
7297
  return {
@@ -6201,7 +7299,7 @@ var Tooltip = (props) => {
6201
7299
  top: Math.min(Math.max(GAP, top), Math.max(GAP, vh - h - GAP))
6202
7300
  };
6203
7301
  }, []);
6204
- const placeAtCursor = React40.useCallback((x, y) => {
7302
+ const placeAtCursor = React42.useCallback((x, y) => {
6205
7303
  const el = tooltipRef.current;
6206
7304
  if (!el) return;
6207
7305
  const w = el.offsetWidth;
@@ -6212,7 +7310,7 @@ var Tooltip = (props) => {
6212
7310
  if (top < GAP) top = y + OFFSET;
6213
7311
  setPos(clamp(left, top, w, h));
6214
7312
  }, [clamp]);
6215
- const placeAtTrigger = React40.useCallback(() => {
7313
+ const placeAtTrigger = React42.useCallback(() => {
6216
7314
  const el = tooltipRef.current;
6217
7315
  const trigger = triggerRef.current;
6218
7316
  if (!el || !trigger) return;
@@ -6224,19 +7322,19 @@ var Tooltip = (props) => {
6224
7322
  const left = rect.left + rect.width / 2 - w / 2;
6225
7323
  setPos(clamp(left, top, w, h));
6226
7324
  }, [clamp]);
6227
- const reposition = React40.useCallback(() => {
7325
+ const reposition = React42.useCallback(() => {
6228
7326
  if (follow && cursor.current) {
6229
7327
  placeAtCursor(cursor.current.x, cursor.current.y);
6230
7328
  } else {
6231
7329
  placeAtTrigger();
6232
7330
  }
6233
7331
  }, [follow, placeAtCursor, placeAtTrigger]);
6234
- const handleMouseEnter = React40.useCallback((e) => {
7332
+ const handleMouseEnter = React42.useCallback((e) => {
6235
7333
  if (disabled) return;
6236
7334
  cursor.current = { x: e.clientX, y: e.clientY };
6237
7335
  delayTimer.current = window.setTimeout(() => setVisible(true), SHOW_DELAY);
6238
7336
  }, [disabled]);
6239
- const handleMouseMove = React40.useCallback((e) => {
7337
+ const handleMouseMove = React42.useCallback((e) => {
6240
7338
  cursor.current = { x: e.clientX, y: e.clientY };
6241
7339
  if (!visible || !follow) return;
6242
7340
  cancelAnimationFrame(rafRef.current);
@@ -6244,25 +7342,25 @@ var Tooltip = (props) => {
6244
7342
  if (cursor.current) placeAtCursor(cursor.current.x, cursor.current.y);
6245
7343
  });
6246
7344
  }, [visible, follow, placeAtCursor]);
6247
- const close = React40.useCallback(() => {
7345
+ const close = React42.useCallback(() => {
6248
7346
  window.clearTimeout(delayTimer.current);
6249
7347
  cancelAnimationFrame(rafRef.current);
6250
7348
  setVisible(false);
6251
7349
  }, []);
6252
- const handleMouseLeave = React40.useCallback(() => {
7350
+ const handleMouseLeave = React42.useCallback(() => {
6253
7351
  cursor.current = null;
6254
7352
  close();
6255
7353
  }, [close]);
6256
- const handleFocus = React40.useCallback(() => {
7354
+ const handleFocus = React42.useCallback(() => {
6257
7355
  if (disabled) return;
6258
7356
  cursor.current = null;
6259
7357
  setVisible(true);
6260
7358
  }, [disabled]);
6261
- React40.useLayoutEffect(() => {
7359
+ React42.useLayoutEffect(() => {
6262
7360
  if (!visible) return;
6263
7361
  reposition();
6264
7362
  }, [visible, reposition]);
6265
- React40.useEffect(() => {
7363
+ React42.useEffect(() => {
6266
7364
  if (!visible || follow) return;
6267
7365
  const onChange = () => placeAtTrigger();
6268
7366
  window.addEventListener("scroll", onChange, true);
@@ -6272,12 +7370,12 @@ var Tooltip = (props) => {
6272
7370
  window.removeEventListener("resize", onChange);
6273
7371
  };
6274
7372
  }, [visible, follow, placeAtTrigger]);
6275
- React40.useEffect(() => () => {
7373
+ React42.useEffect(() => () => {
6276
7374
  window.clearTimeout(delayTimer.current);
6277
7375
  cancelAnimationFrame(rafRef.current);
6278
7376
  }, []);
6279
- if (!title && !description) return /* @__PURE__ */ jsx351(Fragment6, { children });
6280
- return /* @__PURE__ */ jsxs222(
7377
+ if (!title && !description) return /* @__PURE__ */ jsx353(Fragment6, { children });
7378
+ return /* @__PURE__ */ jsxs224(
6281
7379
  "div",
6282
7380
  {
6283
7381
  ref: triggerRef,
@@ -6290,7 +7388,7 @@ var Tooltip = (props) => {
6290
7388
  onBlur: close,
6291
7389
  children: [
6292
7390
  children,
6293
- visible && /* @__PURE__ */ jsx351(Portal_default, { children: /* @__PURE__ */ jsxs222(
7391
+ visible && /* @__PURE__ */ jsx353(Portal_default, { children: /* @__PURE__ */ jsxs224(
6294
7392
  "div",
6295
7393
  {
6296
7394
  ref: tooltipRef,
@@ -6298,8 +7396,8 @@ var Tooltip = (props) => {
6298
7396
  className: clsx_default("lib-xplat-tooltip", shape),
6299
7397
  style: { position: "fixed", left: pos.left, top: pos.top },
6300
7398
  children: [
6301
- title && /* @__PURE__ */ jsx351("div", { className: "tooltip-title", children: title }),
6302
- description && /* @__PURE__ */ jsx351("div", { className: "tooltip-desc", children: description })
7399
+ title && /* @__PURE__ */ jsx353("div", { className: "tooltip-title", children: title }),
7400
+ description && /* @__PURE__ */ jsx353("div", { className: "tooltip-desc", children: description })
6303
7401
  ]
6304
7402
  }
6305
7403
  ) })
@@ -6311,11 +7409,11 @@ Tooltip.displayName = "Tooltip";
6311
7409
  var Tooltip_default = Tooltip;
6312
7410
 
6313
7411
  // src/components/Video/Video.tsx
6314
- import React41 from "react";
6315
- import { jsx as jsx352, jsxs as jsxs223 } from "react/jsx-runtime";
6316
- var PipIcon = () => /* @__PURE__ */ jsxs223("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
6317
- /* @__PURE__ */ jsx352("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
6318
- /* @__PURE__ */ jsx352("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
7412
+ import React43 from "react";
7413
+ import { jsx as jsx354, jsxs as jsxs225 } from "react/jsx-runtime";
7414
+ var PipIcon = () => /* @__PURE__ */ jsxs225("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
7415
+ /* @__PURE__ */ jsx354("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
7416
+ /* @__PURE__ */ jsx354("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
6319
7417
  ] });
6320
7418
  var formatTime = (sec) => {
6321
7419
  if (!Number.isFinite(sec) || sec < 0) return "0:00";
@@ -6323,7 +7421,7 @@ var formatTime = (sec) => {
6323
7421
  const s = Math.floor(sec % 60);
6324
7422
  return `${m}:${s.toString().padStart(2, "0")}`;
6325
7423
  };
6326
- var Video = React41.forwardRef((props, ref) => {
7424
+ var Video = React43.forwardRef((props, ref) => {
6327
7425
  const {
6328
7426
  src,
6329
7427
  poster,
@@ -6347,21 +7445,21 @@ var Video = React41.forwardRef((props, ref) => {
6347
7445
  children,
6348
7446
  ...rest
6349
7447
  } = props;
6350
- const containerRef = React41.useRef(null);
6351
- const videoRef = React41.useRef(null);
6352
- const [isPlaying, setIsPlaying] = React41.useState(Boolean(autoPlay));
6353
- const [isLoaded, setIsLoaded] = React41.useState(false);
6354
- const [currentTime, setCurrentTime] = React41.useState(0);
6355
- const [duration, setDuration] = React41.useState(0);
6356
- const [buffered, setBuffered] = React41.useState(0);
6357
- const [volume, setVolume] = React41.useState(1);
6358
- const [isMuted, setIsMuted] = React41.useState(Boolean(muted));
6359
- const [isFullscreen, setIsFullscreen] = React41.useState(false);
6360
- const [playbackRate, setPlaybackRate] = React41.useState(1);
6361
- const [rateMenuOpen, setRateMenuOpen] = React41.useState(false);
6362
- const [captionsOn, setCaptionsOn] = React41.useState(false);
6363
- const [isPip, setIsPip] = React41.useState(false);
6364
- const setRefs = React41.useCallback(
7448
+ const containerRef = React43.useRef(null);
7449
+ const videoRef = React43.useRef(null);
7450
+ const [isPlaying, setIsPlaying] = React43.useState(Boolean(autoPlay));
7451
+ const [isLoaded, setIsLoaded] = React43.useState(false);
7452
+ const [currentTime, setCurrentTime] = React43.useState(0);
7453
+ const [duration, setDuration] = React43.useState(0);
7454
+ const [buffered, setBuffered] = React43.useState(0);
7455
+ const [volume, setVolume] = React43.useState(1);
7456
+ const [isMuted, setIsMuted] = React43.useState(Boolean(muted));
7457
+ const [isFullscreen, setIsFullscreen] = React43.useState(false);
7458
+ const [playbackRate, setPlaybackRate] = React43.useState(1);
7459
+ const [rateMenuOpen, setRateMenuOpen] = React43.useState(false);
7460
+ const [captionsOn, setCaptionsOn] = React43.useState(false);
7461
+ const [isPip, setIsPip] = React43.useState(false);
7462
+ const setRefs = React43.useCallback(
6365
7463
  (el) => {
6366
7464
  videoRef.current = el;
6367
7465
  if (typeof ref === "function") ref(el);
@@ -6369,14 +7467,14 @@ var Video = React41.forwardRef((props, ref) => {
6369
7467
  },
6370
7468
  [ref]
6371
7469
  );
6372
- React41.useEffect(() => {
7470
+ React43.useEffect(() => {
6373
7471
  const onFsChange = () => {
6374
7472
  setIsFullscreen(document.fullscreenElement === containerRef.current);
6375
7473
  };
6376
7474
  document.addEventListener("fullscreenchange", onFsChange);
6377
7475
  return () => document.removeEventListener("fullscreenchange", onFsChange);
6378
7476
  }, []);
6379
- React41.useEffect(() => {
7477
+ React43.useEffect(() => {
6380
7478
  const v = videoRef.current;
6381
7479
  if (!v) return;
6382
7480
  const onEnter = () => setIsPip(true);
@@ -6490,13 +7588,13 @@ var Video = React41.forwardRef((props, ref) => {
6490
7588
  const volumePct = (isMuted ? 0 : volume) * 100;
6491
7589
  const VolumeGlyph = isMuted || volume === 0 ? VolumeXIcon_default : volume < 0.5 ? VolumeIcon_default : Volume2Icon_default;
6492
7590
  const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
6493
- return /* @__PURE__ */ jsxs223(
7591
+ return /* @__PURE__ */ jsxs225(
6494
7592
  "div",
6495
7593
  {
6496
7594
  ref: containerRef,
6497
7595
  className: clsx_default("lib-xplat-video", showControls && "has-controls"),
6498
7596
  children: [
6499
- /* @__PURE__ */ jsx352(
7597
+ /* @__PURE__ */ jsx354(
6500
7598
  "video",
6501
7599
  {
6502
7600
  ref: setRefs,
@@ -6517,7 +7615,7 @@ var Video = React41.forwardRef((props, ref) => {
6517
7615
  children
6518
7616
  }
6519
7617
  ),
6520
- showCenterPlay && /* @__PURE__ */ jsx352(
7618
+ showCenterPlay && /* @__PURE__ */ jsx354(
6521
7619
  "button",
6522
7620
  {
6523
7621
  type: "button",
@@ -6529,11 +7627,11 @@ var Video = React41.forwardRef((props, ref) => {
6529
7627
  onClick: togglePlay,
6530
7628
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
6531
7629
  tabIndex: -1,
6532
- children: /* @__PURE__ */ jsx352("span", { className: "center-play-icon", children: /* @__PURE__ */ jsx352(PlayCircleIcon_default, {}) })
7630
+ children: /* @__PURE__ */ jsx354("span", { className: "center-play-icon", children: /* @__PURE__ */ jsx354(PlayCircleIcon_default, {}) })
6533
7631
  }
6534
7632
  ),
6535
- showControls && /* @__PURE__ */ jsxs223("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
6536
- /* @__PURE__ */ jsx352(
7633
+ showControls && /* @__PURE__ */ jsxs225("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
7634
+ /* @__PURE__ */ jsx354(
6537
7635
  "input",
6538
7636
  {
6539
7637
  type: "range",
@@ -6550,29 +7648,29 @@ var Video = React41.forwardRef((props, ref) => {
6550
7648
  }
6551
7649
  }
6552
7650
  ),
6553
- /* @__PURE__ */ jsxs223("div", { className: "controls-row", children: [
6554
- /* @__PURE__ */ jsx352(
7651
+ /* @__PURE__ */ jsxs225("div", { className: "controls-row", children: [
7652
+ /* @__PURE__ */ jsx354(
6555
7653
  "button",
6556
7654
  {
6557
7655
  type: "button",
6558
7656
  className: "control-btn",
6559
7657
  onClick: togglePlay,
6560
7658
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
6561
- children: isPlaying ? /* @__PURE__ */ jsx352(PauseIcon_default, {}) : /* @__PURE__ */ jsx352(PlayIcon_default, {})
7659
+ children: isPlaying ? /* @__PURE__ */ jsx354(PauseIcon_default, {}) : /* @__PURE__ */ jsx354(PlayIcon_default, {})
6562
7660
  }
6563
7661
  ),
6564
- /* @__PURE__ */ jsxs223("div", { className: "volume-group", children: [
6565
- /* @__PURE__ */ jsx352(
7662
+ /* @__PURE__ */ jsxs225("div", { className: "volume-group", children: [
7663
+ /* @__PURE__ */ jsx354(
6566
7664
  "button",
6567
7665
  {
6568
7666
  type: "button",
6569
7667
  className: "control-btn",
6570
7668
  onClick: toggleMute,
6571
7669
  "aria-label": isMuted ? "\uC74C\uC18C\uAC70 \uD574\uC81C" : "\uC74C\uC18C\uAC70",
6572
- children: /* @__PURE__ */ jsx352(VolumeGlyph, {})
7670
+ children: /* @__PURE__ */ jsx354(VolumeGlyph, {})
6573
7671
  }
6574
7672
  ),
6575
- /* @__PURE__ */ jsx352(
7673
+ /* @__PURE__ */ jsx354(
6576
7674
  "input",
6577
7675
  {
6578
7676
  type: "range",
@@ -6587,14 +7685,14 @@ var Video = React41.forwardRef((props, ref) => {
6587
7685
  }
6588
7686
  )
6589
7687
  ] }),
6590
- /* @__PURE__ */ jsxs223("span", { className: "time", children: [
7688
+ /* @__PURE__ */ jsxs225("span", { className: "time", children: [
6591
7689
  formatTime(currentTime),
6592
7690
  " / ",
6593
7691
  formatTime(duration)
6594
7692
  ] }),
6595
- /* @__PURE__ */ jsx352("div", { className: "controls-spacer" }),
6596
- playbackRates && playbackRates.length > 0 && /* @__PURE__ */ jsxs223("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
6597
- /* @__PURE__ */ jsxs223(
7693
+ /* @__PURE__ */ jsx354("div", { className: "controls-spacer" }),
7694
+ playbackRates && playbackRates.length > 0 && /* @__PURE__ */ jsxs225("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
7695
+ /* @__PURE__ */ jsxs225(
6598
7696
  "button",
6599
7697
  {
6600
7698
  type: "button",
@@ -6608,7 +7706,7 @@ var Video = React41.forwardRef((props, ref) => {
6608
7706
  ]
6609
7707
  }
6610
7708
  ),
6611
- rateMenuOpen && /* @__PURE__ */ jsx352("ul", { className: "rate-menu", role: "menu", children: playbackRates.map((r2) => /* @__PURE__ */ jsx352("li", { children: /* @__PURE__ */ jsxs223(
7709
+ rateMenuOpen && /* @__PURE__ */ jsx354("ul", { className: "rate-menu", role: "menu", children: playbackRates.map((r2) => /* @__PURE__ */ jsx354("li", { children: /* @__PURE__ */ jsxs225(
6612
7710
  "button",
6613
7711
  {
6614
7712
  type: "button",
@@ -6622,7 +7720,7 @@ var Video = React41.forwardRef((props, ref) => {
6622
7720
  }
6623
7721
  ) }, r2)) })
6624
7722
  ] }),
6625
- showCaptions && /* @__PURE__ */ jsx352(
7723
+ showCaptions && /* @__PURE__ */ jsx354(
6626
7724
  "button",
6627
7725
  {
6628
7726
  type: "button",
@@ -6630,37 +7728,37 @@ var Video = React41.forwardRef((props, ref) => {
6630
7728
  onClick: toggleCaptions,
6631
7729
  "aria-label": captionsOn ? "\uC790\uB9C9 \uB044\uAE30" : "\uC790\uB9C9 \uCF1C\uAE30",
6632
7730
  "aria-pressed": captionsOn,
6633
- children: /* @__PURE__ */ jsx352(TypeIcon_default, {})
7731
+ children: /* @__PURE__ */ jsx354(TypeIcon_default, {})
6634
7732
  }
6635
7733
  ),
6636
- showPip && pipSupported && /* @__PURE__ */ jsx352(
7734
+ showPip && pipSupported && /* @__PURE__ */ jsx354(
6637
7735
  "button",
6638
7736
  {
6639
7737
  type: "button",
6640
7738
  className: clsx_default("control-btn", isPip && "is-active"),
6641
7739
  onClick: togglePip,
6642
7740
  "aria-label": isPip ? "PIP \uC885\uB8CC" : "PIP",
6643
- children: /* @__PURE__ */ jsx352(PipIcon, {})
7741
+ children: /* @__PURE__ */ jsx354(PipIcon, {})
6644
7742
  }
6645
7743
  ),
6646
- showDownload && /* @__PURE__ */ jsx352(
7744
+ showDownload && /* @__PURE__ */ jsx354(
6647
7745
  "a",
6648
7746
  {
6649
7747
  className: "control-btn",
6650
7748
  href: src,
6651
7749
  download: downloadFileName ?? true,
6652
7750
  "aria-label": "\uB2E4\uC6B4\uB85C\uB4DC",
6653
- children: /* @__PURE__ */ jsx352(DownloadIcon_default, {})
7751
+ children: /* @__PURE__ */ jsx354(DownloadIcon_default, {})
6654
7752
  }
6655
7753
  ),
6656
- /* @__PURE__ */ jsx352(
7754
+ /* @__PURE__ */ jsx354(
6657
7755
  "button",
6658
7756
  {
6659
7757
  type: "button",
6660
7758
  className: "control-btn",
6661
7759
  onClick: toggleFullscreen,
6662
7760
  "aria-label": isFullscreen ? "\uC804\uCCB4\uD654\uBA74 \uC885\uB8CC" : "\uC804\uCCB4\uD654\uBA74",
6663
- children: isFullscreen ? /* @__PURE__ */ jsx352(MinimizeIcon_default, {}) : /* @__PURE__ */ jsx352(MaximizeIcon_default, {})
7761
+ children: isFullscreen ? /* @__PURE__ */ jsx354(MinimizeIcon_default, {}) : /* @__PURE__ */ jsx354(MaximizeIcon_default, {})
6664
7762
  }
6665
7763
  )
6666
7764
  ] })
@@ -6717,6 +7815,7 @@ export {
6717
7815
  SingleDatePicker_default as SingleDatePicker,
6718
7816
  Skeleton_default as Skeleton,
6719
7817
  Spinner_default as Spinner,
7818
+ Stat_default as Stat,
6720
7819
  Steps_default as Steps,
6721
7820
  Swiper_default as Swiper,
6722
7821
  Switch_default as Switch,
@@ -6728,6 +7827,7 @@ export {
6728
7827
  TableRow_default as TableRow,
6729
7828
  Tag_default as Tag,
6730
7829
  TextArea_default as TextArea,
7830
+ TimePicker_default as TimePicker,
6731
7831
  ToastProvider,
6732
7832
  Tooltip_default as Tooltip,
6733
7833
  Video_default as Video,