@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
@@ -75,6 +75,7 @@ __export(components_exports, {
75
75
  SingleDatePicker: () => SingleDatePicker_default,
76
76
  Skeleton: () => Skeleton_default,
77
77
  Spinner: () => Spinner_default,
78
+ Stat: () => Stat_default,
78
79
  Steps: () => Steps_default,
79
80
  Swiper: () => Swiper_default,
80
81
  Switch: () => Switch_default,
@@ -86,6 +87,7 @@ __export(components_exports, {
86
87
  TableRow: () => TableRow_default,
87
88
  Tag: () => Tag_default,
88
89
  TextArea: () => TextArea_default,
90
+ TimePicker: () => TimePicker_default,
89
91
  ToastProvider: () => ToastProvider,
90
92
  Tooltip: () => Tooltip_default,
91
93
  Video: () => Video_default,
@@ -1631,11 +1633,16 @@ var FieldMessage = (props) => {
1631
1633
  FieldMessage.displayName = "FieldMessage";
1632
1634
  var FieldMessage_default = FieldMessage;
1633
1635
  var Field = (props) => {
1634
- const { validations, children } = props;
1635
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(import_jsx_runtime297.Fragment, { children });
1636
+ const { label, required = false, htmlFor, validations, children } = props;
1637
+ const hasMessages = !!validations && validations.length > 0;
1638
+ if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(import_jsx_runtime297.Fragment, { children });
1636
1639
  return /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)("div", { className: "lib-xplat-field", children: [
1640
+ label && /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
1641
+ label,
1642
+ required && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
1643
+ ] }),
1637
1644
  children,
1638
- /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(FieldMessage, { validations })
1645
+ hasMessages && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(FieldMessage, { validations })
1639
1646
  ] });
1640
1647
  };
1641
1648
  Field.displayName = "Field";
@@ -1646,7 +1653,9 @@ var import_jsx_runtime298 = require("react/jsx-runtime");
1646
1653
  var MAX_HEIGHT = 400;
1647
1654
  var AutoResizeTextArea = import_react2.default.forwardRef(
1648
1655
  (props, ref) => {
1649
- const { value, className, onChange, disabled, bare = false, validations, ...textareaProps } = props;
1656
+ const { value, className, onChange, disabled, bare = false, validations, label, required, ...textareaProps } = props;
1657
+ const autoId = import_react2.default.useId();
1658
+ const fieldId = props.id ?? autoId;
1650
1659
  const localRef = import_react2.default.useRef(null);
1651
1660
  const setRefs = (el) => {
1652
1661
  localRef.current = el;
@@ -1680,6 +1689,7 @@ var AutoResizeTextArea = import_react2.default.forwardRef(
1680
1689
  "aria-invalid": hasError(validations) || void 0,
1681
1690
  ...textareaProps,
1682
1691
  ref: setRefs,
1692
+ id: fieldId,
1683
1693
  className,
1684
1694
  disabled,
1685
1695
  value,
@@ -1693,12 +1703,13 @@ var AutoResizeTextArea = import_react2.default.forwardRef(
1693
1703
  "aria-invalid": hasError(validations) || void 0,
1694
1704
  ...textareaProps,
1695
1705
  ref: setRefs,
1706
+ id: fieldId,
1696
1707
  disabled,
1697
1708
  value,
1698
1709
  onChange: handleChange
1699
1710
  }
1700
1711
  ) }) });
1701
- return /* @__PURE__ */ (0, import_jsx_runtime298.jsx)(Field, { validations, children: control });
1712
+ return /* @__PURE__ */ (0, import_jsx_runtime298.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
1702
1713
  }
1703
1714
  );
1704
1715
  AutoResizeTextArea.displayName = "AutoResizeTextArea";
@@ -2916,8 +2927,11 @@ var ChatInput = import_react7.default.forwardRef(
2916
2927
  buttonType = "primary",
2917
2928
  onSubmit,
2918
2929
  onChange,
2919
- validations
2930
+ validations,
2931
+ label,
2932
+ required
2920
2933
  } = props;
2934
+ const fieldId = import_react7.default.useId();
2921
2935
  const isControlled = valueProp !== void 0;
2922
2936
  const [internalValue, setInternalValue] = import_react7.default.useState("");
2923
2937
  const value = isControlled ? valueProp : internalValue;
@@ -2942,6 +2956,7 @@ var ChatInput = import_react7.default.forwardRef(
2942
2956
  /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(
2943
2957
  AutoResizeTextArea_default,
2944
2958
  {
2959
+ id: fieldId,
2945
2960
  ref,
2946
2961
  bare: true,
2947
2962
  className: "chat-input-textarea",
@@ -2966,7 +2981,7 @@ var ChatInput = import_react7.default.forwardRef(
2966
2981
  }
2967
2982
  )
2968
2983
  ] });
2969
- return /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(Field, { validations, children: control });
2984
+ return /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
2970
2985
  }
2971
2986
  );
2972
2987
  ChatInput.displayName = "ChatInput";
@@ -3037,7 +3052,7 @@ Chip.displayName = "Chip";
3037
3052
  var Chip_default = Chip;
3038
3053
 
3039
3054
  // src/components/DatePicker/InputDatePicker/index.tsx
3040
- var import_react12 = __toESM(require("react"), 1);
3055
+ var import_react13 = __toESM(require("react"), 1);
3041
3056
 
3042
3057
  // src/components/Input/Input.tsx
3043
3058
  var import_react8 = __toESM(require("react"), 1);
@@ -3135,8 +3150,12 @@ var Input = import_react8.default.forwardRef((props, ref) => {
3135
3150
  suffix,
3136
3151
  disabled,
3137
3152
  validations,
3153
+ label,
3154
+ required,
3138
3155
  ...inputProps
3139
3156
  } = props;
3157
+ const autoId = import_react8.default.useId();
3158
+ const fieldId = inputProps.id ?? autoId;
3140
3159
  const localValue = formatValue(type, value);
3141
3160
  const handleChange = (e) => {
3142
3161
  let val = e.target.value;
@@ -3152,7 +3171,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
3152
3171
  onChange(event);
3153
3172
  }
3154
3173
  };
3155
- return /* @__PURE__ */ (0, import_jsx_runtime311.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
3174
+ return /* @__PURE__ */ (0, import_jsx_runtime311.jsx)(Field, { label, required, htmlFor: fieldId, children: /* @__PURE__ */ (0, import_jsx_runtime311.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
3156
3175
  /* @__PURE__ */ (0, import_jsx_runtime311.jsxs)(
3157
3176
  "div",
3158
3177
  {
@@ -3163,6 +3182,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
3163
3182
  {
3164
3183
  ...inputProps,
3165
3184
  ref,
3185
+ id: fieldId,
3166
3186
  disabled,
3167
3187
  type: type === "number" ? "text" : type,
3168
3188
  value: localValue,
@@ -3175,7 +3195,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
3175
3195
  }
3176
3196
  ),
3177
3197
  /* @__PURE__ */ (0, import_jsx_runtime311.jsx)(FieldMessage_default, { validations })
3178
- ] });
3198
+ ] }) });
3179
3199
  });
3180
3200
  Input.displayName = "Input";
3181
3201
  var Input_default = Input;
@@ -3205,16 +3225,96 @@ PasswordInput.displayName = "PasswordInput";
3205
3225
  var PasswordInput_default = PasswordInput;
3206
3226
 
3207
3227
  // src/components/Modal/Modal.tsx
3208
- var import_react10 = __toESM(require("react"), 1);
3228
+ var import_react11 = __toESM(require("react"), 1);
3209
3229
  var import_react_dom = require("react-dom");
3230
+
3231
+ // src/tokens/hooks/useOverlayKeyboard.ts
3232
+ var import_react10 = __toESM(require("react"), 1);
3233
+ var FOCUSABLE = [
3234
+ "a[href]",
3235
+ "button:not([disabled])",
3236
+ "input:not([disabled]):not([type='hidden'])",
3237
+ "select:not([disabled])",
3238
+ "textarea:not([disabled])",
3239
+ "[tabindex]:not([tabindex='-1'])"
3240
+ ].join(",");
3241
+ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
3242
+ // 화면에 없는 것은 제외 (display:none 등)
3243
+ (n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
3244
+ );
3245
+ var useEscapeKey = (enabled, onEscape) => {
3246
+ import_react10.default.useEffect(() => {
3247
+ if (!enabled) return;
3248
+ const onKey = (e) => {
3249
+ if (e.key !== "Escape") return;
3250
+ if (e.defaultPrevented) return;
3251
+ e.preventDefault();
3252
+ onEscape();
3253
+ };
3254
+ document.addEventListener("keydown", onKey);
3255
+ return () => document.removeEventListener("keydown", onKey);
3256
+ }, [enabled, onEscape]);
3257
+ };
3258
+ var useFocusTrap = (containerRef, enabled, options = {}) => {
3259
+ const { trap = false, initialFocus } = options;
3260
+ const restoreRef = import_react10.default.useRef(null);
3261
+ import_react10.default.useEffect(() => {
3262
+ if (!enabled) return;
3263
+ restoreRef.current = document.activeElement;
3264
+ const raf = requestAnimationFrame(() => {
3265
+ const el = containerRef.current;
3266
+ if (!el) return;
3267
+ const target = initialFocus?.current ?? focusableIn(el)[0] ?? el;
3268
+ if (!target.hasAttribute("tabindex") && !focusableIn(el).includes(target)) {
3269
+ target.setAttribute("tabindex", "-1");
3270
+ }
3271
+ target.focus({ preventScroll: true });
3272
+ });
3273
+ return () => {
3274
+ cancelAnimationFrame(raf);
3275
+ const prev = restoreRef.current;
3276
+ if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
3277
+ };
3278
+ }, [enabled, containerRef, initialFocus]);
3279
+ import_react10.default.useEffect(() => {
3280
+ if (!enabled || !trap) return;
3281
+ const onKey = (e) => {
3282
+ if (e.key !== "Tab") return;
3283
+ const el = containerRef.current;
3284
+ if (!el) return;
3285
+ const items = focusableIn(el);
3286
+ if (items.length === 0) {
3287
+ e.preventDefault();
3288
+ return;
3289
+ }
3290
+ const first = items[0];
3291
+ const last = items[items.length - 1];
3292
+ const active = document.activeElement;
3293
+ if (e.shiftKey && (active === first || !el.contains(active))) {
3294
+ e.preventDefault();
3295
+ last.focus();
3296
+ } else if (!e.shiftKey && (active === last || !el.contains(active))) {
3297
+ e.preventDefault();
3298
+ first.focus();
3299
+ }
3300
+ };
3301
+ document.addEventListener("keydown", onKey);
3302
+ return () => document.removeEventListener("keydown", onKey);
3303
+ }, [enabled, trap, containerRef]);
3304
+ };
3305
+
3306
+ // src/components/Modal/Modal.tsx
3210
3307
  var import_jsx_runtime313 = require("react/jsx-runtime");
3211
3308
  var ANIMATION_DURATION_MS = 200;
3212
3309
  var Modal = (props) => {
3213
3310
  const { isOpen, onClose, children } = props;
3214
- const [mounted, setMounted] = import_react10.default.useState(false);
3215
- const [visible, setVisible] = import_react10.default.useState(false);
3216
- const [settled, setSettled] = import_react10.default.useState(false);
3217
- import_react10.default.useEffect(() => {
3311
+ const [mounted, setMounted] = import_react11.default.useState(false);
3312
+ const [visible, setVisible] = import_react11.default.useState(false);
3313
+ const [settled, setSettled] = import_react11.default.useState(false);
3314
+ const boxRef = import_react11.default.useRef(null);
3315
+ useEscapeKey(isOpen, onClose);
3316
+ useFocusTrap(boxRef, isOpen && mounted, { trap: true });
3317
+ import_react11.default.useEffect(() => {
3218
3318
  if (isOpen) {
3219
3319
  setMounted(true);
3220
3320
  const show = setTimeout(() => setVisible(true), 1);
@@ -3241,6 +3341,7 @@ var Modal = (props) => {
3241
3341
  children: /* @__PURE__ */ (0, import_jsx_runtime313.jsx)(
3242
3342
  "div",
3243
3343
  {
3344
+ ref: boxRef,
3244
3345
  className: clsx_default("lib-xplat-modal", "modal-box", stateClass, settled && "settled"),
3245
3346
  role: "dialog",
3246
3347
  "aria-modal": "true",
@@ -3257,9 +3358,9 @@ Modal.displayName = "Modal";
3257
3358
  var Modal_default = Modal;
3258
3359
 
3259
3360
  // src/components/DatePicker/SingleDatePicker/index.tsx
3260
- var import_react11 = __toESM(require("react"), 1);
3361
+ var import_react12 = __toESM(require("react"), 1);
3261
3362
  var import_jsx_runtime314 = require("react/jsx-runtime");
3262
- var DayCell2 = import_react11.default.memo(
3363
+ var DayCell2 = import_react12.default.memo(
3263
3364
  ({
3264
3365
  day,
3265
3366
  disabled,
@@ -3311,26 +3412,26 @@ var SingleDatePicker = (props) => {
3311
3412
  initialYear,
3312
3413
  initialMonth
3313
3414
  );
3314
- const [pickerMode, setPickerMode] = import_react11.default.useState("days");
3315
- const [yearRangeStart, setYearRangeStart] = import_react11.default.useState(
3415
+ const [pickerMode, setPickerMode] = import_react12.default.useState("days");
3416
+ const [yearRangeStart, setYearRangeStart] = import_react12.default.useState(
3316
3417
  Math.floor((initialYear ?? (/* @__PURE__ */ new Date()).getFullYear()) / 12) * 12
3317
3418
  );
3318
- const minTime = import_react11.default.useMemo(
3419
+ const minTime = import_react12.default.useMemo(
3319
3420
  () => minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()).getTime() : -Infinity,
3320
3421
  [minDate]
3321
3422
  );
3322
- const maxTime = import_react11.default.useMemo(
3423
+ const maxTime = import_react12.default.useMemo(
3323
3424
  () => maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()).getTime() : Infinity,
3324
3425
  [maxDate]
3325
3426
  );
3326
- const highlightSet = import_react11.default.useMemo(() => {
3427
+ const highlightSet = import_react12.default.useMemo(() => {
3327
3428
  const set = /* @__PURE__ */ new Set();
3328
3429
  for (const h of highlightDates) {
3329
3430
  set.add(`${h.getFullYear()}-${h.getMonth()}-${h.getDate()}`);
3330
3431
  }
3331
3432
  return set;
3332
3433
  }, [highlightDates]);
3333
- const handleSelect = import_react11.default.useCallback(
3434
+ const handleSelect = import_react12.default.useCallback(
3334
3435
  (date) => {
3335
3436
  onChange?.(date);
3336
3437
  },
@@ -3459,8 +3560,8 @@ var formatDate = (date) => {
3459
3560
  };
3460
3561
  var InputDatePicker = (props) => {
3461
3562
  const { value, onChange, minDate, maxDate, disabled, locale = "ko", placeholder } = props;
3462
- const [isOpen, setIsOpen] = import_react12.default.useState(false);
3463
- const [tempDate, setTempDate] = import_react12.default.useState(value ?? /* @__PURE__ */ new Date());
3563
+ const [isOpen, setIsOpen] = import_react13.default.useState(false);
3564
+ const [tempDate, setTempDate] = import_react13.default.useState(value ?? /* @__PURE__ */ new Date());
3464
3565
  const handleOpen = () => {
3465
3566
  if (disabled) return;
3466
3567
  setTempDate(value ?? /* @__PURE__ */ new Date());
@@ -3509,23 +3610,27 @@ InputDatePicker.displayName = "InputDatePicker";
3509
3610
  var InputDatePicker_default = InputDatePicker;
3510
3611
 
3511
3612
  // src/components/DatePicker/PopupPicker/index.tsx
3512
- var import_react16 = __toESM(require("react"), 1);
3613
+ var import_react17 = __toESM(require("react"), 1);
3513
3614
 
3514
3615
  // src/components/DatePicker/RangePicker/index.tsx
3515
- var import_react15 = __toESM(require("react"), 1);
3616
+ var import_react16 = __toESM(require("react"), 1);
3516
3617
 
3517
3618
  // src/components/Tab/Tab.tsx
3518
- var import_react14 = __toESM(require("react"), 1);
3619
+ var import_react15 = __toESM(require("react"), 1);
3519
3620
 
3520
3621
  // src/components/Tab/TabItem.tsx
3521
- var import_react13 = __toESM(require("react"), 1);
3622
+ var import_react14 = __toESM(require("react"), 1);
3522
3623
  var import_jsx_runtime316 = require("react/jsx-runtime");
3523
- var TabItem = import_react13.default.forwardRef((props, ref) => {
3624
+ var TabItem = import_react14.default.forwardRef((props, ref) => {
3524
3625
  const { isActive: isActive2, title, onClick } = props;
3525
3626
  return /* @__PURE__ */ (0, import_jsx_runtime316.jsx)(
3526
- "div",
3627
+ "button",
3527
3628
  {
3528
3629
  ref,
3630
+ type: "button",
3631
+ role: "tab",
3632
+ "aria-selected": isActive2,
3633
+ tabIndex: isActive2 ? 0 : -1,
3529
3634
  className: clsx_default("tab-item", isActive2 ? "active" : null),
3530
3635
  onClick,
3531
3636
  children: title
@@ -3540,44 +3645,64 @@ var import_jsx_runtime317 = require("react/jsx-runtime");
3540
3645
  var Tab = (props) => {
3541
3646
  const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
3542
3647
  const shape = variant ?? type ?? "default";
3543
- const [underlineStyle, setUnderlineStyle] = import_react14.default.useState({
3648
+ const [underlineStyle, setUnderlineStyle] = import_react15.default.useState({
3544
3649
  left: 0,
3545
3650
  width: 0
3546
3651
  });
3547
- const itemRefs = import_react14.default.useRef([]);
3652
+ const itemRefs = import_react15.default.useRef([]);
3653
+ const handleKeyDown = (e) => {
3654
+ const last = tabs.length - 1;
3655
+ let next = null;
3656
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
3657
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
3658
+ else if (e.key === "Home") next = 0;
3659
+ else if (e.key === "End") next = last;
3660
+ if (next === null) return;
3661
+ e.preventDefault();
3662
+ onChange(tabs[next], next);
3663
+ itemRefs.current[next]?.focus();
3664
+ };
3548
3665
  const handleChangeActiveTab = (tabItem, tabIdx) => {
3549
3666
  onChange(tabItem, tabIdx);
3550
3667
  };
3551
- import_react14.default.useEffect(() => {
3668
+ import_react15.default.useEffect(() => {
3552
3669
  const el = itemRefs.current[activeIndex];
3553
3670
  if (el) {
3554
3671
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
3555
3672
  }
3556
3673
  }, [activeIndex, tabs.length]);
3557
- return /* @__PURE__ */ (0, import_jsx_runtime317.jsxs)("div", { className: clsx_default("lib-xplat-tab", `type-${shape}`, size), children: [
3558
- tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
3559
- TabItem_default,
3560
- {
3561
- onClick: () => handleChangeActiveTab(tab, idx),
3562
- isActive: activeIndex === idx,
3563
- ref: (el) => {
3564
- itemRefs.current[idx] = el;
3565
- },
3566
- title: tab.title
3567
- },
3568
- `${tab.value}_${idx}`
3569
- )),
3570
- shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
3571
- "div",
3572
- {
3573
- className: "tab-toggle-underline",
3574
- style: {
3575
- left: underlineStyle.left,
3576
- width: underlineStyle.width
3577
- }
3578
- }
3579
- )
3580
- ] });
3674
+ return /* @__PURE__ */ (0, import_jsx_runtime317.jsxs)(
3675
+ "div",
3676
+ {
3677
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
3678
+ role: "tablist",
3679
+ onKeyDown: handleKeyDown,
3680
+ children: [
3681
+ tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
3682
+ TabItem_default,
3683
+ {
3684
+ onClick: () => handleChangeActiveTab(tab, idx),
3685
+ isActive: activeIndex === idx,
3686
+ ref: (el) => {
3687
+ itemRefs.current[idx] = el;
3688
+ },
3689
+ title: tab.title
3690
+ },
3691
+ `${tab.value}_${idx}`
3692
+ )),
3693
+ shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
3694
+ "div",
3695
+ {
3696
+ className: "tab-toggle-underline",
3697
+ style: {
3698
+ left: underlineStyle.left,
3699
+ width: underlineStyle.width
3700
+ }
3701
+ }
3702
+ )
3703
+ ]
3704
+ }
3705
+ );
3581
3706
  };
3582
3707
  Tab.displayName = "Tab";
3583
3708
  var Tab_default = Tab;
@@ -3593,7 +3718,7 @@ var RangePicker = (props) => {
3593
3718
  maxDate,
3594
3719
  locale = "ko"
3595
3720
  } = props;
3596
- const [activeTab, setActiveTab] = import_react15.default.useState("start");
3721
+ const [activeTab, setActiveTab] = import_react16.default.useState("start");
3597
3722
  const handleStartChange = (date) => {
3598
3723
  if (!date) return;
3599
3724
  const newStart = date > endDate ? endDate : date;
@@ -3678,7 +3803,7 @@ var RangePicker_default = RangePicker;
3678
3803
  var import_jsx_runtime319 = require("react/jsx-runtime");
3679
3804
  var PopupPicker = (props) => {
3680
3805
  const { component, type, locale } = props;
3681
- const [isOpen, setIsOpen] = import_react16.default.useState(false);
3806
+ const [isOpen, setIsOpen] = import_react17.default.useState(false);
3682
3807
  const handleClick = () => setIsOpen(true);
3683
3808
  const handleClose = () => setIsOpen(false);
3684
3809
  const handleSingleChange = (date) => {
@@ -3687,7 +3812,7 @@ var PopupPicker = (props) => {
3687
3812
  handleClose();
3688
3813
  };
3689
3814
  return /* @__PURE__ */ (0, import_jsx_runtime319.jsxs)("div", { className: "lib-xplat-popup-datepicker", children: [
3690
- import_react16.default.cloneElement(component, { onClick: handleClick }),
3815
+ import_react17.default.cloneElement(component, { onClick: handleClick }),
3691
3816
  /* @__PURE__ */ (0, import_jsx_runtime319.jsx)(Modal_default, { isOpen, onClose: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime319.jsxs)("div", { className: clsx_default("lib-xplat-popup-datepicker-card", type === "range" && "range-mode"), children: [
3692
3817
  /* @__PURE__ */ (0, import_jsx_runtime319.jsxs)("div", { className: "popup-datepicker-content", children: [
3693
3818
  type === "single" && /* @__PURE__ */ (0, import_jsx_runtime319.jsx)(
@@ -3746,15 +3871,18 @@ Divider.displayName = "Divider";
3746
3871
  var Divider_default = Divider;
3747
3872
 
3748
3873
  // src/components/Drawer/Drawer.tsx
3749
- var import_react17 = __toESM(require("react"), 1);
3874
+ var import_react18 = __toESM(require("react"), 1);
3750
3875
  var import_react_dom2 = require("react-dom");
3751
3876
  var import_jsx_runtime321 = require("react/jsx-runtime");
3752
3877
  var ANIMATION_DURATION_MS2 = 250;
3753
3878
  var Drawer = (props) => {
3754
3879
  const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
3755
- const [mounted, setMounted] = import_react17.default.useState(false);
3756
- const [visible, setVisible] = import_react17.default.useState(false);
3757
- import_react17.default.useEffect(() => {
3880
+ const [mounted, setMounted] = import_react18.default.useState(false);
3881
+ const [visible, setVisible] = import_react18.default.useState(false);
3882
+ const panelRef = import_react18.default.useRef(null);
3883
+ useEscapeKey(isOpen, onClose);
3884
+ useFocusTrap(panelRef, isOpen && mounted, { trap: true });
3885
+ import_react18.default.useEffect(() => {
3758
3886
  if (isOpen) {
3759
3887
  setMounted(true);
3760
3888
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -3776,6 +3904,7 @@ var Drawer = (props) => {
3776
3904
  children: /* @__PURE__ */ (0, import_jsx_runtime321.jsxs)(
3777
3905
  "div",
3778
3906
  {
3907
+ ref: panelRef,
3779
3908
  className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
3780
3909
  role: "dialog",
3781
3910
  "aria-modal": "true",
@@ -3798,7 +3927,7 @@ Drawer.displayName = "Drawer";
3798
3927
  var Drawer_default = Drawer;
3799
3928
 
3800
3929
  // src/components/Dropdown/Dropdown.tsx
3801
- var import_react20 = __toESM(require("react"), 1);
3930
+ var import_react21 = __toESM(require("react"), 1);
3802
3931
 
3803
3932
  // src/tokens/hooks/Portal.tsx
3804
3933
  var import_react_dom3 = __toESM(require("react-dom"), 1);
@@ -3810,13 +3939,15 @@ Portal.displayName = "Portal";
3810
3939
  var Portal_default = Portal;
3811
3940
 
3812
3941
  // src/tokens/hooks/useAutoPosition.ts
3813
- var import_react18 = __toESM(require("react"), 1);
3942
+ var import_react19 = __toESM(require("react"), 1);
3814
3943
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3815
- const [position, setPosition] = import_react18.default.useState({
3944
+ const [position, setPosition] = import_react19.default.useState({
3816
3945
  position: {},
3817
- direction: "bottom"
3946
+ direction: "bottom",
3947
+ repositioning: false
3818
3948
  });
3819
- const calculatePosition = import_react18.default.useCallback(() => {
3949
+ const placedRef = import_react19.default.useRef(false);
3950
+ const calculatePosition = import_react19.default.useCallback(() => {
3820
3951
  if (!triggerRef.current || !popRef.current) return;
3821
3952
  const triggerRect = triggerRef.current.getBoundingClientRect();
3822
3953
  const popW = popRef.current.offsetWidth;
@@ -3840,16 +3971,21 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3840
3971
  if (left < 8) left = 8;
3841
3972
  setPosition({
3842
3973
  position: { top, left },
3843
- direction
3974
+ direction,
3975
+ repositioning: placedRef.current
3844
3976
  });
3977
+ placedRef.current = true;
3845
3978
  }, [triggerRef, popRef]);
3846
- import_react18.default.useLayoutEffect(() => {
3847
- if (!enabled) return;
3979
+ import_react19.default.useLayoutEffect(() => {
3980
+ if (!enabled) {
3981
+ placedRef.current = false;
3982
+ return;
3983
+ }
3848
3984
  calculatePosition();
3849
3985
  const raf = requestAnimationFrame(calculatePosition);
3850
3986
  return () => cancelAnimationFrame(raf);
3851
3987
  }, [calculatePosition, enabled]);
3852
- import_react18.default.useEffect(() => {
3988
+ import_react19.default.useEffect(() => {
3853
3989
  if (!enabled || !popRef.current) return;
3854
3990
  const observer = new ResizeObserver(() => {
3855
3991
  calculatePosition();
@@ -3857,13 +3993,19 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3857
3993
  observer.observe(popRef.current);
3858
3994
  return () => observer.disconnect();
3859
3995
  }, [calculatePosition, enabled, popRef]);
3860
- import_react18.default.useEffect(() => {
3996
+ import_react19.default.useEffect(() => {
3861
3997
  if (!enabled) return;
3862
- window.addEventListener("resize", calculatePosition);
3863
- window.addEventListener("scroll", calculatePosition, true);
3998
+ let raf = 0;
3999
+ const onScrollOrResize = () => {
4000
+ cancelAnimationFrame(raf);
4001
+ raf = requestAnimationFrame(calculatePosition);
4002
+ };
4003
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
4004
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
3864
4005
  return () => {
3865
- window.removeEventListener("resize", calculatePosition);
3866
- window.removeEventListener("scroll", calculatePosition, true);
4006
+ cancelAnimationFrame(raf);
4007
+ window.removeEventListener("resize", onScrollOrResize);
4008
+ window.removeEventListener("scroll", onScrollOrResize, true);
3867
4009
  };
3868
4010
  }, [calculatePosition, enabled]);
3869
4011
  return position;
@@ -3871,9 +4013,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3871
4013
  var useAutoPosition_default = useAutoPosition;
3872
4014
 
3873
4015
  // src/tokens/hooks/useClickOutside.ts
3874
- var import_react19 = __toESM(require("react"), 1);
4016
+ var import_react20 = __toESM(require("react"), 1);
3875
4017
  var useClickOutside = (refs, handler, enabled = true) => {
3876
- import_react19.default.useEffect(() => {
4018
+ import_react20.default.useEffect(() => {
3877
4019
  if (!enabled) return;
3878
4020
  const refArray = Array.isArray(refs) ? refs : [refs];
3879
4021
  const listener = (event) => {
@@ -3899,14 +4041,18 @@ var useClickOutside_default = useClickOutside;
3899
4041
  var import_jsx_runtime322 = require("react/jsx-runtime");
3900
4042
  var Dropdown = (props) => {
3901
4043
  const { items, children } = props;
3902
- const [isOpen, setIsOpen] = import_react20.default.useState(false);
3903
- const [mounted, setMounted] = import_react20.default.useState(false);
3904
- const [visible, setVisible] = import_react20.default.useState(false);
3905
- const triggerRef = import_react20.default.useRef(null);
3906
- const menuRef = import_react20.default.useRef(null);
3907
- const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
4044
+ const [isOpen, setIsOpen] = import_react21.default.useState(false);
4045
+ const [mounted, setMounted] = import_react21.default.useState(false);
4046
+ const [visible, setVisible] = import_react21.default.useState(false);
4047
+ const triggerRef = import_react21.default.useRef(null);
4048
+ const menuRef = import_react21.default.useRef(null);
4049
+ const { position, direction, repositioning } = useAutoPosition_default(triggerRef, menuRef, mounted);
3908
4050
  useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
3909
- import_react20.default.useEffect(() => {
4051
+ useEscapeKey(isOpen, () => {
4052
+ setIsOpen(false);
4053
+ triggerRef.current?.focus({ preventScroll: true });
4054
+ });
4055
+ import_react21.default.useEffect(() => {
3910
4056
  if (isOpen) {
3911
4057
  setMounted(true);
3912
4058
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -3935,7 +4081,7 @@ var Dropdown = (props) => {
3935
4081
  "div",
3936
4082
  {
3937
4083
  ref: menuRef,
3938
- className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }),
4084
+ className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }, repositioning && "repositioning"),
3939
4085
  style: { top: position.top, left: position.left },
3940
4086
  role: "menu",
3941
4087
  children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime322.jsx)(
@@ -3960,7 +4106,112 @@ Dropdown.displayName = "Dropdown";
3960
4106
  var Dropdown_default = Dropdown;
3961
4107
 
3962
4108
  // src/components/Editor/Editor.tsx
3963
- var import_react21 = __toESM(require("react"), 1);
4109
+ var import_react22 = __toESM(require("react"), 1);
4110
+
4111
+ // src/components/Editor/highlight.ts
4112
+ var CODE_LANGUAGES = [
4113
+ { value: "plain", label: "\uC5C6\uC74C" },
4114
+ { value: "javascript", label: "JavaScript" },
4115
+ { value: "typescript", label: "TypeScript" },
4116
+ { value: "json", label: "JSON" },
4117
+ { value: "html", label: "HTML" },
4118
+ { value: "css", label: "CSS" },
4119
+ { value: "python", label: "Python" },
4120
+ { value: "sql", label: "SQL" },
4121
+ { value: "bash", label: "Bash" }
4122
+ ];
4123
+ var KEYWORDS = {
4124
+ plain: [],
4125
+ 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(" "),
4126
+ 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(" "),
4127
+ json: ["true", "false", "null"],
4128
+ html: [],
4129
+ css: [],
4130
+ 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(" "),
4131
+ 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(" "),
4132
+ 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(" ")
4133
+ };
4134
+ var LINE_COMMENT = {
4135
+ javascript: "//",
4136
+ typescript: "//",
4137
+ css: "",
4138
+ python: "#",
4139
+ sql: "--",
4140
+ bash: "#"
4141
+ };
4142
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4143
+ var wrap = (cls, text) => `<span class="tok-${cls}">${escape(text)}</span>`;
4144
+ var highlight = (code, lang) => {
4145
+ if (lang === "plain" || !code) return escape(code);
4146
+ const keywords = new Set(KEYWORDS[lang]);
4147
+ const lineComment = LINE_COMMENT[lang];
4148
+ const caseInsensitive = lang === "sql";
4149
+ let out = "";
4150
+ let i = 0;
4151
+ while (i < code.length) {
4152
+ const rest = code.slice(i);
4153
+ if ((lang === "javascript" || lang === "typescript" || lang === "css") && rest.startsWith("/*")) {
4154
+ const end = code.indexOf("*/", i + 2);
4155
+ const stop = end === -1 ? code.length : end + 2;
4156
+ out += wrap("comment", code.slice(i, stop));
4157
+ i = stop;
4158
+ continue;
4159
+ }
4160
+ if (lineComment && rest.startsWith(lineComment)) {
4161
+ const nl = code.indexOf("\n", i);
4162
+ const stop = nl === -1 ? code.length : nl;
4163
+ out += wrap("comment", code.slice(i, stop));
4164
+ i = stop;
4165
+ continue;
4166
+ }
4167
+ const q = rest[0];
4168
+ if (q === '"' || q === "'" || q === "`") {
4169
+ let j = 1;
4170
+ while (j < rest.length) {
4171
+ if (rest[j] === "\\") {
4172
+ j += 2;
4173
+ continue;
4174
+ }
4175
+ if (rest[j] === q) {
4176
+ j += 1;
4177
+ break;
4178
+ }
4179
+ if (rest[j] === "\n" && q !== "`") break;
4180
+ j += 1;
4181
+ }
4182
+ out += wrap("string", rest.slice(0, j));
4183
+ i += j;
4184
+ continue;
4185
+ }
4186
+ const num = /^\d+(\.\d+)?/.exec(rest);
4187
+ if (num && !/[\w$]/.test(code[i - 1] ?? "")) {
4188
+ out += wrap("number", num[0]);
4189
+ i += num[0].length;
4190
+ continue;
4191
+ }
4192
+ const word = /^[A-Za-z_$][\w$]*/.exec(rest);
4193
+ if (word) {
4194
+ const w = word[0];
4195
+ const hit = caseInsensitive ? keywords.has(w.toUpperCase()) : keywords.has(w);
4196
+ out += hit ? wrap("keyword", w) : escape(w);
4197
+ i += w.length;
4198
+ continue;
4199
+ }
4200
+ if (lang === "html" && rest.startsWith("<")) {
4201
+ const tag = /^<\/?[A-Za-z][\w-]*/.exec(rest);
4202
+ if (tag) {
4203
+ out += wrap("keyword", tag[0]);
4204
+ i += tag[0].length;
4205
+ continue;
4206
+ }
4207
+ }
4208
+ out += escape(code[i]);
4209
+ i += 1;
4210
+ }
4211
+ return out;
4212
+ };
4213
+
4214
+ // src/components/Editor/Editor.tsx
3964
4215
  var import_jsx_runtime323 = require("react/jsx-runtime");
3965
4216
  var DEFAULT_TOOLBAR = [
3966
4217
  "bold",
@@ -3968,14 +4219,27 @@ var DEFAULT_TOOLBAR = [
3968
4219
  "underline",
3969
4220
  "strikethrough",
3970
4221
  "code",
3971
- "heading",
4222
+ "heading-1",
4223
+ "heading-2",
4224
+ "heading-3",
3972
4225
  "list",
3973
4226
  "ordered-list",
3974
4227
  "blockquote",
3975
4228
  "code-block",
3976
4229
  "link",
3977
4230
  "image",
3978
- "divider"
4231
+ "divider",
4232
+ "checklist",
4233
+ "table",
4234
+ "align-left",
4235
+ "align-center",
4236
+ "align-right",
4237
+ "highlight",
4238
+ "superscript",
4239
+ "subscript",
4240
+ "clear-format",
4241
+ "undo",
4242
+ "redo"
3979
4243
  ];
3980
4244
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3981
4245
  "p",
@@ -4004,12 +4268,51 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
4004
4268
  "img",
4005
4269
  "hr",
4006
4270
  "span",
4007
- "div"
4271
+ "div",
4272
+ "sub",
4273
+ "sup",
4274
+ "mark",
4275
+ "table",
4276
+ "thead",
4277
+ "tbody",
4278
+ "tr",
4279
+ "th",
4280
+ "td"
4008
4281
  ]);
4009
4282
  var ALLOWED_ATTRS = {
4010
4283
  a: ["href", "target", "rel"],
4011
- img: ["src", "alt"]
4284
+ img: ["src", "alt", "width"],
4285
+ // 정렬·체크리스트는 클래스로만 표현한다. style 을 열면 붙여넣기로 아무 CSS 나 들어온다
4286
+ p: ["class"],
4287
+ h1: ["class"],
4288
+ h2: ["class"],
4289
+ h3: ["class"],
4290
+ h4: ["class"],
4291
+ h5: ["class"],
4292
+ h6: ["class"],
4293
+ ul: ["class"],
4294
+ ol: ["class"],
4295
+ li: ["class", "data-checked"],
4296
+ blockquote: ["class"],
4297
+ div: ["class"],
4298
+ span: ["class"],
4299
+ pre: ["class"],
4300
+ code: ["class"],
4301
+ mark: ["class"],
4302
+ table: ["class"],
4303
+ td: ["class"],
4304
+ th: ["class"],
4305
+ tr: ["class"]
4012
4306
  };
4307
+ var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
4308
+ "script",
4309
+ "style",
4310
+ "noscript",
4311
+ "iframe",
4312
+ "object",
4313
+ "embed",
4314
+ "template"
4315
+ ]);
4013
4316
  var sanitizeHtml = (input) => {
4014
4317
  const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
4015
4318
  const root = doc.body.firstElementChild;
@@ -4018,6 +4321,10 @@ var sanitizeHtml = (input) => {
4018
4321
  const children = Array.from(node.children);
4019
4322
  for (const child of children) {
4020
4323
  const tag = child.tagName.toLowerCase();
4324
+ if (DROP_WITH_CONTENT.has(tag)) {
4325
+ node.removeChild(child);
4326
+ continue;
4327
+ }
4021
4328
  if (!ALLOWED_TAGS.has(tag)) {
4022
4329
  while (child.firstChild) node.insertBefore(child.firstChild, child);
4023
4330
  node.removeChild(child);
@@ -4048,7 +4355,9 @@ var escapeHtml = (text) => {
4048
4355
  };
4049
4356
  var SLASH_ITEMS = [
4050
4357
  { key: "paragraph", label: "\uB2E8\uB77D", hint: "\uAE30\uBCF8 \uD14D\uC2A4\uD2B8" },
4051
- { key: "heading", label: "\uC81C\uBAA9 1", hint: "\uD070 \uC81C\uBAA9" },
4358
+ { key: "heading-1", label: "\uC81C\uBAA9 1", hint: "\uAC00\uC7A5 \uD070 \uC81C\uBAA9" },
4359
+ { key: "heading-2", label: "\uC81C\uBAA9 2", hint: "" },
4360
+ { key: "heading-3", label: "\uC81C\uBAA9 3", hint: "" },
4052
4361
  { key: "list", label: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D", hint: "\u2022 \uD56D\uBAA9" },
4053
4362
  { key: "ordered-list", label: "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D", hint: "1. \uD56D\uBAA9" },
4054
4363
  { key: "blockquote", label: "\uC778\uC6A9", hint: "" },
@@ -4068,26 +4377,32 @@ var Editor = (props) => {
4068
4377
  onImageUpload,
4069
4378
  minHeight = 200
4070
4379
  } = props;
4071
- const editorRef = import_react21.default.useRef(null);
4072
- const lastRangeRef = import_react21.default.useRef(null);
4073
- const composingRef = import_react21.default.useRef(false);
4074
- const [isFocused, setIsFocused] = import_react21.default.useState(false);
4075
- const [isEmpty, setIsEmpty] = import_react21.default.useState(!value);
4076
- const [activeFormats, setActiveFormats] = import_react21.default.useState(/* @__PURE__ */ new Set());
4077
- const [slashOpen, setSlashOpen] = import_react21.default.useState(false);
4078
- const [slashFilter, setSlashFilter] = import_react21.default.useState("");
4079
- const [slashIdx, setSlashIdx] = import_react21.default.useState(0);
4080
- const [slashPos, setSlashPos] = import_react21.default.useState({ top: 0, left: 0 });
4081
- const slashStartRangeRef = import_react21.default.useRef(null);
4082
- const [linkOpen, setLinkOpen] = import_react21.default.useState(false);
4083
- const [linkValue, setLinkValue] = import_react21.default.useState("");
4084
- const [linkPos, setLinkPos] = import_react21.default.useState({ top: 0, left: 0 });
4085
- import_react21.default.useEffect(() => {
4086
- if (!editorRef.current) return;
4087
- if (editorRef.current.innerHTML !== value) {
4088
- editorRef.current.innerHTML = sanitizeHtml(value || "");
4089
- updateEmpty();
4090
- }
4380
+ const editorRef = import_react22.default.useRef(null);
4381
+ const lastRangeRef = import_react22.default.useRef(null);
4382
+ const composingRef = import_react22.default.useRef(false);
4383
+ const [isFocused, setIsFocused] = import_react22.default.useState(false);
4384
+ const [isEmpty, setIsEmpty] = import_react22.default.useState(!value);
4385
+ const [activeFormats, setActiveFormats] = import_react22.default.useState(/* @__PURE__ */ new Set());
4386
+ const [slashOpen, setSlashOpen] = import_react22.default.useState(false);
4387
+ const [slashFilter, setSlashFilter] = import_react22.default.useState("");
4388
+ const [slashIdx, setSlashIdx] = import_react22.default.useState(0);
4389
+ const [slashPos, setSlashPos] = import_react22.default.useState({ top: 0, left: 0 });
4390
+ const slashStartRangeRef = import_react22.default.useRef(null);
4391
+ const [linkOpen, setLinkOpen] = import_react22.default.useState(false);
4392
+ const [linkValue, setLinkValue] = import_react22.default.useState("");
4393
+ const [linkPos, setLinkPos] = import_react22.default.useState({ top: 0, left: 0 });
4394
+ const lastEmittedRef = import_react22.default.useRef(null);
4395
+ import_react22.default.useEffect(() => {
4396
+ const el = editorRef.current;
4397
+ if (!el) return;
4398
+ if (value === lastEmittedRef.current) return;
4399
+ if (el.innerHTML === value) return;
4400
+ el.innerHTML = sanitizeHtml(value || "");
4401
+ if (!el.firstChild) el.innerHTML = "<p><br></p>";
4402
+ lastEmittedRef.current = null;
4403
+ historyRef.current = [{ html: el.innerHTML, caret: 0 }];
4404
+ historyIdxRef.current = 0;
4405
+ updateEmpty();
4091
4406
  }, [value]);
4092
4407
  const updateEmpty = () => {
4093
4408
  const el = editorRef.current;
@@ -4103,10 +4418,19 @@ var Editor = (props) => {
4103
4418
  }
4104
4419
  };
4105
4420
  const restoreSelection = () => {
4106
- if (!lastRangeRef.current) return;
4421
+ const range = lastRangeRef.current;
4422
+ if (!range) return;
4423
+ if (!editorRef.current?.contains(range.startContainer)) {
4424
+ lastRangeRef.current = null;
4425
+ return;
4426
+ }
4107
4427
  const sel = window.getSelection();
4108
- sel?.removeAllRanges();
4109
- sel?.addRange(lastRangeRef.current);
4428
+ try {
4429
+ sel?.removeAllRanges();
4430
+ sel?.addRange(range);
4431
+ } catch {
4432
+ lastRangeRef.current = null;
4433
+ }
4110
4434
  };
4111
4435
  const updateActiveFormats = () => {
4112
4436
  if (!editorRef.current) return;
@@ -4122,6 +4446,17 @@ var Editor = (props) => {
4122
4446
  if (document.queryCommandState("insertOrderedList")) next.add("ordered-list");
4123
4447
  const block = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
4124
4448
  if (block) next.add(`block:${block}`);
4449
+ if (document.queryCommandState("superscript")) next.add("superscript");
4450
+ if (document.queryCommandState("subscript")) next.add("subscript");
4451
+ } catch {
4452
+ }
4453
+ try {
4454
+ const blockEl = currentBlock();
4455
+ if (blockEl?.classList.contains("align-center")) next.add("align:center");
4456
+ else if (blockEl?.classList.contains("align-right")) next.add("align:right");
4457
+ else if (blockEl) next.add("align:left");
4458
+ if (blockEl?.closest("mark")) next.add("highlight");
4459
+ if (blockEl?.closest("ul.checklist")) next.add("checklist");
4125
4460
  } catch {
4126
4461
  }
4127
4462
  setActiveFormats(next);
@@ -4135,7 +4470,374 @@ var Editor = (props) => {
4135
4470
  const setBlock = (tag) => {
4136
4471
  exec("formatBlock", tag);
4137
4472
  };
4473
+ const toggleBlock = (tag) => {
4474
+ const current = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
4475
+ setBlock(current === tag ? "p" : tag);
4476
+ };
4477
+ const historyRef = import_react22.default.useRef([]);
4478
+ const historyIdxRef = import_react22.default.useRef(-1);
4479
+ const lastPushAtRef = import_react22.default.useRef(0);
4480
+ const isRestoringRef = import_react22.default.useRef(false);
4481
+ const caretOffset = (rootEl) => {
4482
+ const el = rootEl ?? editorRef.current;
4483
+ const sel = window.getSelection();
4484
+ if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return 0;
4485
+ const range = sel.getRangeAt(0).cloneRange();
4486
+ range.selectNodeContents(el);
4487
+ range.setEnd(sel.getRangeAt(0).endContainer, sel.getRangeAt(0).endOffset);
4488
+ return range.toString().length;
4489
+ };
4490
+ const setCaretOffset = (offset, rootEl) => {
4491
+ const el = rootEl ?? editorRef.current;
4492
+ if (!el) return;
4493
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
4494
+ let remaining = offset;
4495
+ let node = null;
4496
+ while (node = walker.nextNode()) {
4497
+ const len = node.textContent?.length ?? 0;
4498
+ if (remaining <= len) {
4499
+ const range = document.createRange();
4500
+ range.setStart(node, remaining);
4501
+ range.collapse(true);
4502
+ const sel = window.getSelection();
4503
+ sel?.removeAllRanges();
4504
+ sel?.addRange(range);
4505
+ return;
4506
+ }
4507
+ remaining -= len;
4508
+ }
4509
+ placeCaretAtEnd(el);
4510
+ };
4511
+ const pushHistory = (force = false) => {
4512
+ const el = editorRef.current;
4513
+ if (!el || isRestoringRef.current) return;
4514
+ const html = el.innerHTML;
4515
+ const hist = historyRef.current;
4516
+ const cur = hist[historyIdxRef.current];
4517
+ if (cur && cur.html === html) return;
4518
+ const now = Date.now();
4519
+ const coalesce = !force && cur && now - lastPushAtRef.current < 500;
4520
+ lastPushAtRef.current = now;
4521
+ if (coalesce) {
4522
+ hist[historyIdxRef.current] = { html, caret: caretOffset() };
4523
+ return;
4524
+ }
4525
+ hist.length = historyIdxRef.current + 1;
4526
+ hist.push({ html, caret: caretOffset() });
4527
+ if (hist.length > 200) hist.shift();
4528
+ historyIdxRef.current = hist.length - 1;
4529
+ };
4530
+ const applySnapshot = (snap) => {
4531
+ const el = editorRef.current;
4532
+ if (!el) return;
4533
+ isRestoringRef.current = true;
4534
+ el.innerHTML = snap.html;
4535
+ setCaretOffset(snap.caret);
4536
+ isRestoringRef.current = false;
4537
+ lastEmittedRef.current = el.innerHTML;
4538
+ onChange?.(el.innerHTML);
4539
+ updateEmpty();
4540
+ updateActiveFormats();
4541
+ };
4542
+ const undo = () => {
4543
+ pushHistory(true);
4544
+ if (historyIdxRef.current <= 0) return;
4545
+ historyIdxRef.current -= 1;
4546
+ applySnapshot(historyRef.current[historyIdxRef.current]);
4547
+ };
4548
+ const redo = () => {
4549
+ if (historyIdxRef.current >= historyRef.current.length - 1) return;
4550
+ historyIdxRef.current += 1;
4551
+ applySnapshot(historyRef.current[historyIdxRef.current]);
4552
+ };
4553
+ const langOf = (pre) => {
4554
+ const cls = Array.from(pre.classList).find((c) => c.startsWith("lang-"));
4555
+ return cls?.slice(5) ?? "plain";
4556
+ };
4557
+ const [codeLang, setCodeLang] = import_react22.default.useState("plain");
4558
+ const highlightRafRef = import_react22.default.useRef(0);
4559
+ const rehighlight = (pre) => {
4560
+ if (composingRef.current) return;
4561
+ const code = pre.querySelector("code");
4562
+ if (!code) return;
4563
+ const lang = langOf(pre);
4564
+ const text = code.textContent || "";
4565
+ const next = highlight(text, lang);
4566
+ if (code.innerHTML === next) return;
4567
+ const caret = caretOffset(code);
4568
+ isRestoringRef.current = true;
4569
+ code.innerHTML = next;
4570
+ setCaretOffset(caret, code);
4571
+ isRestoringRef.current = false;
4572
+ };
4573
+ const scheduleHighlight = () => {
4574
+ const pre = closestPre();
4575
+ if (!pre) return;
4576
+ cancelAnimationFrame(highlightRafRef.current);
4577
+ highlightRafRef.current = requestAnimationFrame(() => rehighlight(pre));
4578
+ };
4579
+ const changeCodeLang = (lang) => {
4580
+ const pre = closestPre();
4581
+ if (!pre) return;
4582
+ pushHistory(true);
4583
+ Array.from(pre.classList).filter((c) => c.startsWith("lang-")).forEach((c) => pre.classList.remove(c));
4584
+ if (lang !== "plain") pre.classList.add(`lang-${lang}`);
4585
+ setCodeLang(lang);
4586
+ rehighlight(pre);
4587
+ handleInput();
4588
+ };
4589
+ const closestPre = () => {
4590
+ const sel = window.getSelection();
4591
+ const node = sel?.anchorNode;
4592
+ if (!node || !editorRef.current?.contains(node)) return null;
4593
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4594
+ return el?.closest("pre") ?? null;
4595
+ };
4596
+ const insertCodeBlock = () => {
4597
+ pushHistory(true);
4598
+ const el = editorRef.current;
4599
+ const sel = window.getSelection();
4600
+ if (!el || !sel || sel.rangeCount === 0) return;
4601
+ const existing = closestPre();
4602
+ if (existing) {
4603
+ const p = document.createElement("p");
4604
+ p.textContent = existing.textContent || "";
4605
+ if (!p.textContent) p.innerHTML = "<br>";
4606
+ existing.replaceWith(p);
4607
+ placeCaretAtEnd(p);
4608
+ handleInput();
4609
+ return;
4610
+ }
4611
+ const range = sel.getRangeAt(0);
4612
+ const frag = range.cloneContents();
4613
+ const holder = document.createElement("div");
4614
+ holder.appendChild(frag);
4615
+ const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
4616
+ const linesOf = (node) => {
4617
+ const out = [];
4618
+ let cur = "";
4619
+ for (const child of Array.from(node.childNodes)) {
4620
+ if (child.nodeType === Node.TEXT_NODE) {
4621
+ cur += child.textContent || "";
4622
+ } else if (child.nodeName === "BR") {
4623
+ out.push(cur);
4624
+ cur = "";
4625
+ } else if (BLOCK.test(child.nodeName)) {
4626
+ if (cur) {
4627
+ out.push(cur);
4628
+ cur = "";
4629
+ }
4630
+ const sub = linesOf(child);
4631
+ out.push(...sub.length > 0 ? sub : [""]);
4632
+ } else {
4633
+ cur += linesOf(child).join("\n");
4634
+ }
4635
+ }
4636
+ if (cur) out.push(cur);
4637
+ return out;
4638
+ };
4639
+ const text = linesOf(holder).join("\n").replace(/\n+$/, "");
4640
+ const pre = document.createElement("pre");
4641
+ const code = document.createElement("code");
4642
+ code.textContent = text || "";
4643
+ pre.appendChild(code);
4644
+ range.deleteContents();
4645
+ range.insertNode(pre);
4646
+ ensureParagraphAfter(pre);
4647
+ placeCaretAtEnd(code);
4648
+ handleInput();
4649
+ };
4650
+ const insertTextAtCaret = (text) => {
4651
+ const sel = window.getSelection();
4652
+ if (!sel || sel.rangeCount === 0) return;
4653
+ const range = sel.getRangeAt(0);
4654
+ range.deleteContents();
4655
+ const node = document.createTextNode(text);
4656
+ range.insertNode(node);
4657
+ range.setStartAfter(node);
4658
+ range.collapse(true);
4659
+ sel.removeAllRanges();
4660
+ sel.addRange(range);
4661
+ handleInput();
4662
+ };
4663
+ const ensureParagraphAfter = (node) => {
4664
+ const next = node.nextElementSibling;
4665
+ if (next && next.tagName === "P") return;
4666
+ const p = document.createElement("p");
4667
+ p.innerHTML = "<br>";
4668
+ node.after(p);
4669
+ };
4670
+ const placeCaretAtEnd = (node) => {
4671
+ const sel = window.getSelection();
4672
+ if (!sel) return;
4673
+ const r2 = document.createRange();
4674
+ r2.selectNodeContents(node);
4675
+ r2.collapse(false);
4676
+ sel.removeAllRanges();
4677
+ sel.addRange(r2);
4678
+ editorRef.current?.focus();
4679
+ };
4680
+ const clearFormat = () => {
4681
+ exec("removeFormat");
4682
+ exec("unlink");
4683
+ setBlock("p");
4684
+ };
4685
+ const indentList = (outdent) => {
4686
+ exec(outdent ? "outdent" : "indent");
4687
+ };
4688
+ const inListItem = () => {
4689
+ const sel = window.getSelection();
4690
+ const node = sel?.anchorNode;
4691
+ if (!node || !editorRef.current?.contains(node)) return false;
4692
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4693
+ return !!el?.closest("li");
4694
+ };
4695
+ const currentBlock = () => {
4696
+ const sel = window.getSelection();
4697
+ const node = sel?.anchorNode;
4698
+ const root = editorRef.current;
4699
+ if (!node || !root?.contains(node)) return null;
4700
+ let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
4701
+ while (el && el !== root && !/^(P|H[1-6]|LI|BLOCKQUOTE|PRE|DIV|TD|TH)$/.test(el.tagName)) {
4702
+ el = el.parentElement;
4703
+ }
4704
+ return el && el !== root ? el : null;
4705
+ };
4706
+ const setAlign = (align) => {
4707
+ const block = currentBlock();
4708
+ if (!block) return;
4709
+ pushHistory(true);
4710
+ block.classList.remove("align-left", "align-center", "align-right");
4711
+ if (align !== "left") block.classList.add(`align-${align}`);
4712
+ handleInput();
4713
+ };
4714
+ const toggleChecklist = () => {
4715
+ pushHistory(true);
4716
+ const block = currentBlock();
4717
+ const list = block?.closest("ul");
4718
+ if (list?.classList.contains("checklist")) {
4719
+ list.classList.remove("checklist");
4720
+ list.querySelectorAll("li").forEach((li) => li.removeAttribute("data-checked"));
4721
+ handleInput();
4722
+ return;
4723
+ }
4724
+ if (list) {
4725
+ list.classList.add("checklist");
4726
+ list.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
4727
+ handleInput();
4728
+ return;
4729
+ }
4730
+ exec("insertUnorderedList");
4731
+ const made = currentBlock()?.closest("ul");
4732
+ if (made) {
4733
+ made.classList.add("checklist");
4734
+ made.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
4735
+ }
4736
+ handleInput();
4737
+ };
4738
+ const handleEditorClick = (e) => {
4739
+ if (readOnly) return;
4740
+ const target = e.target;
4741
+ const li = target.closest?.("li");
4742
+ if (!li || !li.closest("ul.checklist")) return;
4743
+ const rect = li.getBoundingClientRect();
4744
+ if (e.clientX - rect.left > 20) return;
4745
+ e.preventDefault();
4746
+ pushHistory(true);
4747
+ li.setAttribute("data-checked", li.getAttribute("data-checked") === "true" ? "false" : "true");
4748
+ handleInput();
4749
+ };
4750
+ const toggleHighlight = () => {
4751
+ const sel = window.getSelection();
4752
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
4753
+ pushHistory(true);
4754
+ const range = sel.getRangeAt(0);
4755
+ const node = sel.anchorNode;
4756
+ const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
4757
+ const existing = el?.closest("mark");
4758
+ if (existing) {
4759
+ const parent = existing.parentNode;
4760
+ while (existing.firstChild) parent?.insertBefore(existing.firstChild, existing);
4761
+ parent?.removeChild(existing);
4762
+ handleInput();
4763
+ return;
4764
+ }
4765
+ const mark = document.createElement("mark");
4766
+ try {
4767
+ range.surroundContents(mark);
4768
+ } catch {
4769
+ const frag = range.extractContents();
4770
+ mark.appendChild(frag);
4771
+ range.insertNode(mark);
4772
+ }
4773
+ handleInput();
4774
+ };
4775
+ const insertTable = (cols = 3, rows = 2) => {
4776
+ const sel = window.getSelection();
4777
+ if (!sel || sel.rangeCount === 0) return;
4778
+ pushHistory(true);
4779
+ const table = document.createElement("table");
4780
+ const thead = document.createElement("thead");
4781
+ const headRow = document.createElement("tr");
4782
+ for (let c = 0; c < cols; c++) {
4783
+ const th = document.createElement("th");
4784
+ th.innerHTML = "<br>";
4785
+ headRow.appendChild(th);
4786
+ }
4787
+ thead.appendChild(headRow);
4788
+ table.appendChild(thead);
4789
+ const tbody = document.createElement("tbody");
4790
+ for (let r2 = 0; r2 < rows; r2++) {
4791
+ const tr = document.createElement("tr");
4792
+ for (let c = 0; c < cols; c++) {
4793
+ const td = document.createElement("td");
4794
+ td.innerHTML = "<br>";
4795
+ tr.appendChild(td);
4796
+ }
4797
+ tbody.appendChild(tr);
4798
+ }
4799
+ table.appendChild(tbody);
4800
+ const range = sel.getRangeAt(0);
4801
+ range.deleteContents();
4802
+ range.insertNode(table);
4803
+ ensureParagraphAfter(table);
4804
+ const first = table.querySelector("th");
4805
+ if (first) placeCaretAtEnd(first);
4806
+ handleInput();
4807
+ };
4808
+ const moveTableCell = (backwards) => {
4809
+ const block = currentBlock();
4810
+ const cell = block?.closest("td, th");
4811
+ if (!cell) return false;
4812
+ const table = cell.closest("table");
4813
+ if (!table) return false;
4814
+ const cells = Array.from(table.querySelectorAll("td, th"));
4815
+ const i = cells.indexOf(cell);
4816
+ const next = cells[i + (backwards ? -1 : 1)];
4817
+ if (next) {
4818
+ placeCaretAtEnd(next);
4819
+ return true;
4820
+ }
4821
+ if (!backwards) {
4822
+ pushHistory(true);
4823
+ const lastRow = table.querySelector("tbody")?.lastElementChild;
4824
+ if (lastRow) {
4825
+ const tr = document.createElement("tr");
4826
+ for (let c = 0; c < lastRow.children.length; c++) {
4827
+ const td = document.createElement("td");
4828
+ td.innerHTML = "<br>";
4829
+ tr.appendChild(td);
4830
+ }
4831
+ lastRow.after(tr);
4832
+ placeCaretAtEnd(tr.firstElementChild);
4833
+ handleInput();
4834
+ return true;
4835
+ }
4836
+ }
4837
+ return false;
4838
+ };
4138
4839
  const insertDivider = () => {
4840
+ pushHistory(true);
4139
4841
  exec("insertHorizontalRule");
4140
4842
  const el = editorRef.current;
4141
4843
  if (el && el.lastElementChild?.tagName === "HR") {
@@ -4181,7 +4883,9 @@ var Editor = (props) => {
4181
4883
  const range = sel.getRangeAt(0);
4182
4884
  slashStartRangeRef.current = range.cloneRange();
4183
4885
  const rect = range.getBoundingClientRect();
4184
- const editorRect = editorRef.current.getBoundingClientRect();
4886
+ const editorEl = editorRef.current;
4887
+ if (!editorEl) return;
4888
+ const editorRect = editorEl.getBoundingClientRect();
4185
4889
  setSlashPos({
4186
4890
  top: rect.bottom - editorRect.top + 4,
4187
4891
  left: rect.left - editorRect.left
@@ -4196,7 +4900,7 @@ var Editor = (props) => {
4196
4900
  setSlashIdx(0);
4197
4901
  slashStartRangeRef.current = null;
4198
4902
  };
4199
- const filteredSlashItems = import_react21.default.useMemo(() => {
4903
+ const filteredSlashItems = import_react22.default.useMemo(() => {
4200
4904
  if (!slashFilter) return SLASH_ITEMS;
4201
4905
  const f = slashFilter.toLowerCase();
4202
4906
  return SLASH_ITEMS.filter((it) => it.label.toLowerCase().includes(f) || it.key.includes(f));
@@ -4226,8 +4930,14 @@ var Editor = (props) => {
4226
4930
  case "paragraph":
4227
4931
  setBlock("p");
4228
4932
  break;
4229
- case "heading":
4230
- setBlock("h2");
4933
+ case "heading-1":
4934
+ toggleBlock("h1");
4935
+ break;
4936
+ case "heading-2":
4937
+ toggleBlock("h2");
4938
+ break;
4939
+ case "heading-3":
4940
+ toggleBlock("h3");
4231
4941
  break;
4232
4942
  case "list":
4233
4943
  exec("insertUnorderedList");
@@ -4239,7 +4949,7 @@ var Editor = (props) => {
4239
4949
  setBlock("blockquote");
4240
4950
  break;
4241
4951
  case "code-block":
4242
- setBlock("pre");
4952
+ insertCodeBlock();
4243
4953
  break;
4244
4954
  case "divider":
4245
4955
  insertDivider();
@@ -4257,7 +4967,9 @@ var Editor = (props) => {
4257
4967
  if (!sel || sel.rangeCount === 0) return;
4258
4968
  const range = sel.getRangeAt(0);
4259
4969
  const rect = range.getBoundingClientRect();
4260
- const editorRect = editorRef.current.getBoundingClientRect();
4970
+ const editorEl2 = editorRef.current;
4971
+ if (!editorEl2) return;
4972
+ const editorRect = editorEl2.getBoundingClientRect();
4261
4973
  setLinkPos({
4262
4974
  top: rect.bottom - editorRect.top + 4,
4263
4975
  left: rect.left - editorRect.left
@@ -4290,13 +5002,13 @@ var Editor = (props) => {
4290
5002
  if (node.nodeType !== Node.TEXT_NODE) return false;
4291
5003
  const text = (node.textContent || "").slice(0, range.startOffset);
4292
5004
  const patterns = [
4293
- [/^###\s$/, () => setBlock("h3")],
4294
- [/^##\s$/, () => setBlock("h2")],
4295
- [/^#\s$/, () => setBlock("h1")],
4296
- [/^-\s$/, () => exec("insertUnorderedList")],
4297
- [/^\*\s$/, () => exec("insertUnorderedList")],
4298
- [/^1\.\s$/, () => exec("insertOrderedList")],
4299
- [/^>\s$/, () => setBlock("blockquote")]
5005
+ [/^###$/, () => setBlock("h3")],
5006
+ [/^##$/, () => setBlock("h2")],
5007
+ [/^#$/, () => setBlock("h1")],
5008
+ [/^-$/, () => exec("insertUnorderedList")],
5009
+ [/^\*$/, () => exec("insertUnorderedList")],
5010
+ [/^1\.$/, () => exec("insertOrderedList")],
5011
+ [/^>$/, () => setBlock("blockquote")]
4300
5012
  ];
4301
5013
  for (const [re, fn] of patterns) {
4302
5014
  if (re.test(text)) {
@@ -4316,7 +5028,11 @@ var Editor = (props) => {
4316
5028
  if (composingRef.current) return;
4317
5029
  const el = editorRef.current;
4318
5030
  if (!el) return;
4319
- onChange?.(el.innerHTML);
5031
+ const html = el.innerHTML;
5032
+ lastEmittedRef.current = html;
5033
+ pushHistory();
5034
+ scheduleHighlight();
5035
+ onChange?.(html);
4320
5036
  updateEmpty();
4321
5037
  updateActiveFormats();
4322
5038
  if (slashOpen) {
@@ -4344,6 +5060,40 @@ var Editor = (props) => {
4344
5060
  };
4345
5061
  const handleKeyDown = (e) => {
4346
5062
  if (readOnly) return;
5063
+ if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
5064
+ const pre = closestPre();
5065
+ if (pre && !slashOpen) {
5066
+ if (e.key === "Enter" && !e.shiftKey) {
5067
+ const text = pre.textContent || "";
5068
+ if (text.endsWith("\n\n")) {
5069
+ e.preventDefault();
5070
+ const code = pre.querySelector("code") ?? pre;
5071
+ code.textContent = text.replace(/\n\n$/, "");
5072
+ ensureParagraphAfter(pre);
5073
+ const next = pre.nextElementSibling;
5074
+ if (next) placeCaretAtEnd(next);
5075
+ handleInput();
5076
+ return;
5077
+ }
5078
+ e.preventDefault();
5079
+ insertTextAtCaret("\n");
5080
+ return;
5081
+ }
5082
+ if (e.key === "Tab") {
5083
+ e.preventDefault();
5084
+ insertTextAtCaret(" ");
5085
+ return;
5086
+ }
5087
+ }
5088
+ if (e.key === "Tab" && moveTableCell(e.shiftKey)) {
5089
+ e.preventDefault();
5090
+ return;
5091
+ }
5092
+ if (e.key === "Tab" && inListItem()) {
5093
+ e.preventDefault();
5094
+ indentList(e.shiftKey);
5095
+ return;
5096
+ }
4347
5097
  if (slashOpen) {
4348
5098
  if (e.key === "ArrowDown") {
4349
5099
  e.preventDefault();
@@ -4367,6 +5117,21 @@ var Editor = (props) => {
4367
5117
  return;
4368
5118
  }
4369
5119
  }
5120
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
5121
+ e.preventDefault();
5122
+ if (e.shiftKey) redo();
5123
+ else undo();
5124
+ return;
5125
+ }
5126
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "v") {
5127
+ plainPasteRef.current = true;
5128
+ return;
5129
+ }
5130
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "y") {
5131
+ e.preventDefault();
5132
+ redo();
5133
+ return;
5134
+ }
4370
5135
  if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
4371
5136
  const k = e.key.toLowerCase();
4372
5137
  if (k === "b") {
@@ -4390,6 +5155,7 @@ var Editor = (props) => {
4390
5155
  return;
4391
5156
  }
4392
5157
  }
5158
+ if (pre) return;
4393
5159
  if (enableSlashCommand && e.key === "/") {
4394
5160
  setTimeout(openSlashMenu, 0);
4395
5161
  }
@@ -4399,11 +5165,21 @@ var Editor = (props) => {
4399
5165
  }
4400
5166
  }
4401
5167
  };
5168
+ const plainPasteRef = import_react22.default.useRef(false);
4402
5169
  const handlePaste = (e) => {
4403
5170
  if (readOnly) return;
4404
5171
  e.preventDefault();
4405
- const html = e.clipboardData.getData("text/html");
4406
5172
  const text = e.clipboardData.getData("text/plain");
5173
+ if (plainPasteRef.current) {
5174
+ plainPasteRef.current = false;
5175
+ insertTextAtCaret(text);
5176
+ return;
5177
+ }
5178
+ if (closestPre()) {
5179
+ insertTextAtCaret(text);
5180
+ return;
5181
+ }
5182
+ const html = e.clipboardData.getData("text/html");
4407
5183
  const clean = html ? sanitizeHtml(html) : escapeHtml(text);
4408
5184
  document.execCommand("insertHTML", false, clean);
4409
5185
  handleInput();
@@ -4425,8 +5201,11 @@ var Editor = (props) => {
4425
5201
  };
4426
5202
  const handleSelectionUpdate = () => {
4427
5203
  updateActiveFormats();
5204
+ const pre = closestPre();
5205
+ setCodeLang(pre ? langOf(pre) : "plain");
4428
5206
  };
4429
5207
  const onToolbarAction = (item) => {
5208
+ if (closestPre() && !["code-block", "undo", "redo"].includes(item)) return;
4430
5209
  switch (item) {
4431
5210
  case "bold":
4432
5211
  exec("bold");
@@ -4455,8 +5234,14 @@ var Editor = (props) => {
4455
5234
  handleInput();
4456
5235
  break;
4457
5236
  }
4458
- case "heading":
4459
- setBlock("h2");
5237
+ case "heading-1":
5238
+ toggleBlock("h1");
5239
+ break;
5240
+ case "heading-2":
5241
+ toggleBlock("h2");
5242
+ break;
5243
+ case "heading-3":
5244
+ toggleBlock("h3");
4460
5245
  break;
4461
5246
  case "list":
4462
5247
  exec("insertUnorderedList");
@@ -4468,7 +5253,7 @@ var Editor = (props) => {
4468
5253
  setBlock("blockquote");
4469
5254
  break;
4470
5255
  case "code-block":
4471
- setBlock("pre");
5256
+ insertCodeBlock();
4472
5257
  break;
4473
5258
  case "link":
4474
5259
  openLinkEditor();
@@ -4479,6 +5264,39 @@ var Editor = (props) => {
4479
5264
  case "divider":
4480
5265
  insertDivider();
4481
5266
  break;
5267
+ case "checklist":
5268
+ toggleChecklist();
5269
+ break;
5270
+ case "align-left":
5271
+ setAlign("left");
5272
+ break;
5273
+ case "align-center":
5274
+ setAlign("center");
5275
+ break;
5276
+ case "align-right":
5277
+ setAlign("right");
5278
+ break;
5279
+ case "highlight":
5280
+ toggleHighlight();
5281
+ break;
5282
+ case "superscript":
5283
+ exec("superscript");
5284
+ break;
5285
+ case "subscript":
5286
+ exec("subscript");
5287
+ break;
5288
+ case "table":
5289
+ insertTable();
5290
+ break;
5291
+ case "clear-format":
5292
+ clearFormat();
5293
+ break;
5294
+ case "undo":
5295
+ undo();
5296
+ break;
5297
+ case "redo":
5298
+ redo();
5299
+ break;
4482
5300
  }
4483
5301
  };
4484
5302
  return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: clsx_default("lib-xplat-editor", isFocused && "focused", readOnly && "readonly"), children: [
@@ -4487,7 +5305,10 @@ var Editor = (props) => {
4487
5305
  {
4488
5306
  items: toolbar,
4489
5307
  active: activeFormats,
4490
- onAction: onToolbarAction
5308
+ onAction: onToolbarAction,
5309
+ codeLang,
5310
+ inCodeBlock: activeFormats.has("block:pre"),
5311
+ onCodeLangChange: changeCodeLang
4491
5312
  }
4492
5313
  ),
4493
5314
  /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__content", style: { minHeight }, children: [
@@ -4498,9 +5319,14 @@ var Editor = (props) => {
4498
5319
  ref: editorRef,
4499
5320
  contentEditable: !readOnly,
4500
5321
  suppressContentEditableWarning: true,
5322
+ role: "textbox",
5323
+ "aria-multiline": "true",
5324
+ "aria-readonly": readOnly || void 0,
5325
+ "aria-label": placeholder,
4501
5326
  className: "lib-xplat-editor__editable",
4502
5327
  onInput: handleInput,
4503
5328
  onKeyDown: handleKeyDown,
5329
+ onClick: handleEditorClick,
4504
5330
  onKeyUp: handleSelectionUpdate,
4505
5331
  onMouseUp: handleSelectionUpdate,
4506
5332
  onPaste: handlePaste,
@@ -4542,14 +5368,27 @@ var TOOLBAR_LABEL = {
4542
5368
  underline: "U",
4543
5369
  strikethrough: "S",
4544
5370
  code: "<>",
4545
- heading: "H",
5371
+ "heading-1": "H1",
5372
+ "heading-2": "H2",
5373
+ "heading-3": "H3",
4546
5374
  list: "\u2022",
4547
5375
  "ordered-list": "1.",
4548
5376
  blockquote: "\u275D",
4549
5377
  "code-block": "[ ]",
4550
5378
  link: "\u{1F517}",
4551
5379
  image: "\u{1F5BC}",
4552
- divider: "\u2014"
5380
+ divider: "\u2014",
5381
+ checklist: "\u2611",
5382
+ "align-left": "\u21E4",
5383
+ "align-center": "\u2194",
5384
+ "align-right": "\u21E5",
5385
+ highlight: "\u{1F58D}",
5386
+ superscript: "x\xB2",
5387
+ subscript: "x\u2082",
5388
+ table: "\u25A6",
5389
+ "clear-format": "\u232B",
5390
+ undo: "\u21B6",
5391
+ redo: "\u21B7"
4553
5392
  };
4554
5393
  var TOOLBAR_TITLE = {
4555
5394
  bold: "\uAD75\uAC8C (\u2318B)",
@@ -4557,39 +5396,77 @@ var TOOLBAR_TITLE = {
4557
5396
  underline: "\uBC11\uC904 (\u2318U)",
4558
5397
  strikethrough: "\uCDE8\uC18C\uC120",
4559
5398
  code: "\uC778\uB77C\uC778 \uCF54\uB4DC",
4560
- heading: "\uC81C\uBAA9",
5399
+ "heading-1": "\uC81C\uBAA9 1",
5400
+ "heading-2": "\uC81C\uBAA9 2",
5401
+ "heading-3": "\uC81C\uBAA9 3",
4561
5402
  list: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D",
4562
5403
  "ordered-list": "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D",
4563
5404
  blockquote: "\uC778\uC6A9",
4564
5405
  "code-block": "\uCF54\uB4DC \uBE14\uB85D",
4565
5406
  link: "\uB9C1\uD06C (\u2318K)",
4566
5407
  image: "\uC774\uBBF8\uC9C0",
4567
- divider: "\uAD6C\uBD84\uC120"
5408
+ divider: "\uAD6C\uBD84\uC120",
5409
+ checklist: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
5410
+ "align-left": "\uC67C\uCABD \uC815\uB82C",
5411
+ "align-center": "\uAC00\uC6B4\uB370 \uC815\uB82C",
5412
+ "align-right": "\uC624\uB978\uCABD \uC815\uB82C",
5413
+ highlight: "\uD615\uAD11\uD39C",
5414
+ superscript: "\uC704 \uCCA8\uC790",
5415
+ subscript: "\uC544\uB798 \uCCA8\uC790",
5416
+ table: "\uD45C \uC0BD\uC785",
5417
+ "clear-format": "\uC11C\uC2DD \uC9C0\uC6B0\uAE30",
5418
+ undo: "\uB418\uB3CC\uB9AC\uAE30 (\u2318Z)",
5419
+ redo: "\uB2E4\uC2DC\uD558\uAE30 (\u2318\u21E7Z)"
4568
5420
  };
4569
5421
  var isActive = (item, active) => {
4570
- if (item === "heading") return active.has("block:h1") || active.has("block:h2") || active.has("block:h3");
5422
+ if (item === "heading-1") return active.has("block:h1");
5423
+ if (item === "heading-2") return active.has("block:h2");
5424
+ if (item === "heading-3") return active.has("block:h3");
4571
5425
  if (item === "blockquote") return active.has("block:blockquote");
4572
5426
  if (item === "code-block") return active.has("block:pre");
5427
+ if (item === "align-left") return active.has("align:left");
5428
+ if (item === "align-center") return active.has("align:center");
5429
+ if (item === "align-right") return active.has("align:right");
4573
5430
  return active.has(item);
4574
5431
  };
4575
- var EditorToolbar = ({ items, active, onAction }) => {
4576
- return /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("div", { className: "lib-xplat-editor__toolbar", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
4577
- "button",
4578
- {
4579
- type: "button",
4580
- className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
4581
- title: TOOLBAR_TITLE[item],
4582
- onMouseDown: (e) => {
4583
- e.preventDefault();
4584
- onAction(item);
5432
+ var EditorToolbar = ({
5433
+ items,
5434
+ active,
5435
+ onAction,
5436
+ codeLang,
5437
+ inCodeBlock,
5438
+ onCodeLangChange
5439
+ }) => {
5440
+ return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__toolbar", children: [
5441
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
5442
+ "button",
5443
+ {
5444
+ type: "button",
5445
+ className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
5446
+ title: TOOLBAR_TITLE[item],
5447
+ onMouseDown: (e) => {
5448
+ e.preventDefault();
5449
+ onAction(item);
5450
+ },
5451
+ children: TOOLBAR_LABEL[item]
4585
5452
  },
4586
- children: TOOLBAR_LABEL[item]
4587
- },
4588
- item
4589
- )) });
5453
+ item
5454
+ )),
5455
+ inCodeBlock && /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
5456
+ "select",
5457
+ {
5458
+ className: "lib-xplat-editor__lang",
5459
+ value: codeLang,
5460
+ "aria-label": "\uCF54\uB4DC \uC5B8\uC5B4",
5461
+ onMouseDown: (e) => e.stopPropagation(),
5462
+ onChange: (e) => onCodeLangChange(e.target.value),
5463
+ children: CODE_LANGUAGES.map((l) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("option", { value: l.value, children: l.label }, l.value))
5464
+ }
5465
+ )
5466
+ ] });
4590
5467
  };
4591
5468
  var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
4592
- import_react21.default.useEffect(() => {
5469
+ import_react22.default.useEffect(() => {
4593
5470
  const handleClickOutside = (e) => {
4594
5471
  const target = e.target;
4595
5472
  const menu = document.querySelector(".lib-xplat-editor__slash-menu");
@@ -4623,8 +5500,8 @@ var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
4623
5500
  );
4624
5501
  };
4625
5502
  var LinkPopover = ({ position, value, onChange, onConfirm, onClose }) => {
4626
- const inputRef = import_react21.default.useRef(null);
4627
- import_react21.default.useEffect(() => {
5503
+ const inputRef = import_react22.default.useRef(null);
5504
+ import_react22.default.useEffect(() => {
4628
5505
  inputRef.current?.focus();
4629
5506
  }, []);
4630
5507
  return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)(
@@ -4682,7 +5559,7 @@ EmptyState.displayName = "EmptyState";
4682
5559
  var EmptyState_default = EmptyState;
4683
5560
 
4684
5561
  // src/components/FileUpload/FileUpload.tsx
4685
- var import_react22 = __toESM(require("react"), 1);
5562
+ var import_react23 = __toESM(require("react"), 1);
4686
5563
  var import_jsx_runtime325 = require("react/jsx-runtime");
4687
5564
  var FileUpload = (props) => {
4688
5565
  const {
@@ -4694,8 +5571,8 @@ var FileUpload = (props) => {
4694
5571
  description,
4695
5572
  validations
4696
5573
  } = props;
4697
- const [isDragOver, setIsDragOver] = import_react22.default.useState(false);
4698
- const inputRef = import_react22.default.useRef(null);
5574
+ const [isDragOver, setIsDragOver] = import_react23.default.useState(false);
5575
+ const inputRef = import_react23.default.useRef(null);
4699
5576
  const handleFiles = (fileList) => {
4700
5577
  let files = Array.from(fileList);
4701
5578
  if (maxSize) {
@@ -4757,10 +5634,10 @@ FileUpload.displayName = "FileUpload";
4757
5634
  var FileUpload_default = FileUpload;
4758
5635
 
4759
5636
  // src/components/HtmlTypeWriter/HtmlTypeWriter.tsx
4760
- var import_react24 = __toESM(require("react"), 1);
5637
+ var import_react25 = __toESM(require("react"), 1);
4761
5638
 
4762
5639
  // src/components/HtmlTypeWriter/utils.ts
4763
- var import_react23 = __toESM(require("react"), 1);
5640
+ var import_react24 = __toESM(require("react"), 1);
4764
5641
  var voidTags = /* @__PURE__ */ new Set([
4765
5642
  "br",
4766
5643
  "img",
@@ -4828,15 +5705,15 @@ var convertNodeToReactWithRange = (node, typedLen, rangeMap) => {
4828
5705
  props[attr.name] = attr.value;
4829
5706
  });
4830
5707
  if (voidTags.has(tag)) {
4831
- return import_react23.default.createElement(tag, props);
5708
+ return import_react24.default.createElement(tag, props);
4832
5709
  }
4833
5710
  const children = Array.from(element.childNodes).map((child) => convertNodeToReactWithRange(child, typedLen, rangeMap)).filter((n) => n != null);
4834
- return import_react23.default.createElement(tag, props, ...children);
5711
+ return import_react24.default.createElement(tag, props, ...children);
4835
5712
  };
4836
5713
  var htmlToReactProgressive = (root, typedLen, rangeMap) => {
4837
5714
  const nodes = Array.from(root.childNodes).map((child, idx) => {
4838
5715
  const node = convertNodeToReactWithRange(child, typedLen, rangeMap);
4839
- return node == null ? null : import_react23.default.createElement(import_react23.default.Fragment, { key: idx }, node);
5716
+ return node == null ? null : import_react24.default.createElement(import_react24.default.Fragment, { key: idx }, node);
4840
5717
  }).filter(Boolean);
4841
5718
  return nodes.length === 0 ? null : nodes;
4842
5719
  };
@@ -4849,20 +5726,20 @@ var HtmlTypeWriter = ({
4849
5726
  onDone,
4850
5727
  onChange
4851
5728
  }) => {
4852
- const [typedLen, setTypedLen] = import_react24.default.useState(0);
4853
- const doneCalledRef = import_react24.default.useRef(false);
4854
- const { doc, rangeMap, totalLength } = import_react24.default.useMemo(() => {
5729
+ const [typedLen, setTypedLen] = import_react25.default.useState(0);
5730
+ const doneCalledRef = import_react25.default.useRef(false);
5731
+ const { doc, rangeMap, totalLength } = import_react25.default.useMemo(() => {
4855
5732
  if (typeof window === "undefined") return { doc: null, rangeMap: /* @__PURE__ */ new Map(), totalLength: 0 };
4856
5733
  const decoded = decodeHtmlEntities(html);
4857
5734
  const doc2 = new DOMParser().parseFromString(decoded, "text/html");
4858
5735
  const { rangeMap: rangeMap2, totalLength: totalLength2 } = buildRangeMap(doc2.body);
4859
5736
  return { doc: doc2, rangeMap: rangeMap2, totalLength: totalLength2 };
4860
5737
  }, [html]);
4861
- import_react24.default.useEffect(() => {
5738
+ import_react25.default.useEffect(() => {
4862
5739
  setTypedLen(0);
4863
5740
  doneCalledRef.current = false;
4864
5741
  }, [html]);
4865
- import_react24.default.useEffect(() => {
5742
+ import_react25.default.useEffect(() => {
4866
5743
  if (!totalLength) return;
4867
5744
  if (typedLen >= totalLength) return;
4868
5745
  const timer = window.setInterval(() => {
@@ -4870,18 +5747,18 @@ var HtmlTypeWriter = ({
4870
5747
  }, duration);
4871
5748
  return () => window.clearInterval(timer);
4872
5749
  }, [typedLen, totalLength, duration]);
4873
- import_react24.default.useEffect(() => {
5750
+ import_react25.default.useEffect(() => {
4874
5751
  if (typedLen > 0 && typedLen < totalLength) {
4875
5752
  onChange?.();
4876
5753
  }
4877
5754
  }, [typedLen, totalLength, onChange]);
4878
- import_react24.default.useEffect(() => {
5755
+ import_react25.default.useEffect(() => {
4879
5756
  if (typedLen === totalLength && totalLength > 0 && !doneCalledRef.current) {
4880
5757
  doneCalledRef.current = true;
4881
5758
  onDone?.();
4882
5759
  }
4883
5760
  }, [typedLen, totalLength, onDone]);
4884
- const parsed = import_react24.default.useMemo(
5761
+ const parsed = import_react25.default.useMemo(
4885
5762
  () => doc ? htmlToReactProgressive(doc.body, typedLen, rangeMap) : null,
4886
5763
  [doc, typedLen, rangeMap]
4887
5764
  );
@@ -4891,12 +5768,12 @@ HtmlTypeWriter.displayName = "HtmlTypeWriter";
4891
5768
  var HtmlTypeWriter_default = HtmlTypeWriter;
4892
5769
 
4893
5770
  // src/components/ImageSelector/ImageSelector.tsx
4894
- var import_react25 = __toESM(require("react"), 1);
5771
+ var import_react26 = __toESM(require("react"), 1);
4895
5772
  var import_jsx_runtime327 = require("react/jsx-runtime");
4896
5773
  var ImageSelector = (props) => {
4897
5774
  const { value, label, onChange, validations } = props;
4898
- const [previewUrl, setPreviewUrl] = import_react25.default.useState();
4899
- import_react25.default.useEffect(() => {
5775
+ const [previewUrl, setPreviewUrl] = import_react26.default.useState();
5776
+ import_react26.default.useEffect(() => {
4900
5777
  if (!value) {
4901
5778
  setPreviewUrl(void 0);
4902
5779
  return;
@@ -4905,7 +5782,7 @@ var ImageSelector = (props) => {
4905
5782
  setPreviewUrl(url);
4906
5783
  return () => URL.revokeObjectURL(url);
4907
5784
  }, [value]);
4908
- const inputRef = import_react25.default.useRef(null);
5785
+ const inputRef = import_react26.default.useRef(null);
4909
5786
  const handleFileChange = (e) => {
4910
5787
  const selectedFile = e.target.files?.[0];
4911
5788
  if (selectedFile) {
@@ -5088,17 +5965,21 @@ Pagination.displayName = "Pagination";
5088
5965
  var Pagination_default = Pagination;
5089
5966
 
5090
5967
  // src/components/PopOver/PopOver.tsx
5091
- var import_react26 = __toESM(require("react"), 1);
5968
+ var import_react27 = __toESM(require("react"), 1);
5092
5969
  var import_jsx_runtime332 = require("react/jsx-runtime");
5093
5970
  var PopOver = (props) => {
5094
5971
  const { children, isOpen, onClose, PopOverEl } = props;
5095
- const popRef = import_react26.default.useRef(null);
5096
- const triggerRef = import_react26.default.useRef(null);
5097
- const [localOpen, setLocalOpen] = import_react26.default.useState(false);
5098
- const [eventTrigger, setEventTrigger] = import_react26.default.useState(false);
5972
+ const popRef = import_react27.default.useRef(null);
5973
+ const triggerRef = import_react27.default.useRef(null);
5974
+ const [localOpen, setLocalOpen] = import_react27.default.useState(false);
5975
+ const [eventTrigger, setEventTrigger] = import_react27.default.useState(false);
5099
5976
  useClickOutside_default([popRef, triggerRef], onClose, isOpen);
5977
+ useEscapeKey(isOpen, () => {
5978
+ onClose();
5979
+ triggerRef.current?.focus({ preventScroll: true });
5980
+ });
5100
5981
  const position = useAutoPosition_default(triggerRef, popRef, localOpen);
5101
- import_react26.default.useEffect(() => {
5982
+ import_react27.default.useEffect(() => {
5102
5983
  if (isOpen) {
5103
5984
  setLocalOpen(isOpen);
5104
5985
  setTimeout(() => {
@@ -5127,6 +6008,7 @@ var PopOver = (props) => {
5127
6008
  className: clsx_default(
5128
6009
  "lib-xplat-pop-over-content",
5129
6010
  position.direction,
6011
+ position.repositioning && "repositioning",
5130
6012
  eventTrigger && "visible"
5131
6013
  ),
5132
6014
  ref: popRef,
@@ -5183,12 +6065,12 @@ Progress.displayName = "Progress";
5183
6065
  var Progress_default = Progress;
5184
6066
 
5185
6067
  // src/components/Radio/RadioGroupContext.tsx
5186
- var import_react27 = __toESM(require("react"), 1);
5187
- var RadioGroupContext = import_react27.default.createContext(
6068
+ var import_react28 = __toESM(require("react"), 1);
6069
+ var RadioGroupContext = import_react28.default.createContext(
5188
6070
  null
5189
6071
  );
5190
6072
  var useRadioGroupContext = () => {
5191
- return import_react27.default.useContext(RadioGroupContext);
6073
+ return import_react28.default.useContext(RadioGroupContext);
5192
6074
  };
5193
6075
  var RadioGroupContext_default = RadioGroupContext;
5194
6076
 
@@ -5264,19 +6146,19 @@ RadioGroup.displayName = "RadioGroup";
5264
6146
  var RadioGroup_default = RadioGroup;
5265
6147
 
5266
6148
  // src/components/Select/Select.tsx
5267
- var import_react30 = __toESM(require("react"), 1);
6149
+ var import_react31 = __toESM(require("react"), 1);
5268
6150
 
5269
6151
  // src/components/Select/context.ts
5270
- var import_react28 = __toESM(require("react"), 1);
5271
- var SelectContext = import_react28.default.createContext(null);
6152
+ var import_react29 = __toESM(require("react"), 1);
6153
+ var SelectContext = import_react29.default.createContext(null);
5272
6154
  var context_default = SelectContext;
5273
6155
 
5274
6156
  // src/components/Select/SelectItem.tsx
5275
- var import_react29 = __toESM(require("react"), 1);
6157
+ var import_react30 = __toESM(require("react"), 1);
5276
6158
  var import_jsx_runtime336 = require("react/jsx-runtime");
5277
6159
  var SelectItem = (props) => {
5278
6160
  const { children, value, onClick, disabled = false } = props;
5279
- const ctx = import_react29.default.useContext(context_default);
6161
+ const ctx = import_react30.default.useContext(context_default);
5280
6162
  const handleClick = (e) => {
5281
6163
  e.preventDefault();
5282
6164
  e.stopPropagation();
@@ -5316,32 +6198,37 @@ var SelectRoot = (props) => {
5316
6198
  onChange,
5317
6199
  children,
5318
6200
  disabled = false,
6201
+ searchable = false,
6202
+ searchPlaceholder = "\uAC80\uC0C9",
5319
6203
  error = false,
5320
6204
  validations,
6205
+ label,
6206
+ required,
5321
6207
  size = "md"
5322
6208
  } = props;
5323
- const itemChildren = import_react30.default.Children.toArray(children).filter(
5324
- (child) => import_react30.default.isValidElement(child) && child.type === SelectItem_default
6209
+ const fieldId = import_react31.default.useId();
6210
+ const itemChildren = import_react31.default.Children.toArray(children).filter(
6211
+ (child) => import_react31.default.isValidElement(child) && child.type === SelectItem_default
5325
6212
  );
5326
6213
  const isControlled = valueProp !== void 0;
5327
- const [isOpen, setOpen] = import_react30.default.useState(false);
5328
- const [uncontrolledLabel, setUncontrolledLabel] = import_react30.default.useState(null);
5329
- const [uncontrolledValue, setUncontrolledValue] = import_react30.default.useState(void 0);
5330
- const controlledLabel = import_react30.default.useMemo(() => {
6214
+ const [isOpen, setOpen] = import_react31.default.useState(false);
6215
+ const [uncontrolledLabel, setUncontrolledLabel] = import_react31.default.useState(null);
6216
+ const [uncontrolledValue, setUncontrolledValue] = import_react31.default.useState(void 0);
6217
+ const controlledLabel = import_react31.default.useMemo(() => {
5331
6218
  if (!isControlled) return null;
5332
6219
  const match = itemChildren.find((child) => child.props.value === valueProp);
5333
6220
  return match ? match.props.children : null;
5334
6221
  }, [isControlled, valueProp, itemChildren]);
5335
6222
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
5336
6223
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
5337
- const triggerRef = import_react30.default.useRef(null);
5338
- const contentRef = import_react30.default.useRef(null);
5339
- const [mounted, setMounted] = import_react30.default.useState(false);
5340
- const [visible, setVisible] = import_react30.default.useState(false);
5341
- import_react30.default.useEffect(() => {
6224
+ const triggerRef = import_react31.default.useRef(null);
6225
+ const contentRef = import_react31.default.useRef(null);
6226
+ const [mounted, setMounted] = import_react31.default.useState(false);
6227
+ const [visible, setVisible] = import_react31.default.useState(false);
6228
+ import_react31.default.useEffect(() => {
5342
6229
  if (disabled && isOpen) setOpen(false);
5343
6230
  }, [disabled, isOpen]);
5344
- import_react30.default.useEffect(() => {
6231
+ import_react31.default.useEffect(() => {
5345
6232
  if (isOpen) {
5346
6233
  setMounted(true);
5347
6234
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -5351,22 +6238,42 @@ var SelectRoot = (props) => {
5351
6238
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS3);
5352
6239
  return () => clearTimeout(t);
5353
6240
  }, [isOpen]);
5354
- const open = import_react30.default.useCallback(() => setOpen(true), []);
5355
- const close = import_react30.default.useCallback(() => setOpen(false), []);
5356
- const toggle = import_react30.default.useCallback(() => setOpen((prev) => !prev), []);
6241
+ const [query, setQuery] = import_react31.default.useState("");
6242
+ const textOf = (node) => {
6243
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
6244
+ if (typeof node === "string" || typeof node === "number") return String(node);
6245
+ if (Array.isArray(node)) return node.map(textOf).join("");
6246
+ if (import_react31.default.isValidElement(node)) return textOf(node.props.children);
6247
+ return "";
6248
+ };
6249
+ const visibleItems = import_react31.default.useMemo(() => {
6250
+ if (!searchable || !query.trim()) return itemChildren;
6251
+ const q = query.trim().toLowerCase();
6252
+ return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
6253
+ }, [searchable, query, itemChildren]);
6254
+ const open = import_react31.default.useCallback(() => setOpen(true), []);
6255
+ const close = import_react31.default.useCallback(() => {
6256
+ setOpen(false);
6257
+ setQuery("");
6258
+ }, []);
6259
+ const toggle = import_react31.default.useCallback(() => setOpen((prev) => !prev), []);
5357
6260
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
6261
+ useEscapeKey(isOpen, () => {
6262
+ close();
6263
+ triggerRef.current?.focus({ preventScroll: true });
6264
+ });
5358
6265
  const position = useAutoPosition_default(triggerRef, contentRef, mounted);
5359
- const setSelected = import_react30.default.useCallback(
5360
- (label, itemValue) => {
6266
+ const setSelected = import_react31.default.useCallback(
6267
+ (label2, itemValue) => {
5361
6268
  if (!isControlled) {
5362
- setUncontrolledLabel(label);
6269
+ setUncontrolledLabel(label2);
5363
6270
  setUncontrolledValue(itemValue);
5364
6271
  }
5365
- onChange?.(itemValue, label);
6272
+ onChange?.(itemValue, label2);
5366
6273
  },
5367
6274
  [isControlled, onChange]
5368
6275
  );
5369
- const ctxValue = import_react30.default.useMemo(
6276
+ const ctxValue = import_react31.default.useMemo(
5370
6277
  () => ({
5371
6278
  isOpen,
5372
6279
  mounted,
@@ -5402,6 +6309,7 @@ var SelectRoot = (props) => {
5402
6309
  /* @__PURE__ */ (0, import_jsx_runtime337.jsxs)(
5403
6310
  "div",
5404
6311
  {
6312
+ id: fieldId,
5405
6313
  ref: triggerRef,
5406
6314
  className: clsx_default(
5407
6315
  "select-trigger",
@@ -5444,20 +6352,33 @@ var SelectRoot = (props) => {
5444
6352
  ]
5445
6353
  }
5446
6354
  ),
5447
- mounted && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(
6355
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime337.jsxs)(
5448
6356
  "div",
5449
6357
  {
5450
- className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
6358
+ className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass, position.repositioning && "repositioning"),
5451
6359
  ref: contentRef,
5452
6360
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
5453
6361
  role: "listbox",
5454
- children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(context_default.Provider, { value: ctxValue, children: itemChildren })
6362
+ children: [
6363
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)("div", { className: "select-search", children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(
6364
+ "input",
6365
+ {
6366
+ type: "text",
6367
+ value: query,
6368
+ placeholder: searchPlaceholder,
6369
+ onChange: (e) => setQuery(e.target.value),
6370
+ autoFocus: true,
6371
+ "aria-label": searchPlaceholder
6372
+ }
6373
+ ) }),
6374
+ /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(context_default.Provider, { value: ctxValue, children: visibleItems.length > 0 ? visibleItems : /* @__PURE__ */ (0, import_jsx_runtime337.jsx)("div", { className: "select-empty", children: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }) })
6375
+ ]
5455
6376
  }
5456
6377
  ) })
5457
6378
  ]
5458
6379
  }
5459
6380
  ) });
5460
- return /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Field, { validations, children: control });
6381
+ return /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
5461
6382
  };
5462
6383
  SelectRoot.displayName = "Select";
5463
6384
  var Select = Object.assign(SelectRoot, {
@@ -5537,8 +6458,44 @@ var Spinner = (props) => {
5537
6458
  Spinner.displayName = "Spinner";
5538
6459
  var Spinner_default = Spinner;
5539
6460
 
5540
- // src/components/Steps/Steps.tsx
6461
+ // src/components/Stat/Stat.tsx
5541
6462
  var import_jsx_runtime340 = require("react/jsx-runtime");
6463
+ var ARROW = {
6464
+ up: "\u25B2",
6465
+ down: "\u25BC",
6466
+ flat: "\u2014"
6467
+ };
6468
+ var Stat = (props) => {
6469
+ const {
6470
+ label,
6471
+ value,
6472
+ unit,
6473
+ description,
6474
+ trend,
6475
+ icon,
6476
+ size = "md",
6477
+ type = "primary"
6478
+ } = props;
6479
+ const statusType = normalizeStatus(type);
6480
+ return /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: clsx_default("lib-xplat-stat", size, statusType), children: [
6481
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: "stat-label", children: label }),
6482
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: "stat-value-row", children: [
6483
+ icon && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-icon", children: icon }),
6484
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-value", children: value }),
6485
+ unit && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-unit", children: unit }),
6486
+ trend && /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("span", { className: clsx_default("stat-trend", trend.direction), children: [
6487
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { "aria-hidden": "true", children: ARROW[trend.direction] }),
6488
+ trend.value
6489
+ ] })
6490
+ ] }),
6491
+ description && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: "stat-description", children: description })
6492
+ ] });
6493
+ };
6494
+ Stat.displayName = "Stat";
6495
+ var Stat_default = Stat;
6496
+
6497
+ // src/components/Steps/Steps.tsx
6498
+ var import_jsx_runtime341 = require("react/jsx-runtime");
5542
6499
  var Steps = (props) => {
5543
6500
  const {
5544
6501
  items,
@@ -5546,13 +6503,13 @@ var Steps = (props) => {
5546
6503
  type = "primary"
5547
6504
  } = props;
5548
6505
  const statusType = normalizeStatus(type);
5549
- return /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
6506
+ return /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
5550
6507
  const status = index < current ? "completed" : index === current ? "active" : "pending";
5551
- return /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: clsx_default("step-item", status), children: [
5552
- /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: "step-circle", children: status === "completed" ? /* @__PURE__ */ (0, import_jsx_runtime340.jsx)(CheckIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { children: index + 1 }) }),
5553
- /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: "step-content", children: [
5554
- /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "step-title", children: item.title }),
5555
- item.description && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "step-description", children: item.description })
6508
+ return /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: clsx_default("step-item", status), children: [
6509
+ /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: "step-circle", children: status === "completed" ? /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(CheckIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("span", { children: index + 1 }) }),
6510
+ /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: "step-content", children: [
6511
+ /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("span", { className: "step-title", children: item.title }),
6512
+ item.description && /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("span", { className: "step-description", children: item.description })
5556
6513
  ] })
5557
6514
  ] }, index);
5558
6515
  }) });
@@ -5561,8 +6518,8 @@ Steps.displayName = "Steps";
5561
6518
  var Steps_default = Steps;
5562
6519
 
5563
6520
  // src/components/Swiper/Swiper.tsx
5564
- var import_react31 = __toESM(require("react"), 1);
5565
- var import_jsx_runtime341 = require("react/jsx-runtime");
6521
+ var import_react32 = __toESM(require("react"), 1);
6522
+ var import_jsx_runtime342 = require("react/jsx-runtime");
5566
6523
  var Swiper = (props) => {
5567
6524
  const {
5568
6525
  auto = false,
@@ -5586,26 +6543,26 @@ var Swiper = (props) => {
5586
6543
  const maxIndex = Math.max(0, totalSlides - viewItemCount);
5587
6544
  const useLoop = loop && canSlide;
5588
6545
  const cloneCount = useLoop ? totalSlides : 0;
5589
- const extendedItems = import_react31.default.useMemo(() => {
6546
+ const extendedItems = import_react32.default.useMemo(() => {
5590
6547
  if (!useLoop) return items;
5591
6548
  return [...items, ...items, ...items];
5592
6549
  }, [items, useLoop]);
5593
6550
  const initialIdx = Math.max(0, Math.min(indexProp ?? 0, maxIndex));
5594
- const [innerIndex, setInnerIndex] = import_react31.default.useState(
6551
+ const [innerIndex, setInnerIndex] = import_react32.default.useState(
5595
6552
  useLoop ? cloneCount + initialIdx : initialIdx
5596
6553
  );
5597
- const [isDragging, setIsDragging] = import_react31.default.useState(false);
5598
- const [dragOffset, setDragOffset] = import_react31.default.useState(0);
5599
- const [animated, setAnimated] = import_react31.default.useState(false);
5600
- const [containerWidth, setContainerWidth] = import_react31.default.useState(0);
5601
- const containerRef = import_react31.default.useRef(null);
5602
- const startXRef = import_react31.default.useRef(0);
5603
- const startTimeRef = import_react31.default.useRef(0);
5604
- const autoplayTimerRef = import_react31.default.useRef(null);
5605
- const resumeTimeoutRef = import_react31.default.useRef(null);
5606
- const [paused, setPaused] = import_react31.default.useState(false);
5607
- const initializedRef = import_react31.default.useRef(false);
5608
- import_react31.default.useEffect(() => {
6554
+ const [isDragging, setIsDragging] = import_react32.default.useState(false);
6555
+ const [dragOffset, setDragOffset] = import_react32.default.useState(0);
6556
+ const [animated, setAnimated] = import_react32.default.useState(false);
6557
+ const [containerWidth, setContainerWidth] = import_react32.default.useState(0);
6558
+ const containerRef = import_react32.default.useRef(null);
6559
+ const startXRef = import_react32.default.useRef(0);
6560
+ const startTimeRef = import_react32.default.useRef(0);
6561
+ const autoplayTimerRef = import_react32.default.useRef(null);
6562
+ const resumeTimeoutRef = import_react32.default.useRef(null);
6563
+ const [paused, setPaused] = import_react32.default.useState(false);
6564
+ const initializedRef = import_react32.default.useRef(false);
6565
+ import_react32.default.useEffect(() => {
5609
6566
  const el = containerRef.current;
5610
6567
  if (!el) return;
5611
6568
  const ro = new ResizeObserver((entries) => {
@@ -5628,7 +6585,7 @@ var Swiper = (props) => {
5628
6585
  return ((inner - cloneCount) % totalSlides + totalSlides) % totalSlides;
5629
6586
  };
5630
6587
  const realIndex = getRealIndex(innerIndex);
5631
- const moveToInner = import_react31.default.useCallback(
6588
+ const moveToInner = import_react32.default.useCallback(
5632
6589
  (idx, withAnim = true) => {
5633
6590
  if (!useLoop) {
5634
6591
  setAnimated(withAnim);
@@ -5656,7 +6613,7 @@ var Swiper = (props) => {
5656
6613
  },
5657
6614
  [useLoop, cloneCount, totalSlides]
5658
6615
  );
5659
- const handleTransitionEnd = import_react31.default.useCallback(() => {
6616
+ const handleTransitionEnd = import_react32.default.useCallback(() => {
5660
6617
  if (!useLoop) return;
5661
6618
  const real = getRealIndex(innerIndex);
5662
6619
  const canonical = cloneCount + real;
@@ -5666,7 +6623,7 @@ var Swiper = (props) => {
5666
6623
  }
5667
6624
  onChange?.(Math.min(real, maxIndex));
5668
6625
  }, [useLoop, innerIndex, cloneCount, totalSlides, maxIndex, onChange]);
5669
- const slideTo = import_react31.default.useCallback(
6626
+ const slideTo = import_react32.default.useCallback(
5670
6627
  (logicalIndex) => {
5671
6628
  if (!canSlide) return;
5672
6629
  const clamped = useLoop ? logicalIndex : Math.max(0, Math.min(logicalIndex, maxIndex));
@@ -5676,7 +6633,7 @@ var Swiper = (props) => {
5676
6633
  },
5677
6634
  [canSlide, useLoop, cloneCount, maxIndex, onChange, moveToInner]
5678
6635
  );
5679
- const slideNext = import_react31.default.useCallback(() => {
6636
+ const slideNext = import_react32.default.useCallback(() => {
5680
6637
  if (!canSlide) return;
5681
6638
  const nextInner = innerIndex + slideBy;
5682
6639
  if (useLoop) {
@@ -5685,7 +6642,7 @@ var Swiper = (props) => {
5685
6642
  moveToInner(Math.min(nextInner, maxIndex), true);
5686
6643
  }
5687
6644
  }, [canSlide, useLoop, innerIndex, slideBy, maxIndex, moveToInner]);
5688
- const slidePrev = import_react31.default.useCallback(() => {
6645
+ const slidePrev = import_react32.default.useCallback(() => {
5689
6646
  if (!canSlide) return;
5690
6647
  const prevInner = innerIndex - slideBy;
5691
6648
  if (useLoop) {
@@ -5694,7 +6651,7 @@ var Swiper = (props) => {
5694
6651
  moveToInner(Math.max(prevInner, 0), true);
5695
6652
  }
5696
6653
  }, [canSlide, useLoop, innerIndex, slideBy, moveToInner]);
5697
- import_react31.default.useEffect(() => {
6654
+ import_react32.default.useEffect(() => {
5698
6655
  if (indexProp === void 0) return;
5699
6656
  const clamped = Math.max(0, Math.min(indexProp, maxIndex));
5700
6657
  const target = useLoop ? cloneCount + clamped : clamped;
@@ -5702,12 +6659,12 @@ var Swiper = (props) => {
5702
6659
  moveToInner(target, true);
5703
6660
  }
5704
6661
  }, [indexProp]);
5705
- import_react31.default.useImperativeHandle(swiperRef, () => ({
6662
+ import_react32.default.useImperativeHandle(swiperRef, () => ({
5706
6663
  slidePrev,
5707
6664
  slideNext,
5708
6665
  slideTo
5709
6666
  }));
5710
- import_react31.default.useEffect(() => {
6667
+ import_react32.default.useEffect(() => {
5711
6668
  if (!auto || !canSlide || paused) return;
5712
6669
  autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
5713
6670
  return () => {
@@ -5733,7 +6690,7 @@ var Swiper = (props) => {
5733
6690
  resumeTimeoutRef.current = null;
5734
6691
  }, pauseOnInteraction);
5735
6692
  };
5736
- import_react31.default.useEffect(() => {
6693
+ import_react32.default.useEffect(() => {
5737
6694
  return () => {
5738
6695
  if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
5739
6696
  };
@@ -5751,7 +6708,7 @@ var Swiper = (props) => {
5751
6708
  startXRef.current = getClientX(e);
5752
6709
  startTimeRef.current = Date.now();
5753
6710
  };
5754
- import_react31.default.useEffect(() => {
6711
+ import_react32.default.useEffect(() => {
5755
6712
  if (!isDragging) return;
5756
6713
  const handleMove = (e) => {
5757
6714
  setDragOffset(getClientX(e) - startXRef.current);
@@ -5790,8 +6747,8 @@ var Swiper = (props) => {
5790
6747
  }, [isDragging, dragOffset, innerIndex, useLoop, maxIndex, slideStep, moveToInner]);
5791
6748
  const slideWidthPercent = 100 / viewItemCount;
5792
6749
  const gapAdjust = spaceBetween * (viewItemCount - 1) / viewItemCount;
5793
- const slideElements = import_react31.default.useMemo(
5794
- () => extendedItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
6750
+ const slideElements = import_react32.default.useMemo(
6751
+ () => extendedItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
5795
6752
  "div",
5796
6753
  {
5797
6754
  className: "lib-xplat-swiper__slide",
@@ -5810,15 +6767,15 @@ var Swiper = (props) => {
5810
6767
  Math.floor(realIndex / slideBy),
5811
6768
  totalSteps - 1
5812
6769
  );
5813
- return /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
5814
- /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
6770
+ return /* @__PURE__ */ (0, import_jsx_runtime342.jsxs)("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
6771
+ /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
5815
6772
  "div",
5816
6773
  {
5817
6774
  className: "lib-xplat-swiper__viewport",
5818
6775
  onMouseDown: handleDragStart,
5819
6776
  onTouchStart: handleDragStart,
5820
6777
  onDragStart: (e) => e.preventDefault(),
5821
- children: /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
6778
+ children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
5822
6779
  "div",
5823
6780
  {
5824
6781
  className: clsx_default(
@@ -5836,7 +6793,7 @@ var Swiper = (props) => {
5836
6793
  )
5837
6794
  }
5838
6795
  ),
5839
- showProgress && canSlide && /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: "lib-xplat-swiper__progress", children: /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: "lib-xplat-swiper__progress-track", children: /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
6796
+ showProgress && canSlide && /* @__PURE__ */ (0, import_jsx_runtime342.jsx)("div", { className: "lib-xplat-swiper__progress", children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)("div", { className: "lib-xplat-swiper__progress-track", children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
5840
6797
  "span",
5841
6798
  {
5842
6799
  className: "lib-xplat-swiper__progress-fill",
@@ -5846,7 +6803,7 @@ var Swiper = (props) => {
5846
6803
  }
5847
6804
  }
5848
6805
  ) }) }),
5849
- canSlide && /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: "lib-xplat-swiper__dots", children: Array.from({ length: totalSteps }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
6806
+ canSlide && /* @__PURE__ */ (0, import_jsx_runtime342.jsx)("div", { className: "lib-xplat-swiper__dots", children: Array.from({ length: totalSteps }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
5850
6807
  "button",
5851
6808
  {
5852
6809
  className: clsx_default(
@@ -5864,8 +6821,8 @@ Swiper.displayName = "Swiper";
5864
6821
  var Swiper_default = Swiper;
5865
6822
 
5866
6823
  // src/components/Switch/Switch.tsx
5867
- var import_react32 = __toESM(require("react"), 1);
5868
- var import_jsx_runtime342 = require("react/jsx-runtime");
6824
+ var import_react33 = __toESM(require("react"), 1);
6825
+ var import_jsx_runtime343 = require("react/jsx-runtime");
5869
6826
  var KNOB_TRANSITION_MS = 250;
5870
6827
  var Switch = (props) => {
5871
6828
  const {
@@ -5874,12 +6831,15 @@ var Switch = (props) => {
5874
6831
  type = "primary",
5875
6832
  disabled,
5876
6833
  onChange,
5877
- validations
6834
+ validations,
6835
+ label,
6836
+ required
5878
6837
  } = props;
6838
+ const fieldId = import_react33.default.useId();
5879
6839
  const statusType = normalizeStatus(type);
5880
- const [isAnimating, setIsAnimating] = import_react32.default.useState(false);
5881
- const timeoutRef = import_react32.default.useRef(null);
5882
- import_react32.default.useEffect(() => {
6840
+ const [isAnimating, setIsAnimating] = import_react33.default.useState(false);
6841
+ const timeoutRef = import_react33.default.useRef(null);
6842
+ import_react33.default.useEffect(() => {
5883
6843
  return () => {
5884
6844
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5885
6845
  };
@@ -5894,9 +6854,10 @@ var Switch = (props) => {
5894
6854
  timeoutRef.current = null;
5895
6855
  }, KNOB_TRANSITION_MS);
5896
6856
  };
5897
- const control = /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
6857
+ const control = /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(
5898
6858
  "div",
5899
6859
  {
6860
+ id: fieldId,
5900
6861
  className: clsx_default(
5901
6862
  "lib-xplat-switch",
5902
6863
  size,
@@ -5908,26 +6869,26 @@ var Switch = (props) => {
5908
6869
  onClick: handleClick,
5909
6870
  "aria-invalid": hasError(validations) || void 0,
5910
6871
  "aria-disabled": disabled || isAnimating,
5911
- children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)("div", { className: clsx_default("knob", value ? "checked" : void 0) })
6872
+ children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("div", { className: clsx_default("knob", value ? "checked" : void 0) })
5912
6873
  }
5913
6874
  );
5914
- return /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(Field, { validations, children: control });
6875
+ return /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
5915
6876
  };
5916
6877
  Switch.displayName = "Switch";
5917
6878
  var Switch_default = Switch;
5918
6879
 
5919
6880
  // src/components/Table/TableContext.tsx
5920
- var import_react33 = __toESM(require("react"), 1);
5921
- var TableContext = import_react33.default.createContext(null);
6881
+ var import_react34 = __toESM(require("react"), 1);
6882
+ var TableContext = import_react34.default.createContext(null);
5922
6883
  var useTableContext = () => {
5923
- const ctx = import_react33.default.useContext(TableContext);
6884
+ const ctx = import_react34.default.useContext(TableContext);
5924
6885
  if (!ctx) throw new Error("Table components must be used inside <Table>");
5925
6886
  return ctx;
5926
6887
  };
5927
6888
  var TableContext_default = TableContext;
5928
6889
 
5929
6890
  // src/components/Table/Table.tsx
5930
- var import_jsx_runtime343 = require("react/jsx-runtime");
6891
+ var import_jsx_runtime344 = require("react/jsx-runtime");
5931
6892
  var Table = (props) => {
5932
6893
  const {
5933
6894
  children,
@@ -5937,7 +6898,7 @@ var Table = (props) => {
5937
6898
  headerSticky = false,
5938
6899
  stickyShadow = true
5939
6900
  } = props;
5940
- return /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(
6901
+ return /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ (0, import_jsx_runtime344.jsx)(
5941
6902
  TableContext_default.Provider,
5942
6903
  {
5943
6904
  value: {
@@ -5946,7 +6907,7 @@ var Table = (props) => {
5946
6907
  headerSticky,
5947
6908
  stickyShadow
5948
6909
  },
5949
- children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("table", { className: "lib-xplat-table", children })
6910
+ children: /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("table", { className: "lib-xplat-table", children })
5950
6911
  }
5951
6912
  ) });
5952
6913
  };
@@ -5954,41 +6915,41 @@ Table.displayName = "Table";
5954
6915
  var Table_default = Table;
5955
6916
 
5956
6917
  // src/components/Table/TableBody.tsx
5957
- var import_jsx_runtime344 = require("react/jsx-runtime");
6918
+ var import_jsx_runtime345 = require("react/jsx-runtime");
5958
6919
  var TableBody = (props) => {
5959
6920
  const { children } = props;
5960
- return /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("tbody", { children });
6921
+ return /* @__PURE__ */ (0, import_jsx_runtime345.jsx)("tbody", { children });
5961
6922
  };
5962
6923
  TableBody.displayName = "TableBody";
5963
6924
  var TableBody_default = TableBody;
5964
6925
 
5965
6926
  // src/components/Table/TableCell.tsx
5966
- var import_react36 = __toESM(require("react"), 1);
6927
+ var import_react37 = __toESM(require("react"), 1);
5967
6928
 
5968
6929
  // src/components/Table/TableHeadContext.tsx
5969
- var import_react34 = __toESM(require("react"), 1);
5970
- var TableHeadContext = import_react34.default.createContext(
6930
+ var import_react35 = __toESM(require("react"), 1);
6931
+ var TableHeadContext = import_react35.default.createContext(
5971
6932
  null
5972
6933
  );
5973
6934
  var useTableHeadContext = () => {
5974
- const ctx = import_react34.default.useContext(TableHeadContext);
6935
+ const ctx = import_react35.default.useContext(TableHeadContext);
5975
6936
  return ctx;
5976
6937
  };
5977
6938
  var TableHeadContext_default = TableHeadContext;
5978
6939
 
5979
6940
  // src/components/Table/TableRowContext.tsx
5980
- var import_react35 = __toESM(require("react"), 1);
5981
- var TableRowContext = import_react35.default.createContext(null);
6941
+ var import_react36 = __toESM(require("react"), 1);
6942
+ var TableRowContext = import_react36.default.createContext(null);
5982
6943
  var useTableRowContext = () => {
5983
- const ctx = import_react35.default.useContext(TableRowContext);
6944
+ const ctx = import_react36.default.useContext(TableRowContext);
5984
6945
  if (!ctx) throw new Error("Table components must be used inside <Table>");
5985
6946
  return ctx;
5986
6947
  };
5987
6948
  var TableRowContext_default = TableRowContext;
5988
6949
 
5989
6950
  // src/components/Table/TableCell.tsx
5990
- var import_jsx_runtime345 = require("react/jsx-runtime");
5991
- var TableCell = import_react36.default.memo((props) => {
6951
+ var import_jsx_runtime346 = require("react/jsx-runtime");
6952
+ var TableCell = import_react37.default.memo((props) => {
5992
6953
  const {
5993
6954
  children,
5994
6955
  align = "center",
@@ -6002,9 +6963,9 @@ var TableCell = import_react36.default.memo((props) => {
6002
6963
  const { registerStickyCell, stickyCells } = useTableRowContext();
6003
6964
  const { stickyShadow } = useTableContext();
6004
6965
  const headContext = useTableHeadContext();
6005
- const [left, setLeft] = import_react36.default.useState(0);
6006
- const cellRef = import_react36.default.useRef(null);
6007
- const calculateLeft = import_react36.default.useCallback(() => {
6966
+ const [left, setLeft] = import_react37.default.useState(0);
6967
+ const cellRef = import_react37.default.useRef(null);
6968
+ const calculateLeft = import_react37.default.useCallback(() => {
6008
6969
  if (!cellRef.current) return 0;
6009
6970
  let totalLeft = 0;
6010
6971
  for (const ref of stickyCells) {
@@ -6013,7 +6974,7 @@ var TableCell = import_react36.default.memo((props) => {
6013
6974
  }
6014
6975
  return totalLeft;
6015
6976
  }, [stickyCells]);
6016
- import_react36.default.useEffect(() => {
6977
+ import_react37.default.useEffect(() => {
6017
6978
  if (!isSticky || !cellRef.current) return;
6018
6979
  registerStickyCell(cellRef);
6019
6980
  setLeft(calculateLeft());
@@ -6032,7 +6993,7 @@ var TableCell = import_react36.default.memo((props) => {
6032
6993
  const CellTag = isHeaderCell ? "th" : "td";
6033
6994
  const isLastSticky = isSticky && stickyCells[stickyCells.length - 1] === cellRef;
6034
6995
  const enableHover = headContext && headContext.cellHover;
6035
- return /* @__PURE__ */ (0, import_jsx_runtime345.jsx)(
6996
+ return /* @__PURE__ */ (0, import_jsx_runtime346.jsx)(
6036
6997
  CellTag,
6037
6998
  {
6038
6999
  ref: cellRef,
@@ -6059,21 +7020,21 @@ TableCell.displayName = "TableCell";
6059
7020
  var TableCell_default = TableCell;
6060
7021
 
6061
7022
  // src/components/Table/TableHead.tsx
6062
- var import_jsx_runtime346 = require("react/jsx-runtime");
7023
+ var import_jsx_runtime347 = require("react/jsx-runtime");
6063
7024
  var TableHead = ({
6064
7025
  children,
6065
7026
  cellHover = false
6066
7027
  }) => {
6067
7028
  const { headerSticky } = useTableContext();
6068
- return /* @__PURE__ */ (0, import_jsx_runtime346.jsx)(TableHeadContext_default.Provider, { value: { cellHover }, children: /* @__PURE__ */ (0, import_jsx_runtime346.jsx)("thead", { className: clsx_default(headerSticky ? "table-sticky" : null), children }) });
7029
+ return /* @__PURE__ */ (0, import_jsx_runtime347.jsx)(TableHeadContext_default.Provider, { value: { cellHover }, children: /* @__PURE__ */ (0, import_jsx_runtime347.jsx)("thead", { className: clsx_default(headerSticky ? "table-sticky" : null), children }) });
6069
7030
  };
6070
7031
  TableHead.displayName = "TableHead";
6071
7032
  var TableHead_default = TableHead;
6072
7033
 
6073
7034
  // src/components/Table/TableRow.tsx
6074
- var import_react37 = __toESM(require("react"), 1);
6075
- var import_jsx_runtime347 = require("react/jsx-runtime");
6076
- var TableRow = import_react37.default.memo((props) => {
7035
+ var import_react38 = __toESM(require("react"), 1);
7036
+ var import_jsx_runtime348 = require("react/jsx-runtime");
7037
+ var TableRow = import_react38.default.memo((props) => {
6077
7038
  const {
6078
7039
  children,
6079
7040
  type = "secondary",
@@ -6082,14 +7043,14 @@ var TableRow = import_react37.default.memo((props) => {
6082
7043
  onClick
6083
7044
  } = props;
6084
7045
  const { rowBorderUse } = useTableContext();
6085
- const [stickyCells, setStickyCells] = import_react37.default.useState([]);
7046
+ const [stickyCells, setStickyCells] = import_react38.default.useState([]);
6086
7047
  const registerStickyCell = (ref) => {
6087
7048
  setStickyCells((prev) => {
6088
7049
  if (prev.includes(ref)) return prev;
6089
7050
  return [...prev, ref];
6090
7051
  });
6091
7052
  };
6092
- return /* @__PURE__ */ (0, import_jsx_runtime347.jsx)(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ (0, import_jsx_runtime347.jsx)(
7053
+ return /* @__PURE__ */ (0, import_jsx_runtime348.jsx)(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ (0, import_jsx_runtime348.jsx)(
6093
7054
  "tr",
6094
7055
  {
6095
7056
  className: clsx_default(
@@ -6113,7 +7074,7 @@ TableRow.displayName = "TableRow";
6113
7074
  var TableRow_default = TableRow;
6114
7075
 
6115
7076
  // src/components/Tag/Tag.tsx
6116
- var import_jsx_runtime348 = require("react/jsx-runtime");
7077
+ var import_jsx_runtime349 = require("react/jsx-runtime");
6117
7078
  var Tag = (props) => {
6118
7079
  const {
6119
7080
  children,
@@ -6125,7 +7086,7 @@ var Tag = (props) => {
6125
7086
  colorIndex
6126
7087
  } = props;
6127
7088
  const shape = variant ?? type ?? "neutral";
6128
- return /* @__PURE__ */ (0, import_jsx_runtime348.jsxs)(
7089
+ return /* @__PURE__ */ (0, import_jsx_runtime349.jsxs)(
6129
7090
  "span",
6130
7091
  {
6131
7092
  className: clsx_default(
@@ -6136,8 +7097,8 @@ var Tag = (props) => {
6136
7097
  disabled && "disabled"
6137
7098
  ),
6138
7099
  children: [
6139
- /* @__PURE__ */ (0, import_jsx_runtime348.jsx)("span", { className: "tag-label", children }),
6140
- onClose && /* @__PURE__ */ (0, import_jsx_runtime348.jsx)("button", { className: "tag-close", onClick: onClose, "aria-label": "\uC0AD\uC81C", disabled, children: /* @__PURE__ */ (0, import_jsx_runtime348.jsx)(XIcon_default, {}) })
7100
+ /* @__PURE__ */ (0, import_jsx_runtime349.jsx)("span", { className: "tag-label", children }),
7101
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime349.jsx)("button", { className: "tag-close", onClick: onClose, "aria-label": "\uC0AD\uC81C", disabled, children: /* @__PURE__ */ (0, import_jsx_runtime349.jsx)(XIcon_default, {}) })
6141
7102
  ]
6142
7103
  }
6143
7104
  );
@@ -6146,24 +7107,29 @@ Tag.displayName = "Tag";
6146
7107
  var Tag_default = Tag;
6147
7108
 
6148
7109
  // src/components/TextArea/TextArea.tsx
6149
- var import_react38 = __toESM(require("react"), 1);
6150
- var import_jsx_runtime349 = require("react/jsx-runtime");
6151
- var TextArea = import_react38.default.forwardRef(
7110
+ var import_react39 = __toESM(require("react"), 1);
7111
+ var import_jsx_runtime350 = require("react/jsx-runtime");
7112
+ var TextArea = import_react39.default.forwardRef(
6152
7113
  (props, ref) => {
6153
7114
  const {
6154
7115
  className,
6155
7116
  size = "md",
6156
7117
  validations,
7118
+ label,
7119
+ required,
6157
7120
  resize = "vertical",
6158
7121
  rows = 3,
6159
7122
  disabled,
6160
7123
  ...textareaProps
6161
7124
  } = props;
6162
- const control = /* @__PURE__ */ (0, import_jsx_runtime349.jsx)("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ (0, import_jsx_runtime349.jsx)(
7125
+ const autoId = import_react39.default.useId();
7126
+ const fieldId = props.id ?? autoId;
7127
+ const control = /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(
6163
7128
  "textarea",
6164
7129
  {
6165
7130
  ...textareaProps,
6166
7131
  ref,
7132
+ id: fieldId,
6167
7133
  rows,
6168
7134
  disabled,
6169
7135
  "aria-invalid": hasError(validations) || void 0,
@@ -6171,32 +7137,166 @@ var TextArea = import_react38.default.forwardRef(
6171
7137
  style: { resize }
6172
7138
  }
6173
7139
  ) });
6174
- return /* @__PURE__ */ (0, import_jsx_runtime349.jsx)(Field, { validations, children: control });
7140
+ return /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
6175
7141
  }
6176
7142
  );
6177
7143
  TextArea.displayName = "TextArea";
6178
7144
  var TextArea_default = TextArea;
6179
7145
 
7146
+ // src/components/TimePicker/TimePicker.tsx
7147
+ var import_react40 = __toESM(require("react"), 1);
7148
+ var import_jsx_runtime351 = require("react/jsx-runtime");
7149
+ var ANIMATION_DURATION_MS4 = 200;
7150
+ var toMinutes = (hhmm) => {
7151
+ if (!hhmm) return null;
7152
+ const m = /^(\d{1,2}):(\d{2})$/.exec(hhmm.trim());
7153
+ if (!m) return null;
7154
+ const h = Number(m[1]);
7155
+ const min = Number(m[2]);
7156
+ if (h > 23 || min > 59) return null;
7157
+ return h * 60 + min;
7158
+ };
7159
+ var toHHmm = (minutes) => `${String(Math.floor(minutes / 60)).padStart(2, "0")}:${String(minutes % 60).padStart(2, "0")}`;
7160
+ var display = (hhmm, use12Hours) => {
7161
+ if (!use12Hours) return hhmm;
7162
+ const total = toMinutes(hhmm);
7163
+ if (total === null) return hhmm;
7164
+ const h24 = Math.floor(total / 60);
7165
+ const mm = String(total % 60).padStart(2, "0");
7166
+ const period = h24 < 12 ? "\uC624\uC804" : "\uC624\uD6C4";
7167
+ const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
7168
+ return `${period} ${h12}:${mm}`;
7169
+ };
7170
+ var TimePicker = (props) => {
7171
+ const {
7172
+ value,
7173
+ onChange,
7174
+ step = 30,
7175
+ min,
7176
+ max,
7177
+ use12Hours = false,
7178
+ placeholder = "\uC2DC\uAC01 \uC120\uD0DD",
7179
+ disabled = false,
7180
+ size = "md",
7181
+ label,
7182
+ required,
7183
+ validations
7184
+ } = props;
7185
+ const fieldId = import_react40.default.useId();
7186
+ const triggerRef = import_react40.default.useRef(null);
7187
+ const contentRef = import_react40.default.useRef(null);
7188
+ const [isOpen, setOpen] = import_react40.default.useState(false);
7189
+ const [mounted, setMounted] = import_react40.default.useState(false);
7190
+ const [visible, setVisible] = import_react40.default.useState(false);
7191
+ const options = import_react40.default.useMemo(() => {
7192
+ const safeStep = step > 0 && step <= 720 ? step : 30;
7193
+ const lo = toMinutes(min) ?? 0;
7194
+ const hi = toMinutes(max) ?? 24 * 60 - 1;
7195
+ const out = [];
7196
+ for (let t = lo; t <= hi; t += safeStep) out.push(toHHmm(t));
7197
+ return out;
7198
+ }, [step, min, max]);
7199
+ const close = import_react40.default.useCallback(() => setOpen(false), []);
7200
+ useClickOutside_default([triggerRef, contentRef], close, isOpen);
7201
+ useEscapeKey(isOpen, () => {
7202
+ close();
7203
+ triggerRef.current?.focus({ preventScroll: true });
7204
+ });
7205
+ const position = useAutoPosition_default(triggerRef, contentRef, mounted);
7206
+ import_react40.default.useEffect(() => {
7207
+ if (disabled && isOpen) setOpen(false);
7208
+ }, [disabled, isOpen]);
7209
+ import_react40.default.useEffect(() => {
7210
+ if (isOpen) {
7211
+ setMounted(true);
7212
+ const t2 = setTimeout(() => setVisible(true), 1);
7213
+ return () => clearTimeout(t2);
7214
+ }
7215
+ setVisible(false);
7216
+ const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS4);
7217
+ return () => clearTimeout(t);
7218
+ }, [isOpen]);
7219
+ const pick = (hhmm) => {
7220
+ onChange?.(hhmm);
7221
+ close();
7222
+ triggerRef.current?.focus({ preventScroll: true });
7223
+ };
7224
+ const control = /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)("div", { className: clsx_default("lib-xplat-time-picker", size, disabled && "disabled"), children: [
7225
+ /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
7226
+ "div",
7227
+ {
7228
+ id: fieldId,
7229
+ ref: triggerRef,
7230
+ className: clsx_default("time-picker-trigger", hasError(validations) && "error"),
7231
+ role: "combobox",
7232
+ "aria-expanded": isOpen,
7233
+ "aria-haspopup": "listbox",
7234
+ "aria-invalid": hasError(validations) || void 0,
7235
+ tabIndex: disabled ? void 0 : 0,
7236
+ onClick: () => !disabled && setOpen((p) => !p),
7237
+ onKeyDown: (e) => {
7238
+ if (disabled) return;
7239
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
7240
+ e.preventDefault();
7241
+ setOpen(true);
7242
+ }
7243
+ },
7244
+ children: /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("span", { className: clsx_default("time-picker-value", !value && "placeholder"), children: value ? display(value, use12Hours) : placeholder })
7245
+ }
7246
+ ),
7247
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
7248
+ "div",
7249
+ {
7250
+ ref: contentRef,
7251
+ className: clsx_default("lib-xplat-time-picker-content", size, visible ? "enter" : "exit", position.repositioning && "repositioning"),
7252
+ style: { ...position.position, width: triggerRef.current?.offsetWidth },
7253
+ role: "listbox",
7254
+ children: options.map((t) => /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
7255
+ "div",
7256
+ {
7257
+ className: clsx_default("time-picker-item", t === value && "is-selected"),
7258
+ role: "option",
7259
+ "aria-selected": t === value,
7260
+ tabIndex: 0,
7261
+ onClick: () => pick(t),
7262
+ onKeyDown: (e) => {
7263
+ if (e.key === "Enter" || e.key === " ") {
7264
+ e.preventDefault();
7265
+ pick(t);
7266
+ }
7267
+ },
7268
+ children: display(t, use12Hours)
7269
+ },
7270
+ t
7271
+ ))
7272
+ }
7273
+ ) })
7274
+ ] });
7275
+ return /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
7276
+ };
7277
+ TimePicker.displayName = "TimePicker";
7278
+ var TimePicker_default = TimePicker;
7279
+
6180
7280
  // src/components/Toast/Toast.tsx
6181
- var import_react39 = __toESM(require("react"), 1);
7281
+ var import_react41 = __toESM(require("react"), 1);
6182
7282
  var import_react_dom4 = require("react-dom");
6183
- var import_jsx_runtime350 = require("react/jsx-runtime");
6184
- var ToastContext = import_react39.default.createContext(null);
7283
+ var import_jsx_runtime352 = require("react/jsx-runtime");
7284
+ var ToastContext = import_react41.default.createContext(null);
6185
7285
  var useToast = () => {
6186
- const ctx = import_react39.default.useContext(ToastContext);
7286
+ const ctx = import_react41.default.useContext(ToastContext);
6187
7287
  if (!ctx) throw new Error("useToast must be used within ToastProvider");
6188
7288
  return ctx;
6189
7289
  };
6190
7290
  var EXIT_DURATION = 300;
6191
7291
  var ToastItemComponent = ({ item, isExiting, onClose }) => {
6192
- const ref = import_react39.default.useRef(null);
6193
- const [height, setHeight] = import_react39.default.useState(void 0);
6194
- import_react39.default.useEffect(() => {
7292
+ const ref = import_react41.default.useRef(null);
7293
+ const [height, setHeight] = import_react41.default.useState(void 0);
7294
+ import_react41.default.useEffect(() => {
6195
7295
  if (ref.current && !isExiting) {
6196
7296
  setHeight(ref.current.offsetHeight);
6197
7297
  }
6198
7298
  }, [isExiting]);
6199
- return /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(
7299
+ return /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
6200
7300
  "div",
6201
7301
  {
6202
7302
  className: clsx_default("lib-xplat-toast-wrapper", { exit: isExiting }),
@@ -6204,15 +7304,15 @@ var ToastItemComponent = ({ item, isExiting, onClose }) => {
6204
7304
  maxHeight: isExiting ? 0 : height ?? "none",
6205
7305
  marginBottom: isExiting ? 0 : void 0
6206
7306
  },
6207
- children: /* @__PURE__ */ (0, import_jsx_runtime350.jsxs)(
7307
+ children: /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
6208
7308
  "div",
6209
7309
  {
6210
7310
  ref,
6211
7311
  className: clsx_default("lib-xplat-toast", item.type, { exit: isExiting }),
6212
7312
  role: "alert",
6213
7313
  children: [
6214
- /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("span", { className: "message", children: item.message }),
6215
- /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
7314
+ /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("span", { className: "message", children: item.message }),
7315
+ /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
6216
7316
  ]
6217
7317
  }
6218
7318
  )
@@ -6223,13 +7323,13 @@ var ToastProvider = ({
6223
7323
  children,
6224
7324
  position = "top-right"
6225
7325
  }) => {
6226
- const [toasts, setToasts] = import_react39.default.useState([]);
6227
- const [removing, setRemoving] = import_react39.default.useState(/* @__PURE__ */ new Set());
6228
- const [mounted, setMounted] = import_react39.default.useState(false);
6229
- import_react39.default.useEffect(() => {
7326
+ const [toasts, setToasts] = import_react41.default.useState([]);
7327
+ const [removing, setRemoving] = import_react41.default.useState(/* @__PURE__ */ new Set());
7328
+ const [mounted, setMounted] = import_react41.default.useState(false);
7329
+ import_react41.default.useEffect(() => {
6230
7330
  setMounted(true);
6231
7331
  }, []);
6232
- const remove = import_react39.default.useCallback((id) => {
7332
+ const remove = import_react41.default.useCallback((id) => {
6233
7333
  setRemoving((prev) => new Set(prev).add(id));
6234
7334
  setTimeout(() => {
6235
7335
  setToasts((prev) => prev.filter((t) => t.id !== id));
@@ -6240,7 +7340,7 @@ var ToastProvider = ({
6240
7340
  });
6241
7341
  }, EXIT_DURATION);
6242
7342
  }, []);
6243
- const toast = import_react39.default.useCallback(
7343
+ const toast = import_react41.default.useCallback(
6244
7344
  (type, message, duration = 3e3) => {
6245
7345
  const id = `${Date.now()}-${Math.random()}`;
6246
7346
  setToasts((prev) => [...prev, { id, type, message }]);
@@ -6250,10 +7350,10 @@ var ToastProvider = ({
6250
7350
  },
6251
7351
  [remove]
6252
7352
  );
6253
- return /* @__PURE__ */ (0, import_jsx_runtime350.jsxs)(ToastContext.Provider, { value: { toast }, children: [
7353
+ return /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(ToastContext.Provider, { value: { toast }, children: [
6254
7354
  children,
6255
7355
  mounted && (0, import_react_dom4.createPortal)(
6256
- /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("div", { className: clsx_default("lib-xplat-toast-container", position), children: toasts.map((t) => /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(
7356
+ /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("div", { className: clsx_default("lib-xplat-toast-container", position), children: toasts.map((t) => /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
6257
7357
  ToastItemComponent,
6258
7358
  {
6259
7359
  item: t,
@@ -6269,8 +7369,8 @@ var ToastProvider = ({
6269
7369
  ToastProvider.displayName = "ToastProvider";
6270
7370
 
6271
7371
  // src/components/Tooltip/Tooltip.tsx
6272
- var import_react40 = __toESM(require("react"), 1);
6273
- var import_jsx_runtime351 = require("react/jsx-runtime");
7372
+ var import_react42 = __toESM(require("react"), 1);
7373
+ var import_jsx_runtime353 = require("react/jsx-runtime");
6274
7374
  var OFFSET = 12;
6275
7375
  var GAP = 8;
6276
7376
  var SHOW_DELAY = 300;
@@ -6285,14 +7385,14 @@ var Tooltip = (props) => {
6285
7385
  disabled = false
6286
7386
  } = props;
6287
7387
  const shape = variant ?? type ?? "dark";
6288
- const triggerRef = import_react40.default.useRef(null);
6289
- const tooltipRef = import_react40.default.useRef(null);
6290
- const [visible, setVisible] = import_react40.default.useState(false);
6291
- const [pos, setPos] = import_react40.default.useState({ left: 0, top: 0 });
6292
- const delayTimer = import_react40.default.useRef(0);
6293
- const rafRef = import_react40.default.useRef(0);
6294
- const cursor = import_react40.default.useRef(null);
6295
- const clamp = import_react40.default.useCallback((left, top, w, h) => {
7388
+ const triggerRef = import_react42.default.useRef(null);
7389
+ const tooltipRef = import_react42.default.useRef(null);
7390
+ const [visible, setVisible] = import_react42.default.useState(false);
7391
+ const [pos, setPos] = import_react42.default.useState({ left: 0, top: 0 });
7392
+ const delayTimer = import_react42.default.useRef(0);
7393
+ const rafRef = import_react42.default.useRef(0);
7394
+ const cursor = import_react42.default.useRef(null);
7395
+ const clamp = import_react42.default.useCallback((left, top, w, h) => {
6296
7396
  const vw = window.innerWidth;
6297
7397
  const vh = window.innerHeight;
6298
7398
  return {
@@ -6300,7 +7400,7 @@ var Tooltip = (props) => {
6300
7400
  top: Math.min(Math.max(GAP, top), Math.max(GAP, vh - h - GAP))
6301
7401
  };
6302
7402
  }, []);
6303
- const placeAtCursor = import_react40.default.useCallback((x, y) => {
7403
+ const placeAtCursor = import_react42.default.useCallback((x, y) => {
6304
7404
  const el = tooltipRef.current;
6305
7405
  if (!el) return;
6306
7406
  const w = el.offsetWidth;
@@ -6311,7 +7411,7 @@ var Tooltip = (props) => {
6311
7411
  if (top < GAP) top = y + OFFSET;
6312
7412
  setPos(clamp(left, top, w, h));
6313
7413
  }, [clamp]);
6314
- const placeAtTrigger = import_react40.default.useCallback(() => {
7414
+ const placeAtTrigger = import_react42.default.useCallback(() => {
6315
7415
  const el = tooltipRef.current;
6316
7416
  const trigger = triggerRef.current;
6317
7417
  if (!el || !trigger) return;
@@ -6323,19 +7423,19 @@ var Tooltip = (props) => {
6323
7423
  const left = rect.left + rect.width / 2 - w / 2;
6324
7424
  setPos(clamp(left, top, w, h));
6325
7425
  }, [clamp]);
6326
- const reposition = import_react40.default.useCallback(() => {
7426
+ const reposition = import_react42.default.useCallback(() => {
6327
7427
  if (follow && cursor.current) {
6328
7428
  placeAtCursor(cursor.current.x, cursor.current.y);
6329
7429
  } else {
6330
7430
  placeAtTrigger();
6331
7431
  }
6332
7432
  }, [follow, placeAtCursor, placeAtTrigger]);
6333
- const handleMouseEnter = import_react40.default.useCallback((e) => {
7433
+ const handleMouseEnter = import_react42.default.useCallback((e) => {
6334
7434
  if (disabled) return;
6335
7435
  cursor.current = { x: e.clientX, y: e.clientY };
6336
7436
  delayTimer.current = window.setTimeout(() => setVisible(true), SHOW_DELAY);
6337
7437
  }, [disabled]);
6338
- const handleMouseMove = import_react40.default.useCallback((e) => {
7438
+ const handleMouseMove = import_react42.default.useCallback((e) => {
6339
7439
  cursor.current = { x: e.clientX, y: e.clientY };
6340
7440
  if (!visible || !follow) return;
6341
7441
  cancelAnimationFrame(rafRef.current);
@@ -6343,25 +7443,25 @@ var Tooltip = (props) => {
6343
7443
  if (cursor.current) placeAtCursor(cursor.current.x, cursor.current.y);
6344
7444
  });
6345
7445
  }, [visible, follow, placeAtCursor]);
6346
- const close = import_react40.default.useCallback(() => {
7446
+ const close = import_react42.default.useCallback(() => {
6347
7447
  window.clearTimeout(delayTimer.current);
6348
7448
  cancelAnimationFrame(rafRef.current);
6349
7449
  setVisible(false);
6350
7450
  }, []);
6351
- const handleMouseLeave = import_react40.default.useCallback(() => {
7451
+ const handleMouseLeave = import_react42.default.useCallback(() => {
6352
7452
  cursor.current = null;
6353
7453
  close();
6354
7454
  }, [close]);
6355
- const handleFocus = import_react40.default.useCallback(() => {
7455
+ const handleFocus = import_react42.default.useCallback(() => {
6356
7456
  if (disabled) return;
6357
7457
  cursor.current = null;
6358
7458
  setVisible(true);
6359
7459
  }, [disabled]);
6360
- import_react40.default.useLayoutEffect(() => {
7460
+ import_react42.default.useLayoutEffect(() => {
6361
7461
  if (!visible) return;
6362
7462
  reposition();
6363
7463
  }, [visible, reposition]);
6364
- import_react40.default.useEffect(() => {
7464
+ import_react42.default.useEffect(() => {
6365
7465
  if (!visible || follow) return;
6366
7466
  const onChange = () => placeAtTrigger();
6367
7467
  window.addEventListener("scroll", onChange, true);
@@ -6371,12 +7471,12 @@ var Tooltip = (props) => {
6371
7471
  window.removeEventListener("resize", onChange);
6372
7472
  };
6373
7473
  }, [visible, follow, placeAtTrigger]);
6374
- import_react40.default.useEffect(() => () => {
7474
+ import_react42.default.useEffect(() => () => {
6375
7475
  window.clearTimeout(delayTimer.current);
6376
7476
  cancelAnimationFrame(rafRef.current);
6377
7477
  }, []);
6378
- if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(import_jsx_runtime351.Fragment, { children });
6379
- return /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)(
7478
+ if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime353.jsx)(import_jsx_runtime353.Fragment, { children });
7479
+ return /* @__PURE__ */ (0, import_jsx_runtime353.jsxs)(
6380
7480
  "div",
6381
7481
  {
6382
7482
  ref: triggerRef,
@@ -6389,7 +7489,7 @@ var Tooltip = (props) => {
6389
7489
  onBlur: close,
6390
7490
  children: [
6391
7491
  children,
6392
- visible && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)(
7492
+ visible && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime353.jsxs)(
6393
7493
  "div",
6394
7494
  {
6395
7495
  ref: tooltipRef,
@@ -6397,8 +7497,8 @@ var Tooltip = (props) => {
6397
7497
  className: clsx_default("lib-xplat-tooltip", shape),
6398
7498
  style: { position: "fixed", left: pos.left, top: pos.top },
6399
7499
  children: [
6400
- title && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("div", { className: "tooltip-title", children: title }),
6401
- description && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("div", { className: "tooltip-desc", children: description })
7500
+ title && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)("div", { className: "tooltip-title", children: title }),
7501
+ description && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)("div", { className: "tooltip-desc", children: description })
6402
7502
  ]
6403
7503
  }
6404
7504
  ) })
@@ -6410,11 +7510,11 @@ Tooltip.displayName = "Tooltip";
6410
7510
  var Tooltip_default = Tooltip;
6411
7511
 
6412
7512
  // src/components/Video/Video.tsx
6413
- var import_react41 = __toESM(require("react"), 1);
6414
- var import_jsx_runtime352 = require("react/jsx-runtime");
6415
- var PipIcon = () => /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
6416
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
6417
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
7513
+ var import_react43 = __toESM(require("react"), 1);
7514
+ var import_jsx_runtime354 = require("react/jsx-runtime");
7515
+ var PipIcon = () => /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
7516
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
7517
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
6418
7518
  ] });
6419
7519
  var formatTime = (sec) => {
6420
7520
  if (!Number.isFinite(sec) || sec < 0) return "0:00";
@@ -6422,7 +7522,7 @@ var formatTime = (sec) => {
6422
7522
  const s = Math.floor(sec % 60);
6423
7523
  return `${m}:${s.toString().padStart(2, "0")}`;
6424
7524
  };
6425
- var Video = import_react41.default.forwardRef((props, ref) => {
7525
+ var Video = import_react43.default.forwardRef((props, ref) => {
6426
7526
  const {
6427
7527
  src,
6428
7528
  poster,
@@ -6446,21 +7546,21 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6446
7546
  children,
6447
7547
  ...rest
6448
7548
  } = props;
6449
- const containerRef = import_react41.default.useRef(null);
6450
- const videoRef = import_react41.default.useRef(null);
6451
- const [isPlaying, setIsPlaying] = import_react41.default.useState(Boolean(autoPlay));
6452
- const [isLoaded, setIsLoaded] = import_react41.default.useState(false);
6453
- const [currentTime, setCurrentTime] = import_react41.default.useState(0);
6454
- const [duration, setDuration] = import_react41.default.useState(0);
6455
- const [buffered, setBuffered] = import_react41.default.useState(0);
6456
- const [volume, setVolume] = import_react41.default.useState(1);
6457
- const [isMuted, setIsMuted] = import_react41.default.useState(Boolean(muted));
6458
- const [isFullscreen, setIsFullscreen] = import_react41.default.useState(false);
6459
- const [playbackRate, setPlaybackRate] = import_react41.default.useState(1);
6460
- const [rateMenuOpen, setRateMenuOpen] = import_react41.default.useState(false);
6461
- const [captionsOn, setCaptionsOn] = import_react41.default.useState(false);
6462
- const [isPip, setIsPip] = import_react41.default.useState(false);
6463
- const setRefs = import_react41.default.useCallback(
7549
+ const containerRef = import_react43.default.useRef(null);
7550
+ const videoRef = import_react43.default.useRef(null);
7551
+ const [isPlaying, setIsPlaying] = import_react43.default.useState(Boolean(autoPlay));
7552
+ const [isLoaded, setIsLoaded] = import_react43.default.useState(false);
7553
+ const [currentTime, setCurrentTime] = import_react43.default.useState(0);
7554
+ const [duration, setDuration] = import_react43.default.useState(0);
7555
+ const [buffered, setBuffered] = import_react43.default.useState(0);
7556
+ const [volume, setVolume] = import_react43.default.useState(1);
7557
+ const [isMuted, setIsMuted] = import_react43.default.useState(Boolean(muted));
7558
+ const [isFullscreen, setIsFullscreen] = import_react43.default.useState(false);
7559
+ const [playbackRate, setPlaybackRate] = import_react43.default.useState(1);
7560
+ const [rateMenuOpen, setRateMenuOpen] = import_react43.default.useState(false);
7561
+ const [captionsOn, setCaptionsOn] = import_react43.default.useState(false);
7562
+ const [isPip, setIsPip] = import_react43.default.useState(false);
7563
+ const setRefs = import_react43.default.useCallback(
6464
7564
  (el) => {
6465
7565
  videoRef.current = el;
6466
7566
  if (typeof ref === "function") ref(el);
@@ -6468,14 +7568,14 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6468
7568
  },
6469
7569
  [ref]
6470
7570
  );
6471
- import_react41.default.useEffect(() => {
7571
+ import_react43.default.useEffect(() => {
6472
7572
  const onFsChange = () => {
6473
7573
  setIsFullscreen(document.fullscreenElement === containerRef.current);
6474
7574
  };
6475
7575
  document.addEventListener("fullscreenchange", onFsChange);
6476
7576
  return () => document.removeEventListener("fullscreenchange", onFsChange);
6477
7577
  }, []);
6478
- import_react41.default.useEffect(() => {
7578
+ import_react43.default.useEffect(() => {
6479
7579
  const v = videoRef.current;
6480
7580
  if (!v) return;
6481
7581
  const onEnter = () => setIsPip(true);
@@ -6589,13 +7689,13 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6589
7689
  const volumePct = (isMuted ? 0 : volume) * 100;
6590
7690
  const VolumeGlyph = isMuted || volume === 0 ? VolumeXIcon_default : volume < 0.5 ? VolumeIcon_default : Volume2Icon_default;
6591
7691
  const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
6592
- return /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
7692
+ return /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)(
6593
7693
  "div",
6594
7694
  {
6595
7695
  ref: containerRef,
6596
7696
  className: clsx_default("lib-xplat-video", showControls && "has-controls"),
6597
7697
  children: [
6598
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7698
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6599
7699
  "video",
6600
7700
  {
6601
7701
  ref: setRefs,
@@ -6616,7 +7716,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6616
7716
  children
6617
7717
  }
6618
7718
  ),
6619
- showCenterPlay && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7719
+ showCenterPlay && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6620
7720
  "button",
6621
7721
  {
6622
7722
  type: "button",
@@ -6628,11 +7728,11 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6628
7728
  onClick: togglePlay,
6629
7729
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
6630
7730
  tabIndex: -1,
6631
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("span", { className: "center-play-icon", children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PlayCircleIcon_default, {}) })
7731
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("span", { className: "center-play-icon", children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PlayCircleIcon_default, {}) })
6632
7732
  }
6633
7733
  ),
6634
- showControls && /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
6635
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7734
+ showControls && /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
7735
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6636
7736
  "input",
6637
7737
  {
6638
7738
  type: "range",
@@ -6649,29 +7749,29 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6649
7749
  }
6650
7750
  }
6651
7751
  ),
6652
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "controls-row", children: [
6653
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7752
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "controls-row", children: [
7753
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6654
7754
  "button",
6655
7755
  {
6656
7756
  type: "button",
6657
7757
  className: "control-btn",
6658
7758
  onClick: togglePlay,
6659
7759
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
6660
- children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PauseIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PlayIcon_default, {})
7760
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PauseIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PlayIcon_default, {})
6661
7761
  }
6662
7762
  ),
6663
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "volume-group", children: [
6664
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7763
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "volume-group", children: [
7764
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6665
7765
  "button",
6666
7766
  {
6667
7767
  type: "button",
6668
7768
  className: "control-btn",
6669
7769
  onClick: toggleMute,
6670
7770
  "aria-label": isMuted ? "\uC74C\uC18C\uAC70 \uD574\uC81C" : "\uC74C\uC18C\uAC70",
6671
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(VolumeGlyph, {})
7771
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(VolumeGlyph, {})
6672
7772
  }
6673
7773
  ),
6674
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7774
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6675
7775
  "input",
6676
7776
  {
6677
7777
  type: "range",
@@ -6686,14 +7786,14 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6686
7786
  }
6687
7787
  )
6688
7788
  ] }),
6689
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("span", { className: "time", children: [
7789
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("span", { className: "time", children: [
6690
7790
  formatTime(currentTime),
6691
7791
  " / ",
6692
7792
  formatTime(duration)
6693
7793
  ] }),
6694
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("div", { className: "controls-spacer" }),
6695
- playbackRates && playbackRates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
6696
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
7794
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("div", { className: "controls-spacer" }),
7795
+ playbackRates && playbackRates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
7796
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)(
6697
7797
  "button",
6698
7798
  {
6699
7799
  type: "button",
@@ -6707,7 +7807,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6707
7807
  ]
6708
7808
  }
6709
7809
  ),
6710
- rateMenuOpen && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("ul", { className: "rate-menu", role: "menu", children: playbackRates.map((r2) => /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
7810
+ rateMenuOpen && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("ul", { className: "rate-menu", role: "menu", children: playbackRates.map((r2) => /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)(
6711
7811
  "button",
6712
7812
  {
6713
7813
  type: "button",
@@ -6721,7 +7821,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6721
7821
  }
6722
7822
  ) }, r2)) })
6723
7823
  ] }),
6724
- showCaptions && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7824
+ showCaptions && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6725
7825
  "button",
6726
7826
  {
6727
7827
  type: "button",
@@ -6729,37 +7829,37 @@ var Video = import_react41.default.forwardRef((props, ref) => {
6729
7829
  onClick: toggleCaptions,
6730
7830
  "aria-label": captionsOn ? "\uC790\uB9C9 \uB044\uAE30" : "\uC790\uB9C9 \uCF1C\uAE30",
6731
7831
  "aria-pressed": captionsOn,
6732
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(TypeIcon_default, {})
7832
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(TypeIcon_default, {})
6733
7833
  }
6734
7834
  ),
6735
- showPip && pipSupported && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7835
+ showPip && pipSupported && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6736
7836
  "button",
6737
7837
  {
6738
7838
  type: "button",
6739
7839
  className: clsx_default("control-btn", isPip && "is-active"),
6740
7840
  onClick: togglePip,
6741
7841
  "aria-label": isPip ? "PIP \uC885\uB8CC" : "PIP",
6742
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PipIcon, {})
7842
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PipIcon, {})
6743
7843
  }
6744
7844
  ),
6745
- showDownload && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7845
+ showDownload && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6746
7846
  "a",
6747
7847
  {
6748
7848
  className: "control-btn",
6749
7849
  href: src,
6750
7850
  download: downloadFileName ?? true,
6751
7851
  "aria-label": "\uB2E4\uC6B4\uB85C\uB4DC",
6752
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(DownloadIcon_default, {})
7852
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(DownloadIcon_default, {})
6753
7853
  }
6754
7854
  ),
6755
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
7855
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
6756
7856
  "button",
6757
7857
  {
6758
7858
  type: "button",
6759
7859
  className: "control-btn",
6760
7860
  onClick: toggleFullscreen,
6761
7861
  "aria-label": isFullscreen ? "\uC804\uCCB4\uD654\uBA74 \uC885\uB8CC" : "\uC804\uCCB4\uD654\uBA74",
6762
- children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(MinimizeIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(MaximizeIcon_default, {})
7862
+ children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(MinimizeIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(MaximizeIcon_default, {})
6763
7863
  }
6764
7864
  )
6765
7865
  ] })
@@ -6817,6 +7917,7 @@ var Video_default = Video;
6817
7917
  SingleDatePicker,
6818
7918
  Skeleton,
6819
7919
  Spinner,
7920
+ Stat,
6820
7921
  Steps,
6821
7922
  Swiper,
6822
7923
  Switch,
@@ -6828,6 +7929,7 @@ var Video_default = Video;
6828
7929
  TableRow,
6829
7930
  Tag,
6830
7931
  TextArea,
7932
+ TimePicker,
6831
7933
  ToastProvider,
6832
7934
  Tooltip,
6833
7935
  Video,