@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
package/dist/index.cjs CHANGED
@@ -320,6 +320,7 @@ __export(index_exports, {
320
320
  Spinner: () => Spinner_default,
321
321
  SquareIcon: () => SquareIcon_default,
322
322
  StarIcon: () => StarIcon_default,
323
+ Stat: () => Stat_default,
323
324
  Steps: () => Steps_default,
324
325
  StopCircleIcon: () => StopCircleIcon_default,
325
326
  SuccessIcon: () => SuccessIcon_default,
@@ -344,6 +345,7 @@ __export(index_exports, {
344
345
  ThermometerIcon: () => ThermometerIcon_default,
345
346
  ThumbsDownIcon: () => ThumbsDownIcon_default,
346
347
  ThumbsUpIcon: () => ThumbsUpIcon_default,
348
+ TimePicker: () => TimePicker_default,
347
349
  ToastProvider: () => ToastProvider,
348
350
  ToggleLeftIcon: () => ToggleLeftIcon_default,
349
351
  ToggleRightIcon: () => ToggleRightIcon_default,
@@ -6029,11 +6031,16 @@ var FieldMessage = (props) => {
6029
6031
  FieldMessage.displayName = "FieldMessage";
6030
6032
  var FieldMessage_default = FieldMessage;
6031
6033
  var Field = (props) => {
6032
- const { validations, children } = props;
6033
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(import_jsx_runtime297.Fragment, { children });
6034
+ const { label, required = false, htmlFor, validations, children } = props;
6035
+ const hasMessages = !!validations && validations.length > 0;
6036
+ if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(import_jsx_runtime297.Fragment, { children });
6034
6037
  return /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)("div", { className: "lib-xplat-field", children: [
6038
+ label && /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
6039
+ label,
6040
+ required && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
6041
+ ] }),
6035
6042
  children,
6036
- /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(FieldMessage, { validations })
6043
+ hasMessages && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(FieldMessage, { validations })
6037
6044
  ] });
6038
6045
  };
6039
6046
  Field.displayName = "Field";
@@ -6044,7 +6051,9 @@ var import_jsx_runtime298 = require("react/jsx-runtime");
6044
6051
  var MAX_HEIGHT = 400;
6045
6052
  var AutoResizeTextArea = import_react2.default.forwardRef(
6046
6053
  (props, ref) => {
6047
- const { value, className, onChange, disabled, bare = false, validations, ...textareaProps } = props;
6054
+ const { value, className, onChange, disabled, bare = false, validations, label, required, ...textareaProps } = props;
6055
+ const autoId = import_react2.default.useId();
6056
+ const fieldId = props.id ?? autoId;
6048
6057
  const localRef = import_react2.default.useRef(null);
6049
6058
  const setRefs = (el) => {
6050
6059
  localRef.current = el;
@@ -6078,6 +6087,7 @@ var AutoResizeTextArea = import_react2.default.forwardRef(
6078
6087
  "aria-invalid": hasError(validations) || void 0,
6079
6088
  ...textareaProps,
6080
6089
  ref: setRefs,
6090
+ id: fieldId,
6081
6091
  className,
6082
6092
  disabled,
6083
6093
  value,
@@ -6091,12 +6101,13 @@ var AutoResizeTextArea = import_react2.default.forwardRef(
6091
6101
  "aria-invalid": hasError(validations) || void 0,
6092
6102
  ...textareaProps,
6093
6103
  ref: setRefs,
6104
+ id: fieldId,
6094
6105
  disabled,
6095
6106
  value,
6096
6107
  onChange: handleChange
6097
6108
  }
6098
6109
  ) }) });
6099
- return /* @__PURE__ */ (0, import_jsx_runtime298.jsx)(Field, { validations, children: control });
6110
+ return /* @__PURE__ */ (0, import_jsx_runtime298.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
6100
6111
  }
6101
6112
  );
6102
6113
  AutoResizeTextArea.displayName = "AutoResizeTextArea";
@@ -7314,8 +7325,11 @@ var ChatInput = import_react7.default.forwardRef(
7314
7325
  buttonType = "primary",
7315
7326
  onSubmit,
7316
7327
  onChange,
7317
- validations
7328
+ validations,
7329
+ label,
7330
+ required
7318
7331
  } = props;
7332
+ const fieldId = import_react7.default.useId();
7319
7333
  const isControlled = valueProp !== void 0;
7320
7334
  const [internalValue, setInternalValue] = import_react7.default.useState("");
7321
7335
  const value = isControlled ? valueProp : internalValue;
@@ -7340,6 +7354,7 @@ var ChatInput = import_react7.default.forwardRef(
7340
7354
  /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(
7341
7355
  AutoResizeTextArea_default,
7342
7356
  {
7357
+ id: fieldId,
7343
7358
  ref,
7344
7359
  bare: true,
7345
7360
  className: "chat-input-textarea",
@@ -7364,7 +7379,7 @@ var ChatInput = import_react7.default.forwardRef(
7364
7379
  }
7365
7380
  )
7366
7381
  ] });
7367
- return /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(Field, { validations, children: control });
7382
+ return /* @__PURE__ */ (0, import_jsx_runtime308.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
7368
7383
  }
7369
7384
  );
7370
7385
  ChatInput.displayName = "ChatInput";
@@ -7448,7 +7463,7 @@ Chip.displayName = "Chip";
7448
7463
  var Chip_default = Chip;
7449
7464
 
7450
7465
  // src/components/DatePicker/InputDatePicker/index.tsx
7451
- var import_react12 = __toESM(require("react"), 1);
7466
+ var import_react13 = __toESM(require("react"), 1);
7452
7467
 
7453
7468
  // src/components/Input/Input.tsx
7454
7469
  var import_react8 = __toESM(require("react"), 1);
@@ -7546,8 +7561,12 @@ var Input = import_react8.default.forwardRef((props, ref) => {
7546
7561
  suffix,
7547
7562
  disabled,
7548
7563
  validations,
7564
+ label,
7565
+ required,
7549
7566
  ...inputProps
7550
7567
  } = props;
7568
+ const autoId = import_react8.default.useId();
7569
+ const fieldId = inputProps.id ?? autoId;
7551
7570
  const localValue = formatValue(type, value);
7552
7571
  const handleChange = (e) => {
7553
7572
  let val = e.target.value;
@@ -7563,7 +7582,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
7563
7582
  onChange(event);
7564
7583
  }
7565
7584
  };
7566
- return /* @__PURE__ */ (0, import_jsx_runtime311.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
7585
+ 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: [
7567
7586
  /* @__PURE__ */ (0, import_jsx_runtime311.jsxs)(
7568
7587
  "div",
7569
7588
  {
@@ -7574,6 +7593,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
7574
7593
  {
7575
7594
  ...inputProps,
7576
7595
  ref,
7596
+ id: fieldId,
7577
7597
  disabled,
7578
7598
  type: type === "number" ? "text" : type,
7579
7599
  value: localValue,
@@ -7586,7 +7606,7 @@ var Input = import_react8.default.forwardRef((props, ref) => {
7586
7606
  }
7587
7607
  ),
7588
7608
  /* @__PURE__ */ (0, import_jsx_runtime311.jsx)(FieldMessage_default, { validations })
7589
- ] });
7609
+ ] }) });
7590
7610
  });
7591
7611
  Input.displayName = "Input";
7592
7612
  var Input_default = Input;
@@ -7616,16 +7636,96 @@ PasswordInput.displayName = "PasswordInput";
7616
7636
  var PasswordInput_default = PasswordInput;
7617
7637
 
7618
7638
  // src/components/Modal/Modal.tsx
7619
- var import_react10 = __toESM(require("react"), 1);
7639
+ var import_react11 = __toESM(require("react"), 1);
7620
7640
  var import_react_dom = require("react-dom");
7641
+
7642
+ // src/tokens/hooks/useOverlayKeyboard.ts
7643
+ var import_react10 = __toESM(require("react"), 1);
7644
+ var FOCUSABLE = [
7645
+ "a[href]",
7646
+ "button:not([disabled])",
7647
+ "input:not([disabled]):not([type='hidden'])",
7648
+ "select:not([disabled])",
7649
+ "textarea:not([disabled])",
7650
+ "[tabindex]:not([tabindex='-1'])"
7651
+ ].join(",");
7652
+ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
7653
+ // 화면에 없는 것은 제외 (display:none 등)
7654
+ (n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
7655
+ );
7656
+ var useEscapeKey = (enabled, onEscape) => {
7657
+ import_react10.default.useEffect(() => {
7658
+ if (!enabled) return;
7659
+ const onKey = (e) => {
7660
+ if (e.key !== "Escape") return;
7661
+ if (e.defaultPrevented) return;
7662
+ e.preventDefault();
7663
+ onEscape();
7664
+ };
7665
+ document.addEventListener("keydown", onKey);
7666
+ return () => document.removeEventListener("keydown", onKey);
7667
+ }, [enabled, onEscape]);
7668
+ };
7669
+ var useFocusTrap = (containerRef, enabled, options = {}) => {
7670
+ const { trap = false, initialFocus } = options;
7671
+ const restoreRef = import_react10.default.useRef(null);
7672
+ import_react10.default.useEffect(() => {
7673
+ if (!enabled) return;
7674
+ restoreRef.current = document.activeElement;
7675
+ const raf = requestAnimationFrame(() => {
7676
+ const el = containerRef.current;
7677
+ if (!el) return;
7678
+ const target = initialFocus?.current ?? focusableIn(el)[0] ?? el;
7679
+ if (!target.hasAttribute("tabindex") && !focusableIn(el).includes(target)) {
7680
+ target.setAttribute("tabindex", "-1");
7681
+ }
7682
+ target.focus({ preventScroll: true });
7683
+ });
7684
+ return () => {
7685
+ cancelAnimationFrame(raf);
7686
+ const prev = restoreRef.current;
7687
+ if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
7688
+ };
7689
+ }, [enabled, containerRef, initialFocus]);
7690
+ import_react10.default.useEffect(() => {
7691
+ if (!enabled || !trap) return;
7692
+ const onKey = (e) => {
7693
+ if (e.key !== "Tab") return;
7694
+ const el = containerRef.current;
7695
+ if (!el) return;
7696
+ const items = focusableIn(el);
7697
+ if (items.length === 0) {
7698
+ e.preventDefault();
7699
+ return;
7700
+ }
7701
+ const first = items[0];
7702
+ const last = items[items.length - 1];
7703
+ const active = document.activeElement;
7704
+ if (e.shiftKey && (active === first || !el.contains(active))) {
7705
+ e.preventDefault();
7706
+ last.focus();
7707
+ } else if (!e.shiftKey && (active === last || !el.contains(active))) {
7708
+ e.preventDefault();
7709
+ first.focus();
7710
+ }
7711
+ };
7712
+ document.addEventListener("keydown", onKey);
7713
+ return () => document.removeEventListener("keydown", onKey);
7714
+ }, [enabled, trap, containerRef]);
7715
+ };
7716
+
7717
+ // src/components/Modal/Modal.tsx
7621
7718
  var import_jsx_runtime313 = require("react/jsx-runtime");
7622
7719
  var ANIMATION_DURATION_MS = 200;
7623
7720
  var Modal = (props) => {
7624
7721
  const { isOpen, onClose, children } = props;
7625
- const [mounted, setMounted] = import_react10.default.useState(false);
7626
- const [visible, setVisible] = import_react10.default.useState(false);
7627
- const [settled, setSettled] = import_react10.default.useState(false);
7628
- import_react10.default.useEffect(() => {
7722
+ const [mounted, setMounted] = import_react11.default.useState(false);
7723
+ const [visible, setVisible] = import_react11.default.useState(false);
7724
+ const [settled, setSettled] = import_react11.default.useState(false);
7725
+ const boxRef = import_react11.default.useRef(null);
7726
+ useEscapeKey(isOpen, onClose);
7727
+ useFocusTrap(boxRef, isOpen && mounted, { trap: true });
7728
+ import_react11.default.useEffect(() => {
7629
7729
  if (isOpen) {
7630
7730
  setMounted(true);
7631
7731
  const show = setTimeout(() => setVisible(true), 1);
@@ -7652,6 +7752,7 @@ var Modal = (props) => {
7652
7752
  children: /* @__PURE__ */ (0, import_jsx_runtime313.jsx)(
7653
7753
  "div",
7654
7754
  {
7755
+ ref: boxRef,
7655
7756
  className: clsx_default("lib-xplat-modal", "modal-box", stateClass, settled && "settled"),
7656
7757
  role: "dialog",
7657
7758
  "aria-modal": "true",
@@ -7668,9 +7769,9 @@ Modal.displayName = "Modal";
7668
7769
  var Modal_default = Modal;
7669
7770
 
7670
7771
  // src/components/DatePicker/SingleDatePicker/index.tsx
7671
- var import_react11 = __toESM(require("react"), 1);
7772
+ var import_react12 = __toESM(require("react"), 1);
7672
7773
  var import_jsx_runtime314 = require("react/jsx-runtime");
7673
- var DayCell2 = import_react11.default.memo(
7774
+ var DayCell2 = import_react12.default.memo(
7674
7775
  ({
7675
7776
  day,
7676
7777
  disabled,
@@ -7722,26 +7823,26 @@ var SingleDatePicker = (props) => {
7722
7823
  initialYear,
7723
7824
  initialMonth
7724
7825
  );
7725
- const [pickerMode, setPickerMode] = import_react11.default.useState("days");
7726
- const [yearRangeStart, setYearRangeStart] = import_react11.default.useState(
7826
+ const [pickerMode, setPickerMode] = import_react12.default.useState("days");
7827
+ const [yearRangeStart, setYearRangeStart] = import_react12.default.useState(
7727
7828
  Math.floor((initialYear ?? (/* @__PURE__ */ new Date()).getFullYear()) / 12) * 12
7728
7829
  );
7729
- const minTime = import_react11.default.useMemo(
7830
+ const minTime = import_react12.default.useMemo(
7730
7831
  () => minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()).getTime() : -Infinity,
7731
7832
  [minDate]
7732
7833
  );
7733
- const maxTime = import_react11.default.useMemo(
7834
+ const maxTime = import_react12.default.useMemo(
7734
7835
  () => maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()).getTime() : Infinity,
7735
7836
  [maxDate]
7736
7837
  );
7737
- const highlightSet = import_react11.default.useMemo(() => {
7838
+ const highlightSet = import_react12.default.useMemo(() => {
7738
7839
  const set = /* @__PURE__ */ new Set();
7739
7840
  for (const h of highlightDates) {
7740
7841
  set.add(`${h.getFullYear()}-${h.getMonth()}-${h.getDate()}`);
7741
7842
  }
7742
7843
  return set;
7743
7844
  }, [highlightDates]);
7744
- const handleSelect = import_react11.default.useCallback(
7845
+ const handleSelect = import_react12.default.useCallback(
7745
7846
  (date) => {
7746
7847
  onChange?.(date);
7747
7848
  },
@@ -7870,8 +7971,8 @@ var formatDate = (date) => {
7870
7971
  };
7871
7972
  var InputDatePicker = (props) => {
7872
7973
  const { value, onChange, minDate, maxDate, disabled, locale = "ko", placeholder } = props;
7873
- const [isOpen, setIsOpen] = import_react12.default.useState(false);
7874
- const [tempDate, setTempDate] = import_react12.default.useState(value ?? /* @__PURE__ */ new Date());
7974
+ const [isOpen, setIsOpen] = import_react13.default.useState(false);
7975
+ const [tempDate, setTempDate] = import_react13.default.useState(value ?? /* @__PURE__ */ new Date());
7875
7976
  const handleOpen = () => {
7876
7977
  if (disabled) return;
7877
7978
  setTempDate(value ?? /* @__PURE__ */ new Date());
@@ -7920,23 +8021,27 @@ InputDatePicker.displayName = "InputDatePicker";
7920
8021
  var InputDatePicker_default = InputDatePicker;
7921
8022
 
7922
8023
  // src/components/DatePicker/PopupPicker/index.tsx
7923
- var import_react16 = __toESM(require("react"), 1);
8024
+ var import_react17 = __toESM(require("react"), 1);
7924
8025
 
7925
8026
  // src/components/DatePicker/RangePicker/index.tsx
7926
- var import_react15 = __toESM(require("react"), 1);
8027
+ var import_react16 = __toESM(require("react"), 1);
7927
8028
 
7928
8029
  // src/components/Tab/Tab.tsx
7929
- var import_react14 = __toESM(require("react"), 1);
8030
+ var import_react15 = __toESM(require("react"), 1);
7930
8031
 
7931
8032
  // src/components/Tab/TabItem.tsx
7932
- var import_react13 = __toESM(require("react"), 1);
8033
+ var import_react14 = __toESM(require("react"), 1);
7933
8034
  var import_jsx_runtime316 = require("react/jsx-runtime");
7934
- var TabItem = import_react13.default.forwardRef((props, ref) => {
8035
+ var TabItem = import_react14.default.forwardRef((props, ref) => {
7935
8036
  const { isActive: isActive2, title, onClick } = props;
7936
8037
  return /* @__PURE__ */ (0, import_jsx_runtime316.jsx)(
7937
- "div",
8038
+ "button",
7938
8039
  {
7939
8040
  ref,
8041
+ type: "button",
8042
+ role: "tab",
8043
+ "aria-selected": isActive2,
8044
+ tabIndex: isActive2 ? 0 : -1,
7940
8045
  className: clsx_default("tab-item", isActive2 ? "active" : null),
7941
8046
  onClick,
7942
8047
  children: title
@@ -7951,44 +8056,64 @@ var import_jsx_runtime317 = require("react/jsx-runtime");
7951
8056
  var Tab = (props) => {
7952
8057
  const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
7953
8058
  const shape = variant ?? type ?? "default";
7954
- const [underlineStyle, setUnderlineStyle] = import_react14.default.useState({
8059
+ const [underlineStyle, setUnderlineStyle] = import_react15.default.useState({
7955
8060
  left: 0,
7956
8061
  width: 0
7957
8062
  });
7958
- const itemRefs = import_react14.default.useRef([]);
8063
+ const itemRefs = import_react15.default.useRef([]);
8064
+ const handleKeyDown = (e) => {
8065
+ const last = tabs.length - 1;
8066
+ let next = null;
8067
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
8068
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
8069
+ else if (e.key === "Home") next = 0;
8070
+ else if (e.key === "End") next = last;
8071
+ if (next === null) return;
8072
+ e.preventDefault();
8073
+ onChange(tabs[next], next);
8074
+ itemRefs.current[next]?.focus();
8075
+ };
7959
8076
  const handleChangeActiveTab = (tabItem, tabIdx) => {
7960
8077
  onChange(tabItem, tabIdx);
7961
8078
  };
7962
- import_react14.default.useEffect(() => {
8079
+ import_react15.default.useEffect(() => {
7963
8080
  const el = itemRefs.current[activeIndex];
7964
8081
  if (el) {
7965
8082
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
7966
8083
  }
7967
8084
  }, [activeIndex, tabs.length]);
7968
- return /* @__PURE__ */ (0, import_jsx_runtime317.jsxs)("div", { className: clsx_default("lib-xplat-tab", `type-${shape}`, size), children: [
7969
- tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
7970
- TabItem_default,
7971
- {
7972
- onClick: () => handleChangeActiveTab(tab, idx),
7973
- isActive: activeIndex === idx,
7974
- ref: (el) => {
7975
- itemRefs.current[idx] = el;
7976
- },
7977
- title: tab.title
7978
- },
7979
- `${tab.value}_${idx}`
7980
- )),
7981
- shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
7982
- "div",
7983
- {
7984
- className: "tab-toggle-underline",
7985
- style: {
7986
- left: underlineStyle.left,
7987
- width: underlineStyle.width
7988
- }
7989
- }
7990
- )
7991
- ] });
8085
+ return /* @__PURE__ */ (0, import_jsx_runtime317.jsxs)(
8086
+ "div",
8087
+ {
8088
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
8089
+ role: "tablist",
8090
+ onKeyDown: handleKeyDown,
8091
+ children: [
8092
+ tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
8093
+ TabItem_default,
8094
+ {
8095
+ onClick: () => handleChangeActiveTab(tab, idx),
8096
+ isActive: activeIndex === idx,
8097
+ ref: (el) => {
8098
+ itemRefs.current[idx] = el;
8099
+ },
8100
+ title: tab.title
8101
+ },
8102
+ `${tab.value}_${idx}`
8103
+ )),
8104
+ shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime317.jsx)(
8105
+ "div",
8106
+ {
8107
+ className: "tab-toggle-underline",
8108
+ style: {
8109
+ left: underlineStyle.left,
8110
+ width: underlineStyle.width
8111
+ }
8112
+ }
8113
+ )
8114
+ ]
8115
+ }
8116
+ );
7992
8117
  };
7993
8118
  Tab.displayName = "Tab";
7994
8119
  var Tab_default = Tab;
@@ -8004,7 +8129,7 @@ var RangePicker = (props) => {
8004
8129
  maxDate,
8005
8130
  locale = "ko"
8006
8131
  } = props;
8007
- const [activeTab, setActiveTab] = import_react15.default.useState("start");
8132
+ const [activeTab, setActiveTab] = import_react16.default.useState("start");
8008
8133
  const handleStartChange = (date) => {
8009
8134
  if (!date) return;
8010
8135
  const newStart = date > endDate ? endDate : date;
@@ -8089,7 +8214,7 @@ var RangePicker_default = RangePicker;
8089
8214
  var import_jsx_runtime319 = require("react/jsx-runtime");
8090
8215
  var PopupPicker = (props) => {
8091
8216
  const { component, type, locale } = props;
8092
- const [isOpen, setIsOpen] = import_react16.default.useState(false);
8217
+ const [isOpen, setIsOpen] = import_react17.default.useState(false);
8093
8218
  const handleClick = () => setIsOpen(true);
8094
8219
  const handleClose = () => setIsOpen(false);
8095
8220
  const handleSingleChange = (date) => {
@@ -8098,7 +8223,7 @@ var PopupPicker = (props) => {
8098
8223
  handleClose();
8099
8224
  };
8100
8225
  return /* @__PURE__ */ (0, import_jsx_runtime319.jsxs)("div", { className: "lib-xplat-popup-datepicker", children: [
8101
- import_react16.default.cloneElement(component, { onClick: handleClick }),
8226
+ import_react17.default.cloneElement(component, { onClick: handleClick }),
8102
8227
  /* @__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: [
8103
8228
  /* @__PURE__ */ (0, import_jsx_runtime319.jsxs)("div", { className: "popup-datepicker-content", children: [
8104
8229
  type === "single" && /* @__PURE__ */ (0, import_jsx_runtime319.jsx)(
@@ -8157,15 +8282,18 @@ Divider.displayName = "Divider";
8157
8282
  var Divider_default = Divider;
8158
8283
 
8159
8284
  // src/components/Drawer/Drawer.tsx
8160
- var import_react17 = __toESM(require("react"), 1);
8285
+ var import_react18 = __toESM(require("react"), 1);
8161
8286
  var import_react_dom2 = require("react-dom");
8162
8287
  var import_jsx_runtime321 = require("react/jsx-runtime");
8163
8288
  var ANIMATION_DURATION_MS2 = 250;
8164
8289
  var Drawer = (props) => {
8165
8290
  const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
8166
- const [mounted, setMounted] = import_react17.default.useState(false);
8167
- const [visible, setVisible] = import_react17.default.useState(false);
8168
- import_react17.default.useEffect(() => {
8291
+ const [mounted, setMounted] = import_react18.default.useState(false);
8292
+ const [visible, setVisible] = import_react18.default.useState(false);
8293
+ const panelRef = import_react18.default.useRef(null);
8294
+ useEscapeKey(isOpen, onClose);
8295
+ useFocusTrap(panelRef, isOpen && mounted, { trap: true });
8296
+ import_react18.default.useEffect(() => {
8169
8297
  if (isOpen) {
8170
8298
  setMounted(true);
8171
8299
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -8187,6 +8315,7 @@ var Drawer = (props) => {
8187
8315
  children: /* @__PURE__ */ (0, import_jsx_runtime321.jsxs)(
8188
8316
  "div",
8189
8317
  {
8318
+ ref: panelRef,
8190
8319
  className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
8191
8320
  role: "dialog",
8192
8321
  "aria-modal": "true",
@@ -8209,7 +8338,7 @@ Drawer.displayName = "Drawer";
8209
8338
  var Drawer_default = Drawer;
8210
8339
 
8211
8340
  // src/components/Dropdown/Dropdown.tsx
8212
- var import_react20 = __toESM(require("react"), 1);
8341
+ var import_react21 = __toESM(require("react"), 1);
8213
8342
 
8214
8343
  // src/tokens/hooks/Portal.tsx
8215
8344
  var import_react_dom3 = __toESM(require("react-dom"), 1);
@@ -8221,13 +8350,15 @@ Portal.displayName = "Portal";
8221
8350
  var Portal_default = Portal;
8222
8351
 
8223
8352
  // src/tokens/hooks/useAutoPosition.ts
8224
- var import_react18 = __toESM(require("react"), 1);
8353
+ var import_react19 = __toESM(require("react"), 1);
8225
8354
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8226
- const [position, setPosition] = import_react18.default.useState({
8355
+ const [position, setPosition] = import_react19.default.useState({
8227
8356
  position: {},
8228
- direction: "bottom"
8357
+ direction: "bottom",
8358
+ repositioning: false
8229
8359
  });
8230
- const calculatePosition = import_react18.default.useCallback(() => {
8360
+ const placedRef = import_react19.default.useRef(false);
8361
+ const calculatePosition = import_react19.default.useCallback(() => {
8231
8362
  if (!triggerRef.current || !popRef.current) return;
8232
8363
  const triggerRect = triggerRef.current.getBoundingClientRect();
8233
8364
  const popW = popRef.current.offsetWidth;
@@ -8251,16 +8382,21 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8251
8382
  if (left < 8) left = 8;
8252
8383
  setPosition({
8253
8384
  position: { top, left },
8254
- direction
8385
+ direction,
8386
+ repositioning: placedRef.current
8255
8387
  });
8388
+ placedRef.current = true;
8256
8389
  }, [triggerRef, popRef]);
8257
- import_react18.default.useLayoutEffect(() => {
8258
- if (!enabled) return;
8390
+ import_react19.default.useLayoutEffect(() => {
8391
+ if (!enabled) {
8392
+ placedRef.current = false;
8393
+ return;
8394
+ }
8259
8395
  calculatePosition();
8260
8396
  const raf = requestAnimationFrame(calculatePosition);
8261
8397
  return () => cancelAnimationFrame(raf);
8262
8398
  }, [calculatePosition, enabled]);
8263
- import_react18.default.useEffect(() => {
8399
+ import_react19.default.useEffect(() => {
8264
8400
  if (!enabled || !popRef.current) return;
8265
8401
  const observer = new ResizeObserver(() => {
8266
8402
  calculatePosition();
@@ -8268,13 +8404,19 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8268
8404
  observer.observe(popRef.current);
8269
8405
  return () => observer.disconnect();
8270
8406
  }, [calculatePosition, enabled, popRef]);
8271
- import_react18.default.useEffect(() => {
8407
+ import_react19.default.useEffect(() => {
8272
8408
  if (!enabled) return;
8273
- window.addEventListener("resize", calculatePosition);
8274
- window.addEventListener("scroll", calculatePosition, true);
8409
+ let raf = 0;
8410
+ const onScrollOrResize = () => {
8411
+ cancelAnimationFrame(raf);
8412
+ raf = requestAnimationFrame(calculatePosition);
8413
+ };
8414
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
8415
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
8275
8416
  return () => {
8276
- window.removeEventListener("resize", calculatePosition);
8277
- window.removeEventListener("scroll", calculatePosition, true);
8417
+ cancelAnimationFrame(raf);
8418
+ window.removeEventListener("resize", onScrollOrResize);
8419
+ window.removeEventListener("scroll", onScrollOrResize, true);
8278
8420
  };
8279
8421
  }, [calculatePosition, enabled]);
8280
8422
  return position;
@@ -8282,9 +8424,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8282
8424
  var useAutoPosition_default = useAutoPosition;
8283
8425
 
8284
8426
  // src/tokens/hooks/useClickOutside.ts
8285
- var import_react19 = __toESM(require("react"), 1);
8427
+ var import_react20 = __toESM(require("react"), 1);
8286
8428
  var useClickOutside = (refs, handler, enabled = true) => {
8287
- import_react19.default.useEffect(() => {
8429
+ import_react20.default.useEffect(() => {
8288
8430
  if (!enabled) return;
8289
8431
  const refArray = Array.isArray(refs) ? refs : [refs];
8290
8432
  const listener = (event) => {
@@ -8310,14 +8452,18 @@ var useClickOutside_default = useClickOutside;
8310
8452
  var import_jsx_runtime322 = require("react/jsx-runtime");
8311
8453
  var Dropdown = (props) => {
8312
8454
  const { items, children } = props;
8313
- const [isOpen, setIsOpen] = import_react20.default.useState(false);
8314
- const [mounted, setMounted] = import_react20.default.useState(false);
8315
- const [visible, setVisible] = import_react20.default.useState(false);
8316
- const triggerRef = import_react20.default.useRef(null);
8317
- const menuRef = import_react20.default.useRef(null);
8318
- const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
8455
+ const [isOpen, setIsOpen] = import_react21.default.useState(false);
8456
+ const [mounted, setMounted] = import_react21.default.useState(false);
8457
+ const [visible, setVisible] = import_react21.default.useState(false);
8458
+ const triggerRef = import_react21.default.useRef(null);
8459
+ const menuRef = import_react21.default.useRef(null);
8460
+ const { position, direction, repositioning } = useAutoPosition_default(triggerRef, menuRef, mounted);
8319
8461
  useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
8320
- import_react20.default.useEffect(() => {
8462
+ useEscapeKey(isOpen, () => {
8463
+ setIsOpen(false);
8464
+ triggerRef.current?.focus({ preventScroll: true });
8465
+ });
8466
+ import_react21.default.useEffect(() => {
8321
8467
  if (isOpen) {
8322
8468
  setMounted(true);
8323
8469
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -8346,7 +8492,7 @@ var Dropdown = (props) => {
8346
8492
  "div",
8347
8493
  {
8348
8494
  ref: menuRef,
8349
- className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }),
8495
+ className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }, repositioning && "repositioning"),
8350
8496
  style: { top: position.top, left: position.left },
8351
8497
  role: "menu",
8352
8498
  children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime322.jsx)(
@@ -8371,7 +8517,112 @@ Dropdown.displayName = "Dropdown";
8371
8517
  var Dropdown_default = Dropdown;
8372
8518
 
8373
8519
  // src/components/Editor/Editor.tsx
8374
- var import_react21 = __toESM(require("react"), 1);
8520
+ var import_react22 = __toESM(require("react"), 1);
8521
+
8522
+ // src/components/Editor/highlight.ts
8523
+ var CODE_LANGUAGES = [
8524
+ { value: "plain", label: "\uC5C6\uC74C" },
8525
+ { value: "javascript", label: "JavaScript" },
8526
+ { value: "typescript", label: "TypeScript" },
8527
+ { value: "json", label: "JSON" },
8528
+ { value: "html", label: "HTML" },
8529
+ { value: "css", label: "CSS" },
8530
+ { value: "python", label: "Python" },
8531
+ { value: "sql", label: "SQL" },
8532
+ { value: "bash", label: "Bash" }
8533
+ ];
8534
+ var KEYWORDS = {
8535
+ plain: [],
8536
+ 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(" "),
8537
+ 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(" "),
8538
+ json: ["true", "false", "null"],
8539
+ html: [],
8540
+ css: [],
8541
+ 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(" "),
8542
+ 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(" "),
8543
+ 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(" ")
8544
+ };
8545
+ var LINE_COMMENT = {
8546
+ javascript: "//",
8547
+ typescript: "//",
8548
+ css: "",
8549
+ python: "#",
8550
+ sql: "--",
8551
+ bash: "#"
8552
+ };
8553
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
8554
+ var wrap = (cls, text) => `<span class="tok-${cls}">${escape(text)}</span>`;
8555
+ var highlight = (code, lang) => {
8556
+ if (lang === "plain" || !code) return escape(code);
8557
+ const keywords = new Set(KEYWORDS[lang]);
8558
+ const lineComment = LINE_COMMENT[lang];
8559
+ const caseInsensitive = lang === "sql";
8560
+ let out = "";
8561
+ let i = 0;
8562
+ while (i < code.length) {
8563
+ const rest = code.slice(i);
8564
+ if ((lang === "javascript" || lang === "typescript" || lang === "css") && rest.startsWith("/*")) {
8565
+ const end = code.indexOf("*/", i + 2);
8566
+ const stop = end === -1 ? code.length : end + 2;
8567
+ out += wrap("comment", code.slice(i, stop));
8568
+ i = stop;
8569
+ continue;
8570
+ }
8571
+ if (lineComment && rest.startsWith(lineComment)) {
8572
+ const nl = code.indexOf("\n", i);
8573
+ const stop = nl === -1 ? code.length : nl;
8574
+ out += wrap("comment", code.slice(i, stop));
8575
+ i = stop;
8576
+ continue;
8577
+ }
8578
+ const q = rest[0];
8579
+ if (q === '"' || q === "'" || q === "`") {
8580
+ let j = 1;
8581
+ while (j < rest.length) {
8582
+ if (rest[j] === "\\") {
8583
+ j += 2;
8584
+ continue;
8585
+ }
8586
+ if (rest[j] === q) {
8587
+ j += 1;
8588
+ break;
8589
+ }
8590
+ if (rest[j] === "\n" && q !== "`") break;
8591
+ j += 1;
8592
+ }
8593
+ out += wrap("string", rest.slice(0, j));
8594
+ i += j;
8595
+ continue;
8596
+ }
8597
+ const num = /^\d+(\.\d+)?/.exec(rest);
8598
+ if (num && !/[\w$]/.test(code[i - 1] ?? "")) {
8599
+ out += wrap("number", num[0]);
8600
+ i += num[0].length;
8601
+ continue;
8602
+ }
8603
+ const word = /^[A-Za-z_$][\w$]*/.exec(rest);
8604
+ if (word) {
8605
+ const w = word[0];
8606
+ const hit = caseInsensitive ? keywords.has(w.toUpperCase()) : keywords.has(w);
8607
+ out += hit ? wrap("keyword", w) : escape(w);
8608
+ i += w.length;
8609
+ continue;
8610
+ }
8611
+ if (lang === "html" && rest.startsWith("<")) {
8612
+ const tag = /^<\/?[A-Za-z][\w-]*/.exec(rest);
8613
+ if (tag) {
8614
+ out += wrap("keyword", tag[0]);
8615
+ i += tag[0].length;
8616
+ continue;
8617
+ }
8618
+ }
8619
+ out += escape(code[i]);
8620
+ i += 1;
8621
+ }
8622
+ return out;
8623
+ };
8624
+
8625
+ // src/components/Editor/Editor.tsx
8375
8626
  var import_jsx_runtime323 = require("react/jsx-runtime");
8376
8627
  var DEFAULT_TOOLBAR = [
8377
8628
  "bold",
@@ -8379,14 +8630,27 @@ var DEFAULT_TOOLBAR = [
8379
8630
  "underline",
8380
8631
  "strikethrough",
8381
8632
  "code",
8382
- "heading",
8633
+ "heading-1",
8634
+ "heading-2",
8635
+ "heading-3",
8383
8636
  "list",
8384
8637
  "ordered-list",
8385
8638
  "blockquote",
8386
8639
  "code-block",
8387
8640
  "link",
8388
8641
  "image",
8389
- "divider"
8642
+ "divider",
8643
+ "checklist",
8644
+ "table",
8645
+ "align-left",
8646
+ "align-center",
8647
+ "align-right",
8648
+ "highlight",
8649
+ "superscript",
8650
+ "subscript",
8651
+ "clear-format",
8652
+ "undo",
8653
+ "redo"
8390
8654
  ];
8391
8655
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
8392
8656
  "p",
@@ -8415,12 +8679,51 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
8415
8679
  "img",
8416
8680
  "hr",
8417
8681
  "span",
8418
- "div"
8682
+ "div",
8683
+ "sub",
8684
+ "sup",
8685
+ "mark",
8686
+ "table",
8687
+ "thead",
8688
+ "tbody",
8689
+ "tr",
8690
+ "th",
8691
+ "td"
8419
8692
  ]);
8420
8693
  var ALLOWED_ATTRS = {
8421
8694
  a: ["href", "target", "rel"],
8422
- img: ["src", "alt"]
8695
+ img: ["src", "alt", "width"],
8696
+ // 정렬·체크리스트는 클래스로만 표현한다. style 을 열면 붙여넣기로 아무 CSS 나 들어온다
8697
+ p: ["class"],
8698
+ h1: ["class"],
8699
+ h2: ["class"],
8700
+ h3: ["class"],
8701
+ h4: ["class"],
8702
+ h5: ["class"],
8703
+ h6: ["class"],
8704
+ ul: ["class"],
8705
+ ol: ["class"],
8706
+ li: ["class", "data-checked"],
8707
+ blockquote: ["class"],
8708
+ div: ["class"],
8709
+ span: ["class"],
8710
+ pre: ["class"],
8711
+ code: ["class"],
8712
+ mark: ["class"],
8713
+ table: ["class"],
8714
+ td: ["class"],
8715
+ th: ["class"],
8716
+ tr: ["class"]
8423
8717
  };
8718
+ var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
8719
+ "script",
8720
+ "style",
8721
+ "noscript",
8722
+ "iframe",
8723
+ "object",
8724
+ "embed",
8725
+ "template"
8726
+ ]);
8424
8727
  var sanitizeHtml = (input) => {
8425
8728
  const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
8426
8729
  const root = doc.body.firstElementChild;
@@ -8429,6 +8732,10 @@ var sanitizeHtml = (input) => {
8429
8732
  const children = Array.from(node.children);
8430
8733
  for (const child of children) {
8431
8734
  const tag = child.tagName.toLowerCase();
8735
+ if (DROP_WITH_CONTENT.has(tag)) {
8736
+ node.removeChild(child);
8737
+ continue;
8738
+ }
8432
8739
  if (!ALLOWED_TAGS.has(tag)) {
8433
8740
  while (child.firstChild) node.insertBefore(child.firstChild, child);
8434
8741
  node.removeChild(child);
@@ -8459,7 +8766,9 @@ var escapeHtml = (text) => {
8459
8766
  };
8460
8767
  var SLASH_ITEMS = [
8461
8768
  { key: "paragraph", label: "\uB2E8\uB77D", hint: "\uAE30\uBCF8 \uD14D\uC2A4\uD2B8" },
8462
- { key: "heading", label: "\uC81C\uBAA9 1", hint: "\uD070 \uC81C\uBAA9" },
8769
+ { key: "heading-1", label: "\uC81C\uBAA9 1", hint: "\uAC00\uC7A5 \uD070 \uC81C\uBAA9" },
8770
+ { key: "heading-2", label: "\uC81C\uBAA9 2", hint: "" },
8771
+ { key: "heading-3", label: "\uC81C\uBAA9 3", hint: "" },
8463
8772
  { key: "list", label: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D", hint: "\u2022 \uD56D\uBAA9" },
8464
8773
  { key: "ordered-list", label: "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D", hint: "1. \uD56D\uBAA9" },
8465
8774
  { key: "blockquote", label: "\uC778\uC6A9", hint: "" },
@@ -8479,26 +8788,32 @@ var Editor = (props) => {
8479
8788
  onImageUpload,
8480
8789
  minHeight = 200
8481
8790
  } = props;
8482
- const editorRef = import_react21.default.useRef(null);
8483
- const lastRangeRef = import_react21.default.useRef(null);
8484
- const composingRef = import_react21.default.useRef(false);
8485
- const [isFocused, setIsFocused] = import_react21.default.useState(false);
8486
- const [isEmpty, setIsEmpty] = import_react21.default.useState(!value);
8487
- const [activeFormats, setActiveFormats] = import_react21.default.useState(/* @__PURE__ */ new Set());
8488
- const [slashOpen, setSlashOpen] = import_react21.default.useState(false);
8489
- const [slashFilter, setSlashFilter] = import_react21.default.useState("");
8490
- const [slashIdx, setSlashIdx] = import_react21.default.useState(0);
8491
- const [slashPos, setSlashPos] = import_react21.default.useState({ top: 0, left: 0 });
8492
- const slashStartRangeRef = import_react21.default.useRef(null);
8493
- const [linkOpen, setLinkOpen] = import_react21.default.useState(false);
8494
- const [linkValue, setLinkValue] = import_react21.default.useState("");
8495
- const [linkPos, setLinkPos] = import_react21.default.useState({ top: 0, left: 0 });
8496
- import_react21.default.useEffect(() => {
8497
- if (!editorRef.current) return;
8498
- if (editorRef.current.innerHTML !== value) {
8499
- editorRef.current.innerHTML = sanitizeHtml(value || "");
8500
- updateEmpty();
8501
- }
8791
+ const editorRef = import_react22.default.useRef(null);
8792
+ const lastRangeRef = import_react22.default.useRef(null);
8793
+ const composingRef = import_react22.default.useRef(false);
8794
+ const [isFocused, setIsFocused] = import_react22.default.useState(false);
8795
+ const [isEmpty, setIsEmpty] = import_react22.default.useState(!value);
8796
+ const [activeFormats, setActiveFormats] = import_react22.default.useState(/* @__PURE__ */ new Set());
8797
+ const [slashOpen, setSlashOpen] = import_react22.default.useState(false);
8798
+ const [slashFilter, setSlashFilter] = import_react22.default.useState("");
8799
+ const [slashIdx, setSlashIdx] = import_react22.default.useState(0);
8800
+ const [slashPos, setSlashPos] = import_react22.default.useState({ top: 0, left: 0 });
8801
+ const slashStartRangeRef = import_react22.default.useRef(null);
8802
+ const [linkOpen, setLinkOpen] = import_react22.default.useState(false);
8803
+ const [linkValue, setLinkValue] = import_react22.default.useState("");
8804
+ const [linkPos, setLinkPos] = import_react22.default.useState({ top: 0, left: 0 });
8805
+ const lastEmittedRef = import_react22.default.useRef(null);
8806
+ import_react22.default.useEffect(() => {
8807
+ const el = editorRef.current;
8808
+ if (!el) return;
8809
+ if (value === lastEmittedRef.current) return;
8810
+ if (el.innerHTML === value) return;
8811
+ el.innerHTML = sanitizeHtml(value || "");
8812
+ if (!el.firstChild) el.innerHTML = "<p><br></p>";
8813
+ lastEmittedRef.current = null;
8814
+ historyRef.current = [{ html: el.innerHTML, caret: 0 }];
8815
+ historyIdxRef.current = 0;
8816
+ updateEmpty();
8502
8817
  }, [value]);
8503
8818
  const updateEmpty = () => {
8504
8819
  const el = editorRef.current;
@@ -8514,10 +8829,19 @@ var Editor = (props) => {
8514
8829
  }
8515
8830
  };
8516
8831
  const restoreSelection = () => {
8517
- if (!lastRangeRef.current) return;
8832
+ const range = lastRangeRef.current;
8833
+ if (!range) return;
8834
+ if (!editorRef.current?.contains(range.startContainer)) {
8835
+ lastRangeRef.current = null;
8836
+ return;
8837
+ }
8518
8838
  const sel = window.getSelection();
8519
- sel?.removeAllRanges();
8520
- sel?.addRange(lastRangeRef.current);
8839
+ try {
8840
+ sel?.removeAllRanges();
8841
+ sel?.addRange(range);
8842
+ } catch {
8843
+ lastRangeRef.current = null;
8844
+ }
8521
8845
  };
8522
8846
  const updateActiveFormats = () => {
8523
8847
  if (!editorRef.current) return;
@@ -8533,6 +8857,17 @@ var Editor = (props) => {
8533
8857
  if (document.queryCommandState("insertOrderedList")) next.add("ordered-list");
8534
8858
  const block = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
8535
8859
  if (block) next.add(`block:${block}`);
8860
+ if (document.queryCommandState("superscript")) next.add("superscript");
8861
+ if (document.queryCommandState("subscript")) next.add("subscript");
8862
+ } catch {
8863
+ }
8864
+ try {
8865
+ const blockEl = currentBlock();
8866
+ if (blockEl?.classList.contains("align-center")) next.add("align:center");
8867
+ else if (blockEl?.classList.contains("align-right")) next.add("align:right");
8868
+ else if (blockEl) next.add("align:left");
8869
+ if (blockEl?.closest("mark")) next.add("highlight");
8870
+ if (blockEl?.closest("ul.checklist")) next.add("checklist");
8536
8871
  } catch {
8537
8872
  }
8538
8873
  setActiveFormats(next);
@@ -8546,7 +8881,374 @@ var Editor = (props) => {
8546
8881
  const setBlock = (tag) => {
8547
8882
  exec("formatBlock", tag);
8548
8883
  };
8884
+ const toggleBlock = (tag) => {
8885
+ const current = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
8886
+ setBlock(current === tag ? "p" : tag);
8887
+ };
8888
+ const historyRef = import_react22.default.useRef([]);
8889
+ const historyIdxRef = import_react22.default.useRef(-1);
8890
+ const lastPushAtRef = import_react22.default.useRef(0);
8891
+ const isRestoringRef = import_react22.default.useRef(false);
8892
+ const caretOffset = (rootEl) => {
8893
+ const el = rootEl ?? editorRef.current;
8894
+ const sel = window.getSelection();
8895
+ if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return 0;
8896
+ const range = sel.getRangeAt(0).cloneRange();
8897
+ range.selectNodeContents(el);
8898
+ range.setEnd(sel.getRangeAt(0).endContainer, sel.getRangeAt(0).endOffset);
8899
+ return range.toString().length;
8900
+ };
8901
+ const setCaretOffset = (offset, rootEl) => {
8902
+ const el = rootEl ?? editorRef.current;
8903
+ if (!el) return;
8904
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
8905
+ let remaining = offset;
8906
+ let node = null;
8907
+ while (node = walker.nextNode()) {
8908
+ const len = node.textContent?.length ?? 0;
8909
+ if (remaining <= len) {
8910
+ const range = document.createRange();
8911
+ range.setStart(node, remaining);
8912
+ range.collapse(true);
8913
+ const sel = window.getSelection();
8914
+ sel?.removeAllRanges();
8915
+ sel?.addRange(range);
8916
+ return;
8917
+ }
8918
+ remaining -= len;
8919
+ }
8920
+ placeCaretAtEnd(el);
8921
+ };
8922
+ const pushHistory = (force = false) => {
8923
+ const el = editorRef.current;
8924
+ if (!el || isRestoringRef.current) return;
8925
+ const html = el.innerHTML;
8926
+ const hist = historyRef.current;
8927
+ const cur = hist[historyIdxRef.current];
8928
+ if (cur && cur.html === html) return;
8929
+ const now = Date.now();
8930
+ const coalesce = !force && cur && now - lastPushAtRef.current < 500;
8931
+ lastPushAtRef.current = now;
8932
+ if (coalesce) {
8933
+ hist[historyIdxRef.current] = { html, caret: caretOffset() };
8934
+ return;
8935
+ }
8936
+ hist.length = historyIdxRef.current + 1;
8937
+ hist.push({ html, caret: caretOffset() });
8938
+ if (hist.length > 200) hist.shift();
8939
+ historyIdxRef.current = hist.length - 1;
8940
+ };
8941
+ const applySnapshot = (snap) => {
8942
+ const el = editorRef.current;
8943
+ if (!el) return;
8944
+ isRestoringRef.current = true;
8945
+ el.innerHTML = snap.html;
8946
+ setCaretOffset(snap.caret);
8947
+ isRestoringRef.current = false;
8948
+ lastEmittedRef.current = el.innerHTML;
8949
+ onChange?.(el.innerHTML);
8950
+ updateEmpty();
8951
+ updateActiveFormats();
8952
+ };
8953
+ const undo = () => {
8954
+ pushHistory(true);
8955
+ if (historyIdxRef.current <= 0) return;
8956
+ historyIdxRef.current -= 1;
8957
+ applySnapshot(historyRef.current[historyIdxRef.current]);
8958
+ };
8959
+ const redo = () => {
8960
+ if (historyIdxRef.current >= historyRef.current.length - 1) return;
8961
+ historyIdxRef.current += 1;
8962
+ applySnapshot(historyRef.current[historyIdxRef.current]);
8963
+ };
8964
+ const langOf = (pre) => {
8965
+ const cls = Array.from(pre.classList).find((c) => c.startsWith("lang-"));
8966
+ return cls?.slice(5) ?? "plain";
8967
+ };
8968
+ const [codeLang, setCodeLang] = import_react22.default.useState("plain");
8969
+ const highlightRafRef = import_react22.default.useRef(0);
8970
+ const rehighlight = (pre) => {
8971
+ if (composingRef.current) return;
8972
+ const code = pre.querySelector("code");
8973
+ if (!code) return;
8974
+ const lang = langOf(pre);
8975
+ const text = code.textContent || "";
8976
+ const next = highlight(text, lang);
8977
+ if (code.innerHTML === next) return;
8978
+ const caret = caretOffset(code);
8979
+ isRestoringRef.current = true;
8980
+ code.innerHTML = next;
8981
+ setCaretOffset(caret, code);
8982
+ isRestoringRef.current = false;
8983
+ };
8984
+ const scheduleHighlight = () => {
8985
+ const pre = closestPre();
8986
+ if (!pre) return;
8987
+ cancelAnimationFrame(highlightRafRef.current);
8988
+ highlightRafRef.current = requestAnimationFrame(() => rehighlight(pre));
8989
+ };
8990
+ const changeCodeLang = (lang) => {
8991
+ const pre = closestPre();
8992
+ if (!pre) return;
8993
+ pushHistory(true);
8994
+ Array.from(pre.classList).filter((c) => c.startsWith("lang-")).forEach((c) => pre.classList.remove(c));
8995
+ if (lang !== "plain") pre.classList.add(`lang-${lang}`);
8996
+ setCodeLang(lang);
8997
+ rehighlight(pre);
8998
+ handleInput();
8999
+ };
9000
+ const closestPre = () => {
9001
+ const sel = window.getSelection();
9002
+ const node = sel?.anchorNode;
9003
+ if (!node || !editorRef.current?.contains(node)) return null;
9004
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
9005
+ return el?.closest("pre") ?? null;
9006
+ };
9007
+ const insertCodeBlock = () => {
9008
+ pushHistory(true);
9009
+ const el = editorRef.current;
9010
+ const sel = window.getSelection();
9011
+ if (!el || !sel || sel.rangeCount === 0) return;
9012
+ const existing = closestPre();
9013
+ if (existing) {
9014
+ const p = document.createElement("p");
9015
+ p.textContent = existing.textContent || "";
9016
+ if (!p.textContent) p.innerHTML = "<br>";
9017
+ existing.replaceWith(p);
9018
+ placeCaretAtEnd(p);
9019
+ handleInput();
9020
+ return;
9021
+ }
9022
+ const range = sel.getRangeAt(0);
9023
+ const frag = range.cloneContents();
9024
+ const holder = document.createElement("div");
9025
+ holder.appendChild(frag);
9026
+ const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
9027
+ const linesOf = (node) => {
9028
+ const out = [];
9029
+ let cur = "";
9030
+ for (const child of Array.from(node.childNodes)) {
9031
+ if (child.nodeType === Node.TEXT_NODE) {
9032
+ cur += child.textContent || "";
9033
+ } else if (child.nodeName === "BR") {
9034
+ out.push(cur);
9035
+ cur = "";
9036
+ } else if (BLOCK.test(child.nodeName)) {
9037
+ if (cur) {
9038
+ out.push(cur);
9039
+ cur = "";
9040
+ }
9041
+ const sub = linesOf(child);
9042
+ out.push(...sub.length > 0 ? sub : [""]);
9043
+ } else {
9044
+ cur += linesOf(child).join("\n");
9045
+ }
9046
+ }
9047
+ if (cur) out.push(cur);
9048
+ return out;
9049
+ };
9050
+ const text = linesOf(holder).join("\n").replace(/\n+$/, "");
9051
+ const pre = document.createElement("pre");
9052
+ const code = document.createElement("code");
9053
+ code.textContent = text || "";
9054
+ pre.appendChild(code);
9055
+ range.deleteContents();
9056
+ range.insertNode(pre);
9057
+ ensureParagraphAfter(pre);
9058
+ placeCaretAtEnd(code);
9059
+ handleInput();
9060
+ };
9061
+ const insertTextAtCaret = (text) => {
9062
+ const sel = window.getSelection();
9063
+ if (!sel || sel.rangeCount === 0) return;
9064
+ const range = sel.getRangeAt(0);
9065
+ range.deleteContents();
9066
+ const node = document.createTextNode(text);
9067
+ range.insertNode(node);
9068
+ range.setStartAfter(node);
9069
+ range.collapse(true);
9070
+ sel.removeAllRanges();
9071
+ sel.addRange(range);
9072
+ handleInput();
9073
+ };
9074
+ const ensureParagraphAfter = (node) => {
9075
+ const next = node.nextElementSibling;
9076
+ if (next && next.tagName === "P") return;
9077
+ const p = document.createElement("p");
9078
+ p.innerHTML = "<br>";
9079
+ node.after(p);
9080
+ };
9081
+ const placeCaretAtEnd = (node) => {
9082
+ const sel = window.getSelection();
9083
+ if (!sel) return;
9084
+ const r2 = document.createRange();
9085
+ r2.selectNodeContents(node);
9086
+ r2.collapse(false);
9087
+ sel.removeAllRanges();
9088
+ sel.addRange(r2);
9089
+ editorRef.current?.focus();
9090
+ };
9091
+ const clearFormat = () => {
9092
+ exec("removeFormat");
9093
+ exec("unlink");
9094
+ setBlock("p");
9095
+ };
9096
+ const indentList = (outdent) => {
9097
+ exec(outdent ? "outdent" : "indent");
9098
+ };
9099
+ const inListItem = () => {
9100
+ const sel = window.getSelection();
9101
+ const node = sel?.anchorNode;
9102
+ if (!node || !editorRef.current?.contains(node)) return false;
9103
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
9104
+ return !!el?.closest("li");
9105
+ };
9106
+ const currentBlock = () => {
9107
+ const sel = window.getSelection();
9108
+ const node = sel?.anchorNode;
9109
+ const root = editorRef.current;
9110
+ if (!node || !root?.contains(node)) return null;
9111
+ let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
9112
+ while (el && el !== root && !/^(P|H[1-6]|LI|BLOCKQUOTE|PRE|DIV|TD|TH)$/.test(el.tagName)) {
9113
+ el = el.parentElement;
9114
+ }
9115
+ return el && el !== root ? el : null;
9116
+ };
9117
+ const setAlign = (align) => {
9118
+ const block = currentBlock();
9119
+ if (!block) return;
9120
+ pushHistory(true);
9121
+ block.classList.remove("align-left", "align-center", "align-right");
9122
+ if (align !== "left") block.classList.add(`align-${align}`);
9123
+ handleInput();
9124
+ };
9125
+ const toggleChecklist = () => {
9126
+ pushHistory(true);
9127
+ const block = currentBlock();
9128
+ const list = block?.closest("ul");
9129
+ if (list?.classList.contains("checklist")) {
9130
+ list.classList.remove("checklist");
9131
+ list.querySelectorAll("li").forEach((li) => li.removeAttribute("data-checked"));
9132
+ handleInput();
9133
+ return;
9134
+ }
9135
+ if (list) {
9136
+ list.classList.add("checklist");
9137
+ list.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
9138
+ handleInput();
9139
+ return;
9140
+ }
9141
+ exec("insertUnorderedList");
9142
+ const made = currentBlock()?.closest("ul");
9143
+ if (made) {
9144
+ made.classList.add("checklist");
9145
+ made.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
9146
+ }
9147
+ handleInput();
9148
+ };
9149
+ const handleEditorClick = (e) => {
9150
+ if (readOnly) return;
9151
+ const target = e.target;
9152
+ const li = target.closest?.("li");
9153
+ if (!li || !li.closest("ul.checklist")) return;
9154
+ const rect = li.getBoundingClientRect();
9155
+ if (e.clientX - rect.left > 20) return;
9156
+ e.preventDefault();
9157
+ pushHistory(true);
9158
+ li.setAttribute("data-checked", li.getAttribute("data-checked") === "true" ? "false" : "true");
9159
+ handleInput();
9160
+ };
9161
+ const toggleHighlight = () => {
9162
+ const sel = window.getSelection();
9163
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
9164
+ pushHistory(true);
9165
+ const range = sel.getRangeAt(0);
9166
+ const node = sel.anchorNode;
9167
+ const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
9168
+ const existing = el?.closest("mark");
9169
+ if (existing) {
9170
+ const parent = existing.parentNode;
9171
+ while (existing.firstChild) parent?.insertBefore(existing.firstChild, existing);
9172
+ parent?.removeChild(existing);
9173
+ handleInput();
9174
+ return;
9175
+ }
9176
+ const mark = document.createElement("mark");
9177
+ try {
9178
+ range.surroundContents(mark);
9179
+ } catch {
9180
+ const frag = range.extractContents();
9181
+ mark.appendChild(frag);
9182
+ range.insertNode(mark);
9183
+ }
9184
+ handleInput();
9185
+ };
9186
+ const insertTable = (cols = 3, rows = 2) => {
9187
+ const sel = window.getSelection();
9188
+ if (!sel || sel.rangeCount === 0) return;
9189
+ pushHistory(true);
9190
+ const table = document.createElement("table");
9191
+ const thead = document.createElement("thead");
9192
+ const headRow = document.createElement("tr");
9193
+ for (let c = 0; c < cols; c++) {
9194
+ const th = document.createElement("th");
9195
+ th.innerHTML = "<br>";
9196
+ headRow.appendChild(th);
9197
+ }
9198
+ thead.appendChild(headRow);
9199
+ table.appendChild(thead);
9200
+ const tbody = document.createElement("tbody");
9201
+ for (let r2 = 0; r2 < rows; r2++) {
9202
+ const tr = document.createElement("tr");
9203
+ for (let c = 0; c < cols; c++) {
9204
+ const td = document.createElement("td");
9205
+ td.innerHTML = "<br>";
9206
+ tr.appendChild(td);
9207
+ }
9208
+ tbody.appendChild(tr);
9209
+ }
9210
+ table.appendChild(tbody);
9211
+ const range = sel.getRangeAt(0);
9212
+ range.deleteContents();
9213
+ range.insertNode(table);
9214
+ ensureParagraphAfter(table);
9215
+ const first = table.querySelector("th");
9216
+ if (first) placeCaretAtEnd(first);
9217
+ handleInput();
9218
+ };
9219
+ const moveTableCell = (backwards) => {
9220
+ const block = currentBlock();
9221
+ const cell = block?.closest("td, th");
9222
+ if (!cell) return false;
9223
+ const table = cell.closest("table");
9224
+ if (!table) return false;
9225
+ const cells = Array.from(table.querySelectorAll("td, th"));
9226
+ const i = cells.indexOf(cell);
9227
+ const next = cells[i + (backwards ? -1 : 1)];
9228
+ if (next) {
9229
+ placeCaretAtEnd(next);
9230
+ return true;
9231
+ }
9232
+ if (!backwards) {
9233
+ pushHistory(true);
9234
+ const lastRow = table.querySelector("tbody")?.lastElementChild;
9235
+ if (lastRow) {
9236
+ const tr = document.createElement("tr");
9237
+ for (let c = 0; c < lastRow.children.length; c++) {
9238
+ const td = document.createElement("td");
9239
+ td.innerHTML = "<br>";
9240
+ tr.appendChild(td);
9241
+ }
9242
+ lastRow.after(tr);
9243
+ placeCaretAtEnd(tr.firstElementChild);
9244
+ handleInput();
9245
+ return true;
9246
+ }
9247
+ }
9248
+ return false;
9249
+ };
8549
9250
  const insertDivider = () => {
9251
+ pushHistory(true);
8550
9252
  exec("insertHorizontalRule");
8551
9253
  const el = editorRef.current;
8552
9254
  if (el && el.lastElementChild?.tagName === "HR") {
@@ -8592,7 +9294,9 @@ var Editor = (props) => {
8592
9294
  const range = sel.getRangeAt(0);
8593
9295
  slashStartRangeRef.current = range.cloneRange();
8594
9296
  const rect = range.getBoundingClientRect();
8595
- const editorRect = editorRef.current.getBoundingClientRect();
9297
+ const editorEl = editorRef.current;
9298
+ if (!editorEl) return;
9299
+ const editorRect = editorEl.getBoundingClientRect();
8596
9300
  setSlashPos({
8597
9301
  top: rect.bottom - editorRect.top + 4,
8598
9302
  left: rect.left - editorRect.left
@@ -8607,7 +9311,7 @@ var Editor = (props) => {
8607
9311
  setSlashIdx(0);
8608
9312
  slashStartRangeRef.current = null;
8609
9313
  };
8610
- const filteredSlashItems = import_react21.default.useMemo(() => {
9314
+ const filteredSlashItems = import_react22.default.useMemo(() => {
8611
9315
  if (!slashFilter) return SLASH_ITEMS;
8612
9316
  const f = slashFilter.toLowerCase();
8613
9317
  return SLASH_ITEMS.filter((it) => it.label.toLowerCase().includes(f) || it.key.includes(f));
@@ -8637,8 +9341,14 @@ var Editor = (props) => {
8637
9341
  case "paragraph":
8638
9342
  setBlock("p");
8639
9343
  break;
8640
- case "heading":
8641
- setBlock("h2");
9344
+ case "heading-1":
9345
+ toggleBlock("h1");
9346
+ break;
9347
+ case "heading-2":
9348
+ toggleBlock("h2");
9349
+ break;
9350
+ case "heading-3":
9351
+ toggleBlock("h3");
8642
9352
  break;
8643
9353
  case "list":
8644
9354
  exec("insertUnorderedList");
@@ -8650,7 +9360,7 @@ var Editor = (props) => {
8650
9360
  setBlock("blockquote");
8651
9361
  break;
8652
9362
  case "code-block":
8653
- setBlock("pre");
9363
+ insertCodeBlock();
8654
9364
  break;
8655
9365
  case "divider":
8656
9366
  insertDivider();
@@ -8668,7 +9378,9 @@ var Editor = (props) => {
8668
9378
  if (!sel || sel.rangeCount === 0) return;
8669
9379
  const range = sel.getRangeAt(0);
8670
9380
  const rect = range.getBoundingClientRect();
8671
- const editorRect = editorRef.current.getBoundingClientRect();
9381
+ const editorEl2 = editorRef.current;
9382
+ if (!editorEl2) return;
9383
+ const editorRect = editorEl2.getBoundingClientRect();
8672
9384
  setLinkPos({
8673
9385
  top: rect.bottom - editorRect.top + 4,
8674
9386
  left: rect.left - editorRect.left
@@ -8701,13 +9413,13 @@ var Editor = (props) => {
8701
9413
  if (node.nodeType !== Node.TEXT_NODE) return false;
8702
9414
  const text = (node.textContent || "").slice(0, range.startOffset);
8703
9415
  const patterns = [
8704
- [/^###\s$/, () => setBlock("h3")],
8705
- [/^##\s$/, () => setBlock("h2")],
8706
- [/^#\s$/, () => setBlock("h1")],
8707
- [/^-\s$/, () => exec("insertUnorderedList")],
8708
- [/^\*\s$/, () => exec("insertUnorderedList")],
8709
- [/^1\.\s$/, () => exec("insertOrderedList")],
8710
- [/^>\s$/, () => setBlock("blockquote")]
9416
+ [/^###$/, () => setBlock("h3")],
9417
+ [/^##$/, () => setBlock("h2")],
9418
+ [/^#$/, () => setBlock("h1")],
9419
+ [/^-$/, () => exec("insertUnorderedList")],
9420
+ [/^\*$/, () => exec("insertUnorderedList")],
9421
+ [/^1\.$/, () => exec("insertOrderedList")],
9422
+ [/^>$/, () => setBlock("blockquote")]
8711
9423
  ];
8712
9424
  for (const [re, fn] of patterns) {
8713
9425
  if (re.test(text)) {
@@ -8727,7 +9439,11 @@ var Editor = (props) => {
8727
9439
  if (composingRef.current) return;
8728
9440
  const el = editorRef.current;
8729
9441
  if (!el) return;
8730
- onChange?.(el.innerHTML);
9442
+ const html = el.innerHTML;
9443
+ lastEmittedRef.current = html;
9444
+ pushHistory();
9445
+ scheduleHighlight();
9446
+ onChange?.(html);
8731
9447
  updateEmpty();
8732
9448
  updateActiveFormats();
8733
9449
  if (slashOpen) {
@@ -8755,6 +9471,40 @@ var Editor = (props) => {
8755
9471
  };
8756
9472
  const handleKeyDown = (e) => {
8757
9473
  if (readOnly) return;
9474
+ if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
9475
+ const pre = closestPre();
9476
+ if (pre && !slashOpen) {
9477
+ if (e.key === "Enter" && !e.shiftKey) {
9478
+ const text = pre.textContent || "";
9479
+ if (text.endsWith("\n\n")) {
9480
+ e.preventDefault();
9481
+ const code = pre.querySelector("code") ?? pre;
9482
+ code.textContent = text.replace(/\n\n$/, "");
9483
+ ensureParagraphAfter(pre);
9484
+ const next = pre.nextElementSibling;
9485
+ if (next) placeCaretAtEnd(next);
9486
+ handleInput();
9487
+ return;
9488
+ }
9489
+ e.preventDefault();
9490
+ insertTextAtCaret("\n");
9491
+ return;
9492
+ }
9493
+ if (e.key === "Tab") {
9494
+ e.preventDefault();
9495
+ insertTextAtCaret(" ");
9496
+ return;
9497
+ }
9498
+ }
9499
+ if (e.key === "Tab" && moveTableCell(e.shiftKey)) {
9500
+ e.preventDefault();
9501
+ return;
9502
+ }
9503
+ if (e.key === "Tab" && inListItem()) {
9504
+ e.preventDefault();
9505
+ indentList(e.shiftKey);
9506
+ return;
9507
+ }
8758
9508
  if (slashOpen) {
8759
9509
  if (e.key === "ArrowDown") {
8760
9510
  e.preventDefault();
@@ -8778,6 +9528,21 @@ var Editor = (props) => {
8778
9528
  return;
8779
9529
  }
8780
9530
  }
9531
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
9532
+ e.preventDefault();
9533
+ if (e.shiftKey) redo();
9534
+ else undo();
9535
+ return;
9536
+ }
9537
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "v") {
9538
+ plainPasteRef.current = true;
9539
+ return;
9540
+ }
9541
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "y") {
9542
+ e.preventDefault();
9543
+ redo();
9544
+ return;
9545
+ }
8781
9546
  if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
8782
9547
  const k = e.key.toLowerCase();
8783
9548
  if (k === "b") {
@@ -8801,6 +9566,7 @@ var Editor = (props) => {
8801
9566
  return;
8802
9567
  }
8803
9568
  }
9569
+ if (pre) return;
8804
9570
  if (enableSlashCommand && e.key === "/") {
8805
9571
  setTimeout(openSlashMenu, 0);
8806
9572
  }
@@ -8810,11 +9576,21 @@ var Editor = (props) => {
8810
9576
  }
8811
9577
  }
8812
9578
  };
9579
+ const plainPasteRef = import_react22.default.useRef(false);
8813
9580
  const handlePaste = (e) => {
8814
9581
  if (readOnly) return;
8815
9582
  e.preventDefault();
8816
- const html = e.clipboardData.getData("text/html");
8817
9583
  const text = e.clipboardData.getData("text/plain");
9584
+ if (plainPasteRef.current) {
9585
+ plainPasteRef.current = false;
9586
+ insertTextAtCaret(text);
9587
+ return;
9588
+ }
9589
+ if (closestPre()) {
9590
+ insertTextAtCaret(text);
9591
+ return;
9592
+ }
9593
+ const html = e.clipboardData.getData("text/html");
8818
9594
  const clean = html ? sanitizeHtml(html) : escapeHtml(text);
8819
9595
  document.execCommand("insertHTML", false, clean);
8820
9596
  handleInput();
@@ -8836,8 +9612,11 @@ var Editor = (props) => {
8836
9612
  };
8837
9613
  const handleSelectionUpdate = () => {
8838
9614
  updateActiveFormats();
9615
+ const pre = closestPre();
9616
+ setCodeLang(pre ? langOf(pre) : "plain");
8839
9617
  };
8840
9618
  const onToolbarAction = (item) => {
9619
+ if (closestPre() && !["code-block", "undo", "redo"].includes(item)) return;
8841
9620
  switch (item) {
8842
9621
  case "bold":
8843
9622
  exec("bold");
@@ -8866,8 +9645,14 @@ var Editor = (props) => {
8866
9645
  handleInput();
8867
9646
  break;
8868
9647
  }
8869
- case "heading":
8870
- setBlock("h2");
9648
+ case "heading-1":
9649
+ toggleBlock("h1");
9650
+ break;
9651
+ case "heading-2":
9652
+ toggleBlock("h2");
9653
+ break;
9654
+ case "heading-3":
9655
+ toggleBlock("h3");
8871
9656
  break;
8872
9657
  case "list":
8873
9658
  exec("insertUnorderedList");
@@ -8879,7 +9664,7 @@ var Editor = (props) => {
8879
9664
  setBlock("blockquote");
8880
9665
  break;
8881
9666
  case "code-block":
8882
- setBlock("pre");
9667
+ insertCodeBlock();
8883
9668
  break;
8884
9669
  case "link":
8885
9670
  openLinkEditor();
@@ -8890,6 +9675,39 @@ var Editor = (props) => {
8890
9675
  case "divider":
8891
9676
  insertDivider();
8892
9677
  break;
9678
+ case "checklist":
9679
+ toggleChecklist();
9680
+ break;
9681
+ case "align-left":
9682
+ setAlign("left");
9683
+ break;
9684
+ case "align-center":
9685
+ setAlign("center");
9686
+ break;
9687
+ case "align-right":
9688
+ setAlign("right");
9689
+ break;
9690
+ case "highlight":
9691
+ toggleHighlight();
9692
+ break;
9693
+ case "superscript":
9694
+ exec("superscript");
9695
+ break;
9696
+ case "subscript":
9697
+ exec("subscript");
9698
+ break;
9699
+ case "table":
9700
+ insertTable();
9701
+ break;
9702
+ case "clear-format":
9703
+ clearFormat();
9704
+ break;
9705
+ case "undo":
9706
+ undo();
9707
+ break;
9708
+ case "redo":
9709
+ redo();
9710
+ break;
8893
9711
  }
8894
9712
  };
8895
9713
  return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: clsx_default("lib-xplat-editor", isFocused && "focused", readOnly && "readonly"), children: [
@@ -8898,7 +9716,10 @@ var Editor = (props) => {
8898
9716
  {
8899
9717
  items: toolbar,
8900
9718
  active: activeFormats,
8901
- onAction: onToolbarAction
9719
+ onAction: onToolbarAction,
9720
+ codeLang,
9721
+ inCodeBlock: activeFormats.has("block:pre"),
9722
+ onCodeLangChange: changeCodeLang
8902
9723
  }
8903
9724
  ),
8904
9725
  /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__content", style: { minHeight }, children: [
@@ -8909,9 +9730,14 @@ var Editor = (props) => {
8909
9730
  ref: editorRef,
8910
9731
  contentEditable: !readOnly,
8911
9732
  suppressContentEditableWarning: true,
9733
+ role: "textbox",
9734
+ "aria-multiline": "true",
9735
+ "aria-readonly": readOnly || void 0,
9736
+ "aria-label": placeholder,
8912
9737
  className: "lib-xplat-editor__editable",
8913
9738
  onInput: handleInput,
8914
9739
  onKeyDown: handleKeyDown,
9740
+ onClick: handleEditorClick,
8915
9741
  onKeyUp: handleSelectionUpdate,
8916
9742
  onMouseUp: handleSelectionUpdate,
8917
9743
  onPaste: handlePaste,
@@ -8953,14 +9779,27 @@ var TOOLBAR_LABEL = {
8953
9779
  underline: "U",
8954
9780
  strikethrough: "S",
8955
9781
  code: "<>",
8956
- heading: "H",
9782
+ "heading-1": "H1",
9783
+ "heading-2": "H2",
9784
+ "heading-3": "H3",
8957
9785
  list: "\u2022",
8958
9786
  "ordered-list": "1.",
8959
9787
  blockquote: "\u275D",
8960
9788
  "code-block": "[ ]",
8961
9789
  link: "\u{1F517}",
8962
9790
  image: "\u{1F5BC}",
8963
- divider: "\u2014"
9791
+ divider: "\u2014",
9792
+ checklist: "\u2611",
9793
+ "align-left": "\u21E4",
9794
+ "align-center": "\u2194",
9795
+ "align-right": "\u21E5",
9796
+ highlight: "\u{1F58D}",
9797
+ superscript: "x\xB2",
9798
+ subscript: "x\u2082",
9799
+ table: "\u25A6",
9800
+ "clear-format": "\u232B",
9801
+ undo: "\u21B6",
9802
+ redo: "\u21B7"
8964
9803
  };
8965
9804
  var TOOLBAR_TITLE = {
8966
9805
  bold: "\uAD75\uAC8C (\u2318B)",
@@ -8968,39 +9807,77 @@ var TOOLBAR_TITLE = {
8968
9807
  underline: "\uBC11\uC904 (\u2318U)",
8969
9808
  strikethrough: "\uCDE8\uC18C\uC120",
8970
9809
  code: "\uC778\uB77C\uC778 \uCF54\uB4DC",
8971
- heading: "\uC81C\uBAA9",
9810
+ "heading-1": "\uC81C\uBAA9 1",
9811
+ "heading-2": "\uC81C\uBAA9 2",
9812
+ "heading-3": "\uC81C\uBAA9 3",
8972
9813
  list: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D",
8973
9814
  "ordered-list": "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D",
8974
9815
  blockquote: "\uC778\uC6A9",
8975
9816
  "code-block": "\uCF54\uB4DC \uBE14\uB85D",
8976
9817
  link: "\uB9C1\uD06C (\u2318K)",
8977
9818
  image: "\uC774\uBBF8\uC9C0",
8978
- divider: "\uAD6C\uBD84\uC120"
9819
+ divider: "\uAD6C\uBD84\uC120",
9820
+ checklist: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
9821
+ "align-left": "\uC67C\uCABD \uC815\uB82C",
9822
+ "align-center": "\uAC00\uC6B4\uB370 \uC815\uB82C",
9823
+ "align-right": "\uC624\uB978\uCABD \uC815\uB82C",
9824
+ highlight: "\uD615\uAD11\uD39C",
9825
+ superscript: "\uC704 \uCCA8\uC790",
9826
+ subscript: "\uC544\uB798 \uCCA8\uC790",
9827
+ table: "\uD45C \uC0BD\uC785",
9828
+ "clear-format": "\uC11C\uC2DD \uC9C0\uC6B0\uAE30",
9829
+ undo: "\uB418\uB3CC\uB9AC\uAE30 (\u2318Z)",
9830
+ redo: "\uB2E4\uC2DC\uD558\uAE30 (\u2318\u21E7Z)"
8979
9831
  };
8980
9832
  var isActive = (item, active) => {
8981
- if (item === "heading") return active.has("block:h1") || active.has("block:h2") || active.has("block:h3");
9833
+ if (item === "heading-1") return active.has("block:h1");
9834
+ if (item === "heading-2") return active.has("block:h2");
9835
+ if (item === "heading-3") return active.has("block:h3");
8982
9836
  if (item === "blockquote") return active.has("block:blockquote");
8983
9837
  if (item === "code-block") return active.has("block:pre");
9838
+ if (item === "align-left") return active.has("align:left");
9839
+ if (item === "align-center") return active.has("align:center");
9840
+ if (item === "align-right") return active.has("align:right");
8984
9841
  return active.has(item);
8985
9842
  };
8986
- var EditorToolbar = ({ items, active, onAction }) => {
8987
- return /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("div", { className: "lib-xplat-editor__toolbar", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
8988
- "button",
8989
- {
8990
- type: "button",
8991
- className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
8992
- title: TOOLBAR_TITLE[item],
8993
- onMouseDown: (e) => {
8994
- e.preventDefault();
8995
- onAction(item);
9843
+ var EditorToolbar = ({
9844
+ items,
9845
+ active,
9846
+ onAction,
9847
+ codeLang,
9848
+ inCodeBlock,
9849
+ onCodeLangChange
9850
+ }) => {
9851
+ return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__toolbar", children: [
9852
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
9853
+ "button",
9854
+ {
9855
+ type: "button",
9856
+ className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
9857
+ title: TOOLBAR_TITLE[item],
9858
+ onMouseDown: (e) => {
9859
+ e.preventDefault();
9860
+ onAction(item);
9861
+ },
9862
+ children: TOOLBAR_LABEL[item]
8996
9863
  },
8997
- children: TOOLBAR_LABEL[item]
8998
- },
8999
- item
9000
- )) });
9864
+ item
9865
+ )),
9866
+ inCodeBlock && /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
9867
+ "select",
9868
+ {
9869
+ className: "lib-xplat-editor__lang",
9870
+ value: codeLang,
9871
+ "aria-label": "\uCF54\uB4DC \uC5B8\uC5B4",
9872
+ onMouseDown: (e) => e.stopPropagation(),
9873
+ onChange: (e) => onCodeLangChange(e.target.value),
9874
+ children: CODE_LANGUAGES.map((l) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("option", { value: l.value, children: l.label }, l.value))
9875
+ }
9876
+ )
9877
+ ] });
9001
9878
  };
9002
9879
  var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
9003
- import_react21.default.useEffect(() => {
9880
+ import_react22.default.useEffect(() => {
9004
9881
  const handleClickOutside = (e) => {
9005
9882
  const target = e.target;
9006
9883
  const menu = document.querySelector(".lib-xplat-editor__slash-menu");
@@ -9034,8 +9911,8 @@ var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
9034
9911
  );
9035
9912
  };
9036
9913
  var LinkPopover = ({ position, value, onChange, onConfirm, onClose }) => {
9037
- const inputRef = import_react21.default.useRef(null);
9038
- import_react21.default.useEffect(() => {
9914
+ const inputRef = import_react22.default.useRef(null);
9915
+ import_react22.default.useEffect(() => {
9039
9916
  inputRef.current?.focus();
9040
9917
  }, []);
9041
9918
  return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)(
@@ -9093,7 +9970,7 @@ EmptyState.displayName = "EmptyState";
9093
9970
  var EmptyState_default = EmptyState;
9094
9971
 
9095
9972
  // src/components/FileUpload/FileUpload.tsx
9096
- var import_react22 = __toESM(require("react"), 1);
9973
+ var import_react23 = __toESM(require("react"), 1);
9097
9974
  var import_jsx_runtime325 = require("react/jsx-runtime");
9098
9975
  var FileUpload = (props) => {
9099
9976
  const {
@@ -9105,8 +9982,8 @@ var FileUpload = (props) => {
9105
9982
  description,
9106
9983
  validations
9107
9984
  } = props;
9108
- const [isDragOver, setIsDragOver] = import_react22.default.useState(false);
9109
- const inputRef = import_react22.default.useRef(null);
9985
+ const [isDragOver, setIsDragOver] = import_react23.default.useState(false);
9986
+ const inputRef = import_react23.default.useRef(null);
9110
9987
  const handleFiles = (fileList) => {
9111
9988
  let files = Array.from(fileList);
9112
9989
  if (maxSize) {
@@ -9168,10 +10045,10 @@ FileUpload.displayName = "FileUpload";
9168
10045
  var FileUpload_default = FileUpload;
9169
10046
 
9170
10047
  // src/components/HtmlTypeWriter/HtmlTypeWriter.tsx
9171
- var import_react24 = __toESM(require("react"), 1);
10048
+ var import_react25 = __toESM(require("react"), 1);
9172
10049
 
9173
10050
  // src/components/HtmlTypeWriter/utils.ts
9174
- var import_react23 = __toESM(require("react"), 1);
10051
+ var import_react24 = __toESM(require("react"), 1);
9175
10052
  var voidTags = /* @__PURE__ */ new Set([
9176
10053
  "br",
9177
10054
  "img",
@@ -9239,15 +10116,15 @@ var convertNodeToReactWithRange = (node, typedLen, rangeMap) => {
9239
10116
  props[attr.name] = attr.value;
9240
10117
  });
9241
10118
  if (voidTags.has(tag)) {
9242
- return import_react23.default.createElement(tag, props);
10119
+ return import_react24.default.createElement(tag, props);
9243
10120
  }
9244
10121
  const children = Array.from(element.childNodes).map((child) => convertNodeToReactWithRange(child, typedLen, rangeMap)).filter((n) => n != null);
9245
- return import_react23.default.createElement(tag, props, ...children);
10122
+ return import_react24.default.createElement(tag, props, ...children);
9246
10123
  };
9247
10124
  var htmlToReactProgressive = (root, typedLen, rangeMap) => {
9248
10125
  const nodes = Array.from(root.childNodes).map((child, idx) => {
9249
10126
  const node = convertNodeToReactWithRange(child, typedLen, rangeMap);
9250
- return node == null ? null : import_react23.default.createElement(import_react23.default.Fragment, { key: idx }, node);
10127
+ return node == null ? null : import_react24.default.createElement(import_react24.default.Fragment, { key: idx }, node);
9251
10128
  }).filter(Boolean);
9252
10129
  return nodes.length === 0 ? null : nodes;
9253
10130
  };
@@ -9260,20 +10137,20 @@ var HtmlTypeWriter = ({
9260
10137
  onDone,
9261
10138
  onChange
9262
10139
  }) => {
9263
- const [typedLen, setTypedLen] = import_react24.default.useState(0);
9264
- const doneCalledRef = import_react24.default.useRef(false);
9265
- const { doc, rangeMap, totalLength } = import_react24.default.useMemo(() => {
10140
+ const [typedLen, setTypedLen] = import_react25.default.useState(0);
10141
+ const doneCalledRef = import_react25.default.useRef(false);
10142
+ const { doc, rangeMap, totalLength } = import_react25.default.useMemo(() => {
9266
10143
  if (typeof window === "undefined") return { doc: null, rangeMap: /* @__PURE__ */ new Map(), totalLength: 0 };
9267
10144
  const decoded = decodeHtmlEntities(html);
9268
10145
  const doc2 = new DOMParser().parseFromString(decoded, "text/html");
9269
10146
  const { rangeMap: rangeMap2, totalLength: totalLength2 } = buildRangeMap(doc2.body);
9270
10147
  return { doc: doc2, rangeMap: rangeMap2, totalLength: totalLength2 };
9271
10148
  }, [html]);
9272
- import_react24.default.useEffect(() => {
10149
+ import_react25.default.useEffect(() => {
9273
10150
  setTypedLen(0);
9274
10151
  doneCalledRef.current = false;
9275
10152
  }, [html]);
9276
- import_react24.default.useEffect(() => {
10153
+ import_react25.default.useEffect(() => {
9277
10154
  if (!totalLength) return;
9278
10155
  if (typedLen >= totalLength) return;
9279
10156
  const timer = window.setInterval(() => {
@@ -9281,18 +10158,18 @@ var HtmlTypeWriter = ({
9281
10158
  }, duration);
9282
10159
  return () => window.clearInterval(timer);
9283
10160
  }, [typedLen, totalLength, duration]);
9284
- import_react24.default.useEffect(() => {
10161
+ import_react25.default.useEffect(() => {
9285
10162
  if (typedLen > 0 && typedLen < totalLength) {
9286
10163
  onChange?.();
9287
10164
  }
9288
10165
  }, [typedLen, totalLength, onChange]);
9289
- import_react24.default.useEffect(() => {
10166
+ import_react25.default.useEffect(() => {
9290
10167
  if (typedLen === totalLength && totalLength > 0 && !doneCalledRef.current) {
9291
10168
  doneCalledRef.current = true;
9292
10169
  onDone?.();
9293
10170
  }
9294
10171
  }, [typedLen, totalLength, onDone]);
9295
- const parsed = import_react24.default.useMemo(
10172
+ const parsed = import_react25.default.useMemo(
9296
10173
  () => doc ? htmlToReactProgressive(doc.body, typedLen, rangeMap) : null,
9297
10174
  [doc, typedLen, rangeMap]
9298
10175
  );
@@ -9302,12 +10179,12 @@ HtmlTypeWriter.displayName = "HtmlTypeWriter";
9302
10179
  var HtmlTypeWriter_default = HtmlTypeWriter;
9303
10180
 
9304
10181
  // src/components/ImageSelector/ImageSelector.tsx
9305
- var import_react25 = __toESM(require("react"), 1);
10182
+ var import_react26 = __toESM(require("react"), 1);
9306
10183
  var import_jsx_runtime327 = require("react/jsx-runtime");
9307
10184
  var ImageSelector = (props) => {
9308
10185
  const { value, label, onChange, validations } = props;
9309
- const [previewUrl, setPreviewUrl] = import_react25.default.useState();
9310
- import_react25.default.useEffect(() => {
10186
+ const [previewUrl, setPreviewUrl] = import_react26.default.useState();
10187
+ import_react26.default.useEffect(() => {
9311
10188
  if (!value) {
9312
10189
  setPreviewUrl(void 0);
9313
10190
  return;
@@ -9316,7 +10193,7 @@ var ImageSelector = (props) => {
9316
10193
  setPreviewUrl(url);
9317
10194
  return () => URL.revokeObjectURL(url);
9318
10195
  }, [value]);
9319
- const inputRef = import_react25.default.useRef(null);
10196
+ const inputRef = import_react26.default.useRef(null);
9320
10197
  const handleFileChange = (e) => {
9321
10198
  const selectedFile = e.target.files?.[0];
9322
10199
  if (selectedFile) {
@@ -9499,17 +10376,21 @@ Pagination.displayName = "Pagination";
9499
10376
  var Pagination_default = Pagination;
9500
10377
 
9501
10378
  // src/components/PopOver/PopOver.tsx
9502
- var import_react26 = __toESM(require("react"), 1);
10379
+ var import_react27 = __toESM(require("react"), 1);
9503
10380
  var import_jsx_runtime332 = require("react/jsx-runtime");
9504
10381
  var PopOver = (props) => {
9505
10382
  const { children, isOpen, onClose, PopOverEl } = props;
9506
- const popRef = import_react26.default.useRef(null);
9507
- const triggerRef = import_react26.default.useRef(null);
9508
- const [localOpen, setLocalOpen] = import_react26.default.useState(false);
9509
- const [eventTrigger, setEventTrigger] = import_react26.default.useState(false);
10383
+ const popRef = import_react27.default.useRef(null);
10384
+ const triggerRef = import_react27.default.useRef(null);
10385
+ const [localOpen, setLocalOpen] = import_react27.default.useState(false);
10386
+ const [eventTrigger, setEventTrigger] = import_react27.default.useState(false);
9510
10387
  useClickOutside_default([popRef, triggerRef], onClose, isOpen);
10388
+ useEscapeKey(isOpen, () => {
10389
+ onClose();
10390
+ triggerRef.current?.focus({ preventScroll: true });
10391
+ });
9511
10392
  const position = useAutoPosition_default(triggerRef, popRef, localOpen);
9512
- import_react26.default.useEffect(() => {
10393
+ import_react27.default.useEffect(() => {
9513
10394
  if (isOpen) {
9514
10395
  setLocalOpen(isOpen);
9515
10396
  setTimeout(() => {
@@ -9538,6 +10419,7 @@ var PopOver = (props) => {
9538
10419
  className: clsx_default(
9539
10420
  "lib-xplat-pop-over-content",
9540
10421
  position.direction,
10422
+ position.repositioning && "repositioning",
9541
10423
  eventTrigger && "visible"
9542
10424
  ),
9543
10425
  ref: popRef,
@@ -9594,12 +10476,12 @@ Progress.displayName = "Progress";
9594
10476
  var Progress_default = Progress;
9595
10477
 
9596
10478
  // src/components/Radio/RadioGroupContext.tsx
9597
- var import_react27 = __toESM(require("react"), 1);
9598
- var RadioGroupContext = import_react27.default.createContext(
10479
+ var import_react28 = __toESM(require("react"), 1);
10480
+ var RadioGroupContext = import_react28.default.createContext(
9599
10481
  null
9600
10482
  );
9601
10483
  var useRadioGroupContext = () => {
9602
- return import_react27.default.useContext(RadioGroupContext);
10484
+ return import_react28.default.useContext(RadioGroupContext);
9603
10485
  };
9604
10486
  var RadioGroupContext_default = RadioGroupContext;
9605
10487
 
@@ -9675,19 +10557,19 @@ RadioGroup.displayName = "RadioGroup";
9675
10557
  var RadioGroup_default = RadioGroup;
9676
10558
 
9677
10559
  // src/components/Select/Select.tsx
9678
- var import_react30 = __toESM(require("react"), 1);
10560
+ var import_react31 = __toESM(require("react"), 1);
9679
10561
 
9680
10562
  // src/components/Select/context.ts
9681
- var import_react28 = __toESM(require("react"), 1);
9682
- var SelectContext = import_react28.default.createContext(null);
10563
+ var import_react29 = __toESM(require("react"), 1);
10564
+ var SelectContext = import_react29.default.createContext(null);
9683
10565
  var context_default = SelectContext;
9684
10566
 
9685
10567
  // src/components/Select/SelectItem.tsx
9686
- var import_react29 = __toESM(require("react"), 1);
10568
+ var import_react30 = __toESM(require("react"), 1);
9687
10569
  var import_jsx_runtime336 = require("react/jsx-runtime");
9688
10570
  var SelectItem = (props) => {
9689
10571
  const { children, value, onClick, disabled = false } = props;
9690
- const ctx = import_react29.default.useContext(context_default);
10572
+ const ctx = import_react30.default.useContext(context_default);
9691
10573
  const handleClick = (e) => {
9692
10574
  e.preventDefault();
9693
10575
  e.stopPropagation();
@@ -9727,32 +10609,37 @@ var SelectRoot = (props) => {
9727
10609
  onChange,
9728
10610
  children,
9729
10611
  disabled = false,
10612
+ searchable = false,
10613
+ searchPlaceholder = "\uAC80\uC0C9",
9730
10614
  error = false,
9731
10615
  validations,
10616
+ label,
10617
+ required,
9732
10618
  size = "md"
9733
10619
  } = props;
9734
- const itemChildren = import_react30.default.Children.toArray(children).filter(
9735
- (child) => import_react30.default.isValidElement(child) && child.type === SelectItem_default
10620
+ const fieldId = import_react31.default.useId();
10621
+ const itemChildren = import_react31.default.Children.toArray(children).filter(
10622
+ (child) => import_react31.default.isValidElement(child) && child.type === SelectItem_default
9736
10623
  );
9737
10624
  const isControlled = valueProp !== void 0;
9738
- const [isOpen, setOpen] = import_react30.default.useState(false);
9739
- const [uncontrolledLabel, setUncontrolledLabel] = import_react30.default.useState(null);
9740
- const [uncontrolledValue, setUncontrolledValue] = import_react30.default.useState(void 0);
9741
- const controlledLabel = import_react30.default.useMemo(() => {
10625
+ const [isOpen, setOpen] = import_react31.default.useState(false);
10626
+ const [uncontrolledLabel, setUncontrolledLabel] = import_react31.default.useState(null);
10627
+ const [uncontrolledValue, setUncontrolledValue] = import_react31.default.useState(void 0);
10628
+ const controlledLabel = import_react31.default.useMemo(() => {
9742
10629
  if (!isControlled) return null;
9743
10630
  const match = itemChildren.find((child) => child.props.value === valueProp);
9744
10631
  return match ? match.props.children : null;
9745
10632
  }, [isControlled, valueProp, itemChildren]);
9746
10633
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
9747
10634
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
9748
- const triggerRef = import_react30.default.useRef(null);
9749
- const contentRef = import_react30.default.useRef(null);
9750
- const [mounted, setMounted] = import_react30.default.useState(false);
9751
- const [visible, setVisible] = import_react30.default.useState(false);
9752
- import_react30.default.useEffect(() => {
10635
+ const triggerRef = import_react31.default.useRef(null);
10636
+ const contentRef = import_react31.default.useRef(null);
10637
+ const [mounted, setMounted] = import_react31.default.useState(false);
10638
+ const [visible, setVisible] = import_react31.default.useState(false);
10639
+ import_react31.default.useEffect(() => {
9753
10640
  if (disabled && isOpen) setOpen(false);
9754
10641
  }, [disabled, isOpen]);
9755
- import_react30.default.useEffect(() => {
10642
+ import_react31.default.useEffect(() => {
9756
10643
  if (isOpen) {
9757
10644
  setMounted(true);
9758
10645
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -9762,22 +10649,42 @@ var SelectRoot = (props) => {
9762
10649
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS3);
9763
10650
  return () => clearTimeout(t);
9764
10651
  }, [isOpen]);
9765
- const open = import_react30.default.useCallback(() => setOpen(true), []);
9766
- const close = import_react30.default.useCallback(() => setOpen(false), []);
9767
- const toggle = import_react30.default.useCallback(() => setOpen((prev) => !prev), []);
10652
+ const [query, setQuery] = import_react31.default.useState("");
10653
+ const textOf = (node) => {
10654
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
10655
+ if (typeof node === "string" || typeof node === "number") return String(node);
10656
+ if (Array.isArray(node)) return node.map(textOf).join("");
10657
+ if (import_react31.default.isValidElement(node)) return textOf(node.props.children);
10658
+ return "";
10659
+ };
10660
+ const visibleItems = import_react31.default.useMemo(() => {
10661
+ if (!searchable || !query.trim()) return itemChildren;
10662
+ const q = query.trim().toLowerCase();
10663
+ return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
10664
+ }, [searchable, query, itemChildren]);
10665
+ const open = import_react31.default.useCallback(() => setOpen(true), []);
10666
+ const close = import_react31.default.useCallback(() => {
10667
+ setOpen(false);
10668
+ setQuery("");
10669
+ }, []);
10670
+ const toggle = import_react31.default.useCallback(() => setOpen((prev) => !prev), []);
9768
10671
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
10672
+ useEscapeKey(isOpen, () => {
10673
+ close();
10674
+ triggerRef.current?.focus({ preventScroll: true });
10675
+ });
9769
10676
  const position = useAutoPosition_default(triggerRef, contentRef, mounted);
9770
- const setSelected = import_react30.default.useCallback(
9771
- (label, itemValue) => {
10677
+ const setSelected = import_react31.default.useCallback(
10678
+ (label2, itemValue) => {
9772
10679
  if (!isControlled) {
9773
- setUncontrolledLabel(label);
10680
+ setUncontrolledLabel(label2);
9774
10681
  setUncontrolledValue(itemValue);
9775
10682
  }
9776
- onChange?.(itemValue, label);
10683
+ onChange?.(itemValue, label2);
9777
10684
  },
9778
10685
  [isControlled, onChange]
9779
10686
  );
9780
- const ctxValue = import_react30.default.useMemo(
10687
+ const ctxValue = import_react31.default.useMemo(
9781
10688
  () => ({
9782
10689
  isOpen,
9783
10690
  mounted,
@@ -9813,6 +10720,7 @@ var SelectRoot = (props) => {
9813
10720
  /* @__PURE__ */ (0, import_jsx_runtime337.jsxs)(
9814
10721
  "div",
9815
10722
  {
10723
+ id: fieldId,
9816
10724
  ref: triggerRef,
9817
10725
  className: clsx_default(
9818
10726
  "select-trigger",
@@ -9855,20 +10763,33 @@ var SelectRoot = (props) => {
9855
10763
  ]
9856
10764
  }
9857
10765
  ),
9858
- mounted && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(
10766
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime337.jsxs)(
9859
10767
  "div",
9860
10768
  {
9861
- className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
10769
+ className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass, position.repositioning && "repositioning"),
9862
10770
  ref: contentRef,
9863
10771
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
9864
10772
  role: "listbox",
9865
- children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(context_default.Provider, { value: ctxValue, children: itemChildren })
10773
+ children: [
10774
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)("div", { className: "select-search", children: /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(
10775
+ "input",
10776
+ {
10777
+ type: "text",
10778
+ value: query,
10779
+ placeholder: searchPlaceholder,
10780
+ onChange: (e) => setQuery(e.target.value),
10781
+ autoFocus: true,
10782
+ "aria-label": searchPlaceholder
10783
+ }
10784
+ ) }),
10785
+ /* @__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" }) })
10786
+ ]
9866
10787
  }
9867
10788
  ) })
9868
10789
  ]
9869
10790
  }
9870
10791
  ) });
9871
- return /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Field, { validations, children: control });
10792
+ return /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
9872
10793
  };
9873
10794
  SelectRoot.displayName = "Select";
9874
10795
  var Select = Object.assign(SelectRoot, {
@@ -9948,8 +10869,44 @@ var Spinner = (props) => {
9948
10869
  Spinner.displayName = "Spinner";
9949
10870
  var Spinner_default = Spinner;
9950
10871
 
9951
- // src/components/Steps/Steps.tsx
10872
+ // src/components/Stat/Stat.tsx
9952
10873
  var import_jsx_runtime340 = require("react/jsx-runtime");
10874
+ var ARROW = {
10875
+ up: "\u25B2",
10876
+ down: "\u25BC",
10877
+ flat: "\u2014"
10878
+ };
10879
+ var Stat = (props) => {
10880
+ const {
10881
+ label,
10882
+ value,
10883
+ unit,
10884
+ description,
10885
+ trend,
10886
+ icon,
10887
+ size = "md",
10888
+ type = "primary"
10889
+ } = props;
10890
+ const statusType = normalizeStatus(type);
10891
+ return /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: clsx_default("lib-xplat-stat", size, statusType), children: [
10892
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: "stat-label", children: label }),
10893
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: "stat-value-row", children: [
10894
+ icon && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-icon", children: icon }),
10895
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-value", children: value }),
10896
+ unit && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "stat-unit", children: unit }),
10897
+ trend && /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("span", { className: clsx_default("stat-trend", trend.direction), children: [
10898
+ /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { "aria-hidden": "true", children: ARROW[trend.direction] }),
10899
+ trend.value
10900
+ ] })
10901
+ ] }),
10902
+ description && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: "stat-description", children: description })
10903
+ ] });
10904
+ };
10905
+ Stat.displayName = "Stat";
10906
+ var Stat_default = Stat;
10907
+
10908
+ // src/components/Steps/Steps.tsx
10909
+ var import_jsx_runtime341 = require("react/jsx-runtime");
9953
10910
  var Steps = (props) => {
9954
10911
  const {
9955
10912
  items,
@@ -9957,13 +10914,13 @@ var Steps = (props) => {
9957
10914
  type = "primary"
9958
10915
  } = props;
9959
10916
  const statusType = normalizeStatus(type);
9960
- return /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
10917
+ return /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("div", { className: clsx_default("lib-xplat-steps", statusType), children: items.map((item, index) => {
9961
10918
  const status = index < current ? "completed" : index === current ? "active" : "pending";
9962
- return /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: clsx_default("step-item", status), children: [
9963
- /* @__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 }) }),
9964
- /* @__PURE__ */ (0, import_jsx_runtime340.jsxs)("div", { className: "step-content", children: [
9965
- /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "step-title", children: item.title }),
9966
- item.description && /* @__PURE__ */ (0, import_jsx_runtime340.jsx)("span", { className: "step-description", children: item.description })
10919
+ return /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: clsx_default("step-item", status), children: [
10920
+ /* @__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 }) }),
10921
+ /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: "step-content", children: [
10922
+ /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("span", { className: "step-title", children: item.title }),
10923
+ item.description && /* @__PURE__ */ (0, import_jsx_runtime341.jsx)("span", { className: "step-description", children: item.description })
9967
10924
  ] })
9968
10925
  ] }, index);
9969
10926
  }) });
@@ -9972,8 +10929,8 @@ Steps.displayName = "Steps";
9972
10929
  var Steps_default = Steps;
9973
10930
 
9974
10931
  // src/components/Swiper/Swiper.tsx
9975
- var import_react31 = __toESM(require("react"), 1);
9976
- var import_jsx_runtime341 = require("react/jsx-runtime");
10932
+ var import_react32 = __toESM(require("react"), 1);
10933
+ var import_jsx_runtime342 = require("react/jsx-runtime");
9977
10934
  var Swiper = (props) => {
9978
10935
  const {
9979
10936
  auto = false,
@@ -9997,26 +10954,26 @@ var Swiper = (props) => {
9997
10954
  const maxIndex = Math.max(0, totalSlides - viewItemCount);
9998
10955
  const useLoop = loop && canSlide;
9999
10956
  const cloneCount = useLoop ? totalSlides : 0;
10000
- const extendedItems = import_react31.default.useMemo(() => {
10957
+ const extendedItems = import_react32.default.useMemo(() => {
10001
10958
  if (!useLoop) return items;
10002
10959
  return [...items, ...items, ...items];
10003
10960
  }, [items, useLoop]);
10004
10961
  const initialIdx = Math.max(0, Math.min(indexProp ?? 0, maxIndex));
10005
- const [innerIndex, setInnerIndex] = import_react31.default.useState(
10962
+ const [innerIndex, setInnerIndex] = import_react32.default.useState(
10006
10963
  useLoop ? cloneCount + initialIdx : initialIdx
10007
10964
  );
10008
- const [isDragging, setIsDragging] = import_react31.default.useState(false);
10009
- const [dragOffset, setDragOffset] = import_react31.default.useState(0);
10010
- const [animated, setAnimated] = import_react31.default.useState(false);
10011
- const [containerWidth, setContainerWidth] = import_react31.default.useState(0);
10012
- const containerRef = import_react31.default.useRef(null);
10013
- const startXRef = import_react31.default.useRef(0);
10014
- const startTimeRef = import_react31.default.useRef(0);
10015
- const autoplayTimerRef = import_react31.default.useRef(null);
10016
- const resumeTimeoutRef = import_react31.default.useRef(null);
10017
- const [paused, setPaused] = import_react31.default.useState(false);
10018
- const initializedRef = import_react31.default.useRef(false);
10019
- import_react31.default.useEffect(() => {
10965
+ const [isDragging, setIsDragging] = import_react32.default.useState(false);
10966
+ const [dragOffset, setDragOffset] = import_react32.default.useState(0);
10967
+ const [animated, setAnimated] = import_react32.default.useState(false);
10968
+ const [containerWidth, setContainerWidth] = import_react32.default.useState(0);
10969
+ const containerRef = import_react32.default.useRef(null);
10970
+ const startXRef = import_react32.default.useRef(0);
10971
+ const startTimeRef = import_react32.default.useRef(0);
10972
+ const autoplayTimerRef = import_react32.default.useRef(null);
10973
+ const resumeTimeoutRef = import_react32.default.useRef(null);
10974
+ const [paused, setPaused] = import_react32.default.useState(false);
10975
+ const initializedRef = import_react32.default.useRef(false);
10976
+ import_react32.default.useEffect(() => {
10020
10977
  const el = containerRef.current;
10021
10978
  if (!el) return;
10022
10979
  const ro = new ResizeObserver((entries) => {
@@ -10039,7 +10996,7 @@ var Swiper = (props) => {
10039
10996
  return ((inner - cloneCount) % totalSlides + totalSlides) % totalSlides;
10040
10997
  };
10041
10998
  const realIndex = getRealIndex(innerIndex);
10042
- const moveToInner = import_react31.default.useCallback(
10999
+ const moveToInner = import_react32.default.useCallback(
10043
11000
  (idx, withAnim = true) => {
10044
11001
  if (!useLoop) {
10045
11002
  setAnimated(withAnim);
@@ -10067,7 +11024,7 @@ var Swiper = (props) => {
10067
11024
  },
10068
11025
  [useLoop, cloneCount, totalSlides]
10069
11026
  );
10070
- const handleTransitionEnd = import_react31.default.useCallback(() => {
11027
+ const handleTransitionEnd = import_react32.default.useCallback(() => {
10071
11028
  if (!useLoop) return;
10072
11029
  const real = getRealIndex(innerIndex);
10073
11030
  const canonical = cloneCount + real;
@@ -10077,7 +11034,7 @@ var Swiper = (props) => {
10077
11034
  }
10078
11035
  onChange?.(Math.min(real, maxIndex));
10079
11036
  }, [useLoop, innerIndex, cloneCount, totalSlides, maxIndex, onChange]);
10080
- const slideTo = import_react31.default.useCallback(
11037
+ const slideTo = import_react32.default.useCallback(
10081
11038
  (logicalIndex) => {
10082
11039
  if (!canSlide) return;
10083
11040
  const clamped = useLoop ? logicalIndex : Math.max(0, Math.min(logicalIndex, maxIndex));
@@ -10087,7 +11044,7 @@ var Swiper = (props) => {
10087
11044
  },
10088
11045
  [canSlide, useLoop, cloneCount, maxIndex, onChange, moveToInner]
10089
11046
  );
10090
- const slideNext = import_react31.default.useCallback(() => {
11047
+ const slideNext = import_react32.default.useCallback(() => {
10091
11048
  if (!canSlide) return;
10092
11049
  const nextInner = innerIndex + slideBy;
10093
11050
  if (useLoop) {
@@ -10096,7 +11053,7 @@ var Swiper = (props) => {
10096
11053
  moveToInner(Math.min(nextInner, maxIndex), true);
10097
11054
  }
10098
11055
  }, [canSlide, useLoop, innerIndex, slideBy, maxIndex, moveToInner]);
10099
- const slidePrev = import_react31.default.useCallback(() => {
11056
+ const slidePrev = import_react32.default.useCallback(() => {
10100
11057
  if (!canSlide) return;
10101
11058
  const prevInner = innerIndex - slideBy;
10102
11059
  if (useLoop) {
@@ -10105,7 +11062,7 @@ var Swiper = (props) => {
10105
11062
  moveToInner(Math.max(prevInner, 0), true);
10106
11063
  }
10107
11064
  }, [canSlide, useLoop, innerIndex, slideBy, moveToInner]);
10108
- import_react31.default.useEffect(() => {
11065
+ import_react32.default.useEffect(() => {
10109
11066
  if (indexProp === void 0) return;
10110
11067
  const clamped = Math.max(0, Math.min(indexProp, maxIndex));
10111
11068
  const target = useLoop ? cloneCount + clamped : clamped;
@@ -10113,12 +11070,12 @@ var Swiper = (props) => {
10113
11070
  moveToInner(target, true);
10114
11071
  }
10115
11072
  }, [indexProp]);
10116
- import_react31.default.useImperativeHandle(swiperRef, () => ({
11073
+ import_react32.default.useImperativeHandle(swiperRef, () => ({
10117
11074
  slidePrev,
10118
11075
  slideNext,
10119
11076
  slideTo
10120
11077
  }));
10121
- import_react31.default.useEffect(() => {
11078
+ import_react32.default.useEffect(() => {
10122
11079
  if (!auto || !canSlide || paused) return;
10123
11080
  autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
10124
11081
  return () => {
@@ -10144,7 +11101,7 @@ var Swiper = (props) => {
10144
11101
  resumeTimeoutRef.current = null;
10145
11102
  }, pauseOnInteraction);
10146
11103
  };
10147
- import_react31.default.useEffect(() => {
11104
+ import_react32.default.useEffect(() => {
10148
11105
  return () => {
10149
11106
  if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
10150
11107
  };
@@ -10162,7 +11119,7 @@ var Swiper = (props) => {
10162
11119
  startXRef.current = getClientX(e);
10163
11120
  startTimeRef.current = Date.now();
10164
11121
  };
10165
- import_react31.default.useEffect(() => {
11122
+ import_react32.default.useEffect(() => {
10166
11123
  if (!isDragging) return;
10167
11124
  const handleMove = (e) => {
10168
11125
  setDragOffset(getClientX(e) - startXRef.current);
@@ -10201,8 +11158,8 @@ var Swiper = (props) => {
10201
11158
  }, [isDragging, dragOffset, innerIndex, useLoop, maxIndex, slideStep, moveToInner]);
10202
11159
  const slideWidthPercent = 100 / viewItemCount;
10203
11160
  const gapAdjust = spaceBetween * (viewItemCount - 1) / viewItemCount;
10204
- const slideElements = import_react31.default.useMemo(
10205
- () => extendedItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
11161
+ const slideElements = import_react32.default.useMemo(
11162
+ () => extendedItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
10206
11163
  "div",
10207
11164
  {
10208
11165
  className: "lib-xplat-swiper__slide",
@@ -10221,15 +11178,15 @@ var Swiper = (props) => {
10221
11178
  Math.floor(realIndex / slideBy),
10222
11179
  totalSteps - 1
10223
11180
  );
10224
- return /* @__PURE__ */ (0, import_jsx_runtime341.jsxs)("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
10225
- /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
11181
+ return /* @__PURE__ */ (0, import_jsx_runtime342.jsxs)("div", { className: "lib-xplat-swiper", ref: containerRef, children: [
11182
+ /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
10226
11183
  "div",
10227
11184
  {
10228
11185
  className: "lib-xplat-swiper__viewport",
10229
11186
  onMouseDown: handleDragStart,
10230
11187
  onTouchStart: handleDragStart,
10231
11188
  onDragStart: (e) => e.preventDefault(),
10232
- children: /* @__PURE__ */ (0, import_jsx_runtime341.jsx)(
11189
+ children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
10233
11190
  "div",
10234
11191
  {
10235
11192
  className: clsx_default(
@@ -10247,7 +11204,7 @@ var Swiper = (props) => {
10247
11204
  )
10248
11205
  }
10249
11206
  ),
10250
- 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)(
11207
+ 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)(
10251
11208
  "span",
10252
11209
  {
10253
11210
  className: "lib-xplat-swiper__progress-fill",
@@ -10257,7 +11214,7 @@ var Swiper = (props) => {
10257
11214
  }
10258
11215
  }
10259
11216
  ) }) }),
10260
- 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)(
11217
+ 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)(
10261
11218
  "button",
10262
11219
  {
10263
11220
  className: clsx_default(
@@ -10275,8 +11232,8 @@ Swiper.displayName = "Swiper";
10275
11232
  var Swiper_default = Swiper;
10276
11233
 
10277
11234
  // src/components/Switch/Switch.tsx
10278
- var import_react32 = __toESM(require("react"), 1);
10279
- var import_jsx_runtime342 = require("react/jsx-runtime");
11235
+ var import_react33 = __toESM(require("react"), 1);
11236
+ var import_jsx_runtime343 = require("react/jsx-runtime");
10280
11237
  var KNOB_TRANSITION_MS = 250;
10281
11238
  var Switch = (props) => {
10282
11239
  const {
@@ -10285,12 +11242,15 @@ var Switch = (props) => {
10285
11242
  type = "primary",
10286
11243
  disabled,
10287
11244
  onChange,
10288
- validations
11245
+ validations,
11246
+ label,
11247
+ required
10289
11248
  } = props;
11249
+ const fieldId = import_react33.default.useId();
10290
11250
  const statusType = normalizeStatus(type);
10291
- const [isAnimating, setIsAnimating] = import_react32.default.useState(false);
10292
- const timeoutRef = import_react32.default.useRef(null);
10293
- import_react32.default.useEffect(() => {
11251
+ const [isAnimating, setIsAnimating] = import_react33.default.useState(false);
11252
+ const timeoutRef = import_react33.default.useRef(null);
11253
+ import_react33.default.useEffect(() => {
10294
11254
  return () => {
10295
11255
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
10296
11256
  };
@@ -10305,9 +11265,10 @@ var Switch = (props) => {
10305
11265
  timeoutRef.current = null;
10306
11266
  }, KNOB_TRANSITION_MS);
10307
11267
  };
10308
- const control = /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(
11268
+ const control = /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(
10309
11269
  "div",
10310
11270
  {
11271
+ id: fieldId,
10311
11272
  className: clsx_default(
10312
11273
  "lib-xplat-switch",
10313
11274
  size,
@@ -10319,26 +11280,26 @@ var Switch = (props) => {
10319
11280
  onClick: handleClick,
10320
11281
  "aria-invalid": hasError(validations) || void 0,
10321
11282
  "aria-disabled": disabled || isAnimating,
10322
- children: /* @__PURE__ */ (0, import_jsx_runtime342.jsx)("div", { className: clsx_default("knob", value ? "checked" : void 0) })
11283
+ children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("div", { className: clsx_default("knob", value ? "checked" : void 0) })
10323
11284
  }
10324
11285
  );
10325
- return /* @__PURE__ */ (0, import_jsx_runtime342.jsx)(Field, { validations, children: control });
11286
+ return /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
10326
11287
  };
10327
11288
  Switch.displayName = "Switch";
10328
11289
  var Switch_default = Switch;
10329
11290
 
10330
11291
  // src/components/Table/TableContext.tsx
10331
- var import_react33 = __toESM(require("react"), 1);
10332
- var TableContext = import_react33.default.createContext(null);
11292
+ var import_react34 = __toESM(require("react"), 1);
11293
+ var TableContext = import_react34.default.createContext(null);
10333
11294
  var useTableContext = () => {
10334
- const ctx = import_react33.default.useContext(TableContext);
11295
+ const ctx = import_react34.default.useContext(TableContext);
10335
11296
  if (!ctx) throw new Error("Table components must be used inside <Table>");
10336
11297
  return ctx;
10337
11298
  };
10338
11299
  var TableContext_default = TableContext;
10339
11300
 
10340
11301
  // src/components/Table/Table.tsx
10341
- var import_jsx_runtime343 = require("react/jsx-runtime");
11302
+ var import_jsx_runtime344 = require("react/jsx-runtime");
10342
11303
  var Table = (props) => {
10343
11304
  const {
10344
11305
  children,
@@ -10348,7 +11309,7 @@ var Table = (props) => {
10348
11309
  headerSticky = false,
10349
11310
  stickyShadow = true
10350
11311
  } = props;
10351
- return /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)(
11312
+ return /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("div", { className: `lib-xplat-table-wrapper ${size}`, children: /* @__PURE__ */ (0, import_jsx_runtime344.jsx)(
10352
11313
  TableContext_default.Provider,
10353
11314
  {
10354
11315
  value: {
@@ -10357,7 +11318,7 @@ var Table = (props) => {
10357
11318
  headerSticky,
10358
11319
  stickyShadow
10359
11320
  },
10360
- children: /* @__PURE__ */ (0, import_jsx_runtime343.jsx)("table", { className: "lib-xplat-table", children })
11321
+ children: /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("table", { className: "lib-xplat-table", children })
10361
11322
  }
10362
11323
  ) });
10363
11324
  };
@@ -10365,41 +11326,41 @@ Table.displayName = "Table";
10365
11326
  var Table_default = Table;
10366
11327
 
10367
11328
  // src/components/Table/TableBody.tsx
10368
- var import_jsx_runtime344 = require("react/jsx-runtime");
11329
+ var import_jsx_runtime345 = require("react/jsx-runtime");
10369
11330
  var TableBody = (props) => {
10370
11331
  const { children } = props;
10371
- return /* @__PURE__ */ (0, import_jsx_runtime344.jsx)("tbody", { children });
11332
+ return /* @__PURE__ */ (0, import_jsx_runtime345.jsx)("tbody", { children });
10372
11333
  };
10373
11334
  TableBody.displayName = "TableBody";
10374
11335
  var TableBody_default = TableBody;
10375
11336
 
10376
11337
  // src/components/Table/TableCell.tsx
10377
- var import_react36 = __toESM(require("react"), 1);
11338
+ var import_react37 = __toESM(require("react"), 1);
10378
11339
 
10379
11340
  // src/components/Table/TableHeadContext.tsx
10380
- var import_react34 = __toESM(require("react"), 1);
10381
- var TableHeadContext = import_react34.default.createContext(
11341
+ var import_react35 = __toESM(require("react"), 1);
11342
+ var TableHeadContext = import_react35.default.createContext(
10382
11343
  null
10383
11344
  );
10384
11345
  var useTableHeadContext = () => {
10385
- const ctx = import_react34.default.useContext(TableHeadContext);
11346
+ const ctx = import_react35.default.useContext(TableHeadContext);
10386
11347
  return ctx;
10387
11348
  };
10388
11349
  var TableHeadContext_default = TableHeadContext;
10389
11350
 
10390
11351
  // src/components/Table/TableRowContext.tsx
10391
- var import_react35 = __toESM(require("react"), 1);
10392
- var TableRowContext = import_react35.default.createContext(null);
11352
+ var import_react36 = __toESM(require("react"), 1);
11353
+ var TableRowContext = import_react36.default.createContext(null);
10393
11354
  var useTableRowContext = () => {
10394
- const ctx = import_react35.default.useContext(TableRowContext);
11355
+ const ctx = import_react36.default.useContext(TableRowContext);
10395
11356
  if (!ctx) throw new Error("Table components must be used inside <Table>");
10396
11357
  return ctx;
10397
11358
  };
10398
11359
  var TableRowContext_default = TableRowContext;
10399
11360
 
10400
11361
  // src/components/Table/TableCell.tsx
10401
- var import_jsx_runtime345 = require("react/jsx-runtime");
10402
- var TableCell = import_react36.default.memo((props) => {
11362
+ var import_jsx_runtime346 = require("react/jsx-runtime");
11363
+ var TableCell = import_react37.default.memo((props) => {
10403
11364
  const {
10404
11365
  children,
10405
11366
  align = "center",
@@ -10413,9 +11374,9 @@ var TableCell = import_react36.default.memo((props) => {
10413
11374
  const { registerStickyCell, stickyCells } = useTableRowContext();
10414
11375
  const { stickyShadow } = useTableContext();
10415
11376
  const headContext = useTableHeadContext();
10416
- const [left, setLeft] = import_react36.default.useState(0);
10417
- const cellRef = import_react36.default.useRef(null);
10418
- const calculateLeft = import_react36.default.useCallback(() => {
11377
+ const [left, setLeft] = import_react37.default.useState(0);
11378
+ const cellRef = import_react37.default.useRef(null);
11379
+ const calculateLeft = import_react37.default.useCallback(() => {
10419
11380
  if (!cellRef.current) return 0;
10420
11381
  let totalLeft = 0;
10421
11382
  for (const ref of stickyCells) {
@@ -10424,7 +11385,7 @@ var TableCell = import_react36.default.memo((props) => {
10424
11385
  }
10425
11386
  return totalLeft;
10426
11387
  }, [stickyCells]);
10427
- import_react36.default.useEffect(() => {
11388
+ import_react37.default.useEffect(() => {
10428
11389
  if (!isSticky || !cellRef.current) return;
10429
11390
  registerStickyCell(cellRef);
10430
11391
  setLeft(calculateLeft());
@@ -10443,7 +11404,7 @@ var TableCell = import_react36.default.memo((props) => {
10443
11404
  const CellTag = isHeaderCell ? "th" : "td";
10444
11405
  const isLastSticky = isSticky && stickyCells[stickyCells.length - 1] === cellRef;
10445
11406
  const enableHover = headContext && headContext.cellHover;
10446
- return /* @__PURE__ */ (0, import_jsx_runtime345.jsx)(
11407
+ return /* @__PURE__ */ (0, import_jsx_runtime346.jsx)(
10447
11408
  CellTag,
10448
11409
  {
10449
11410
  ref: cellRef,
@@ -10470,21 +11431,21 @@ TableCell.displayName = "TableCell";
10470
11431
  var TableCell_default = TableCell;
10471
11432
 
10472
11433
  // src/components/Table/TableHead.tsx
10473
- var import_jsx_runtime346 = require("react/jsx-runtime");
11434
+ var import_jsx_runtime347 = require("react/jsx-runtime");
10474
11435
  var TableHead = ({
10475
11436
  children,
10476
11437
  cellHover = false
10477
11438
  }) => {
10478
11439
  const { headerSticky } = useTableContext();
10479
- 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 }) });
11440
+ 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 }) });
10480
11441
  };
10481
11442
  TableHead.displayName = "TableHead";
10482
11443
  var TableHead_default = TableHead;
10483
11444
 
10484
11445
  // src/components/Table/TableRow.tsx
10485
- var import_react37 = __toESM(require("react"), 1);
10486
- var import_jsx_runtime347 = require("react/jsx-runtime");
10487
- var TableRow = import_react37.default.memo((props) => {
11446
+ var import_react38 = __toESM(require("react"), 1);
11447
+ var import_jsx_runtime348 = require("react/jsx-runtime");
11448
+ var TableRow = import_react38.default.memo((props) => {
10488
11449
  const {
10489
11450
  children,
10490
11451
  type = "secondary",
@@ -10493,14 +11454,14 @@ var TableRow = import_react37.default.memo((props) => {
10493
11454
  onClick
10494
11455
  } = props;
10495
11456
  const { rowBorderUse } = useTableContext();
10496
- const [stickyCells, setStickyCells] = import_react37.default.useState([]);
11457
+ const [stickyCells, setStickyCells] = import_react38.default.useState([]);
10497
11458
  const registerStickyCell = (ref) => {
10498
11459
  setStickyCells((prev) => {
10499
11460
  if (prev.includes(ref)) return prev;
10500
11461
  return [...prev, ref];
10501
11462
  });
10502
11463
  };
10503
- return /* @__PURE__ */ (0, import_jsx_runtime347.jsx)(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ (0, import_jsx_runtime347.jsx)(
11464
+ return /* @__PURE__ */ (0, import_jsx_runtime348.jsx)(TableRowContext_default.Provider, { value: { stickyCells, registerStickyCell }, children: /* @__PURE__ */ (0, import_jsx_runtime348.jsx)(
10504
11465
  "tr",
10505
11466
  {
10506
11467
  className: clsx_default(
@@ -10524,7 +11485,7 @@ TableRow.displayName = "TableRow";
10524
11485
  var TableRow_default = TableRow;
10525
11486
 
10526
11487
  // src/components/Tag/Tag.tsx
10527
- var import_jsx_runtime348 = require("react/jsx-runtime");
11488
+ var import_jsx_runtime349 = require("react/jsx-runtime");
10528
11489
  var Tag = (props) => {
10529
11490
  const {
10530
11491
  children,
@@ -10536,7 +11497,7 @@ var Tag = (props) => {
10536
11497
  colorIndex
10537
11498
  } = props;
10538
11499
  const shape = variant ?? type ?? "neutral";
10539
- return /* @__PURE__ */ (0, import_jsx_runtime348.jsxs)(
11500
+ return /* @__PURE__ */ (0, import_jsx_runtime349.jsxs)(
10540
11501
  "span",
10541
11502
  {
10542
11503
  className: clsx_default(
@@ -10547,8 +11508,8 @@ var Tag = (props) => {
10547
11508
  disabled && "disabled"
10548
11509
  ),
10549
11510
  children: [
10550
- /* @__PURE__ */ (0, import_jsx_runtime348.jsx)("span", { className: "tag-label", children }),
10551
- 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, {}) })
11511
+ /* @__PURE__ */ (0, import_jsx_runtime349.jsx)("span", { className: "tag-label", children }),
11512
+ 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, {}) })
10552
11513
  ]
10553
11514
  }
10554
11515
  );
@@ -10557,24 +11518,29 @@ Tag.displayName = "Tag";
10557
11518
  var Tag_default = Tag;
10558
11519
 
10559
11520
  // src/components/TextArea/TextArea.tsx
10560
- var import_react38 = __toESM(require("react"), 1);
10561
- var import_jsx_runtime349 = require("react/jsx-runtime");
10562
- var TextArea = import_react38.default.forwardRef(
11521
+ var import_react39 = __toESM(require("react"), 1);
11522
+ var import_jsx_runtime350 = require("react/jsx-runtime");
11523
+ var TextArea = import_react39.default.forwardRef(
10563
11524
  (props, ref) => {
10564
11525
  const {
10565
11526
  className,
10566
11527
  size = "md",
10567
11528
  validations,
11529
+ label,
11530
+ required,
10568
11531
  resize = "vertical",
10569
11532
  rows = 3,
10570
11533
  disabled,
10571
11534
  ...textareaProps
10572
11535
  } = props;
10573
- const control = /* @__PURE__ */ (0, import_jsx_runtime349.jsx)("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ (0, import_jsx_runtime349.jsx)(
11536
+ const autoId = import_react39.default.useId();
11537
+ const fieldId = props.id ?? autoId;
11538
+ const control = /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(
10574
11539
  "textarea",
10575
11540
  {
10576
11541
  ...textareaProps,
10577
11542
  ref,
11543
+ id: fieldId,
10578
11544
  rows,
10579
11545
  disabled,
10580
11546
  "aria-invalid": hasError(validations) || void 0,
@@ -10582,32 +11548,166 @@ var TextArea = import_react38.default.forwardRef(
10582
11548
  style: { resize }
10583
11549
  }
10584
11550
  ) });
10585
- return /* @__PURE__ */ (0, import_jsx_runtime349.jsx)(Field, { validations, children: control });
11551
+ return /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
10586
11552
  }
10587
11553
  );
10588
11554
  TextArea.displayName = "TextArea";
10589
11555
  var TextArea_default = TextArea;
10590
11556
 
11557
+ // src/components/TimePicker/TimePicker.tsx
11558
+ var import_react40 = __toESM(require("react"), 1);
11559
+ var import_jsx_runtime351 = require("react/jsx-runtime");
11560
+ var ANIMATION_DURATION_MS4 = 200;
11561
+ var toMinutes = (hhmm) => {
11562
+ if (!hhmm) return null;
11563
+ const m = /^(\d{1,2}):(\d{2})$/.exec(hhmm.trim());
11564
+ if (!m) return null;
11565
+ const h = Number(m[1]);
11566
+ const min = Number(m[2]);
11567
+ if (h > 23 || min > 59) return null;
11568
+ return h * 60 + min;
11569
+ };
11570
+ var toHHmm = (minutes) => `${String(Math.floor(minutes / 60)).padStart(2, "0")}:${String(minutes % 60).padStart(2, "0")}`;
11571
+ var display = (hhmm, use12Hours) => {
11572
+ if (!use12Hours) return hhmm;
11573
+ const total = toMinutes(hhmm);
11574
+ if (total === null) return hhmm;
11575
+ const h24 = Math.floor(total / 60);
11576
+ const mm = String(total % 60).padStart(2, "0");
11577
+ const period = h24 < 12 ? "\uC624\uC804" : "\uC624\uD6C4";
11578
+ const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
11579
+ return `${period} ${h12}:${mm}`;
11580
+ };
11581
+ var TimePicker = (props) => {
11582
+ const {
11583
+ value,
11584
+ onChange,
11585
+ step = 30,
11586
+ min,
11587
+ max,
11588
+ use12Hours = false,
11589
+ placeholder = "\uC2DC\uAC01 \uC120\uD0DD",
11590
+ disabled = false,
11591
+ size = "md",
11592
+ label,
11593
+ required,
11594
+ validations
11595
+ } = props;
11596
+ const fieldId = import_react40.default.useId();
11597
+ const triggerRef = import_react40.default.useRef(null);
11598
+ const contentRef = import_react40.default.useRef(null);
11599
+ const [isOpen, setOpen] = import_react40.default.useState(false);
11600
+ const [mounted, setMounted] = import_react40.default.useState(false);
11601
+ const [visible, setVisible] = import_react40.default.useState(false);
11602
+ const options = import_react40.default.useMemo(() => {
11603
+ const safeStep = step > 0 && step <= 720 ? step : 30;
11604
+ const lo = toMinutes(min) ?? 0;
11605
+ const hi = toMinutes(max) ?? 24 * 60 - 1;
11606
+ const out = [];
11607
+ for (let t = lo; t <= hi; t += safeStep) out.push(toHHmm(t));
11608
+ return out;
11609
+ }, [step, min, max]);
11610
+ const close = import_react40.default.useCallback(() => setOpen(false), []);
11611
+ useClickOutside_default([triggerRef, contentRef], close, isOpen);
11612
+ useEscapeKey(isOpen, () => {
11613
+ close();
11614
+ triggerRef.current?.focus({ preventScroll: true });
11615
+ });
11616
+ const position = useAutoPosition_default(triggerRef, contentRef, mounted);
11617
+ import_react40.default.useEffect(() => {
11618
+ if (disabled && isOpen) setOpen(false);
11619
+ }, [disabled, isOpen]);
11620
+ import_react40.default.useEffect(() => {
11621
+ if (isOpen) {
11622
+ setMounted(true);
11623
+ const t2 = setTimeout(() => setVisible(true), 1);
11624
+ return () => clearTimeout(t2);
11625
+ }
11626
+ setVisible(false);
11627
+ const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS4);
11628
+ return () => clearTimeout(t);
11629
+ }, [isOpen]);
11630
+ const pick = (hhmm) => {
11631
+ onChange?.(hhmm);
11632
+ close();
11633
+ triggerRef.current?.focus({ preventScroll: true });
11634
+ };
11635
+ const control = /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)("div", { className: clsx_default("lib-xplat-time-picker", size, disabled && "disabled"), children: [
11636
+ /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
11637
+ "div",
11638
+ {
11639
+ id: fieldId,
11640
+ ref: triggerRef,
11641
+ className: clsx_default("time-picker-trigger", hasError(validations) && "error"),
11642
+ role: "combobox",
11643
+ "aria-expanded": isOpen,
11644
+ "aria-haspopup": "listbox",
11645
+ "aria-invalid": hasError(validations) || void 0,
11646
+ tabIndex: disabled ? void 0 : 0,
11647
+ onClick: () => !disabled && setOpen((p) => !p),
11648
+ onKeyDown: (e) => {
11649
+ if (disabled) return;
11650
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
11651
+ e.preventDefault();
11652
+ setOpen(true);
11653
+ }
11654
+ },
11655
+ children: /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("span", { className: clsx_default("time-picker-value", !value && "placeholder"), children: value ? display(value, use12Hours) : placeholder })
11656
+ }
11657
+ ),
11658
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
11659
+ "div",
11660
+ {
11661
+ ref: contentRef,
11662
+ className: clsx_default("lib-xplat-time-picker-content", size, visible ? "enter" : "exit", position.repositioning && "repositioning"),
11663
+ style: { ...position.position, width: triggerRef.current?.offsetWidth },
11664
+ role: "listbox",
11665
+ children: options.map((t) => /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(
11666
+ "div",
11667
+ {
11668
+ className: clsx_default("time-picker-item", t === value && "is-selected"),
11669
+ role: "option",
11670
+ "aria-selected": t === value,
11671
+ tabIndex: 0,
11672
+ onClick: () => pick(t),
11673
+ onKeyDown: (e) => {
11674
+ if (e.key === "Enter" || e.key === " ") {
11675
+ e.preventDefault();
11676
+ pick(t);
11677
+ }
11678
+ },
11679
+ children: display(t, use12Hours)
11680
+ },
11681
+ t
11682
+ ))
11683
+ }
11684
+ ) })
11685
+ ] });
11686
+ return /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
11687
+ };
11688
+ TimePicker.displayName = "TimePicker";
11689
+ var TimePicker_default = TimePicker;
11690
+
10591
11691
  // src/components/Toast/Toast.tsx
10592
- var import_react39 = __toESM(require("react"), 1);
11692
+ var import_react41 = __toESM(require("react"), 1);
10593
11693
  var import_react_dom4 = require("react-dom");
10594
- var import_jsx_runtime350 = require("react/jsx-runtime");
10595
- var ToastContext = import_react39.default.createContext(null);
11694
+ var import_jsx_runtime352 = require("react/jsx-runtime");
11695
+ var ToastContext = import_react41.default.createContext(null);
10596
11696
  var useToast = () => {
10597
- const ctx = import_react39.default.useContext(ToastContext);
11697
+ const ctx = import_react41.default.useContext(ToastContext);
10598
11698
  if (!ctx) throw new Error("useToast must be used within ToastProvider");
10599
11699
  return ctx;
10600
11700
  };
10601
11701
  var EXIT_DURATION = 300;
10602
11702
  var ToastItemComponent = ({ item, isExiting, onClose }) => {
10603
- const ref = import_react39.default.useRef(null);
10604
- const [height, setHeight] = import_react39.default.useState(void 0);
10605
- import_react39.default.useEffect(() => {
11703
+ const ref = import_react41.default.useRef(null);
11704
+ const [height, setHeight] = import_react41.default.useState(void 0);
11705
+ import_react41.default.useEffect(() => {
10606
11706
  if (ref.current && !isExiting) {
10607
11707
  setHeight(ref.current.offsetHeight);
10608
11708
  }
10609
11709
  }, [isExiting]);
10610
- return /* @__PURE__ */ (0, import_jsx_runtime350.jsx)(
11710
+ return /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
10611
11711
  "div",
10612
11712
  {
10613
11713
  className: clsx_default("lib-xplat-toast-wrapper", { exit: isExiting }),
@@ -10615,15 +11715,15 @@ var ToastItemComponent = ({ item, isExiting, onClose }) => {
10615
11715
  maxHeight: isExiting ? 0 : height ?? "none",
10616
11716
  marginBottom: isExiting ? 0 : void 0
10617
11717
  },
10618
- children: /* @__PURE__ */ (0, import_jsx_runtime350.jsxs)(
11718
+ children: /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
10619
11719
  "div",
10620
11720
  {
10621
11721
  ref,
10622
11722
  className: clsx_default("lib-xplat-toast", item.type, { exit: isExiting }),
10623
11723
  role: "alert",
10624
11724
  children: [
10625
- /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("span", { className: "message", children: item.message }),
10626
- /* @__PURE__ */ (0, import_jsx_runtime350.jsx)("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
11725
+ /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("span", { className: "message", children: item.message }),
11726
+ /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("button", { className: "close-btn", onClick: onClose, "aria-label": "\uB2EB\uAE30", children: "\xD7" })
10627
11727
  ]
10628
11728
  }
10629
11729
  )
@@ -10634,13 +11734,13 @@ var ToastProvider = ({
10634
11734
  children,
10635
11735
  position = "top-right"
10636
11736
  }) => {
10637
- const [toasts, setToasts] = import_react39.default.useState([]);
10638
- const [removing, setRemoving] = import_react39.default.useState(/* @__PURE__ */ new Set());
10639
- const [mounted, setMounted] = import_react39.default.useState(false);
10640
- import_react39.default.useEffect(() => {
11737
+ const [toasts, setToasts] = import_react41.default.useState([]);
11738
+ const [removing, setRemoving] = import_react41.default.useState(/* @__PURE__ */ new Set());
11739
+ const [mounted, setMounted] = import_react41.default.useState(false);
11740
+ import_react41.default.useEffect(() => {
10641
11741
  setMounted(true);
10642
11742
  }, []);
10643
- const remove = import_react39.default.useCallback((id) => {
11743
+ const remove = import_react41.default.useCallback((id) => {
10644
11744
  setRemoving((prev) => new Set(prev).add(id));
10645
11745
  setTimeout(() => {
10646
11746
  setToasts((prev) => prev.filter((t) => t.id !== id));
@@ -10651,7 +11751,7 @@ var ToastProvider = ({
10651
11751
  });
10652
11752
  }, EXIT_DURATION);
10653
11753
  }, []);
10654
- const toast = import_react39.default.useCallback(
11754
+ const toast = import_react41.default.useCallback(
10655
11755
  (type, message, duration = 3e3) => {
10656
11756
  const id = `${Date.now()}-${Math.random()}`;
10657
11757
  setToasts((prev) => [...prev, { id, type, message }]);
@@ -10661,10 +11761,10 @@ var ToastProvider = ({
10661
11761
  },
10662
11762
  [remove]
10663
11763
  );
10664
- return /* @__PURE__ */ (0, import_jsx_runtime350.jsxs)(ToastContext.Provider, { value: { toast }, children: [
11764
+ return /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(ToastContext.Provider, { value: { toast }, children: [
10665
11765
  children,
10666
11766
  mounted && (0, import_react_dom4.createPortal)(
10667
- /* @__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)(
11767
+ /* @__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)(
10668
11768
  ToastItemComponent,
10669
11769
  {
10670
11770
  item: t,
@@ -10680,8 +11780,8 @@ var ToastProvider = ({
10680
11780
  ToastProvider.displayName = "ToastProvider";
10681
11781
 
10682
11782
  // src/components/Tooltip/Tooltip.tsx
10683
- var import_react40 = __toESM(require("react"), 1);
10684
- var import_jsx_runtime351 = require("react/jsx-runtime");
11783
+ var import_react42 = __toESM(require("react"), 1);
11784
+ var import_jsx_runtime353 = require("react/jsx-runtime");
10685
11785
  var OFFSET = 12;
10686
11786
  var GAP = 8;
10687
11787
  var SHOW_DELAY = 300;
@@ -10696,14 +11796,14 @@ var Tooltip = (props) => {
10696
11796
  disabled = false
10697
11797
  } = props;
10698
11798
  const shape = variant ?? type ?? "dark";
10699
- const triggerRef = import_react40.default.useRef(null);
10700
- const tooltipRef = import_react40.default.useRef(null);
10701
- const [visible, setVisible] = import_react40.default.useState(false);
10702
- const [pos, setPos] = import_react40.default.useState({ left: 0, top: 0 });
10703
- const delayTimer = import_react40.default.useRef(0);
10704
- const rafRef = import_react40.default.useRef(0);
10705
- const cursor = import_react40.default.useRef(null);
10706
- const clamp = import_react40.default.useCallback((left, top, w, h) => {
11799
+ const triggerRef = import_react42.default.useRef(null);
11800
+ const tooltipRef = import_react42.default.useRef(null);
11801
+ const [visible, setVisible] = import_react42.default.useState(false);
11802
+ const [pos, setPos] = import_react42.default.useState({ left: 0, top: 0 });
11803
+ const delayTimer = import_react42.default.useRef(0);
11804
+ const rafRef = import_react42.default.useRef(0);
11805
+ const cursor = import_react42.default.useRef(null);
11806
+ const clamp = import_react42.default.useCallback((left, top, w, h) => {
10707
11807
  const vw = window.innerWidth;
10708
11808
  const vh = window.innerHeight;
10709
11809
  return {
@@ -10711,7 +11811,7 @@ var Tooltip = (props) => {
10711
11811
  top: Math.min(Math.max(GAP, top), Math.max(GAP, vh - h - GAP))
10712
11812
  };
10713
11813
  }, []);
10714
- const placeAtCursor = import_react40.default.useCallback((x, y) => {
11814
+ const placeAtCursor = import_react42.default.useCallback((x, y) => {
10715
11815
  const el = tooltipRef.current;
10716
11816
  if (!el) return;
10717
11817
  const w = el.offsetWidth;
@@ -10722,7 +11822,7 @@ var Tooltip = (props) => {
10722
11822
  if (top < GAP) top = y + OFFSET;
10723
11823
  setPos(clamp(left, top, w, h));
10724
11824
  }, [clamp]);
10725
- const placeAtTrigger = import_react40.default.useCallback(() => {
11825
+ const placeAtTrigger = import_react42.default.useCallback(() => {
10726
11826
  const el = tooltipRef.current;
10727
11827
  const trigger = triggerRef.current;
10728
11828
  if (!el || !trigger) return;
@@ -10734,19 +11834,19 @@ var Tooltip = (props) => {
10734
11834
  const left = rect.left + rect.width / 2 - w / 2;
10735
11835
  setPos(clamp(left, top, w, h));
10736
11836
  }, [clamp]);
10737
- const reposition = import_react40.default.useCallback(() => {
11837
+ const reposition = import_react42.default.useCallback(() => {
10738
11838
  if (follow && cursor.current) {
10739
11839
  placeAtCursor(cursor.current.x, cursor.current.y);
10740
11840
  } else {
10741
11841
  placeAtTrigger();
10742
11842
  }
10743
11843
  }, [follow, placeAtCursor, placeAtTrigger]);
10744
- const handleMouseEnter = import_react40.default.useCallback((e) => {
11844
+ const handleMouseEnter = import_react42.default.useCallback((e) => {
10745
11845
  if (disabled) return;
10746
11846
  cursor.current = { x: e.clientX, y: e.clientY };
10747
11847
  delayTimer.current = window.setTimeout(() => setVisible(true), SHOW_DELAY);
10748
11848
  }, [disabled]);
10749
- const handleMouseMove = import_react40.default.useCallback((e) => {
11849
+ const handleMouseMove = import_react42.default.useCallback((e) => {
10750
11850
  cursor.current = { x: e.clientX, y: e.clientY };
10751
11851
  if (!visible || !follow) return;
10752
11852
  cancelAnimationFrame(rafRef.current);
@@ -10754,25 +11854,25 @@ var Tooltip = (props) => {
10754
11854
  if (cursor.current) placeAtCursor(cursor.current.x, cursor.current.y);
10755
11855
  });
10756
11856
  }, [visible, follow, placeAtCursor]);
10757
- const close = import_react40.default.useCallback(() => {
11857
+ const close = import_react42.default.useCallback(() => {
10758
11858
  window.clearTimeout(delayTimer.current);
10759
11859
  cancelAnimationFrame(rafRef.current);
10760
11860
  setVisible(false);
10761
11861
  }, []);
10762
- const handleMouseLeave = import_react40.default.useCallback(() => {
11862
+ const handleMouseLeave = import_react42.default.useCallback(() => {
10763
11863
  cursor.current = null;
10764
11864
  close();
10765
11865
  }, [close]);
10766
- const handleFocus = import_react40.default.useCallback(() => {
11866
+ const handleFocus = import_react42.default.useCallback(() => {
10767
11867
  if (disabled) return;
10768
11868
  cursor.current = null;
10769
11869
  setVisible(true);
10770
11870
  }, [disabled]);
10771
- import_react40.default.useLayoutEffect(() => {
11871
+ import_react42.default.useLayoutEffect(() => {
10772
11872
  if (!visible) return;
10773
11873
  reposition();
10774
11874
  }, [visible, reposition]);
10775
- import_react40.default.useEffect(() => {
11875
+ import_react42.default.useEffect(() => {
10776
11876
  if (!visible || follow) return;
10777
11877
  const onChange = () => placeAtTrigger();
10778
11878
  window.addEventListener("scroll", onChange, true);
@@ -10782,12 +11882,12 @@ var Tooltip = (props) => {
10782
11882
  window.removeEventListener("resize", onChange);
10783
11883
  };
10784
11884
  }, [visible, follow, placeAtTrigger]);
10785
- import_react40.default.useEffect(() => () => {
11885
+ import_react42.default.useEffect(() => () => {
10786
11886
  window.clearTimeout(delayTimer.current);
10787
11887
  cancelAnimationFrame(rafRef.current);
10788
11888
  }, []);
10789
- if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(import_jsx_runtime351.Fragment, { children });
10790
- return /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)(
11889
+ if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime353.jsx)(import_jsx_runtime353.Fragment, { children });
11890
+ return /* @__PURE__ */ (0, import_jsx_runtime353.jsxs)(
10791
11891
  "div",
10792
11892
  {
10793
11893
  ref: triggerRef,
@@ -10800,7 +11900,7 @@ var Tooltip = (props) => {
10800
11900
  onBlur: close,
10801
11901
  children: [
10802
11902
  children,
10803
- visible && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime351.jsxs)(
11903
+ visible && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime353.jsxs)(
10804
11904
  "div",
10805
11905
  {
10806
11906
  ref: tooltipRef,
@@ -10808,8 +11908,8 @@ var Tooltip = (props) => {
10808
11908
  className: clsx_default("lib-xplat-tooltip", shape),
10809
11909
  style: { position: "fixed", left: pos.left, top: pos.top },
10810
11910
  children: [
10811
- title && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("div", { className: "tooltip-title", children: title }),
10812
- description && /* @__PURE__ */ (0, import_jsx_runtime351.jsx)("div", { className: "tooltip-desc", children: description })
11911
+ title && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)("div", { className: "tooltip-title", children: title }),
11912
+ description && /* @__PURE__ */ (0, import_jsx_runtime353.jsx)("div", { className: "tooltip-desc", children: description })
10813
11913
  ]
10814
11914
  }
10815
11915
  ) })
@@ -10821,11 +11921,11 @@ Tooltip.displayName = "Tooltip";
10821
11921
  var Tooltip_default = Tooltip;
10822
11922
 
10823
11923
  // src/components/Video/Video.tsx
10824
- var import_react41 = __toESM(require("react"), 1);
10825
- var import_jsx_runtime352 = require("react/jsx-runtime");
10826
- 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: [
10827
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
10828
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
11924
+ var import_react43 = __toESM(require("react"), 1);
11925
+ var import_jsx_runtime354 = require("react/jsx-runtime");
11926
+ 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: [
11927
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
11928
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("rect", { x: "12", y: "11", width: "7", height: "6", rx: "1", fill: "currentColor" })
10829
11929
  ] });
10830
11930
  var formatTime = (sec) => {
10831
11931
  if (!Number.isFinite(sec) || sec < 0) return "0:00";
@@ -10833,7 +11933,7 @@ var formatTime = (sec) => {
10833
11933
  const s = Math.floor(sec % 60);
10834
11934
  return `${m}:${s.toString().padStart(2, "0")}`;
10835
11935
  };
10836
- var Video = import_react41.default.forwardRef((props, ref) => {
11936
+ var Video = import_react43.default.forwardRef((props, ref) => {
10837
11937
  const {
10838
11938
  src,
10839
11939
  poster,
@@ -10857,21 +11957,21 @@ var Video = import_react41.default.forwardRef((props, ref) => {
10857
11957
  children,
10858
11958
  ...rest
10859
11959
  } = props;
10860
- const containerRef = import_react41.default.useRef(null);
10861
- const videoRef = import_react41.default.useRef(null);
10862
- const [isPlaying, setIsPlaying] = import_react41.default.useState(Boolean(autoPlay));
10863
- const [isLoaded, setIsLoaded] = import_react41.default.useState(false);
10864
- const [currentTime, setCurrentTime] = import_react41.default.useState(0);
10865
- const [duration, setDuration] = import_react41.default.useState(0);
10866
- const [buffered, setBuffered] = import_react41.default.useState(0);
10867
- const [volume, setVolume] = import_react41.default.useState(1);
10868
- const [isMuted, setIsMuted] = import_react41.default.useState(Boolean(muted));
10869
- const [isFullscreen, setIsFullscreen] = import_react41.default.useState(false);
10870
- const [playbackRate, setPlaybackRate] = import_react41.default.useState(1);
10871
- const [rateMenuOpen, setRateMenuOpen] = import_react41.default.useState(false);
10872
- const [captionsOn, setCaptionsOn] = import_react41.default.useState(false);
10873
- const [isPip, setIsPip] = import_react41.default.useState(false);
10874
- const setRefs = import_react41.default.useCallback(
11960
+ const containerRef = import_react43.default.useRef(null);
11961
+ const videoRef = import_react43.default.useRef(null);
11962
+ const [isPlaying, setIsPlaying] = import_react43.default.useState(Boolean(autoPlay));
11963
+ const [isLoaded, setIsLoaded] = import_react43.default.useState(false);
11964
+ const [currentTime, setCurrentTime] = import_react43.default.useState(0);
11965
+ const [duration, setDuration] = import_react43.default.useState(0);
11966
+ const [buffered, setBuffered] = import_react43.default.useState(0);
11967
+ const [volume, setVolume] = import_react43.default.useState(1);
11968
+ const [isMuted, setIsMuted] = import_react43.default.useState(Boolean(muted));
11969
+ const [isFullscreen, setIsFullscreen] = import_react43.default.useState(false);
11970
+ const [playbackRate, setPlaybackRate] = import_react43.default.useState(1);
11971
+ const [rateMenuOpen, setRateMenuOpen] = import_react43.default.useState(false);
11972
+ const [captionsOn, setCaptionsOn] = import_react43.default.useState(false);
11973
+ const [isPip, setIsPip] = import_react43.default.useState(false);
11974
+ const setRefs = import_react43.default.useCallback(
10875
11975
  (el) => {
10876
11976
  videoRef.current = el;
10877
11977
  if (typeof ref === "function") ref(el);
@@ -10879,14 +11979,14 @@ var Video = import_react41.default.forwardRef((props, ref) => {
10879
11979
  },
10880
11980
  [ref]
10881
11981
  );
10882
- import_react41.default.useEffect(() => {
11982
+ import_react43.default.useEffect(() => {
10883
11983
  const onFsChange = () => {
10884
11984
  setIsFullscreen(document.fullscreenElement === containerRef.current);
10885
11985
  };
10886
11986
  document.addEventListener("fullscreenchange", onFsChange);
10887
11987
  return () => document.removeEventListener("fullscreenchange", onFsChange);
10888
11988
  }, []);
10889
- import_react41.default.useEffect(() => {
11989
+ import_react43.default.useEffect(() => {
10890
11990
  const v = videoRef.current;
10891
11991
  if (!v) return;
10892
11992
  const onEnter = () => setIsPip(true);
@@ -11000,13 +12100,13 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11000
12100
  const volumePct = (isMuted ? 0 : volume) * 100;
11001
12101
  const VolumeGlyph = isMuted || volume === 0 ? VolumeXIcon_default : volume < 0.5 ? VolumeIcon_default : Volume2Icon_default;
11002
12102
  const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
11003
- return /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
12103
+ return /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)(
11004
12104
  "div",
11005
12105
  {
11006
12106
  ref: containerRef,
11007
12107
  className: clsx_default("lib-xplat-video", showControls && "has-controls"),
11008
12108
  children: [
11009
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12109
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11010
12110
  "video",
11011
12111
  {
11012
12112
  ref: setRefs,
@@ -11027,7 +12127,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11027
12127
  children
11028
12128
  }
11029
12129
  ),
11030
- showCenterPlay && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12130
+ showCenterPlay && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11031
12131
  "button",
11032
12132
  {
11033
12133
  type: "button",
@@ -11039,11 +12139,11 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11039
12139
  onClick: togglePlay,
11040
12140
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
11041
12141
  tabIndex: -1,
11042
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("span", { className: "center-play-icon", children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PlayCircleIcon_default, {}) })
12142
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("span", { className: "center-play-icon", children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PlayCircleIcon_default, {}) })
11043
12143
  }
11044
12144
  ),
11045
- showControls && /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
11046
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12145
+ showControls && /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "controls", onClick: (e) => e.stopPropagation(), children: [
12146
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11047
12147
  "input",
11048
12148
  {
11049
12149
  type: "range",
@@ -11060,29 +12160,29 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11060
12160
  }
11061
12161
  }
11062
12162
  ),
11063
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "controls-row", children: [
11064
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12163
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "controls-row", children: [
12164
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11065
12165
  "button",
11066
12166
  {
11067
12167
  type: "button",
11068
12168
  className: "control-btn",
11069
12169
  onClick: togglePlay,
11070
12170
  "aria-label": isPlaying ? "\uC77C\uC2DC\uC815\uC9C0" : "\uC7AC\uC0DD",
11071
- children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PauseIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PlayIcon_default, {})
12171
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PauseIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PlayIcon_default, {})
11072
12172
  }
11073
12173
  ),
11074
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: "volume-group", children: [
11075
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12174
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: "volume-group", children: [
12175
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11076
12176
  "button",
11077
12177
  {
11078
12178
  type: "button",
11079
12179
  className: "control-btn",
11080
12180
  onClick: toggleMute,
11081
12181
  "aria-label": isMuted ? "\uC74C\uC18C\uAC70 \uD574\uC81C" : "\uC74C\uC18C\uAC70",
11082
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(VolumeGlyph, {})
12182
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(VolumeGlyph, {})
11083
12183
  }
11084
12184
  ),
11085
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12185
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11086
12186
  "input",
11087
12187
  {
11088
12188
  type: "range",
@@ -11097,14 +12197,14 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11097
12197
  }
11098
12198
  )
11099
12199
  ] }),
11100
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("span", { className: "time", children: [
12200
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("span", { className: "time", children: [
11101
12201
  formatTime(currentTime),
11102
12202
  " / ",
11103
12203
  formatTime(duration)
11104
12204
  ] }),
11105
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)("div", { className: "controls-spacer" }),
11106
- playbackRates && playbackRates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
11107
- /* @__PURE__ */ (0, import_jsx_runtime352.jsxs)(
12205
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("div", { className: "controls-spacer" }),
12206
+ playbackRates && playbackRates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)("div", { className: clsx_default("rate-group", rateMenuOpen && "is-open"), children: [
12207
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsxs)(
11108
12208
  "button",
11109
12209
  {
11110
12210
  type: "button",
@@ -11118,7 +12218,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11118
12218
  ]
11119
12219
  }
11120
12220
  ),
11121
- 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)(
12221
+ 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)(
11122
12222
  "button",
11123
12223
  {
11124
12224
  type: "button",
@@ -11132,7 +12232,7 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11132
12232
  }
11133
12233
  ) }, r2)) })
11134
12234
  ] }),
11135
- showCaptions && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12235
+ showCaptions && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11136
12236
  "button",
11137
12237
  {
11138
12238
  type: "button",
@@ -11140,37 +12240,37 @@ var Video = import_react41.default.forwardRef((props, ref) => {
11140
12240
  onClick: toggleCaptions,
11141
12241
  "aria-label": captionsOn ? "\uC790\uB9C9 \uB044\uAE30" : "\uC790\uB9C9 \uCF1C\uAE30",
11142
12242
  "aria-pressed": captionsOn,
11143
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(TypeIcon_default, {})
12243
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(TypeIcon_default, {})
11144
12244
  }
11145
12245
  ),
11146
- showPip && pipSupported && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12246
+ showPip && pipSupported && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11147
12247
  "button",
11148
12248
  {
11149
12249
  type: "button",
11150
12250
  className: clsx_default("control-btn", isPip && "is-active"),
11151
12251
  onClick: togglePip,
11152
12252
  "aria-label": isPip ? "PIP \uC885\uB8CC" : "PIP",
11153
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(PipIcon, {})
12253
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(PipIcon, {})
11154
12254
  }
11155
12255
  ),
11156
- showDownload && /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12256
+ showDownload && /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11157
12257
  "a",
11158
12258
  {
11159
12259
  className: "control-btn",
11160
12260
  href: src,
11161
12261
  download: downloadFileName ?? true,
11162
12262
  "aria-label": "\uB2E4\uC6B4\uB85C\uB4DC",
11163
- children: /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(DownloadIcon_default, {})
12263
+ children: /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(DownloadIcon_default, {})
11164
12264
  }
11165
12265
  ),
11166
- /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(
12266
+ /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(
11167
12267
  "button",
11168
12268
  {
11169
12269
  type: "button",
11170
12270
  className: "control-btn",
11171
12271
  onClick: toggleFullscreen,
11172
12272
  "aria-label": isFullscreen ? "\uC804\uCCB4\uD654\uBA74 \uC885\uB8CC" : "\uC804\uCCB4\uD654\uBA74",
11173
- children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(MinimizeIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime352.jsx)(MaximizeIcon_default, {})
12273
+ children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(MinimizeIcon_default, {}) : /* @__PURE__ */ (0, import_jsx_runtime354.jsx)(MaximizeIcon_default, {})
11174
12274
  }
11175
12275
  )
11176
12276
  ] })
@@ -11183,7 +12283,7 @@ Video.displayName = "Video";
11183
12283
  var Video_default = Video;
11184
12284
 
11185
12285
  // src/layout/Grid/FullGrid/FullGrid.tsx
11186
- var import_jsx_runtime353 = require("react/jsx-runtime");
12286
+ var import_jsx_runtime355 = require("react/jsx-runtime");
11187
12287
  var GAP_MAP = {
11188
12288
  none: "var(--spacing-space-none)",
11189
12289
  xs: "var(--spacing-space-1)",
@@ -11196,13 +12296,13 @@ var GAP_MAP = {
11196
12296
  var FullGrid = (props) => {
11197
12297
  const { children, gap } = props;
11198
12298
  const style = gap != null ? { gap: GAP_MAP[gap] } : void 0;
11199
- return /* @__PURE__ */ (0, import_jsx_runtime353.jsx)("div", { className: "lib-xplat-full-grid", style, children });
12299
+ return /* @__PURE__ */ (0, import_jsx_runtime355.jsx)("div", { className: "lib-xplat-full-grid", style, children });
11200
12300
  };
11201
12301
  FullGrid.displayName = "FullGrid";
11202
12302
  var FullGrid_default = FullGrid;
11203
12303
 
11204
12304
  // src/layout/Grid/FullScreen/FullScreen.tsx
11205
- var import_jsx_runtime354 = require("react/jsx-runtime");
12305
+ var import_jsx_runtime356 = require("react/jsx-runtime");
11206
12306
  var GAP_MAP2 = {
11207
12307
  none: "var(--spacing-space-none)",
11208
12308
  xs: "var(--spacing-space-1)",
@@ -11215,13 +12315,13 @@ var GAP_MAP2 = {
11215
12315
  var FullScreen = (props) => {
11216
12316
  const { children, gap } = props;
11217
12317
  const style = gap != null ? { gap: GAP_MAP2[gap] } : void 0;
11218
- return /* @__PURE__ */ (0, import_jsx_runtime354.jsx)("div", { className: "lib-xplat-full-screen", style, children });
12318
+ return /* @__PURE__ */ (0, import_jsx_runtime356.jsx)("div", { className: "lib-xplat-full-screen", style, children });
11219
12319
  };
11220
12320
  FullScreen.displayName = "FullScreen";
11221
12321
  var FullScreen_default = FullScreen;
11222
12322
 
11223
12323
  // src/layout/Grid/Item/Item.tsx
11224
- var import_jsx_runtime355 = require("react/jsx-runtime");
12324
+ var import_jsx_runtime357 = require("react/jsx-runtime");
11225
12325
  var calculateSpans = (column) => {
11226
12326
  const spans = {};
11227
12327
  let inherited = column.default;
@@ -11238,35 +12338,35 @@ var GridItem = ({ column, children, className }) => {
11238
12338
  Object.entries(spans).forEach(([key, value]) => {
11239
12339
  style[`--column-${key}`] = value;
11240
12340
  });
11241
- return /* @__PURE__ */ (0, import_jsx_runtime355.jsx)("div", { className: clsx_default("lib-xplat-grid-item", className), style, children });
12341
+ return /* @__PURE__ */ (0, import_jsx_runtime357.jsx)("div", { className: clsx_default("lib-xplat-grid-item", className), style, children });
11242
12342
  };
11243
12343
  GridItem.displayName = "GridItem";
11244
12344
  var Item_default = GridItem;
11245
12345
 
11246
12346
  // src/layout/Header/Header.tsx
11247
- var import_jsx_runtime356 = require("react/jsx-runtime");
12347
+ var import_jsx_runtime358 = require("react/jsx-runtime");
11248
12348
  var Header = ({
11249
12349
  logo,
11250
12350
  centerContent,
11251
12351
  rightContent
11252
12352
  }) => {
11253
- return /* @__PURE__ */ (0, import_jsx_runtime356.jsxs)("div", { className: "lib-xplat-layout-header", children: [
11254
- /* @__PURE__ */ (0, import_jsx_runtime356.jsx)("div", { children: logo }),
11255
- /* @__PURE__ */ (0, import_jsx_runtime356.jsx)("div", { children: centerContent }),
11256
- /* @__PURE__ */ (0, import_jsx_runtime356.jsx)("div", { children: rightContent })
12353
+ return /* @__PURE__ */ (0, import_jsx_runtime358.jsxs)("div", { className: "lib-xplat-layout-header", children: [
12354
+ /* @__PURE__ */ (0, import_jsx_runtime358.jsx)("div", { children: logo }),
12355
+ /* @__PURE__ */ (0, import_jsx_runtime358.jsx)("div", { children: centerContent }),
12356
+ /* @__PURE__ */ (0, import_jsx_runtime358.jsx)("div", { children: rightContent })
11257
12357
  ] });
11258
12358
  };
11259
12359
  Header.displayName = "Header";
11260
12360
  var Header_default = Header;
11261
12361
 
11262
12362
  // src/layout/Layout/Layout.tsx
11263
- var import_jsx_runtime357 = require("react/jsx-runtime");
12363
+ var import_jsx_runtime359 = require("react/jsx-runtime");
11264
12364
  var Layout = (props) => {
11265
12365
  const { header, sideBar, children } = props;
11266
- return /* @__PURE__ */ (0, import_jsx_runtime357.jsx)("div", { className: "lib-xplat-layout", children: /* @__PURE__ */ (0, import_jsx_runtime357.jsxs)("div", { className: "lib-xplat-layout-content-wrapper", children: [
11267
- sideBar && /* @__PURE__ */ (0, import_jsx_runtime357.jsx)(import_jsx_runtime357.Fragment, { children: sideBar }),
11268
- /* @__PURE__ */ (0, import_jsx_runtime357.jsxs)("div", { className: "lib-xplat-layout-content", children: [
11269
- header && /* @__PURE__ */ (0, import_jsx_runtime357.jsx)("div", { className: "lib-xplat-layout-conent-header", children: header }),
12366
+ return /* @__PURE__ */ (0, import_jsx_runtime359.jsx)("div", { className: "lib-xplat-layout", children: /* @__PURE__ */ (0, import_jsx_runtime359.jsxs)("div", { className: "lib-xplat-layout-content-wrapper", children: [
12367
+ sideBar && /* @__PURE__ */ (0, import_jsx_runtime359.jsx)(import_jsx_runtime359.Fragment, { children: sideBar }),
12368
+ /* @__PURE__ */ (0, import_jsx_runtime359.jsxs)("div", { className: "lib-xplat-layout-content", children: [
12369
+ header && /* @__PURE__ */ (0, import_jsx_runtime359.jsx)("div", { className: "lib-xplat-layout-conent-header", children: header }),
11270
12370
  children
11271
12371
  ] })
11272
12372
  ] }) });
@@ -11275,31 +12375,31 @@ Layout.displayName = "Layout";
11275
12375
  var Layout_default = Layout;
11276
12376
 
11277
12377
  // src/layout/SideBar/SideBar.tsx
11278
- var import_react43 = __toESM(require("react"), 1);
12378
+ var import_react45 = __toESM(require("react"), 1);
11279
12379
 
11280
12380
  // src/layout/SideBar/SideBarContext.tsx
11281
- var import_react42 = __toESM(require("react"), 1);
11282
- var SideBarContext = import_react42.default.createContext(null);
12381
+ var import_react44 = __toESM(require("react"), 1);
12382
+ var SideBarContext = import_react44.default.createContext(null);
11283
12383
  var useSideBarContext = () => {
11284
- const ctx = import_react42.default.useContext(SideBarContext);
12384
+ const ctx = import_react44.default.useContext(SideBarContext);
11285
12385
  if (!ctx) throw new Error("Error");
11286
12386
  return ctx;
11287
12387
  };
11288
12388
  var SideBarContext_default = SideBarContext;
11289
12389
 
11290
12390
  // src/layout/SideBar/SideBar.tsx
11291
- var import_jsx_runtime358 = require("react/jsx-runtime");
12391
+ var import_jsx_runtime360 = require("react/jsx-runtime");
11292
12392
  var SideBar = (props) => {
11293
12393
  const { children, className } = props;
11294
- const [isOpen, setIsOpen] = import_react43.default.useState(true);
12394
+ const [isOpen, setIsOpen] = import_react45.default.useState(true);
11295
12395
  const handleSwitchSideBar = () => {
11296
12396
  setIsOpen((prev) => !prev);
11297
12397
  };
11298
- return /* @__PURE__ */ (0, import_jsx_runtime358.jsx)(
12398
+ return /* @__PURE__ */ (0, import_jsx_runtime360.jsx)(
11299
12399
  SideBarContext_default.Provider,
11300
12400
  {
11301
12401
  value: { isSidebarOpen: isOpen, handleSwitchSideBar },
11302
- children: /* @__PURE__ */ (0, import_jsx_runtime358.jsx)(
12402
+ children: /* @__PURE__ */ (0, import_jsx_runtime360.jsx)(
11303
12403
  "div",
11304
12404
  {
11305
12405
  className: clsx_default(
@@ -11607,6 +12707,7 @@ var SideBar_default = SideBar;
11607
12707
  Spinner,
11608
12708
  SquareIcon,
11609
12709
  StarIcon,
12710
+ Stat,
11610
12711
  Steps,
11611
12712
  StopCircleIcon,
11612
12713
  SuccessIcon,
@@ -11631,6 +12732,7 @@ var SideBar_default = SideBar;
11631
12732
  ThermometerIcon,
11632
12733
  ThumbsDownIcon,
11633
12734
  ThumbsUpIcon,
12735
+ TimePicker,
11634
12736
  ToastProvider,
11635
12737
  ToggleLeftIcon,
11636
12738
  ToggleRightIcon,