@storkspear/post-editor-react 0.1.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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/dist/ColorPicker.d.ts +11 -0
  4. package/dist/ColorPicker.d.ts.map +1 -0
  5. package/dist/ColorPicker.js +30 -0
  6. package/dist/ColorPicker.js.map +1 -0
  7. package/dist/DrawioModal.d.ts +9 -0
  8. package/dist/DrawioModal.d.ts.map +1 -0
  9. package/dist/DrawioModal.js +97 -0
  10. package/dist/DrawioModal.js.map +1 -0
  11. package/dist/FontMenu.d.ts +14 -0
  12. package/dist/FontMenu.d.ts.map +1 -0
  13. package/dist/FontMenu.js +14 -0
  14. package/dist/FontMenu.js.map +1 -0
  15. package/dist/Icon.d.ts +69 -0
  16. package/dist/Icon.d.ts.map +1 -0
  17. package/dist/Icon.js +47 -0
  18. package/dist/Icon.js.map +1 -0
  19. package/dist/InsertHandle.d.ts +14 -0
  20. package/dist/InsertHandle.d.ts.map +1 -0
  21. package/dist/InsertHandle.js +60 -0
  22. package/dist/InsertHandle.js.map +1 -0
  23. package/dist/PickMenu.d.ts +61 -0
  24. package/dist/PickMenu.d.ts.map +1 -0
  25. package/dist/PickMenu.js +57 -0
  26. package/dist/PickMenu.js.map +1 -0
  27. package/dist/Popover.d.ts +20 -0
  28. package/dist/Popover.d.ts.map +1 -0
  29. package/dist/Popover.js +22 -0
  30. package/dist/Popover.js.map +1 -0
  31. package/dist/PostTitle.d.ts +33 -0
  32. package/dist/PostTitle.d.ts.map +1 -0
  33. package/dist/PostTitle.js +171 -0
  34. package/dist/PostTitle.js.map +1 -0
  35. package/dist/SelectionBubble.d.ts +6 -0
  36. package/dist/SelectionBubble.d.ts.map +1 -0
  37. package/dist/SelectionBubble.js +71 -0
  38. package/dist/SelectionBubble.js.map +1 -0
  39. package/dist/TableTools.d.ts +10 -0
  40. package/dist/TableTools.d.ts.map +1 -0
  41. package/dist/TableTools.js +200 -0
  42. package/dist/TableTools.js.map +1 -0
  43. package/dist/Toolbar.d.ts +17 -0
  44. package/dist/Toolbar.d.ts.map +1 -0
  45. package/dist/Toolbar.js +333 -0
  46. package/dist/Toolbar.js.map +1 -0
  47. package/dist/UrlPrompt.d.ts +24 -0
  48. package/dist/UrlPrompt.d.ts.map +1 -0
  49. package/dist/UrlPrompt.js +53 -0
  50. package/dist/UrlPrompt.js.map +1 -0
  51. package/dist/VariantMenu.d.ts +47 -0
  52. package/dist/VariantMenu.d.ts.map +1 -0
  53. package/dist/VariantMenu.js +21 -0
  54. package/dist/VariantMenu.js.map +1 -0
  55. package/dist/WidthToggle.d.ts +19 -0
  56. package/dist/WidthToggle.d.ts.map +1 -0
  57. package/dist/WidthToggle.js +17 -0
  58. package/dist/WidthToggle.js.map +1 -0
  59. package/dist/bubbleShows.d.ts +10 -0
  60. package/dist/bubbleShows.d.ts.map +1 -0
  61. package/dist/bubbleShows.js +29 -0
  62. package/dist/bubbleShows.js.map +1 -0
  63. package/dist/content-baked.d.ts +22 -0
  64. package/dist/content-baked.d.ts.map +1 -0
  65. package/dist/content-baked.js +21 -0
  66. package/dist/content-baked.js.map +1 -0
  67. package/dist/content.d.ts +29 -0
  68. package/dist/content.d.ts.map +1 -0
  69. package/dist/content.js +35 -0
  70. package/dist/content.js.map +1 -0
  71. package/dist/extensions/DiagramView.d.ts +26 -0
  72. package/dist/extensions/DiagramView.d.ts.map +1 -0
  73. package/dist/extensions/DiagramView.js +146 -0
  74. package/dist/extensions/DiagramView.js.map +1 -0
  75. package/dist/extensions/FigureView.d.ts +3 -0
  76. package/dist/extensions/FigureView.d.ts.map +1 -0
  77. package/dist/extensions/FigureView.js +108 -0
  78. package/dist/extensions/FigureView.js.map +1 -0
  79. package/dist/index.d.ts +100 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +166 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/objects/ObjectFloat.d.ts +11 -0
  84. package/dist/objects/ObjectFloat.d.ts.map +1 -0
  85. package/dist/objects/ObjectFloat.js +42 -0
  86. package/dist/objects/ObjectFloat.js.map +1 -0
  87. package/dist/objects/ObjectLayer.d.ts +10 -0
  88. package/dist/objects/ObjectLayer.d.ts.map +1 -0
  89. package/dist/objects/ObjectLayer.js +20 -0
  90. package/dist/objects/ObjectLayer.js.map +1 -0
  91. package/dist/objects/blockSize.d.ts +30 -0
  92. package/dist/objects/blockSize.d.ts.map +1 -0
  93. package/dist/objects/blockSize.js +56 -0
  94. package/dist/objects/blockSize.js.map +1 -0
  95. package/dist/objects/code.d.ts +3 -0
  96. package/dist/objects/code.d.ts.map +1 -0
  97. package/dist/objects/code.js +55 -0
  98. package/dist/objects/code.js.map +1 -0
  99. package/dist/objects/diagram.d.ts +21 -0
  100. package/dist/objects/diagram.d.ts.map +1 -0
  101. package/dist/objects/diagram.js +109 -0
  102. package/dist/objects/diagram.js.map +1 -0
  103. package/dist/objects/diagramGlyphs.d.ts +12 -0
  104. package/dist/objects/diagramGlyphs.d.ts.map +1 -0
  105. package/dist/objects/diagramGlyphs.js +49 -0
  106. package/dist/objects/diagramGlyphs.js.map +1 -0
  107. package/dist/objects/divider.d.ts +22 -0
  108. package/dist/objects/divider.d.ts.map +1 -0
  109. package/dist/objects/divider.js +60 -0
  110. package/dist/objects/divider.js.map +1 -0
  111. package/dist/objects/figure.d.ts +3 -0
  112. package/dist/objects/figure.d.ts.map +1 -0
  113. package/dist/objects/figure.js +62 -0
  114. package/dist/objects/figure.js.map +1 -0
  115. package/dist/objects/imageRow.d.ts +3 -0
  116. package/dist/objects/imageRow.d.ts.map +1 -0
  117. package/dist/objects/imageRow.js +29 -0
  118. package/dist/objects/imageRow.js.map +1 -0
  119. package/dist/objects/quote.d.ts +6 -0
  120. package/dist/objects/quote.d.ts.map +1 -0
  121. package/dist/objects/quote.js +13 -0
  122. package/dist/objects/quote.js.map +1 -0
  123. package/dist/objects/quoteGlyphs.d.ts +6 -0
  124. package/dist/objects/quoteGlyphs.d.ts.map +1 -0
  125. package/dist/objects/quoteGlyphs.js +28 -0
  126. package/dist/objects/quoteGlyphs.js.map +1 -0
  127. package/dist/objects/quoteRow.d.ts +3 -0
  128. package/dist/objects/quoteRow.d.ts.map +1 -0
  129. package/dist/objects/quoteRow.js +61 -0
  130. package/dist/objects/quoteRow.js.map +1 -0
  131. package/dist/objects/registry.d.ts +60 -0
  132. package/dist/objects/registry.d.ts.map +1 -0
  133. package/dist/objects/registry.js +45 -0
  134. package/dist/objects/registry.js.map +1 -0
  135. package/dist/objects/table.d.ts +11 -0
  136. package/dist/objects/table.d.ts.map +1 -0
  137. package/dist/objects/table.js +136 -0
  138. package/dist/objects/table.js.map +1 -0
  139. package/dist/objects/useDomRect.d.ts +14 -0
  140. package/dist/objects/useDomRect.d.ts.map +1 -0
  141. package/dist/objects/useDomRect.js +32 -0
  142. package/dist/objects/useDomRect.js.map +1 -0
  143. package/dist/objects/useEngaged.d.ts +14 -0
  144. package/dist/objects/useEngaged.d.ts.map +1 -0
  145. package/dist/objects/useEngaged.js +41 -0
  146. package/dist/objects/useEngaged.js.map +1 -0
  147. package/dist/pickItems.d.ts +36 -0
  148. package/dist/pickItems.d.ts.map +1 -0
  149. package/dist/pickItems.js +40 -0
  150. package/dist/pickItems.js.map +1 -0
  151. package/dist/recentColors.d.ts +7 -0
  152. package/dist/recentColors.d.ts.map +1 -0
  153. package/dist/recentColors.js +30 -0
  154. package/dist/recentColors.js.map +1 -0
  155. package/dist/seeds/drawio/aws.d.ts +15 -0
  156. package/dist/seeds/drawio/aws.d.ts.map +1 -0
  157. package/dist/seeds/drawio/aws.js +15 -0
  158. package/dist/seeds/drawio/aws.js.map +1 -0
  159. package/dist/seeds/drawio/azure.d.ts +14 -0
  160. package/dist/seeds/drawio/azure.d.ts.map +1 -0
  161. package/dist/seeds/drawio/azure.js +14 -0
  162. package/dist/seeds/drawio/azure.js.map +1 -0
  163. package/dist/seeds/drawio/ec2docker.d.ts +15 -0
  164. package/dist/seeds/drawio/ec2docker.d.ts.map +1 -0
  165. package/dist/seeds/drawio/ec2docker.js +15 -0
  166. package/dist/seeds/drawio/ec2docker.js.map +1 -0
  167. package/dist/seeds/drawio/userflow.d.ts +21 -0
  168. package/dist/seeds/drawio/userflow.d.ts.map +1 -0
  169. package/dist/seeds/drawio/userflow.js +21 -0
  170. package/dist/seeds/drawio/userflow.js.map +1 -0
  171. package/dist/seeds/mermaid.d.ts +19 -0
  172. package/dist/seeds/mermaid.d.ts.map +1 -0
  173. package/dist/seeds/mermaid.js +82 -0
  174. package/dist/seeds/mermaid.js.map +1 -0
  175. package/dist/useAnchoredPos.d.ts +22 -0
  176. package/dist/useAnchoredPos.d.ts.map +1 -0
  177. package/dist/useAnchoredPos.js +57 -0
  178. package/dist/useAnchoredPos.js.map +1 -0
  179. package/dist/useDismiss.d.ts +17 -0
  180. package/dist/useDismiss.d.ts.map +1 -0
  181. package/dist/useDismiss.js +36 -0
  182. package/dist/useDismiss.js.map +1 -0
  183. package/dist/useLastVariant.d.ts +9 -0
  184. package/dist/useLastVariant.d.ts.map +1 -0
  185. package/dist/useLastVariant.js +33 -0
  186. package/dist/useLastVariant.js.map +1 -0
  187. package/dist/useUploadCount.d.ts +3 -0
  188. package/dist/useUploadCount.d.ts.map +1 -0
  189. package/dist/useUploadCount.js +9 -0
  190. package/dist/useUploadCount.js.map +1 -0
  191. package/package.json +57 -0
  192. package/src/editor-ui.css +737 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 storkspear
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @storkspear/post-editor-react
2
+
3
+ `@storkspear/post-editor-core` 의 React UI — 툴바·글자 버블·객체 도구·좌측 `+`.
4
+
5
+ ```tsx
6
+ import { PostEditor } from '@storkspear/post-editor-react'
7
+ import '@storkspear/post-editor-core/styles.css'
8
+ import '@storkspear/post-editor-react/styles.css'
9
+ ```
10
+
11
+ **공개 페이지는 이 패키지의 `index` 를 쓰면 안 된다.** 편집기 전체가 딸려 온다.
12
+ 읽기 전용은 `@storkspear/post-editor-react/content` 에서 `PostContent` 만 가져온다.
@@ -0,0 +1,11 @@
1
+ import { type RefObject } from 'react';
2
+ export declare function ColorPicker({ value, onPick, onClear, onClose, label, recent, anchorRef, }: {
3
+ anchorRef?: RefObject<HTMLElement | null>;
4
+ value?: string;
5
+ onPick: (hex: string) => void;
6
+ onClear: () => void;
7
+ onClose: () => void;
8
+ label: string;
9
+ recent?: string[];
10
+ }): import("react").JSX.Element;
11
+ //# sourceMappingURL=ColorPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorPicker.d.ts","sourceRoot":"","sources":["../src/ColorPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAsB9C,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAW,EAAE,SAAS,GAC/D,EAAE;IACD,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACzC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB,+BA0CA"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { useAnchoredPos } from './useAnchoredPos.js';
4
+ import { useDismiss } from './useDismiss.js';
5
+ import { Portal } from './Popover.js';
6
+ /**
7
+ * 색 팔레트 — 네이버처럼 격자로 펼치되, 채도가 튀는 원색 대신 단계가 고른 색을 쓴다.
8
+ * 열 = 색상, 행 = 밝기. 맨 앞은 "색 없음".
9
+ */
10
+ const HUES = [
11
+ ['회색', ['#f1f3f5', '#dee2e6', '#adb5bd', '#5c636a', '#16191d']],
12
+ ['빨강', ['#ffe3e3', '#ffa8a8', '#fa5252', '#c92a2a', '#801515']],
13
+ ['주황', ['#ffe8cc', '#ffc078', '#fd7e14', '#d9480f', '#8c2d04']],
14
+ ['노랑', ['#fff3bf', '#ffe066', '#fab005', '#e67700', '#8a4b00']],
15
+ ['초록', ['#d3f9d8', '#8ce99a', '#40c057', '#2b8a3e', '#14532d']],
16
+ ['청록', ['#c3fae8', '#63e6be', '#12b886', '#087f5b', '#0b3d2e']],
17
+ ['하늘', ['#d0ebff', '#74c0fc', '#228be6', '#1864ab', '#0b3c69']],
18
+ ['파랑', ['#dbe4ff', '#91a7ff', '#4c6ef5', '#3b5bdb', '#1e3a8a']],
19
+ ['보라', ['#e5dbff', '#b197fc', '#7950f2', '#5f3dc4', '#3b2380']],
20
+ ['분홍', ['#ffdeeb', '#faa2c1', '#e64980', '#a61e4d', '#6b1233']],
21
+ ];
22
+ export function ColorPicker({ value, onPick, onClear, onClose, label, recent = [], anchorRef, }) {
23
+ const ref = useRef(null);
24
+ const pos = useAnchoredPos(anchorRef ?? { current: null }, !!anchorRef, 280, ref);
25
+ // 여는 버튼을 「바깥」으로 치면 다시 눌러도 안 닫힌다(mousedown 이 닫고 click 이 도로 연다).
26
+ // 그 함정을 이미 한 번 밟아서 useDismiss 가 anchor 를 받게 만들어 뒀다 — 팔레트도 같은 규약을 쓴다.
27
+ useDismiss(true, ref, onClose, anchorRef);
28
+ return (_jsx(Portal, { active: !!pos, children: _jsxs("div", { className: "cp", "data-editor-ui": "", ref: ref, role: "dialog", "aria-label": label, style: pos ? { position: 'fixed', left: pos.left, top: pos.top, maxHeight: pos.maxHeight, overflowY: 'auto' } : undefined, children: [_jsx("div", { className: "cp-head", children: label }), recent.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cp-sub", children: "\uCD5C\uADFC \uC0AC\uC6A9\uD55C \uC0C9" }), _jsx("div", { className: "cp-recent", children: recent.map((hex) => (_jsx("button", { className: "cp-cell", style: { background: hex }, title: hex, onMouseDown: (e) => e.preventDefault(), onClick: () => { onPick(hex); onClose(); } }, hex))) })] })), _jsxs("div", { className: "cp-grid", children: [_jsx("button", { className: "cp-cell cp-none", title: "\uC0C9 \uC5C6\uC74C", onMouseDown: (e) => e.preventDefault(), onClick: () => { onClear(); onClose(); }, children: _jsx("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", children: _jsx("line", { x1: "3", y1: "17", x2: "17", y2: "3" }) }) }), HUES.map(([name, shades]) => (_jsx("div", { className: "cp-col", children: shades.map((hex) => (_jsx("button", { className: `cp-cell${value?.toLowerCase() === hex ? ' is-on' : ''}`, style: { background: hex }, title: `${name} ${hex}`, "aria-label": `${name} ${hex}`, onMouseDown: (e) => e.preventDefault(), onClick: () => { onPick(hex); onClose(); } }, hex))) }, name)))] })] }) }));
29
+ }
30
+ //# sourceMappingURL=ColorPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorPicker.js","sourceRoot":"","sources":["../src/ColorPicker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare function DrawioModal({ xml, onDone, onCancel, }: {
2
+ xml: string;
3
+ onDone: (next: {
4
+ xml: string;
5
+ svg: string;
6
+ }) => void;
7
+ onCancel: () => void;
8
+ }): import("react").ReactPortal;
9
+ //# sourceMappingURL=DrawioModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DrawioModal.d.ts","sourceRoot":"","sources":["../src/DrawioModal.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,MAAM,EACN,QAAQ,GACT,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACpD,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,+BA6EA"}
@@ -0,0 +1,97 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ /**
5
+ * draw.io 를 iframe 으로 띄운다.
6
+ *
7
+ * ## 왜 iframe 인가
8
+ *
9
+ * AWS·Azure·GCP 아이콘까지 포함해 도형이 1만 개다. 그걸 우리 번들에 넣을 방법은 없고,
10
+ * 넣을 이유도 없다 — 공식 임베드 모드가 `postMessage` 로 말을 주고받는 길을 열어 뒀다.
11
+ * **라이브러리를 하나도 안 싣는다.**
12
+ *
13
+ * ## 주고받는 말
14
+ *
15
+ * iframe → 우리 init 준비됐다. `load` 를 달라
16
+ * save 사용자가 저장을 눌렀다 (xml 이 딸려 온다)
17
+ * export 내보내기가 끝났다 (data 가 딸려 온다)
18
+ * exit 나가겠다
19
+ * 우리 → iframe load 이 도면을 열어라
20
+ * export 이 형식으로 내놔라
21
+ *
22
+ * 저장 흐름은 **save → export(xmlsvg) → 닫기** 다. `xmlsvg` 는 도면 XML 이 박힌 SVG 라
23
+ * 그림과 원본이 한 덩어리지만, 우리는 원본을 따로 들고 있으므로 **SVG 만 쓰고 XML 은
24
+ * `save` 가 준 것을 쓴다** — 속성에서 XML 을 다시 캐내는 일을 안 하려고.
25
+ *
26
+ * ## 밖으로 무엇이 나가나
27
+ *
28
+ * 도면 XML 이 iframe 안에서 돈다(embed.diagrams.net). 저장은 우리 서버로만 간다 —
29
+ * 그쪽에 파일이 남지 않는다. 그게 걸리면 draw.io 를 직접 띄우고 `EMBED_URL` 만 바꾸면 된다.
30
+ */
31
+ const EMBED_URL = 'https://embed.diagrams.net/?embed=1&ui=min&spin=1&proto=json&noSaveBtn=0&saveAndExit=1&noExitBtn=0&libraries=1';
32
+ export function DrawioModal({ xml, onDone, onCancel, }) {
33
+ const frameRef = useRef(null);
34
+ const xmlRef = useRef(xml);
35
+ const [ready, setReady] = useState(false);
36
+ useEffect(() => {
37
+ const send = (msg) => frameRef.current?.contentWindow?.postMessage(JSON.stringify(msg), '*');
38
+ const onMsg = (e) => {
39
+ /* 우리 iframe 이 보낸 것만 듣는다 — 다른 창이 흉내 내지 못하게 */
40
+ if (e.source !== frameRef.current?.contentWindow)
41
+ return;
42
+ let m;
43
+ try {
44
+ m = JSON.parse(String(e.data));
45
+ }
46
+ catch {
47
+ return;
48
+ }
49
+ if (m.event === 'init') {
50
+ setReady(true);
51
+ send({ action: 'load', autosave: 0, xml: xmlRef.current || '' });
52
+ return;
53
+ }
54
+ if (m.event === 'save') {
55
+ /* 저장한 XML 을 들고, 그림은 따로 뽑는다 */
56
+ xmlRef.current = m.xml ?? xmlRef.current;
57
+ send({ action: 'export', format: 'xmlsvg', spin: '내보내는 중' });
58
+ return;
59
+ }
60
+ if (m.event === 'export') {
61
+ const data = String(m.data ?? '');
62
+ /* data:image/svg+xml;base64,… 로 온다 */
63
+ const svg = data.startsWith('data:')
64
+ ? decodeURIComponent(escape(atob(data.slice(data.indexOf(',') + 1))))
65
+ : data;
66
+ onDone({ xml: xmlRef.current, svg });
67
+ return;
68
+ }
69
+ if (m.event === 'exit')
70
+ onCancel();
71
+ };
72
+ window.addEventListener('message', onMsg);
73
+ return () => window.removeEventListener('message', onMsg);
74
+ }, [onDone, onCancel]);
75
+ /* Esc 로 닫기 — 편집기가 이 키를 먹지 않게 여기서 멈춘다 */
76
+ useEffect(() => {
77
+ const onKey = (e) => {
78
+ if (e.key === 'Escape') {
79
+ e.stopPropagation();
80
+ onCancel();
81
+ }
82
+ };
83
+ window.addEventListener('keydown', onKey, true);
84
+ return () => window.removeEventListener('keydown', onKey, true);
85
+ }, [onCancel]);
86
+ /**
87
+ * **`document.body` 로 내보낸다(포털).**
88
+ *
89
+ * 이 컴포넌트는 툴바 줄 안에서 그려지는데, 그 줄은 제 쌓임 맥락(sticky + z-index)을 만든다.
90
+ * 그 안에서는 `position: fixed; z-index: 200` 이 바깥의 머리줄을 못 이긴다 —
91
+ * 화면을 덮은 것처럼 보이지만 머리줄이 그 위에 남고 iframe 이 잘린다. 실제로 그랬다.
92
+ */
93
+ return createPortal(_jsxs("div", { className: "dio", "data-editor-ui": "", role: "dialog", "aria-modal": "true", "aria-label": "\uB2E4\uC774\uC5B4\uADF8\uB7A8 \uADF8\uB9AC\uAE30", children: [_jsxs("div", { className: "dio-bar", children: [_jsx("span", { className: "dio-t", children: "\uB2E4\uC774\uC5B4\uADF8\uB7A8 \uADF8\uB9AC\uAE30" }), _jsx("span", { className: "dio-hint", children: "\uC800\uC7A5 \uC2DC, \uC791\uC131\uD558\uB358 \uD654\uBA74\uC73C\uB85C \uB418\uB3CC\uC544\uAC11\uB2C8\uB2E4." }), _jsx("button", { type: "button", className: "dio-x", onClick: onCancel, "aria-label": "\uB2EB\uAE30", children: "\u2715" })] }), !ready && _jsx("p", { className: "dio-wait", children: "draw.io \uB97C \uBD88\uB7EC\uC624\uB294 \uC911\u2026" }), _jsx("iframe", { ref: frameRef, className: "dio-frame", src: EMBED_URL, title: "draw.io",
94
+ /* 저장은 postMessage 로만 온다 — 이 창이 우리 쪽을 직접 만지지 못하게 */
95
+ sandbox: "allow-scripts allow-same-origin allow-popups allow-forms" })] }), document.body);
96
+ }
97
+ //# sourceMappingURL=DrawioModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DrawioModal.js","sourceRoot":"","sources":["../src/DrawioModal.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 글꼴 고르기 — 이름을 **그 글꼴로** 보여 준다(네이버 블로그와 같은 방식).
3
+ * 이름만 나열하면 「고운바탕」이 어떻게 생겼는지 모른 채 골라야 한다.
4
+ *
5
+ * 고르는 칸의 공통 부품(PickMenu)에 글꼴만 얹은 것이다 —
6
+ * 문단 스타일·글자 크기·언어가 전부 같은 메뉴를 쓴다.
7
+ */
8
+ export declare function FontMenu({ value, onPick, onClose, anchorRef }: {
9
+ value: string;
10
+ onPick: (v: string) => void;
11
+ onClose: () => void;
12
+ anchorRef: React.RefObject<HTMLElement | null>;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=FontMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FontMenu.d.ts","sourceRoot":"","sources":["../src/FontMenu.tsx"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAC9D,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;CAC/C,+BAKA"}
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PickMenu } from './PickMenu.js';
3
+ import { fontItems } from './pickItems.js';
4
+ /**
5
+ * 글꼴 고르기 — 이름을 **그 글꼴로** 보여 준다(네이버 블로그와 같은 방식).
6
+ * 이름만 나열하면 「고운바탕」이 어떻게 생겼는지 모른 채 골라야 한다.
7
+ *
8
+ * 고르는 칸의 공통 부품(PickMenu)에 글꼴만 얹은 것이다 —
9
+ * 문단 스타일·글자 크기·언어가 전부 같은 메뉴를 쓴다.
10
+ */
11
+ export function FontMenu({ value, onPick, onClose, anchorRef }) {
12
+ return (_jsx(PickMenu, { ariaLabel: "\uAE00\uAF34", width: 200, items: fontItems(), value: value, onPick: onPick, onClose: onClose, anchorRef: anchorRef }));
13
+ }
14
+ //# sourceMappingURL=FontMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FontMenu.js","sourceRoot":"","sources":["../src/FontMenu.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAK3D;IACC,OAAO,CACL,KAAC,QAAQ,IAAC,SAAS,EAAC,cAAI,EAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EACnE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAI,CAC7D,CAAA;AACH,CAAC"}
package/dist/Icon.d.ts ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * 툴바 아이콘 — lucide-react 를 쓴다.
3
+ *
4
+ * 직접 그리지 않는 이유: 손으로 그린 SVG 는 획 굵기·여백·시각 중심이 제각각이라
5
+ * 나란히 놓으면 티가 나고, 아이콘이 늘 때마다 그 부담이 커진다.
6
+ * template-react-admin 도 lucide-react 를 쓰므로 스택도 맞는다.
7
+ *
8
+ * 호출부는 이름만 알면 되게 얇게 감싼다 — 나중에 라이브러리를 바꿔도 여기만 고친다.
9
+ * (라이브러리 분리 시 이 파일은 react 패키지로 간다. core 는 아이콘을 모른다.)
10
+ */
11
+ declare const MAP: {
12
+ readonly bold: import("lucide-react").LucideIcon;
13
+ readonly italic: import("lucide-react").LucideIcon;
14
+ readonly underline: import("lucide-react").LucideIcon;
15
+ readonly strike: import("lucide-react").LucideIcon;
16
+ readonly code: import("lucide-react").LucideIcon;
17
+ readonly highlight: import("lucide-react").LucideIcon;
18
+ readonly h2: import("lucide-react").LucideIcon;
19
+ readonly h3: import("lucide-react").LucideIcon;
20
+ readonly bullet: import("lucide-react").LucideIcon;
21
+ readonly ordered: import("lucide-react").LucideIcon;
22
+ readonly alignLeft: import("lucide-react").LucideIcon;
23
+ readonly alignCenter: import("lucide-react").LucideIcon;
24
+ readonly alignRight: import("lucide-react").LucideIcon;
25
+ readonly alignJustify: import("lucide-react").LucideIcon;
26
+ readonly lineHeight: import("lucide-react").LucideIcon;
27
+ readonly quote: import("lucide-react").LucideIcon;
28
+ readonly pullQuote: import("lucide-react").LucideIcon;
29
+ readonly divider: import("lucide-react").LucideIcon;
30
+ readonly codeBlock: import("lucide-react").LucideIcon;
31
+ readonly diagram: import("lucide-react").LucideIcon;
32
+ readonly info: import("lucide-react").LucideIcon;
33
+ readonly tip: import("lucide-react").LucideIcon;
34
+ readonly warn: import("lucide-react").LucideIcon;
35
+ readonly callout: import("lucide-react").LucideIcon;
36
+ readonly image: import("lucide-react").LucideIcon;
37
+ readonly table: import("lucide-react").LucideIcon;
38
+ readonly video: import("lucide-react").LucideIcon;
39
+ readonly link: import("lucide-react").LucideIcon;
40
+ readonly rowPlus: import("lucide-react").LucideIcon;
41
+ readonly colPlus: import("lucide-react").LucideIcon;
42
+ readonly trash: import("lucide-react").LucideIcon;
43
+ readonly plus: import("lucide-react").LucideIcon;
44
+ readonly pencil: import("lucide-react").LucideIcon;
45
+ readonly bucket: import("lucide-react").LucideIcon;
46
+ readonly text: import("lucide-react").LucideIcon;
47
+ readonly none: import("lucide-react").LucideIcon;
48
+ readonly move: import("lucide-react").LucideIcon;
49
+ readonly rowMerge: import("lucide-react").LucideIcon;
50
+ readonly rowSplit: import("lucide-react").LucideIcon;
51
+ readonly rowLift: import("lucide-react").LucideIcon;
52
+ readonly swap: import("lucide-react").LucideIcon;
53
+ readonly tableStyle: import("lucide-react").LucideIcon;
54
+ readonly tableBorder: import("lucide-react").LucideIcon;
55
+ readonly cellMerge: import("lucide-react").LucideIcon;
56
+ readonly cellSplit: import("lucide-react").LucideIcon;
57
+ };
58
+ export type IconName = keyof typeof MAP;
59
+ /**
60
+ * `stroke` 는 획 굵기다. 기본 1.9 는 17px 아이콘 기준이고,
61
+ * 20px 짜리 넣기 타일에서는 같은 굵기가 뭉툭해 보여 더 가늘게 준다.
62
+ */
63
+ export declare function Icon({ name, size, stroke }: {
64
+ name: IconName | string;
65
+ size?: number;
66
+ stroke?: number;
67
+ }): import("react").JSX.Element | null;
68
+ export {};
69
+ //# sourceMappingURL=Icon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../src/Icon.tsx"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,QAAA,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBC,CAAA;AAEV,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,GAAG,CAAA;AAEvC;;;GAGG;AACH,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,IAAS,EAAE,MAAY,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,sCAIlH"}
package/dist/Icon.js ADDED
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalSpaceAround, ArrowLeftRight, BadgeInfo, Ban, BetweenHorizontalStart, BetweenVerticalStart, Bold, Braces, CircleCheck, CirclePlay, Code, Columns2, GitBranch, Heading2, Heading3, Highlighter, Image as ImageIcon, Info, Italic, Link as LinkIcon, List, ListOrdered, Minus, Move, PaintBucket, Pencil, Plus, Quote, Square, SquareSplitHorizontal, Strikethrough, Ungroup, Table as TableIcon, TableCellsMerge, TableCellsSplit, TableProperties, TextQuote, Trash2, TriangleAlert, Type, Underline, } from 'lucide-react';
3
+ /**
4
+ * 툴바 아이콘 — lucide-react 를 쓴다.
5
+ *
6
+ * 직접 그리지 않는 이유: 손으로 그린 SVG 는 획 굵기·여백·시각 중심이 제각각이라
7
+ * 나란히 놓으면 티가 나고, 아이콘이 늘 때마다 그 부담이 커진다.
8
+ * template-react-admin 도 lucide-react 를 쓰므로 스택도 맞는다.
9
+ *
10
+ * 호출부는 이름만 알면 되게 얇게 감싼다 — 나중에 라이브러리를 바꿔도 여기만 고친다.
11
+ * (라이브러리 분리 시 이 파일은 react 패키지로 간다. core 는 아이콘을 모른다.)
12
+ */
13
+ const MAP = {
14
+ bold: Bold, italic: Italic, underline: Underline, strike: Strikethrough,
15
+ code: Code, highlight: Highlighter,
16
+ h2: Heading2, h3: Heading3,
17
+ bullet: List, ordered: ListOrdered,
18
+ alignLeft: AlignLeft, alignCenter: AlignCenter, alignRight: AlignRight, alignJustify: AlignJustify,
19
+ // 줄 간격 — 위아래 선 사이의 덩어리. 「행간」을 글자 없이 말하는 가장 흔한 그림이다.
20
+ lineHeight: AlignVerticalSpaceAround,
21
+ // 인용은 따옴표 덩어리(Quote)가 아니라 **따옴표+글줄**. 큰 인용만 덩어리 쪽을 쓴다.
22
+ quote: TextQuote, pullQuote: Quote, divider: Minus,
23
+ /* 소스코드는 네모 상자가 아니라 **중괄호**다. 네이버도 `{ }` 를 쓴다 —
24
+ 넣기 줄에 네모 상자가 셋이나 서면(사진·영상·코드) 서로 구별이 안 되고 줄이 둔해 보인다. */
25
+ codeBlock: Braces,
26
+ diagram: GitBranch,
27
+ info: Info, tip: CircleCheck, warn: TriangleAlert, callout: BadgeInfo,
28
+ // 영상은 **동그란 재생 표시**. 네모 무리 속에서 혼자 둥글어 한눈에 갈린다.
29
+ image: ImageIcon, table: TableIcon, video: CirclePlay, link: LinkIcon,
30
+ rowPlus: BetweenHorizontalStart, colPlus: BetweenVerticalStart,
31
+ trash: Trash2, plus: Plus, pencil: Pencil, bucket: PaintBucket, text: Type, none: Ban,
32
+ move: Move,
33
+ // 사진 행 — 합치기(두 칸)·풀기(가운데를 가름)·한 장 빼기·자리 바꿈
34
+ rowMerge: Columns2, rowSplit: SquareSplitHorizontal, rowLift: Ungroup, swap: ArrowLeftRight,
35
+ tableStyle: TableProperties, tableBorder: Square, cellMerge: TableCellsMerge, cellSplit: TableCellsSplit,
36
+ };
37
+ /**
38
+ * `stroke` 는 획 굵기다. 기본 1.9 는 17px 아이콘 기준이고,
39
+ * 20px 짜리 넣기 타일에서는 같은 굵기가 뭉툭해 보여 더 가늘게 준다.
40
+ */
41
+ export function Icon({ name, size = 17, stroke = 1.9 }) {
42
+ const C = MAP[name];
43
+ if (!C)
44
+ return null;
45
+ return _jsx(C, { size: size, strokeWidth: stroke, "aria-hidden": "true" });
46
+ }
47
+ //# sourceMappingURL=Icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Icon.js","sourceRoot":"","sources":["../src/Icon.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,wBAAwB,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,EAAE,sBAAsB,EAAE,oBAAoB,EACxJ,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,SAAS,EAAE,IAAI,EAC3H,MAAM,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EACnF,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO,EAC5D,KAAK,IAAI,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,SAAS,EAChF,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,GACvC,MAAM,cAAc,CAAA;AAErB;;;;;;;;;GASG;AACH,MAAM,GAAG,GAAG;IACV,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa;IACvE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW;IAClC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ;IAC1B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW;IAClC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY;IAClG,oDAAoD;IACpD,UAAU,EAAE,wBAAwB;IACpC,uDAAuD;IACvD,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK;IAClD;8DAC0D;IAC1D,SAAS,EAAE,MAAM;IACjB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS;IACrE,+CAA+C;IAC/C,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ;IACrE,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,oBAAoB;IAC9D,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG;IACrF,IAAI,EAAE,IAAI;IACV,2CAA2C;IAC3C,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc;IAC3F,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe;CAChG,CAAA;AAIV;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,GAAG,GAAG,EAA+D;IACjH,MAAM,CAAC,GAAG,GAAG,CAAC,IAAgB,CAAC,CAAA;IAC/B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACnB,OAAO,KAAC,CAAC,IAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,iBAAc,MAAM,GAAG,CAAA;AAClE,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type Editor } from '@tiptap/react';
2
+ /**
3
+ * 현재 줄 왼쪽 여백에 뜨는 `+`.
4
+ *
5
+ * FloatingMenu 를 안 쓰는 이유: 그건 빈 텍스트블록에서만 뜨고 기준이 커서다.
6
+ * 여기서는 커서가 있는 **최상위 블록**이 기준이라 표 오버레이와 같은 방식(fixed 좌표)으로 그린다.
7
+ *
8
+ * 편집면에 패딩을 더해 여백을 만들지 않는다 — 발행면과 기하가 어긋난다.
9
+ * 여백이 없으면 왼쪽 끝에 붙여 클램프한다.
10
+ */
11
+ export declare function InsertHandle({ editor }: {
12
+ editor: Editor;
13
+ }): import("react").JSX.Element | null;
14
+ //# sourceMappingURL=InsertHandle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InsertHandle.d.ts","sourceRoot":"","sources":["../src/InsertHandle.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAO3D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,sCAsE1D"}
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useRef, useState } from 'react';
3
+ import { useDismiss } from './useDismiss.js';
4
+ import { insertImageFiles, pickImageFiles } from '@storkspear/post-editor-core/image';
5
+ import { useEditorState } from '@tiptap/react';
6
+ import { Icon } from './Icon.js';
7
+ import { VariantMenu } from './VariantMenu.js';
8
+ import { dividerItems } from './objects/divider.js';
9
+ import { quoteItems } from './objects/quote.js';
10
+ import { DIVIDER_KINDS, QUOTE_VARIANTS } from '@storkspear/post-editor-core/vocab';
11
+ /**
12
+ * 현재 줄 왼쪽 여백에 뜨는 `+`.
13
+ *
14
+ * FloatingMenu 를 안 쓰는 이유: 그건 빈 텍스트블록에서만 뜨고 기준이 커서다.
15
+ * 여기서는 커서가 있는 **최상위 블록**이 기준이라 표 오버레이와 같은 방식(fixed 좌표)으로 그린다.
16
+ *
17
+ * 편집면에 패딩을 더해 여백을 만들지 않는다 — 발행면과 기하가 어긋난다.
18
+ * 여백이 없으면 왼쪽 끝에 붙여 클램프한다.
19
+ */
20
+ export function InsertHandle({ editor }) {
21
+ const [menu, setMenu] = useState(null);
22
+ const anchor = useRef(null);
23
+ const spot = useEditorState({
24
+ editor,
25
+ selector: ({ editor: e }) => {
26
+ if (!e.isEditable)
27
+ return null;
28
+ const { $from } = e.state.selection;
29
+ if ($from.depth < 1)
30
+ return null;
31
+ try {
32
+ const pos = $from.before(1);
33
+ const dom = e.view.nodeDOM(pos);
34
+ const el = dom instanceof HTMLElement ? dom : dom?.parentElement;
35
+ if (!el)
36
+ return null;
37
+ const r = el.getBoundingClientRect();
38
+ return { pos, left: r.left, top: r.top, lineH: parseFloat(getComputedStyle(el).lineHeight) || 24 };
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ },
44
+ });
45
+ useDismiss(menu !== null, anchor, useCallback(() => setMenu(null), []));
46
+ if (!spot)
47
+ return null;
48
+ // 폰에서는 붙일 여백이 없다 — 아랫줄의 넣기 버튼이 같은 일을 한다.
49
+ // 터치 여부만 보면 좁은 데스크톱 창에서 여백을 뚫고 나오므로 폭도 같이 본다.
50
+ if (window.innerWidth <= 640 || window.matchMedia('(pointer: coarse)').matches)
51
+ return null;
52
+ const left = Math.max(4, spot.left - 36);
53
+ const top = spot.top + Math.max(0, (spot.lineH - 28) / 2);
54
+ return (_jsxs("div", { className: "ins-anchor", "data-editor-ui": "", style: { left, top }, ref: anchor, children: [_jsx("button", { type: "button", className: "ins-btn", title: "\uC5EC\uAE30\uC5D0 \uB123\uAE30", "aria-label": "\uC5EC\uAE30\uC5D0 \uB123\uAE30", "aria-haspopup": "menu", "aria-expanded": menu !== null, onMouseDown: (e) => e.preventDefault(), onClick: () => setMenu(menu ? null : 'root'), children: _jsx(Icon, { name: "plus", size: 16 }) }), menu === 'root' && (_jsxs("div", { className: "tb-menu ins-menu", role: "menu", children: [_jsxs("button", { onClick: () => { editor.chain().focus().toggleCodeBlock().run(); setMenu(null); }, children: [_jsx(Icon, { name: "codeBlock" }), _jsxs("span", { children: [_jsx("b", { children: "\uCF54\uB4DC" }), _jsx("i", { children: "\uBB38\uBC95 \uC0C9\u00B7\uC904 \uBC88\uD638" })] })] }), _jsxs("button", { onClick: () => {
55
+ setMenu(null);
56
+ void pickImageFiles().then((files) => { if (files.length)
57
+ void insertImageFiles(editor, files); });
58
+ }, children: [_jsx(Icon, { name: "image" }), _jsxs("span", { children: [_jsx("b", { children: "\uC0AC\uC9C4" }), _jsx("i", { children: "\uACE0\uB974\uAE30 \u00B7 \uB04C\uC5B4\uB193\uAE30 \u00B7 \uBD99\uC5EC\uB123\uAE30" })] })] }), _jsxs("button", { onClick: () => setMenu('divider'), children: [_jsx(Icon, { name: "divider" }), _jsxs("span", { children: [_jsx("b", { children: "\uAD6C\uBD84\uC120" }), _jsxs("i", { children: [DIVIDER_KINDS.length, "\uAC00\uC9C0 \uBAA8\uC591"] })] })] }), _jsxs("button", { onClick: () => setMenu('quote'), children: [_jsx(Icon, { name: "quote" }), _jsxs("span", { children: [_jsx("b", { children: "\uC778\uC6A9\uAD6C" }), _jsxs("i", { children: [QUOTE_VARIANTS.length, "\uAC00\uC9C0 \uBAA8\uC591"] })] })] }), _jsxs("button", { onClick: () => { editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); setMenu(null); }, children: [_jsx(Icon, { name: "table" }), _jsx("span", { children: _jsx("b", { children: "\uD45C" }) })] })] })), menu === 'divider' && (_jsx(VariantMenu, { ariaLabel: "\uAD6C\uBD84\uC120 \uBAA8\uC591", columns: 4, anchorRef: anchor, items: dividerItems, onPick: (k) => editor.chain().focus().setDivider({ kind: k, align: 'center' }).run(), onClose: () => setMenu(null) })), menu === 'quote' && (_jsx(VariantMenu, { ariaLabel: "\uC778\uC6A9 \uBAA8\uC591", columns: 3, anchorRef: anchor, items: quoteItems, onPick: (v) => editor.chain().focus().setQuoteVariant(v).run(), onClose: () => setMenu(null) }))] }));
59
+ }
60
+ //# sourceMappingURL=InsertHandle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InsertHandle.js","sourceRoot":"","sources":["../src/InsertHandle.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { type CSSProperties, type ReactNode, type RefObject } from 'react';
2
+ import { type IconName } from './Icon.js';
3
+ export type PickOption<T extends string> = {
4
+ value: T;
5
+ label: string;
6
+ /** 이름을 그 글꼴로 보여 줄 때만 (글꼴 고르기) */
7
+ font?: string;
8
+ };
9
+ /**
10
+ * 이름을 늘어놓고 하나 고르는 메뉴 — `<select>` 를 대신한다.
11
+ *
12
+ * `<select>` 를 안 쓰는 이유: macOS·윈도는 option 을 **OS 메뉴**로 그려서
13
+ * 우리 CSS 가 하나도 안 먹는다. 그래서 문단 스타일·글자 크기만 시커먼 시스템 메뉴로 떠서
14
+ * 나머지 편집 UI 와 따로 놀았다.
15
+ * 글꼴 메뉴는 같은 이유로 진작에 직접 만들어 뒀고 — 이제 셋이 한 부품을 쓴다.
16
+ */
17
+ export declare function PickMenu<T extends string>({ items, value, onPick, onClose, anchorRef, width, ariaLabel, align, }: {
18
+ items: readonly PickOption<T>[];
19
+ value: T;
20
+ onPick: (v: T) => void;
21
+ onClose: () => void;
22
+ anchorRef: RefObject<HTMLElement | null>;
23
+ width?: number;
24
+ ariaLabel: string;
25
+ align?: 'left' | 'center';
26
+ }): import("react").JSX.Element;
27
+ /**
28
+ * 고르는 칸 한 벌 — 버튼 + 메뉴.
29
+ * 툴바에 넷(글꼴·문단·크기·언어)이 같은 모양으로 서므로 여는 상태까지 여기서 갖는다.
30
+ */
31
+ export declare function Picker<T extends string>({ items, value, onPick, title, width, className, style, align, children, }: {
32
+ items: readonly PickOption<T>[];
33
+ value: T;
34
+ onPick: (v: T) => void;
35
+ title: string;
36
+ width?: number;
37
+ className?: string;
38
+ style?: CSSProperties;
39
+ align?: 'left' | 'center';
40
+ /** 버튼에 보일 것 — 없으면 고른 항목의 이름 */
41
+ children?: ReactNode;
42
+ }): import("react").JSX.Element;
43
+ /**
44
+ * 아이콘만 세로로 늘어놓는 메뉴 — 목록·정렬이 쓴다(네이버 블로그와 같은 모양).
45
+ * 글자 설명이 없어도 아이콘 넷이면 무엇인지 다 말한다.
46
+ */
47
+ export declare function IconMenu<T extends string>({ items, value, onPick, onClose, anchorRef, ariaLabel, footer, }: {
48
+ items: readonly {
49
+ value: T;
50
+ icon: IconName;
51
+ label: string;
52
+ }[];
53
+ value: T | null;
54
+ onPick: (v: T) => void;
55
+ onClose: () => void;
56
+ anchorRef: RefObject<HTMLElement | null>;
57
+ ariaLabel: string;
58
+ /** 목록 아래에 덧붙이는 줄 (예: 목록의 마커 모양) */
59
+ footer?: ReactNode;
60
+ }): import("react").JSX.Element;
61
+ //# sourceMappingURL=PickMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PickMenu.d.ts","sourceRoot":"","sources":["../src/PickMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAI7G,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE5C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI;IACzC,KAAK,EAAE,CAAC,CAAA;IACR,KAAK,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,EACzC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAW,EAAE,SAAS,EAAE,KAAc,GACjF,EAAE;IACD,KAAK,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/B,KAAK,EAAE,CAAC,CAAA;IACR,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAA;IACtB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CAC1B,+BAsCA;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,EACvC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAW,EAAE,SAAc,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,GACjF,EAAE;IACD,KAAK,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/B,KAAK,EAAE,CAAC,CAAA;IACR,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,+BAiBA;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,EACzC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAC5D,EAAE;IACD,KAAK,EAAE,SAAS;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC7D,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAA;IACtB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,mCAAmC;IACnC,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,+BAoBA"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef, useState } from 'react';
3
+ import { useDismiss } from './useDismiss.js';
4
+ import { useAnchoredPos } from './useAnchoredPos.js';
5
+ import { Portal } from './Popover.js';
6
+ import { Icon } from './Icon.js';
7
+ /**
8
+ * 이름을 늘어놓고 하나 고르는 메뉴 — `<select>` 를 대신한다.
9
+ *
10
+ * `<select>` 를 안 쓰는 이유: macOS·윈도는 option 을 **OS 메뉴**로 그려서
11
+ * 우리 CSS 가 하나도 안 먹는다. 그래서 문단 스타일·글자 크기만 시커먼 시스템 메뉴로 떠서
12
+ * 나머지 편집 UI 와 따로 놀았다.
13
+ * 글꼴 메뉴는 같은 이유로 진작에 직접 만들어 뒀고 — 이제 셋이 한 부품을 쓴다.
14
+ */
15
+ export function PickMenu({ items, value, onPick, onClose, anchorRef, width = 150, ariaLabel, align = 'left', }) {
16
+ const ref = useRef(null);
17
+ const pos = useAnchoredPos(anchorRef, true, width, ref);
18
+ useDismiss(true, ref, onClose, anchorRef);
19
+ // 크기 16줄·언어 27줄은 화면을 넘는다 — 열자마자 지금 값이 보이게 끌어온다.
20
+ // **딱 한 번만**: useAnchoredPos 는 스크롤마다 새 좌표 객체를 만들어 이 효과를 다시 돌리는데,
21
+ // 그러면 메뉴를 굴릴 때마다 고른 줄로 도로 튄다. 자리를 잡은 뒤(pos) 한 번이면 된다.
22
+ const pulled = useRef(false);
23
+ useLayoutEffect(() => {
24
+ if (!pos || pulled.current)
25
+ return;
26
+ pulled.current = true;
27
+ ref.current?.querySelector('.is-on')?.scrollIntoView({ block: 'nearest' });
28
+ }, [pos]);
29
+ return (_jsx(Portal, { active: !!pos, children: _jsx("div", { "data-editor-ui": "", ref: ref, role: "menu", "aria-label": ariaLabel, className: `pm${align === 'center' ? ' pm-center' : ''}${
30
+ // 글꼴 메뉴는 이름을 **그 글꼴로** 그린다 — 손글씨는 획이 가늘고 작게 앉아
31
+ // 본문과 같은 크기로는 읽히지 않는다. 이 메뉴만 키운다.
32
+ items.some((i) => i.font !== undefined) ? ' pm-fonts' : ''}${pos ? ' is-fixed' : ''}`, style: {
33
+ width,
34
+ ...(pos ? { position: 'fixed', left: pos.left, top: pos.top, maxHeight: Math.min(pos.maxHeight, 430) } : null),
35
+ }, children: items.map((it) => (_jsx("button", { type: "button", role: "menuitemradio", "aria-checked": value === it.value, className: `pm-item${value === it.value ? ' is-on' : ''}`, style: it.font !== undefined ? { fontFamily: it.font || 'var(--font-body)' } : undefined, onMouseDown: (e) => e.preventDefault(), onClick: () => { onPick(it.value); onClose(); }, children: it.label }, it.value))) }) }));
36
+ }
37
+ /**
38
+ * 고르는 칸 한 벌 — 버튼 + 메뉴.
39
+ * 툴바에 넷(글꼴·문단·크기·언어)이 같은 모양으로 서므로 여는 상태까지 여기서 갖는다.
40
+ */
41
+ export function Picker({ items, value, onPick, title, width = 150, className = '', style, align, children, }) {
42
+ const anchor = useRef(null);
43
+ const [open, setOpen] = useState(false);
44
+ const label = children ?? items.find((i) => i.value === value)?.label ?? '';
45
+ return (_jsxs("div", { className: "tb-anchor", ref: anchor, children: [_jsx("button", { type: "button", className: `tb-select ${className}${open ? ' is-open' : ''}`, title: title, "aria-haspopup": "menu", "aria-expanded": open, style: style, onMouseDown: (e) => e.preventDefault(), onClick: () => setOpen((v) => !v), children: label }), open && (_jsx(PickMenu, { ariaLabel: title, items: items, value: value, width: width, align: align, anchorRef: anchor, onPick: onPick, onClose: () => setOpen(false) }))] }));
46
+ }
47
+ /**
48
+ * 아이콘만 세로로 늘어놓는 메뉴 — 목록·정렬이 쓴다(네이버 블로그와 같은 모양).
49
+ * 글자 설명이 없어도 아이콘 넷이면 무엇인지 다 말한다.
50
+ */
51
+ export function IconMenu({ items, value, onPick, onClose, anchorRef, ariaLabel, footer, }) {
52
+ const ref = useRef(null);
53
+ const pos = useAnchoredPos(anchorRef, true, 46, ref);
54
+ useDismiss(true, ref, onClose, anchorRef);
55
+ return (_jsx(Portal, { active: !!pos, children: _jsxs("div", { "data-editor-ui": "", ref: ref, role: "menu", "aria-label": ariaLabel, className: `tb-listmenu${pos ? ' is-fixed' : ''}`, style: pos ? { position: 'fixed', left: pos.left, top: pos.top, maxHeight: pos.maxHeight, overflowY: 'auto' } : undefined, children: [items.map((it) => (_jsx("button", { type: "button", role: "menuitemradio", "aria-checked": value === it.value, className: value === it.value ? 'is-on' : '', title: it.label, "aria-label": it.label, onMouseDown: (e) => e.preventDefault(), onClick: () => { onPick(it.value); onClose(); }, children: _jsx(Icon, { name: it.icon, size: 18 }) }, it.value))), footer] }) }));
56
+ }
57
+ //# sourceMappingURL=PickMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PickMenu.js","sourceRoot":"","sources":["../src/PickMenu.tsx"],"names":[],"mappings":"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"}