@x-plat/design-system 0.11.0 → 0.13.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 (63) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +2 -2
  2. package/dist/components/AutoResizeTextArea/index.js +2 -2
  3. package/dist/components/ChatInput/index.cjs +2 -2
  4. package/dist/components/ChatInput/index.js +2 -2
  5. package/dist/components/CheckBox/index.cjs +2 -2
  6. package/dist/components/CheckBox/index.js +2 -2
  7. package/dist/components/DatePicker/index.cjs +109 -75
  8. package/dist/components/DatePicker/index.d.cts +19 -0
  9. package/dist/components/DatePicker/index.d.ts +19 -0
  10. package/dist/components/DatePicker/index.js +109 -75
  11. package/dist/components/Dropdown/index.cjs +22 -9
  12. package/dist/components/Dropdown/index.css +3 -0
  13. package/dist/components/Dropdown/index.js +22 -9
  14. package/dist/components/Editor/index.cjs +602 -30
  15. package/dist/components/Editor/index.css +103 -0
  16. package/dist/components/Editor/index.d.cts +1 -1
  17. package/dist/components/Editor/index.d.ts +1 -1
  18. package/dist/components/Editor/index.js +602 -30
  19. package/dist/components/FieldMessage/index.cjs +2 -2
  20. package/dist/components/FieldMessage/index.d.cts +8 -0
  21. package/dist/components/FieldMessage/index.d.ts +8 -0
  22. package/dist/components/FieldMessage/index.js +2 -2
  23. package/dist/components/FileUpload/index.cjs +10 -6
  24. package/dist/components/FileUpload/index.d.cts +15 -2
  25. package/dist/components/FileUpload/index.d.ts +15 -2
  26. package/dist/components/FileUpload/index.js +10 -6
  27. package/dist/components/ImageSelector/index.cjs +7 -6
  28. package/dist/components/ImageSelector/index.d.cts +15 -2
  29. package/dist/components/ImageSelector/index.d.ts +15 -2
  30. package/dist/components/ImageSelector/index.js +7 -6
  31. package/dist/components/Input/index.cjs +2 -2
  32. package/dist/components/Input/index.d.cts +4 -0
  33. package/dist/components/Input/index.d.ts +4 -0
  34. package/dist/components/Input/index.js +2 -2
  35. package/dist/components/PopOver/index.cjs +21 -7
  36. package/dist/components/PopOver/index.css +3 -0
  37. package/dist/components/PopOver/index.js +21 -7
  38. package/dist/components/Radio/index.cjs +2 -2
  39. package/dist/components/Radio/index.js +2 -2
  40. package/dist/components/Select/index.cjs +23 -10
  41. package/dist/components/Select/index.css +3 -0
  42. package/dist/components/Select/index.d.cts +1 -2
  43. package/dist/components/Select/index.d.ts +1 -2
  44. package/dist/components/Select/index.js +23 -10
  45. package/dist/components/Switch/index.cjs +2 -2
  46. package/dist/components/Switch/index.js +2 -2
  47. package/dist/components/TextArea/index.cjs +2 -2
  48. package/dist/components/TextArea/index.js +2 -2
  49. package/dist/components/TimePicker/index.cjs +23 -10
  50. package/dist/components/TimePicker/index.css +3 -0
  51. package/dist/components/TimePicker/index.js +23 -10
  52. package/dist/components/index.cjs +751 -124
  53. package/dist/components/index.css +113 -0
  54. package/dist/components/index.js +751 -124
  55. package/dist/index.cjs +751 -124
  56. package/dist/index.css +113 -0
  57. package/dist/index.js +751 -124
  58. package/guidelines/Guidelines.md +12 -0
  59. package/guidelines/MIGRATION.md +270 -0
  60. package/guidelines/components/datepicker.md +26 -0
  61. package/guidelines/components/editor.md +58 -3
  62. package/guidelines/components/file-media.md +21 -4
  63. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -6031,11 +6031,11 @@ var FieldMessage = (props) => {
6031
6031
  FieldMessage.displayName = "FieldMessage";
6032
6032
  var FieldMessage_default = FieldMessage;
6033
6033
  var Field = (props) => {
6034
- const { label, required = false, htmlFor, validations, children } = props;
6034
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
6035
6035
  const hasMessages = !!validations && validations.length > 0;
6036
6036
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(import_jsx_runtime297.Fragment, { children });
6037
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: [
6038
+ label && /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
6039
6039
  label,
6040
6040
  required && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
6041
6041
  ] }),
@@ -7815,8 +7815,12 @@ var SingleDatePicker = (props) => {
7815
7815
  highlightDates = [],
7816
7816
  locale = "ko",
7817
7817
  rangeStart,
7818
- rangeEnd
7818
+ rangeEnd,
7819
+ label,
7820
+ required,
7821
+ validations
7819
7822
  } = props;
7823
+ const labelId = import_react12.default.useId();
7820
7824
  const initialYear = value?.getFullYear();
7821
7825
  const initialMonth = value?.getMonth();
7822
7826
  const { year, month, days, goToPrevMonth, goToNextMonth, setYear, setMonth } = useCalendar(
@@ -7879,10 +7883,12 @@ var SingleDatePicker = (props) => {
7879
7883
  const monthLabels = MONTH_LABELS[locale];
7880
7884
  const titleText = pickerMode === "days" ? locale === "ko" ? `${year}\uB144 ${monthLabels[month]}` : `${monthLabels[month]} ${year}` : pickerMode === "months" ? `${year}` : `${yearRangeStart} - ${yearRangeStart + 11}`;
7881
7885
  const hasRange = rangeStart != null && rangeEnd != null;
7882
- return /* @__PURE__ */ (0, import_jsx_runtime314.jsxs)(
7886
+ const calendar = /* @__PURE__ */ (0, import_jsx_runtime314.jsxs)(
7883
7887
  "div",
7884
7888
  {
7885
7889
  className: clsx_default("lib-xplat-datepicker", "single"),
7890
+ role: "group",
7891
+ "aria-labelledby": label ? labelId : void 0,
7886
7892
  children: [
7887
7893
  /* @__PURE__ */ (0, import_jsx_runtime314.jsxs)("div", { className: "datepicker-header", children: [
7888
7894
  /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("button", { className: "datepicker-nav", onClick: handlePrev, type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(ChevronLeftIcon_default, {}) }),
@@ -7903,18 +7909,18 @@ var SingleDatePicker = (props) => {
7903
7909
  y
7904
7910
  );
7905
7911
  }) }),
7906
- pickerMode === "months" && /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("div", { className: "datepicker-picker-grid", children: monthLabels.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(
7912
+ pickerMode === "months" && /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("div", { className: "datepicker-picker-grid", children: monthLabels.map((label2, i) => /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(
7907
7913
  "button",
7908
7914
  {
7909
7915
  type: "button",
7910
7916
  className: clsx_default("datepicker-picker-cell", i === month && "active"),
7911
7917
  onClick: () => handleMonthSelect(i),
7912
- children: label
7918
+ children: label2
7913
7919
  },
7914
7920
  i
7915
7921
  )) }),
7916
7922
  pickerMode === "days" && /* @__PURE__ */ (0, import_jsx_runtime314.jsxs)(import_jsx_runtime314.Fragment, { children: [
7917
- /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("div", { className: "datepicker-weekdays", children: weekdays.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(
7923
+ /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("div", { className: "datepicker-weekdays", children: weekdays.map((label2, i) => /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(
7918
7924
  "div",
7919
7925
  {
7920
7926
  className: clsx_default(
@@ -7922,9 +7928,9 @@ var SingleDatePicker = (props) => {
7922
7928
  i === 0 && "sunday",
7923
7929
  i === 6 && "saturday"
7924
7930
  ),
7925
- children: label
7931
+ children: label2
7926
7932
  },
7927
- label
7933
+ label2
7928
7934
  )) }),
7929
7935
  /* @__PURE__ */ (0, import_jsx_runtime314.jsx)("div", { className: "datepicker-grid", children: days.map((day, idx) => {
7930
7936
  const t = day.date.getTime();
@@ -7956,6 +7962,7 @@ var SingleDatePicker = (props) => {
7956
7962
  ]
7957
7963
  }
7958
7964
  );
7965
+ return /* @__PURE__ */ (0, import_jsx_runtime314.jsx)(Field, { label, required, labelId, validations, children: calendar });
7959
7966
  };
7960
7967
  SingleDatePicker.displayName = "SingleDatePicker";
7961
7968
  var SingleDatePicker_default = SingleDatePicker;
@@ -7970,7 +7977,18 @@ var formatDate = (date) => {
7970
7977
  return `${y}/${m}/${d}`;
7971
7978
  };
7972
7979
  var InputDatePicker = (props) => {
7973
- const { value, onChange, minDate, maxDate, disabled, locale = "ko", placeholder } = props;
7980
+ const {
7981
+ value,
7982
+ onChange,
7983
+ minDate,
7984
+ maxDate,
7985
+ disabled,
7986
+ locale = "ko",
7987
+ placeholder,
7988
+ label,
7989
+ required,
7990
+ validations
7991
+ } = props;
7974
7992
  const [isOpen, setIsOpen] = import_react13.default.useState(false);
7975
7993
  const [tempDate, setTempDate] = import_react13.default.useState(value ?? /* @__PURE__ */ new Date());
7976
7994
  const handleOpen = () => {
@@ -7996,6 +8014,9 @@ var InputDatePicker = (props) => {
7996
8014
  placeholder,
7997
8015
  suffix: /* @__PURE__ */ (0, import_jsx_runtime315.jsx)(CalenderIcon_default, {}),
7998
8016
  disabled,
8017
+ label,
8018
+ required,
8019
+ validations,
7999
8020
  readOnly: true
8000
8021
  }
8001
8022
  ) }),
@@ -8127,8 +8148,12 @@ var RangePicker = (props) => {
8127
8148
  onChange,
8128
8149
  minDate,
8129
8150
  maxDate,
8130
- locale = "ko"
8151
+ locale = "ko",
8152
+ label,
8153
+ required,
8154
+ validations
8131
8155
  } = props;
8156
+ const labelId = import_react16.default.useId();
8132
8157
  const [activeTab, setActiveTab] = import_react16.default.useState("start");
8133
8158
  const handleStartChange = (date) => {
8134
8159
  if (!date) return;
@@ -8142,70 +8167,79 @@ var RangePicker = (props) => {
8142
8167
  };
8143
8168
  const startMaxDate = maxDate && endDate < maxDate ? endDate : endDate;
8144
8169
  const endMinDate = minDate && startDate > minDate ? startDate : startDate;
8145
- return /* @__PURE__ */ (0, import_jsx_runtime318.jsxs)("div", { className: clsx_default("lib-xplat-datepicker", "range"), children: [
8146
- /* @__PURE__ */ (0, import_jsx_runtime318.jsx)("div", { className: "datepicker-range-tabs", children: /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8147
- Tab_default,
8148
- {
8149
- activeIndex: activeTab === "start" ? 0 : 1,
8150
- tabs: [
8151
- { value: "start", title: locale === "ko" ? "\uC2DC\uC791\uC77C" : "Start" },
8152
- { value: "end", title: locale === "ko" ? "\uC885\uB8CC\uC77C" : "End" }
8153
- ],
8154
- onChange: (tab) => setActiveTab(tab.value),
8155
- type: "toggle",
8156
- size: "sm"
8157
- }
8158
- ) }),
8159
- /* @__PURE__ */ (0, import_jsx_runtime318.jsxs)("div", { className: "datepicker-range-panels", children: [
8160
- /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8161
- SingleDatePicker_default,
8162
- {
8163
- value: startDate,
8164
- onChange: handleStartChange,
8165
- minDate,
8166
- maxDate: startMaxDate,
8167
- rangeStart: startDate,
8168
- rangeEnd: endDate,
8169
- locale
8170
- }
8171
- ),
8172
- /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8173
- SingleDatePicker_default,
8174
- {
8175
- value: endDate,
8176
- onChange: handleEndChange,
8177
- minDate: endMinDate,
8178
- maxDate,
8179
- rangeStart: startDate,
8180
- rangeEnd: endDate,
8181
- locale
8182
- }
8183
- )
8184
- ] }),
8185
- /* @__PURE__ */ (0, import_jsx_runtime318.jsx)("div", { className: "datepicker-range-mobile", children: activeTab === "start" ? /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8186
- SingleDatePicker_default,
8187
- {
8188
- value: startDate,
8189
- onChange: handleStartChange,
8190
- minDate,
8191
- maxDate: startMaxDate,
8192
- rangeStart: startDate,
8193
- rangeEnd: endDate,
8194
- locale
8195
- }
8196
- ) : /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8197
- SingleDatePicker_default,
8198
- {
8199
- value: endDate,
8200
- onChange: handleEndChange,
8201
- minDate: endMinDate,
8202
- maxDate,
8203
- rangeStart: startDate,
8204
- rangeEnd: endDate,
8205
- locale
8206
- }
8207
- ) })
8208
- ] });
8170
+ const calendar = /* @__PURE__ */ (0, import_jsx_runtime318.jsxs)(
8171
+ "div",
8172
+ {
8173
+ className: clsx_default("lib-xplat-datepicker", "range"),
8174
+ role: "group",
8175
+ "aria-labelledby": label ? labelId : void 0,
8176
+ children: [
8177
+ /* @__PURE__ */ (0, import_jsx_runtime318.jsx)("div", { className: "datepicker-range-tabs", children: /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8178
+ Tab_default,
8179
+ {
8180
+ activeIndex: activeTab === "start" ? 0 : 1,
8181
+ tabs: [
8182
+ { value: "start", title: locale === "ko" ? "\uC2DC\uC791\uC77C" : "Start" },
8183
+ { value: "end", title: locale === "ko" ? "\uC885\uB8CC\uC77C" : "End" }
8184
+ ],
8185
+ onChange: (tab) => setActiveTab(tab.value),
8186
+ type: "toggle",
8187
+ size: "sm"
8188
+ }
8189
+ ) }),
8190
+ /* @__PURE__ */ (0, import_jsx_runtime318.jsxs)("div", { className: "datepicker-range-panels", children: [
8191
+ /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8192
+ SingleDatePicker_default,
8193
+ {
8194
+ value: startDate,
8195
+ onChange: handleStartChange,
8196
+ minDate,
8197
+ maxDate: startMaxDate,
8198
+ rangeStart: startDate,
8199
+ rangeEnd: endDate,
8200
+ locale
8201
+ }
8202
+ ),
8203
+ /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8204
+ SingleDatePicker_default,
8205
+ {
8206
+ value: endDate,
8207
+ onChange: handleEndChange,
8208
+ minDate: endMinDate,
8209
+ maxDate,
8210
+ rangeStart: startDate,
8211
+ rangeEnd: endDate,
8212
+ locale
8213
+ }
8214
+ )
8215
+ ] }),
8216
+ /* @__PURE__ */ (0, import_jsx_runtime318.jsx)("div", { className: "datepicker-range-mobile", children: activeTab === "start" ? /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8217
+ SingleDatePicker_default,
8218
+ {
8219
+ value: startDate,
8220
+ onChange: handleStartChange,
8221
+ minDate,
8222
+ maxDate: startMaxDate,
8223
+ rangeStart: startDate,
8224
+ rangeEnd: endDate,
8225
+ locale
8226
+ }
8227
+ ) : /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(
8228
+ SingleDatePicker_default,
8229
+ {
8230
+ value: endDate,
8231
+ onChange: handleEndChange,
8232
+ minDate: endMinDate,
8233
+ maxDate,
8234
+ rangeStart: startDate,
8235
+ rangeEnd: endDate,
8236
+ locale
8237
+ }
8238
+ ) })
8239
+ ]
8240
+ }
8241
+ );
8242
+ return /* @__PURE__ */ (0, import_jsx_runtime318.jsx)(Field, { label, required, labelId, validations, children: calendar });
8209
8243
  };
8210
8244
  RangePicker.displayName = "RangePicker";
8211
8245
  var RangePicker_default = RangePicker;
@@ -8354,8 +8388,10 @@ var import_react19 = __toESM(require("react"), 1);
8354
8388
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8355
8389
  const [position, setPosition] = import_react19.default.useState({
8356
8390
  position: {},
8357
- direction: "bottom"
8391
+ direction: "bottom",
8392
+ repositioning: false
8358
8393
  });
8394
+ const placedRef = import_react19.default.useRef(false);
8359
8395
  const calculatePosition = import_react19.default.useCallback(() => {
8360
8396
  if (!triggerRef.current || !popRef.current) return;
8361
8397
  const triggerRect = triggerRef.current.getBoundingClientRect();
@@ -8380,11 +8416,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8380
8416
  if (left < 8) left = 8;
8381
8417
  setPosition({
8382
8418
  position: { top, left },
8383
- direction
8419
+ direction,
8420
+ repositioning: placedRef.current
8384
8421
  });
8422
+ placedRef.current = true;
8385
8423
  }, [triggerRef, popRef]);
8386
8424
  import_react19.default.useLayoutEffect(() => {
8387
- if (!enabled) return;
8425
+ if (!enabled) {
8426
+ placedRef.current = false;
8427
+ return;
8428
+ }
8388
8429
  calculatePosition();
8389
8430
  const raf = requestAnimationFrame(calculatePosition);
8390
8431
  return () => cancelAnimationFrame(raf);
@@ -8399,11 +8440,17 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
8399
8440
  }, [calculatePosition, enabled, popRef]);
8400
8441
  import_react19.default.useEffect(() => {
8401
8442
  if (!enabled) return;
8402
- window.addEventListener("resize", calculatePosition);
8403
- window.addEventListener("scroll", calculatePosition, true);
8443
+ let raf = 0;
8444
+ const onScrollOrResize = () => {
8445
+ cancelAnimationFrame(raf);
8446
+ raf = requestAnimationFrame(calculatePosition);
8447
+ };
8448
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
8449
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
8404
8450
  return () => {
8405
- window.removeEventListener("resize", calculatePosition);
8406
- window.removeEventListener("scroll", calculatePosition, true);
8451
+ cancelAnimationFrame(raf);
8452
+ window.removeEventListener("resize", onScrollOrResize);
8453
+ window.removeEventListener("scroll", onScrollOrResize, true);
8407
8454
  };
8408
8455
  }, [calculatePosition, enabled]);
8409
8456
  return position;
@@ -8444,7 +8491,7 @@ var Dropdown = (props) => {
8444
8491
  const [visible, setVisible] = import_react21.default.useState(false);
8445
8492
  const triggerRef = import_react21.default.useRef(null);
8446
8493
  const menuRef = import_react21.default.useRef(null);
8447
- const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
8494
+ const { position, direction, repositioning } = useAutoPosition_default(triggerRef, menuRef, mounted);
8448
8495
  useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
8449
8496
  useEscapeKey(isOpen, () => {
8450
8497
  setIsOpen(false);
@@ -8479,7 +8526,7 @@ var Dropdown = (props) => {
8479
8526
  "div",
8480
8527
  {
8481
8528
  ref: menuRef,
8482
- className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }),
8529
+ className: clsx_default("lib-xplat-dropdown-menu", direction, { visible }, repositioning && "repositioning"),
8483
8530
  style: { top: position.top, left: position.left },
8484
8531
  role: "menu",
8485
8532
  children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime322.jsx)(
@@ -8505,6 +8552,111 @@ var Dropdown_default = Dropdown;
8505
8552
 
8506
8553
  // src/components/Editor/Editor.tsx
8507
8554
  var import_react22 = __toESM(require("react"), 1);
8555
+
8556
+ // src/components/Editor/highlight.ts
8557
+ var CODE_LANGUAGES = [
8558
+ { value: "plain", label: "\uC5C6\uC74C" },
8559
+ { value: "javascript", label: "JavaScript" },
8560
+ { value: "typescript", label: "TypeScript" },
8561
+ { value: "json", label: "JSON" },
8562
+ { value: "html", label: "HTML" },
8563
+ { value: "css", label: "CSS" },
8564
+ { value: "python", label: "Python" },
8565
+ { value: "sql", label: "SQL" },
8566
+ { value: "bash", label: "Bash" }
8567
+ ];
8568
+ var KEYWORDS = {
8569
+ plain: [],
8570
+ 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(" "),
8571
+ 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(" "),
8572
+ json: ["true", "false", "null"],
8573
+ html: [],
8574
+ css: [],
8575
+ 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(" "),
8576
+ 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(" "),
8577
+ 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(" ")
8578
+ };
8579
+ var LINE_COMMENT = {
8580
+ javascript: "//",
8581
+ typescript: "//",
8582
+ css: "",
8583
+ python: "#",
8584
+ sql: "--",
8585
+ bash: "#"
8586
+ };
8587
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
8588
+ var wrap = (cls, text) => `<span class="tok-${cls}">${escape(text)}</span>`;
8589
+ var highlight = (code, lang) => {
8590
+ if (lang === "plain" || !code) return escape(code);
8591
+ const keywords = new Set(KEYWORDS[lang]);
8592
+ const lineComment = LINE_COMMENT[lang];
8593
+ const caseInsensitive = lang === "sql";
8594
+ let out = "";
8595
+ let i = 0;
8596
+ while (i < code.length) {
8597
+ const rest = code.slice(i);
8598
+ if ((lang === "javascript" || lang === "typescript" || lang === "css") && rest.startsWith("/*")) {
8599
+ const end = code.indexOf("*/", i + 2);
8600
+ const stop = end === -1 ? code.length : end + 2;
8601
+ out += wrap("comment", code.slice(i, stop));
8602
+ i = stop;
8603
+ continue;
8604
+ }
8605
+ if (lineComment && rest.startsWith(lineComment)) {
8606
+ const nl = code.indexOf("\n", i);
8607
+ const stop = nl === -1 ? code.length : nl;
8608
+ out += wrap("comment", code.slice(i, stop));
8609
+ i = stop;
8610
+ continue;
8611
+ }
8612
+ const q = rest[0];
8613
+ if (q === '"' || q === "'" || q === "`") {
8614
+ let j = 1;
8615
+ while (j < rest.length) {
8616
+ if (rest[j] === "\\") {
8617
+ j += 2;
8618
+ continue;
8619
+ }
8620
+ if (rest[j] === q) {
8621
+ j += 1;
8622
+ break;
8623
+ }
8624
+ if (rest[j] === "\n" && q !== "`") break;
8625
+ j += 1;
8626
+ }
8627
+ out += wrap("string", rest.slice(0, j));
8628
+ i += j;
8629
+ continue;
8630
+ }
8631
+ const num = /^\d+(\.\d+)?/.exec(rest);
8632
+ if (num && !/[\w$]/.test(code[i - 1] ?? "")) {
8633
+ out += wrap("number", num[0]);
8634
+ i += num[0].length;
8635
+ continue;
8636
+ }
8637
+ const word = /^[A-Za-z_$][\w$]*/.exec(rest);
8638
+ if (word) {
8639
+ const w = word[0];
8640
+ const hit = caseInsensitive ? keywords.has(w.toUpperCase()) : keywords.has(w);
8641
+ out += hit ? wrap("keyword", w) : escape(w);
8642
+ i += w.length;
8643
+ continue;
8644
+ }
8645
+ if (lang === "html" && rest.startsWith("<")) {
8646
+ const tag = /^<\/?[A-Za-z][\w-]*/.exec(rest);
8647
+ if (tag) {
8648
+ out += wrap("keyword", tag[0]);
8649
+ i += tag[0].length;
8650
+ continue;
8651
+ }
8652
+ }
8653
+ out += escape(code[i]);
8654
+ i += 1;
8655
+ }
8656
+ return out;
8657
+ };
8658
+
8659
+ // src/components/Editor/Editor.tsx
8508
8660
  var import_jsx_runtime323 = require("react/jsx-runtime");
8509
8661
  var DEFAULT_TOOLBAR = [
8510
8662
  "bold",
@@ -8512,14 +8664,27 @@ var DEFAULT_TOOLBAR = [
8512
8664
  "underline",
8513
8665
  "strikethrough",
8514
8666
  "code",
8515
- "heading",
8667
+ "heading-1",
8668
+ "heading-2",
8669
+ "heading-3",
8516
8670
  "list",
8517
8671
  "ordered-list",
8518
8672
  "blockquote",
8519
8673
  "code-block",
8520
8674
  "link",
8521
8675
  "image",
8522
- "divider"
8676
+ "divider",
8677
+ "checklist",
8678
+ "table",
8679
+ "align-left",
8680
+ "align-center",
8681
+ "align-right",
8682
+ "highlight",
8683
+ "superscript",
8684
+ "subscript",
8685
+ "clear-format",
8686
+ "undo",
8687
+ "redo"
8523
8688
  ];
8524
8689
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
8525
8690
  "p",
@@ -8548,11 +8713,41 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
8548
8713
  "img",
8549
8714
  "hr",
8550
8715
  "span",
8551
- "div"
8716
+ "div",
8717
+ "sub",
8718
+ "sup",
8719
+ "mark",
8720
+ "table",
8721
+ "thead",
8722
+ "tbody",
8723
+ "tr",
8724
+ "th",
8725
+ "td"
8552
8726
  ]);
8553
8727
  var ALLOWED_ATTRS = {
8554
8728
  a: ["href", "target", "rel"],
8555
- img: ["src", "alt"]
8729
+ img: ["src", "alt", "width"],
8730
+ // 정렬·체크리스트는 클래스로만 표현한다. style 을 열면 붙여넣기로 아무 CSS 나 들어온다
8731
+ p: ["class"],
8732
+ h1: ["class"],
8733
+ h2: ["class"],
8734
+ h3: ["class"],
8735
+ h4: ["class"],
8736
+ h5: ["class"],
8737
+ h6: ["class"],
8738
+ ul: ["class"],
8739
+ ol: ["class"],
8740
+ li: ["class", "data-checked"],
8741
+ blockquote: ["class"],
8742
+ div: ["class"],
8743
+ span: ["class"],
8744
+ pre: ["class"],
8745
+ code: ["class"],
8746
+ mark: ["class"],
8747
+ table: ["class"],
8748
+ td: ["class"],
8749
+ th: ["class"],
8750
+ tr: ["class"]
8556
8751
  };
8557
8752
  var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
8558
8753
  "script",
@@ -8605,7 +8800,9 @@ var escapeHtml = (text) => {
8605
8800
  };
8606
8801
  var SLASH_ITEMS = [
8607
8802
  { key: "paragraph", label: "\uB2E8\uB77D", hint: "\uAE30\uBCF8 \uD14D\uC2A4\uD2B8" },
8608
- { key: "heading", label: "\uC81C\uBAA9 1", hint: "\uD070 \uC81C\uBAA9" },
8803
+ { key: "heading-1", label: "\uC81C\uBAA9 1", hint: "\uAC00\uC7A5 \uD070 \uC81C\uBAA9" },
8804
+ { key: "heading-2", label: "\uC81C\uBAA9 2", hint: "" },
8805
+ { key: "heading-3", label: "\uC81C\uBAA9 3", hint: "" },
8609
8806
  { key: "list", label: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D", hint: "\u2022 \uD56D\uBAA9" },
8610
8807
  { key: "ordered-list", label: "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D", hint: "1. \uD56D\uBAA9" },
8611
8808
  { key: "blockquote", label: "\uC778\uC6A9", hint: "" },
@@ -8648,6 +8845,8 @@ var Editor = (props) => {
8648
8845
  el.innerHTML = sanitizeHtml(value || "");
8649
8846
  if (!el.firstChild) el.innerHTML = "<p><br></p>";
8650
8847
  lastEmittedRef.current = null;
8848
+ historyRef.current = [{ html: el.innerHTML, caret: 0 }];
8849
+ historyIdxRef.current = 0;
8651
8850
  updateEmpty();
8652
8851
  }, [value]);
8653
8852
  const updateEmpty = () => {
@@ -8692,6 +8891,17 @@ var Editor = (props) => {
8692
8891
  if (document.queryCommandState("insertOrderedList")) next.add("ordered-list");
8693
8892
  const block = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
8694
8893
  if (block) next.add(`block:${block}`);
8894
+ if (document.queryCommandState("superscript")) next.add("superscript");
8895
+ if (document.queryCommandState("subscript")) next.add("subscript");
8896
+ } catch {
8897
+ }
8898
+ try {
8899
+ const blockEl = currentBlock();
8900
+ if (blockEl?.classList.contains("align-center")) next.add("align:center");
8901
+ else if (blockEl?.classList.contains("align-right")) next.add("align:right");
8902
+ else if (blockEl) next.add("align:left");
8903
+ if (blockEl?.closest("mark")) next.add("highlight");
8904
+ if (blockEl?.closest("ul.checklist")) next.add("checklist");
8695
8905
  } catch {
8696
8906
  }
8697
8907
  setActiveFormats(next);
@@ -8705,6 +8915,122 @@ var Editor = (props) => {
8705
8915
  const setBlock = (tag) => {
8706
8916
  exec("formatBlock", tag);
8707
8917
  };
8918
+ const toggleBlock = (tag) => {
8919
+ const current = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
8920
+ setBlock(current === tag ? "p" : tag);
8921
+ };
8922
+ const historyRef = import_react22.default.useRef([]);
8923
+ const historyIdxRef = import_react22.default.useRef(-1);
8924
+ const lastPushAtRef = import_react22.default.useRef(0);
8925
+ const isRestoringRef = import_react22.default.useRef(false);
8926
+ const caretOffset = (rootEl) => {
8927
+ const el = rootEl ?? editorRef.current;
8928
+ const sel = window.getSelection();
8929
+ if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return 0;
8930
+ const range = sel.getRangeAt(0).cloneRange();
8931
+ range.selectNodeContents(el);
8932
+ range.setEnd(sel.getRangeAt(0).endContainer, sel.getRangeAt(0).endOffset);
8933
+ return range.toString().length;
8934
+ };
8935
+ const setCaretOffset = (offset, rootEl) => {
8936
+ const el = rootEl ?? editorRef.current;
8937
+ if (!el) return;
8938
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
8939
+ let remaining = offset;
8940
+ let node = null;
8941
+ while (node = walker.nextNode()) {
8942
+ const len = node.textContent?.length ?? 0;
8943
+ if (remaining <= len) {
8944
+ const range = document.createRange();
8945
+ range.setStart(node, remaining);
8946
+ range.collapse(true);
8947
+ const sel = window.getSelection();
8948
+ sel?.removeAllRanges();
8949
+ sel?.addRange(range);
8950
+ return;
8951
+ }
8952
+ remaining -= len;
8953
+ }
8954
+ placeCaretAtEnd(el);
8955
+ };
8956
+ const pushHistory = (force = false) => {
8957
+ const el = editorRef.current;
8958
+ if (!el || isRestoringRef.current) return;
8959
+ const html = el.innerHTML;
8960
+ const hist = historyRef.current;
8961
+ const cur = hist[historyIdxRef.current];
8962
+ if (cur && cur.html === html) return;
8963
+ const now = Date.now();
8964
+ const coalesce = !force && cur && now - lastPushAtRef.current < 500;
8965
+ lastPushAtRef.current = now;
8966
+ if (coalesce) {
8967
+ hist[historyIdxRef.current] = { html, caret: caretOffset() };
8968
+ return;
8969
+ }
8970
+ hist.length = historyIdxRef.current + 1;
8971
+ hist.push({ html, caret: caretOffset() });
8972
+ if (hist.length > 200) hist.shift();
8973
+ historyIdxRef.current = hist.length - 1;
8974
+ };
8975
+ const applySnapshot = (snap) => {
8976
+ const el = editorRef.current;
8977
+ if (!el) return;
8978
+ isRestoringRef.current = true;
8979
+ el.innerHTML = snap.html;
8980
+ setCaretOffset(snap.caret);
8981
+ isRestoringRef.current = false;
8982
+ lastEmittedRef.current = el.innerHTML;
8983
+ onChange?.(el.innerHTML);
8984
+ updateEmpty();
8985
+ updateActiveFormats();
8986
+ };
8987
+ const undo = () => {
8988
+ pushHistory(true);
8989
+ if (historyIdxRef.current <= 0) return;
8990
+ historyIdxRef.current -= 1;
8991
+ applySnapshot(historyRef.current[historyIdxRef.current]);
8992
+ };
8993
+ const redo = () => {
8994
+ if (historyIdxRef.current >= historyRef.current.length - 1) return;
8995
+ historyIdxRef.current += 1;
8996
+ applySnapshot(historyRef.current[historyIdxRef.current]);
8997
+ };
8998
+ const langOf = (pre) => {
8999
+ const cls = Array.from(pre.classList).find((c) => c.startsWith("lang-"));
9000
+ return cls?.slice(5) ?? "plain";
9001
+ };
9002
+ const [codeLang, setCodeLang] = import_react22.default.useState("plain");
9003
+ const highlightRafRef = import_react22.default.useRef(0);
9004
+ const rehighlight = (pre) => {
9005
+ if (composingRef.current) return;
9006
+ const code = pre.querySelector("code");
9007
+ if (!code) return;
9008
+ const lang = langOf(pre);
9009
+ const text = code.textContent || "";
9010
+ const next = highlight(text, lang);
9011
+ if (code.innerHTML === next) return;
9012
+ const caret = caretOffset(code);
9013
+ isRestoringRef.current = true;
9014
+ code.innerHTML = next;
9015
+ setCaretOffset(caret, code);
9016
+ isRestoringRef.current = false;
9017
+ };
9018
+ const scheduleHighlight = () => {
9019
+ const pre = closestPre();
9020
+ if (!pre) return;
9021
+ cancelAnimationFrame(highlightRafRef.current);
9022
+ highlightRafRef.current = requestAnimationFrame(() => rehighlight(pre));
9023
+ };
9024
+ const changeCodeLang = (lang) => {
9025
+ const pre = closestPre();
9026
+ if (!pre) return;
9027
+ pushHistory(true);
9028
+ Array.from(pre.classList).filter((c) => c.startsWith("lang-")).forEach((c) => pre.classList.remove(c));
9029
+ if (lang !== "plain") pre.classList.add(`lang-${lang}`);
9030
+ setCodeLang(lang);
9031
+ rehighlight(pre);
9032
+ handleInput();
9033
+ };
8708
9034
  const closestPre = () => {
8709
9035
  const sel = window.getSelection();
8710
9036
  const node = sel?.anchorNode;
@@ -8713,6 +9039,7 @@ var Editor = (props) => {
8713
9039
  return el?.closest("pre") ?? null;
8714
9040
  };
8715
9041
  const insertCodeBlock = () => {
9042
+ pushHistory(true);
8716
9043
  const el = editorRef.current;
8717
9044
  const sel = window.getSelection();
8718
9045
  if (!el || !sel || sel.rangeCount === 0) return;
@@ -8795,7 +9122,167 @@ var Editor = (props) => {
8795
9122
  sel.addRange(r2);
8796
9123
  editorRef.current?.focus();
8797
9124
  };
9125
+ const clearFormat = () => {
9126
+ exec("removeFormat");
9127
+ exec("unlink");
9128
+ setBlock("p");
9129
+ };
9130
+ const indentList = (outdent) => {
9131
+ exec(outdent ? "outdent" : "indent");
9132
+ };
9133
+ const inListItem = () => {
9134
+ const sel = window.getSelection();
9135
+ const node = sel?.anchorNode;
9136
+ if (!node || !editorRef.current?.contains(node)) return false;
9137
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
9138
+ return !!el?.closest("li");
9139
+ };
9140
+ const currentBlock = () => {
9141
+ const sel = window.getSelection();
9142
+ const node = sel?.anchorNode;
9143
+ const root = editorRef.current;
9144
+ if (!node || !root?.contains(node)) return null;
9145
+ let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
9146
+ while (el && el !== root && !/^(P|H[1-6]|LI|BLOCKQUOTE|PRE|DIV|TD|TH)$/.test(el.tagName)) {
9147
+ el = el.parentElement;
9148
+ }
9149
+ return el && el !== root ? el : null;
9150
+ };
9151
+ const setAlign = (align) => {
9152
+ const block = currentBlock();
9153
+ if (!block) return;
9154
+ pushHistory(true);
9155
+ block.classList.remove("align-left", "align-center", "align-right");
9156
+ if (align !== "left") block.classList.add(`align-${align}`);
9157
+ handleInput();
9158
+ };
9159
+ const toggleChecklist = () => {
9160
+ pushHistory(true);
9161
+ const block = currentBlock();
9162
+ const list = block?.closest("ul");
9163
+ if (list?.classList.contains("checklist")) {
9164
+ list.classList.remove("checklist");
9165
+ list.querySelectorAll("li").forEach((li) => li.removeAttribute("data-checked"));
9166
+ handleInput();
9167
+ return;
9168
+ }
9169
+ if (list) {
9170
+ list.classList.add("checklist");
9171
+ list.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
9172
+ handleInput();
9173
+ return;
9174
+ }
9175
+ exec("insertUnorderedList");
9176
+ const made = currentBlock()?.closest("ul");
9177
+ if (made) {
9178
+ made.classList.add("checklist");
9179
+ made.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
9180
+ }
9181
+ handleInput();
9182
+ };
9183
+ const handleEditorClick = (e) => {
9184
+ if (readOnly) return;
9185
+ const target = e.target;
9186
+ const li = target.closest?.("li");
9187
+ if (!li || !li.closest("ul.checklist")) return;
9188
+ const rect = li.getBoundingClientRect();
9189
+ if (e.clientX - rect.left > 20) return;
9190
+ e.preventDefault();
9191
+ pushHistory(true);
9192
+ li.setAttribute("data-checked", li.getAttribute("data-checked") === "true" ? "false" : "true");
9193
+ handleInput();
9194
+ };
9195
+ const toggleHighlight = () => {
9196
+ const sel = window.getSelection();
9197
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
9198
+ pushHistory(true);
9199
+ const range = sel.getRangeAt(0);
9200
+ const node = sel.anchorNode;
9201
+ const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
9202
+ const existing = el?.closest("mark");
9203
+ if (existing) {
9204
+ const parent = existing.parentNode;
9205
+ while (existing.firstChild) parent?.insertBefore(existing.firstChild, existing);
9206
+ parent?.removeChild(existing);
9207
+ handleInput();
9208
+ return;
9209
+ }
9210
+ const mark = document.createElement("mark");
9211
+ try {
9212
+ range.surroundContents(mark);
9213
+ } catch {
9214
+ const frag = range.extractContents();
9215
+ mark.appendChild(frag);
9216
+ range.insertNode(mark);
9217
+ }
9218
+ handleInput();
9219
+ };
9220
+ const insertTable = (cols = 3, rows = 2) => {
9221
+ const sel = window.getSelection();
9222
+ if (!sel || sel.rangeCount === 0) return;
9223
+ pushHistory(true);
9224
+ const table = document.createElement("table");
9225
+ const thead = document.createElement("thead");
9226
+ const headRow = document.createElement("tr");
9227
+ for (let c = 0; c < cols; c++) {
9228
+ const th = document.createElement("th");
9229
+ th.innerHTML = "<br>";
9230
+ headRow.appendChild(th);
9231
+ }
9232
+ thead.appendChild(headRow);
9233
+ table.appendChild(thead);
9234
+ const tbody = document.createElement("tbody");
9235
+ for (let r2 = 0; r2 < rows; r2++) {
9236
+ const tr = document.createElement("tr");
9237
+ for (let c = 0; c < cols; c++) {
9238
+ const td = document.createElement("td");
9239
+ td.innerHTML = "<br>";
9240
+ tr.appendChild(td);
9241
+ }
9242
+ tbody.appendChild(tr);
9243
+ }
9244
+ table.appendChild(tbody);
9245
+ const range = sel.getRangeAt(0);
9246
+ range.deleteContents();
9247
+ range.insertNode(table);
9248
+ ensureParagraphAfter(table);
9249
+ const first = table.querySelector("th");
9250
+ if (first) placeCaretAtEnd(first);
9251
+ handleInput();
9252
+ };
9253
+ const moveTableCell = (backwards) => {
9254
+ const block = currentBlock();
9255
+ const cell = block?.closest("td, th");
9256
+ if (!cell) return false;
9257
+ const table = cell.closest("table");
9258
+ if (!table) return false;
9259
+ const cells = Array.from(table.querySelectorAll("td, th"));
9260
+ const i = cells.indexOf(cell);
9261
+ const next = cells[i + (backwards ? -1 : 1)];
9262
+ if (next) {
9263
+ placeCaretAtEnd(next);
9264
+ return true;
9265
+ }
9266
+ if (!backwards) {
9267
+ pushHistory(true);
9268
+ const lastRow = table.querySelector("tbody")?.lastElementChild;
9269
+ if (lastRow) {
9270
+ const tr = document.createElement("tr");
9271
+ for (let c = 0; c < lastRow.children.length; c++) {
9272
+ const td = document.createElement("td");
9273
+ td.innerHTML = "<br>";
9274
+ tr.appendChild(td);
9275
+ }
9276
+ lastRow.after(tr);
9277
+ placeCaretAtEnd(tr.firstElementChild);
9278
+ handleInput();
9279
+ return true;
9280
+ }
9281
+ }
9282
+ return false;
9283
+ };
8798
9284
  const insertDivider = () => {
9285
+ pushHistory(true);
8799
9286
  exec("insertHorizontalRule");
8800
9287
  const el = editorRef.current;
8801
9288
  if (el && el.lastElementChild?.tagName === "HR") {
@@ -8888,8 +9375,14 @@ var Editor = (props) => {
8888
9375
  case "paragraph":
8889
9376
  setBlock("p");
8890
9377
  break;
8891
- case "heading":
8892
- setBlock("h2");
9378
+ case "heading-1":
9379
+ toggleBlock("h1");
9380
+ break;
9381
+ case "heading-2":
9382
+ toggleBlock("h2");
9383
+ break;
9384
+ case "heading-3":
9385
+ toggleBlock("h3");
8893
9386
  break;
8894
9387
  case "list":
8895
9388
  exec("insertUnorderedList");
@@ -8982,6 +9475,8 @@ var Editor = (props) => {
8982
9475
  if (!el) return;
8983
9476
  const html = el.innerHTML;
8984
9477
  lastEmittedRef.current = html;
9478
+ pushHistory();
9479
+ scheduleHighlight();
8985
9480
  onChange?.(html);
8986
9481
  updateEmpty();
8987
9482
  updateActiveFormats();
@@ -9035,6 +9530,15 @@ var Editor = (props) => {
9035
9530
  return;
9036
9531
  }
9037
9532
  }
9533
+ if (e.key === "Tab" && moveTableCell(e.shiftKey)) {
9534
+ e.preventDefault();
9535
+ return;
9536
+ }
9537
+ if (e.key === "Tab" && inListItem()) {
9538
+ e.preventDefault();
9539
+ indentList(e.shiftKey);
9540
+ return;
9541
+ }
9038
9542
  if (slashOpen) {
9039
9543
  if (e.key === "ArrowDown") {
9040
9544
  e.preventDefault();
@@ -9058,6 +9562,21 @@ var Editor = (props) => {
9058
9562
  return;
9059
9563
  }
9060
9564
  }
9565
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
9566
+ e.preventDefault();
9567
+ if (e.shiftKey) redo();
9568
+ else undo();
9569
+ return;
9570
+ }
9571
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "v") {
9572
+ plainPasteRef.current = true;
9573
+ return;
9574
+ }
9575
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "y") {
9576
+ e.preventDefault();
9577
+ redo();
9578
+ return;
9579
+ }
9061
9580
  if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
9062
9581
  const k = e.key.toLowerCase();
9063
9582
  if (k === "b") {
@@ -9091,10 +9610,16 @@ var Editor = (props) => {
9091
9610
  }
9092
9611
  }
9093
9612
  };
9613
+ const plainPasteRef = import_react22.default.useRef(false);
9094
9614
  const handlePaste = (e) => {
9095
9615
  if (readOnly) return;
9096
9616
  e.preventDefault();
9097
9617
  const text = e.clipboardData.getData("text/plain");
9618
+ if (plainPasteRef.current) {
9619
+ plainPasteRef.current = false;
9620
+ insertTextAtCaret(text);
9621
+ return;
9622
+ }
9098
9623
  if (closestPre()) {
9099
9624
  insertTextAtCaret(text);
9100
9625
  return;
@@ -9121,9 +9646,11 @@ var Editor = (props) => {
9121
9646
  };
9122
9647
  const handleSelectionUpdate = () => {
9123
9648
  updateActiveFormats();
9649
+ const pre = closestPre();
9650
+ setCodeLang(pre ? langOf(pre) : "plain");
9124
9651
  };
9125
9652
  const onToolbarAction = (item) => {
9126
- if (closestPre() && item !== "code-block") return;
9653
+ if (closestPre() && !["code-block", "undo", "redo"].includes(item)) return;
9127
9654
  switch (item) {
9128
9655
  case "bold":
9129
9656
  exec("bold");
@@ -9152,8 +9679,14 @@ var Editor = (props) => {
9152
9679
  handleInput();
9153
9680
  break;
9154
9681
  }
9155
- case "heading":
9156
- setBlock("h2");
9682
+ case "heading-1":
9683
+ toggleBlock("h1");
9684
+ break;
9685
+ case "heading-2":
9686
+ toggleBlock("h2");
9687
+ break;
9688
+ case "heading-3":
9689
+ toggleBlock("h3");
9157
9690
  break;
9158
9691
  case "list":
9159
9692
  exec("insertUnorderedList");
@@ -9176,6 +9709,39 @@ var Editor = (props) => {
9176
9709
  case "divider":
9177
9710
  insertDivider();
9178
9711
  break;
9712
+ case "checklist":
9713
+ toggleChecklist();
9714
+ break;
9715
+ case "align-left":
9716
+ setAlign("left");
9717
+ break;
9718
+ case "align-center":
9719
+ setAlign("center");
9720
+ break;
9721
+ case "align-right":
9722
+ setAlign("right");
9723
+ break;
9724
+ case "highlight":
9725
+ toggleHighlight();
9726
+ break;
9727
+ case "superscript":
9728
+ exec("superscript");
9729
+ break;
9730
+ case "subscript":
9731
+ exec("subscript");
9732
+ break;
9733
+ case "table":
9734
+ insertTable();
9735
+ break;
9736
+ case "clear-format":
9737
+ clearFormat();
9738
+ break;
9739
+ case "undo":
9740
+ undo();
9741
+ break;
9742
+ case "redo":
9743
+ redo();
9744
+ break;
9179
9745
  }
9180
9746
  };
9181
9747
  return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: clsx_default("lib-xplat-editor", isFocused && "focused", readOnly && "readonly"), children: [
@@ -9184,7 +9750,10 @@ var Editor = (props) => {
9184
9750
  {
9185
9751
  items: toolbar,
9186
9752
  active: activeFormats,
9187
- onAction: onToolbarAction
9753
+ onAction: onToolbarAction,
9754
+ codeLang,
9755
+ inCodeBlock: activeFormats.has("block:pre"),
9756
+ onCodeLangChange: changeCodeLang
9188
9757
  }
9189
9758
  ),
9190
9759
  /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__content", style: { minHeight }, children: [
@@ -9202,6 +9771,7 @@ var Editor = (props) => {
9202
9771
  className: "lib-xplat-editor__editable",
9203
9772
  onInput: handleInput,
9204
9773
  onKeyDown: handleKeyDown,
9774
+ onClick: handleEditorClick,
9205
9775
  onKeyUp: handleSelectionUpdate,
9206
9776
  onMouseUp: handleSelectionUpdate,
9207
9777
  onPaste: handlePaste,
@@ -9243,14 +9813,27 @@ var TOOLBAR_LABEL = {
9243
9813
  underline: "U",
9244
9814
  strikethrough: "S",
9245
9815
  code: "<>",
9246
- heading: "H",
9816
+ "heading-1": "H1",
9817
+ "heading-2": "H2",
9818
+ "heading-3": "H3",
9247
9819
  list: "\u2022",
9248
9820
  "ordered-list": "1.",
9249
9821
  blockquote: "\u275D",
9250
9822
  "code-block": "[ ]",
9251
9823
  link: "\u{1F517}",
9252
9824
  image: "\u{1F5BC}",
9253
- divider: "\u2014"
9825
+ divider: "\u2014",
9826
+ checklist: "\u2611",
9827
+ "align-left": "\u21E4",
9828
+ "align-center": "\u2194",
9829
+ "align-right": "\u21E5",
9830
+ highlight: "\u{1F58D}",
9831
+ superscript: "x\xB2",
9832
+ subscript: "x\u2082",
9833
+ table: "\u25A6",
9834
+ "clear-format": "\u232B",
9835
+ undo: "\u21B6",
9836
+ redo: "\u21B7"
9254
9837
  };
9255
9838
  var TOOLBAR_TITLE = {
9256
9839
  bold: "\uAD75\uAC8C (\u2318B)",
@@ -9258,36 +9841,74 @@ var TOOLBAR_TITLE = {
9258
9841
  underline: "\uBC11\uC904 (\u2318U)",
9259
9842
  strikethrough: "\uCDE8\uC18C\uC120",
9260
9843
  code: "\uC778\uB77C\uC778 \uCF54\uB4DC",
9261
- heading: "\uC81C\uBAA9",
9844
+ "heading-1": "\uC81C\uBAA9 1",
9845
+ "heading-2": "\uC81C\uBAA9 2",
9846
+ "heading-3": "\uC81C\uBAA9 3",
9262
9847
  list: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D",
9263
9848
  "ordered-list": "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D",
9264
9849
  blockquote: "\uC778\uC6A9",
9265
9850
  "code-block": "\uCF54\uB4DC \uBE14\uB85D",
9266
9851
  link: "\uB9C1\uD06C (\u2318K)",
9267
9852
  image: "\uC774\uBBF8\uC9C0",
9268
- divider: "\uAD6C\uBD84\uC120"
9853
+ divider: "\uAD6C\uBD84\uC120",
9854
+ checklist: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
9855
+ "align-left": "\uC67C\uCABD \uC815\uB82C",
9856
+ "align-center": "\uAC00\uC6B4\uB370 \uC815\uB82C",
9857
+ "align-right": "\uC624\uB978\uCABD \uC815\uB82C",
9858
+ highlight: "\uD615\uAD11\uD39C",
9859
+ superscript: "\uC704 \uCCA8\uC790",
9860
+ subscript: "\uC544\uB798 \uCCA8\uC790",
9861
+ table: "\uD45C \uC0BD\uC785",
9862
+ "clear-format": "\uC11C\uC2DD \uC9C0\uC6B0\uAE30",
9863
+ undo: "\uB418\uB3CC\uB9AC\uAE30 (\u2318Z)",
9864
+ redo: "\uB2E4\uC2DC\uD558\uAE30 (\u2318\u21E7Z)"
9269
9865
  };
9270
9866
  var isActive = (item, active) => {
9271
- if (item === "heading") return active.has("block:h1") || active.has("block:h2") || active.has("block:h3");
9867
+ if (item === "heading-1") return active.has("block:h1");
9868
+ if (item === "heading-2") return active.has("block:h2");
9869
+ if (item === "heading-3") return active.has("block:h3");
9272
9870
  if (item === "blockquote") return active.has("block:blockquote");
9273
9871
  if (item === "code-block") return active.has("block:pre");
9872
+ if (item === "align-left") return active.has("align:left");
9873
+ if (item === "align-center") return active.has("align:center");
9874
+ if (item === "align-right") return active.has("align:right");
9274
9875
  return active.has(item);
9275
9876
  };
9276
- var EditorToolbar = ({ items, active, onAction }) => {
9277
- return /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("div", { className: "lib-xplat-editor__toolbar", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
9278
- "button",
9279
- {
9280
- type: "button",
9281
- className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
9282
- title: TOOLBAR_TITLE[item],
9283
- onMouseDown: (e) => {
9284
- e.preventDefault();
9285
- onAction(item);
9877
+ var EditorToolbar = ({
9878
+ items,
9879
+ active,
9880
+ onAction,
9881
+ codeLang,
9882
+ inCodeBlock,
9883
+ onCodeLangChange
9884
+ }) => {
9885
+ return /* @__PURE__ */ (0, import_jsx_runtime323.jsxs)("div", { className: "lib-xplat-editor__toolbar", children: [
9886
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
9887
+ "button",
9888
+ {
9889
+ type: "button",
9890
+ className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
9891
+ title: TOOLBAR_TITLE[item],
9892
+ onMouseDown: (e) => {
9893
+ e.preventDefault();
9894
+ onAction(item);
9895
+ },
9896
+ children: TOOLBAR_LABEL[item]
9286
9897
  },
9287
- children: TOOLBAR_LABEL[item]
9288
- },
9289
- item
9290
- )) });
9898
+ item
9899
+ )),
9900
+ inCodeBlock && /* @__PURE__ */ (0, import_jsx_runtime323.jsx)(
9901
+ "select",
9902
+ {
9903
+ className: "lib-xplat-editor__lang",
9904
+ value: codeLang,
9905
+ "aria-label": "\uCF54\uB4DC \uC5B8\uC5B4",
9906
+ onMouseDown: (e) => e.stopPropagation(),
9907
+ onChange: (e) => onCodeLangChange(e.target.value),
9908
+ children: CODE_LANGUAGES.map((l) => /* @__PURE__ */ (0, import_jsx_runtime323.jsx)("option", { value: l.value, children: l.label }, l.value))
9909
+ }
9910
+ )
9911
+ ] });
9291
9912
  };
9292
9913
  var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
9293
9914
  import_react22.default.useEffect(() => {
@@ -9391,10 +10012,13 @@ var FileUpload = (props) => {
9391
10012
  multiple = false,
9392
10013
  maxSize,
9393
10014
  onChange,
9394
- label = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
10015
+ placeholder = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
9395
10016
  description,
9396
- validations
10017
+ validations,
10018
+ label,
10019
+ required
9397
10020
  } = props;
10021
+ const fieldId = import_react23.default.useId();
9398
10022
  const [isDragOver, setIsDragOver] = import_react23.default.useState(false);
9399
10023
  const inputRef = import_react23.default.useRef(null);
9400
10024
  const handleFiles = (fileList) => {
@@ -9439,6 +10063,7 @@ var FileUpload = (props) => {
9439
10063
  "input",
9440
10064
  {
9441
10065
  ref: inputRef,
10066
+ id: fieldId,
9442
10067
  type: "file",
9443
10068
  accept,
9444
10069
  multiple,
@@ -9447,12 +10072,12 @@ var FileUpload = (props) => {
9447
10072
  }
9448
10073
  ),
9449
10074
  /* @__PURE__ */ (0, import_jsx_runtime325.jsx)("div", { className: "upload-icon", children: /* @__PURE__ */ (0, import_jsx_runtime325.jsx)(UploadIcon_default, {}) }),
9450
- /* @__PURE__ */ (0, import_jsx_runtime325.jsx)("p", { className: "upload-label", children: label }),
10075
+ /* @__PURE__ */ (0, import_jsx_runtime325.jsx)("p", { className: "upload-label", children: placeholder }),
9451
10076
  description && /* @__PURE__ */ (0, import_jsx_runtime325.jsx)("p", { className: "upload-description", children: description })
9452
10077
  ]
9453
10078
  }
9454
10079
  );
9455
- return /* @__PURE__ */ (0, import_jsx_runtime325.jsx)(Field, { validations, children: control });
10080
+ return /* @__PURE__ */ (0, import_jsx_runtime325.jsx)(Field, { label, required, htmlFor: fieldId, validations, children: control });
9456
10081
  };
9457
10082
  FileUpload.displayName = "FileUpload";
9458
10083
  var FileUpload_default = FileUpload;
@@ -9595,7 +10220,8 @@ var HtmlTypeWriter_default = HtmlTypeWriter;
9595
10220
  var import_react26 = __toESM(require("react"), 1);
9596
10221
  var import_jsx_runtime327 = require("react/jsx-runtime");
9597
10222
  var ImageSelector = (props) => {
9598
- const { value, label, onChange, validations } = props;
10223
+ const { value, placeholder, onChange, validations, label, required } = props;
10224
+ const fieldId = import_react26.default.useId();
9599
10225
  const [previewUrl, setPreviewUrl] = import_react26.default.useState();
9600
10226
  import_react26.default.useEffect(() => {
9601
10227
  if (!value) {
@@ -9625,7 +10251,7 @@ var ImageSelector = (props) => {
9625
10251
  {
9626
10252
  type: "file",
9627
10253
  accept: "image/*",
9628
- id: "image-input",
10254
+ id: fieldId,
9629
10255
  onChange: handleFileChange,
9630
10256
  ref: inputRef,
9631
10257
  "aria-invalid": hasError(validations) || void 0
@@ -9637,10 +10263,10 @@ var ImageSelector = (props) => {
9637
10263
  ] }),
9638
10264
  /* @__PURE__ */ (0, import_jsx_runtime327.jsx)("div", { className: "content", children: previewUrl ? /* @__PURE__ */ (0, import_jsx_runtime327.jsx)("img", { src: previewUrl, alt: "preview" }) : /* @__PURE__ */ (0, import_jsx_runtime327.jsxs)("div", { className: "skeleton", onClick: handleOpenFileDialog, children: [
9639
10265
  /* @__PURE__ */ (0, import_jsx_runtime327.jsx)("div", { className: "icon-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime327.jsx)(ImageIcon_default, {}) }),
9640
- /* @__PURE__ */ (0, import_jsx_runtime327.jsx)("div", { className: "label", children: label || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
10266
+ /* @__PURE__ */ (0, import_jsx_runtime327.jsx)("div", { className: "label", children: placeholder || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
9641
10267
  ] }) })
9642
10268
  ] });
9643
- return /* @__PURE__ */ (0, import_jsx_runtime327.jsx)(Field, { validations, children: control });
10269
+ return /* @__PURE__ */ (0, import_jsx_runtime327.jsx)(Field, { label, required, htmlFor: fieldId, validations, children: control });
9644
10270
  };
9645
10271
  ImageSelector.displayName = "ImageSelector";
9646
10272
  var ImageSelector_default = ImageSelector;
@@ -9832,6 +10458,7 @@ var PopOver = (props) => {
9832
10458
  className: clsx_default(
9833
10459
  "lib-xplat-pop-over-content",
9834
10460
  position.direction,
10461
+ position.repositioning && "repositioning",
9835
10462
  eventTrigger && "visible"
9836
10463
  ),
9837
10464
  ref: popRef,
@@ -10178,7 +10805,7 @@ var SelectRoot = (props) => {
10178
10805
  mounted && /* @__PURE__ */ (0, import_jsx_runtime337.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime337.jsxs)(
10179
10806
  "div",
10180
10807
  {
10181
- className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
10808
+ className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass, position.repositioning && "repositioning"),
10182
10809
  ref: contentRef,
10183
10810
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
10184
10811
  role: "listbox",
@@ -11071,7 +11698,7 @@ var TimePicker = (props) => {
11071
11698
  "div",
11072
11699
  {
11073
11700
  ref: contentRef,
11074
- className: clsx_default("lib-xplat-time-picker-content", size, visible ? "enter" : "exit"),
11701
+ className: clsx_default("lib-xplat-time-picker-content", size, visible ? "enter" : "exit", position.repositioning && "repositioning"),
11075
11702
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
11076
11703
  role: "listbox",
11077
11704
  children: options.map((t) => /* @__PURE__ */ (0, import_jsx_runtime351.jsx)(