@nkzw/mdx-editor 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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/UPSTREAM.md +21 -0
  4. package/dist/EditorIcon.js +75 -0
  5. package/dist/FormatConstants.js +20 -0
  6. package/dist/MDXEditor.js +189 -0
  7. package/dist/MarkdownEditor.js +281 -0
  8. package/dist/PersistentMarkdownEditor.js +358 -0
  9. package/dist/RealmWithPlugins.js +35 -0
  10. package/dist/core.d.ts +3232 -0
  11. package/dist/core.js +354 -0
  12. package/dist/defaultSvgIcons.js +371 -0
  13. package/dist/directive-editors/AdmonitionDirectiveDescriptor.js +28 -0
  14. package/dist/directive-editors/GenericDirectiveEditor.js +37 -0
  15. package/dist/exportMarkdownFromLexical.js +262 -0
  16. package/dist/horizontalRuleShortcut.js +37 -0
  17. package/dist/importMarkdownToLexical.js +172 -0
  18. package/dist/index.d.ts +86 -0
  19. package/dist/index.js +8 -0
  20. package/dist/jsx-editors/GenericJsxEditor.js +84 -0
  21. package/dist/mdastUtilHtmlComment.js +125 -0
  22. package/dist/persistence.d.ts +128 -0
  23. package/dist/persistence.js +4 -0
  24. package/dist/plugins/codeblock/CodeBlockNode.js +183 -0
  25. package/dist/plugins/codeblock/CodeBlockVisitor.js +14 -0
  26. package/dist/plugins/codeblock/MdastCodeVisitor.js +23 -0
  27. package/dist/plugins/codeblock/findCodeBlockDescriptor.js +8 -0
  28. package/dist/plugins/codeblock/index.js +46 -0
  29. package/dist/plugins/codemirror/CodeMirrorEditor.js +145 -0
  30. package/dist/plugins/codemirror/index.js +115 -0
  31. package/dist/plugins/codemirror/useCodeMirrorRef.js +101 -0
  32. package/dist/plugins/core/GenericHTMLNode.js +118 -0
  33. package/dist/plugins/core/LexicalGenericHTMLNodeVisitor.js +15 -0
  34. package/dist/plugins/core/LexicalLinebreakVisitor.js +10 -0
  35. package/dist/plugins/core/LexicalParagraphVisitor.js +10 -0
  36. package/dist/plugins/core/LexicalRootVisitor.js +10 -0
  37. package/dist/plugins/core/LexicalTextVisitor.js +160 -0
  38. package/dist/plugins/core/MdastBreakVisitor.js +10 -0
  39. package/dist/plugins/core/MdastFormattingVisitor.js +81 -0
  40. package/dist/plugins/core/MdastHTMLNode.js +120 -0
  41. package/dist/plugins/core/MdastHTMLVisitor.js +17 -0
  42. package/dist/plugins/core/MdastParagraphVisitor.js +23 -0
  43. package/dist/plugins/core/MdastRootVisitor.js +9 -0
  44. package/dist/plugins/core/MdastTextVisitor.js +16 -0
  45. package/dist/plugins/core/NestedLexicalEditor.js +221 -0
  46. package/dist/plugins/core/PropertyPopover.js +75 -0
  47. package/dist/plugins/core/SharedHistoryPlugin.js +10 -0
  48. package/dist/plugins/core/index.js +692 -0
  49. package/dist/plugins/core/ui/DownshiftAutoComplete.js +89 -0
  50. package/dist/plugins/core/ui/PopoverUtils.js +22 -0
  51. package/dist/plugins/diff-source/DiffSourceWrapper.js +24 -0
  52. package/dist/plugins/diff-source/DiffViewer.js +84 -0
  53. package/dist/plugins/diff-source/SourceEditor.js +60 -0
  54. package/dist/plugins/diff-source/index.js +27 -0
  55. package/dist/plugins/directives/DirectiveNode.js +107 -0
  56. package/dist/plugins/directives/DirectiveVisitor.js +10 -0
  57. package/dist/plugins/directives/MdastDirectiveVisitor.js +30 -0
  58. package/dist/plugins/directives/index.js +45 -0
  59. package/dist/plugins/frontmatter/FrontmatterEditor.js +137 -0
  60. package/dist/plugins/frontmatter/FrontmatterNode.js +70 -0
  61. package/dist/plugins/frontmatter/LexicalFrontmatterVisitor.js +10 -0
  62. package/dist/plugins/frontmatter/MdastFrontmatterVisitor.js +10 -0
  63. package/dist/plugins/frontmatter/index.js +113 -0
  64. package/dist/plugins/headings/LexicalHeadingVisitor.js +11 -0
  65. package/dist/plugins/headings/MdastHeadingVisitor.js +10 -0
  66. package/dist/plugins/headings/index.js +63 -0
  67. package/dist/plugins/image/EditImageToolbar.js +58 -0
  68. package/dist/plugins/image/ImageDialog.js +132 -0
  69. package/dist/plugins/image/ImageEditor.js +279 -0
  70. package/dist/plugins/image/ImageNode.js +187 -0
  71. package/dist/plugins/image/ImagePlaceholder.js +9 -0
  72. package/dist/plugins/image/ImageResizer.js +223 -0
  73. package/dist/plugins/image/LexicalImageVisitor.js +42 -0
  74. package/dist/plugins/image/MdastImageVisitor.js +91 -0
  75. package/dist/plugins/image/index.js +364 -0
  76. package/dist/plugins/jsx/LexicalJsxNode.js +103 -0
  77. package/dist/plugins/jsx/LexicalJsxVisitor.js +27 -0
  78. package/dist/plugins/jsx/LexicalMdxExpressionNode.js +130 -0
  79. package/dist/plugins/jsx/LexicalMdxExpressionVisitor.js +14 -0
  80. package/dist/plugins/jsx/MdastMdxExpressionVisitor.js +11 -0
  81. package/dist/plugins/jsx/MdastMdxJsEsmVisitor.js +8 -0
  82. package/dist/plugins/jsx/MdastMdxJsxElementVisitor.js +28 -0
  83. package/dist/plugins/jsx/index.js +97 -0
  84. package/dist/plugins/jsx/jsxTagName.js +7 -0
  85. package/dist/plugins/link/AutoLinkPlugin.js +18 -0
  86. package/dist/plugins/link/LexicalLinkVisitor.js +10 -0
  87. package/dist/plugins/link/MdastLinkVisitor.js +14 -0
  88. package/dist/plugins/link/index.js +34 -0
  89. package/dist/plugins/link-dialog/LinkDialog.js +262 -0
  90. package/dist/plugins/link-dialog/index.js +304 -0
  91. package/dist/plugins/lists/CheckListPlugin.js +270 -0
  92. package/dist/plugins/lists/LexicalListItemVisitor.js +41 -0
  93. package/dist/plugins/lists/LexicalListVisitor.js +13 -0
  94. package/dist/plugins/lists/MdastListItemVisitor.js +11 -0
  95. package/dist/plugins/lists/MdastListVisitor.js +19 -0
  96. package/dist/plugins/lists/NotesListItemNode.js +22 -0
  97. package/dist/plugins/lists/index.js +111 -0
  98. package/dist/plugins/markdown-shortcut/index.js +114 -0
  99. package/dist/plugins/maxlength/index.js +36 -0
  100. package/dist/plugins/quote/LexicalQuoteVisitor.js +10 -0
  101. package/dist/plugins/quote/MdastBlockQuoteVisitor.js +10 -0
  102. package/dist/plugins/quote/index.js +18 -0
  103. package/dist/plugins/remote/index.js +52 -0
  104. package/dist/plugins/search/index.js +360 -0
  105. package/dist/plugins/table/LexicalTableVisitor.js +10 -0
  106. package/dist/plugins/table/MdastTableVisitor.js +10 -0
  107. package/dist/plugins/table/TableEditor.js +527 -0
  108. package/dist/plugins/table/TableNode.js +208 -0
  109. package/dist/plugins/table/index.js +66 -0
  110. package/dist/plugins/thematic-break/LexicalThematicBreakVisitor.js +10 -0
  111. package/dist/plugins/thematic-break/MdastThematicBreakVisitor.js +10 -0
  112. package/dist/plugins/thematic-break/index.js +27 -0
  113. package/dist/plugins/toolbar/components/BlockTypeSelect.js +62 -0
  114. package/dist/plugins/toolbar/components/BoldItalicUnderlineToggles.js +98 -0
  115. package/dist/plugins/toolbar/components/ChangeAdmonitionType.js +43 -0
  116. package/dist/plugins/toolbar/components/ChangeCodeMirrorLanguage.js +42 -0
  117. package/dist/plugins/toolbar/components/CodeToggle.js +21 -0
  118. package/dist/plugins/toolbar/components/CreateLink.js +24 -0
  119. package/dist/plugins/toolbar/components/DiffSourceToggleWrapper.js +42 -0
  120. package/dist/plugins/toolbar/components/HighlightToggle.js +28 -0
  121. package/dist/plugins/toolbar/components/InsertAdmonition.js +34 -0
  122. package/dist/plugins/toolbar/components/InsertCodeBlock.js +23 -0
  123. package/dist/plugins/toolbar/components/InsertFrontmatter.js +28 -0
  124. package/dist/plugins/toolbar/components/InsertImage.js +29 -0
  125. package/dist/plugins/toolbar/components/InsertTable.js +25 -0
  126. package/dist/plugins/toolbar/components/InsertThematicBreak.js +23 -0
  127. package/dist/plugins/toolbar/components/KitchenSinkToolbar.js +82 -0
  128. package/dist/plugins/toolbar/components/ListsToggle.js +29 -0
  129. package/dist/plugins/toolbar/components/UndoRedo.js +60 -0
  130. package/dist/plugins/toolbar/index.js +32 -0
  131. package/dist/plugins/toolbar/primitives/DialogButton.js +130 -0
  132. package/dist/plugins/toolbar/primitives/TooltipWrap.js +17 -0
  133. package/dist/plugins/toolbar/primitives/select.js +76 -0
  134. package/dist/plugins/toolbar/primitives/toolbar.js +144 -0
  135. package/dist/registerCodeBoundaryEscape.js +40 -0
  136. package/dist/styles/lexical-theme.module.css.js +62 -0
  137. package/dist/styles/lexicalTheme.js +32 -0
  138. package/dist/styles/ui.module.css.js +296 -0
  139. package/dist/styles.css +2838 -0
  140. package/dist/utils/detectMac.js +16 -0
  141. package/dist/utils/fp.js +44 -0
  142. package/dist/utils/isPartOftheEditorUI.js +12 -0
  143. package/dist/utils/lexicalHelpers.js +185 -0
  144. package/dist/utils/makeHslTransparent.js +6 -0
  145. package/dist/utils/mergeStyleAttributes.js +22 -0
  146. package/dist/utils/uuid4.js +10 -0
  147. package/dist/utils/voidEmitter.js +15 -0
  148. package/package.json +133 -0
@@ -0,0 +1,279 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
+ import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
5
+ import { mergeRegister } from "@lexical/utils";
6
+ import { useCellValues } from "@mdxeditor/gurx";
7
+ import classNames from "classnames";
8
+ import { $isNodeSelection, $getSelection, $getNodeByKey, $setSelection, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_LOW, CLICK_COMMAND, DRAGSTART_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND } from "lexical";
9
+ import { imagePlaceholder$, disableImageResize$, allowSetImageDimensions$, imagePreviewHandler$, editImageToolbarComponent$ } from "./index.js";
10
+ import styles from "../../styles/ui.module.css.js";
11
+ import { readOnly$ } from "../core/index.js";
12
+ import { $isImageNode } from "./ImageNode.js";
13
+ import ImageResizer from "./ImageResizer.js";
14
+ const BROKEN_IMG_URI = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(
15
+ /* xml */
16
+ `
17
+ <svg id="imgLoadError" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
18
+ <rect x="0" y="0" width="100" height="100" fill="none" stroke="red" stroke-width="4" stroke-dasharray="4" />
19
+ <text x="50" y="55" text-anchor="middle" font-size="20" fill="red">⚠️</text>
20
+ </svg>
21
+ `
22
+ );
23
+ const imgCache = {
24
+ __cache: {},
25
+ read(src) {
26
+ if (!this.__cache[src]) {
27
+ this.__cache[src] = new Promise((resolve) => {
28
+ const img = new Image();
29
+ img.onerror = () => {
30
+ this.__cache[src] = BROKEN_IMG_URI;
31
+ resolve();
32
+ };
33
+ img.onload = () => {
34
+ this.__cache[src] = src;
35
+ resolve();
36
+ };
37
+ img.src = src;
38
+ });
39
+ }
40
+ if (this.__cache[src] instanceof Promise) {
41
+ throw this.__cache[src];
42
+ }
43
+ return this.__cache[src];
44
+ }
45
+ };
46
+ function LazyImage({
47
+ title,
48
+ alt,
49
+ className,
50
+ imageRef,
51
+ src,
52
+ width,
53
+ height
54
+ }) {
55
+ return /* @__PURE__ */ jsx(
56
+ "img",
57
+ {
58
+ className: className ?? void 0,
59
+ alt,
60
+ src: imgCache.read(src),
61
+ title,
62
+ ref: imageRef,
63
+ draggable: "false",
64
+ width,
65
+ height
66
+ }
67
+ );
68
+ }
69
+ function ImageEditor({ src, title, alt, nodeKey, width, height, rest }) {
70
+ const [ImagePlaceholderComponent, disableImageResize, allowSetImageDimensions, imagePreviewHandler, readOnly, EditImageToolbar] = useCellValues(
71
+ imagePlaceholder$,
72
+ disableImageResize$,
73
+ allowSetImageDimensions$,
74
+ imagePreviewHandler$,
75
+ readOnly$,
76
+ editImageToolbarComponent$
77
+ );
78
+ const imageRef = React.useRef(null);
79
+ const buttonRef = React.useRef(null);
80
+ const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
81
+ const [editor] = useLexicalComposerContext();
82
+ const [selection, setSelection] = React.useState(null);
83
+ const activeEditorRef = React.useRef(null);
84
+ const [isResizing, setIsResizing] = React.useState(false);
85
+ const [imageSource, setImageSource] = React.useState(null);
86
+ const [initialImagePath, setInitialImagePath] = React.useState(null);
87
+ const onDelete = React.useCallback(
88
+ (payload) => {
89
+ if (isSelected && $isNodeSelection($getSelection())) {
90
+ const event = payload;
91
+ event.preventDefault();
92
+ const node = $getNodeByKey(nodeKey);
93
+ if ($isImageNode(node)) {
94
+ node.remove();
95
+ }
96
+ }
97
+ return false;
98
+ },
99
+ [isSelected, nodeKey]
100
+ );
101
+ const onEnter = React.useCallback(
102
+ (event) => {
103
+ const latestSelection = $getSelection();
104
+ const buttonElem = buttonRef.current;
105
+ if (isSelected && $isNodeSelection(latestSelection) && latestSelection.getNodes().length === 1) {
106
+ if (buttonElem !== null && buttonElem !== document.activeElement) {
107
+ event.preventDefault();
108
+ buttonElem.focus();
109
+ return true;
110
+ }
111
+ }
112
+ return false;
113
+ },
114
+ [isSelected]
115
+ );
116
+ const onEscape = React.useCallback(
117
+ (event) => {
118
+ if (buttonRef.current === event.target) {
119
+ $setSelection(null);
120
+ editor.update(() => {
121
+ setSelected(true);
122
+ const parentRootElement = editor.getRootElement();
123
+ if (parentRootElement !== null) {
124
+ parentRootElement.focus();
125
+ }
126
+ });
127
+ return true;
128
+ }
129
+ return false;
130
+ },
131
+ [editor, setSelected]
132
+ );
133
+ React.useEffect(() => {
134
+ if (imagePreviewHandler) {
135
+ const callPreviewHandler = async () => {
136
+ if (!initialImagePath) setInitialImagePath(src);
137
+ const updatedSrc = await imagePreviewHandler(src);
138
+ setImageSource(updatedSrc);
139
+ };
140
+ callPreviewHandler().catch((e) => {
141
+ console.error(e);
142
+ });
143
+ } else {
144
+ setImageSource(src);
145
+ }
146
+ }, [src, imagePreviewHandler, initialImagePath]);
147
+ React.useEffect(() => {
148
+ if (allowSetImageDimensions && imageRef.current) {
149
+ const { current: image } = imageRef;
150
+ syncDimensionWithImageResizer(image, "width", width);
151
+ syncDimensionWithImageResizer(image, "height", height);
152
+ }
153
+ }, [allowSetImageDimensions, width, height]);
154
+ React.useEffect(() => {
155
+ let isMounted = true;
156
+ const unregister = mergeRegister(
157
+ editor.registerUpdateListener(({ editorState }) => {
158
+ if (isMounted) {
159
+ setSelection(editorState.read(() => $getSelection()));
160
+ }
161
+ }),
162
+ editor.registerCommand(
163
+ SELECTION_CHANGE_COMMAND,
164
+ (_, activeEditor) => {
165
+ activeEditorRef.current = activeEditor;
166
+ return false;
167
+ },
168
+ COMMAND_PRIORITY_LOW
169
+ ),
170
+ editor.registerCommand(
171
+ CLICK_COMMAND,
172
+ (payload) => {
173
+ const event = payload;
174
+ if (isResizing) {
175
+ return true;
176
+ }
177
+ if (event.target === imageRef.current) {
178
+ if (event.shiftKey) {
179
+ setSelected(!isSelected);
180
+ } else {
181
+ clearSelection();
182
+ setSelected(true);
183
+ }
184
+ return true;
185
+ }
186
+ return false;
187
+ },
188
+ COMMAND_PRIORITY_LOW
189
+ ),
190
+ editor.registerCommand(
191
+ DRAGSTART_COMMAND,
192
+ (event) => {
193
+ if (event.target === imageRef.current) {
194
+ event.preventDefault();
195
+ return true;
196
+ }
197
+ return false;
198
+ },
199
+ COMMAND_PRIORITY_LOW
200
+ ),
201
+ editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
202
+ editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
203
+ editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),
204
+ editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW)
205
+ );
206
+ return () => {
207
+ isMounted = false;
208
+ unregister();
209
+ };
210
+ }, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
211
+ const onResizeEnd = (nextWidth, nextHeight) => {
212
+ setTimeout(() => {
213
+ setIsResizing(false);
214
+ }, 200);
215
+ editor.update(() => {
216
+ const node = $getNodeByKey(nodeKey);
217
+ if ($isImageNode(node)) {
218
+ node.setWidthAndHeight(nextWidth, nextHeight);
219
+ }
220
+ });
221
+ };
222
+ const onResizeStart = () => {
223
+ setIsResizing(true);
224
+ };
225
+ const draggable = $isNodeSelection(selection);
226
+ const isFocused = isSelected;
227
+ const passedClassName = React.useMemo(() => {
228
+ if (rest.length === 0) {
229
+ return null;
230
+ }
231
+ const className = rest.find((attr) => attr.type === "mdxJsxAttribute" && (attr.name === "class" || attr.name === "className"));
232
+ if (className) {
233
+ return className.value;
234
+ }
235
+ return null;
236
+ }, [rest]);
237
+ return imageSource !== null ? /* @__PURE__ */ jsx(React.Suspense, { fallback: ImagePlaceholderComponent ? /* @__PURE__ */ jsx(ImagePlaceholderComponent, {}) : null, children: /* @__PURE__ */ jsxs("div", { className: styles.imageWrapper, "data-editor-block-type": "image", children: [
238
+ /* @__PURE__ */ jsx("div", { draggable, children: /* @__PURE__ */ jsx(
239
+ LazyImage,
240
+ {
241
+ width,
242
+ height,
243
+ className: classNames(
244
+ {
245
+ [styles.focusedImage]: isFocused
246
+ },
247
+ passedClassName
248
+ ),
249
+ src: imageSource,
250
+ title: title ?? "",
251
+ alt: alt ?? "",
252
+ imageRef
253
+ }
254
+ ) }),
255
+ draggable && isFocused && !disableImageResize && /* @__PURE__ */ jsx(ImageResizer, { editor, imageRef, onResizeStart, onResizeEnd }),
256
+ readOnly || /* @__PURE__ */ jsx(
257
+ EditImageToolbar,
258
+ {
259
+ nodeKey,
260
+ imageSource,
261
+ initialImagePath,
262
+ title: title ?? "",
263
+ alt: alt ?? "",
264
+ width,
265
+ height
266
+ }
267
+ )
268
+ ] }) }) : null;
269
+ }
270
+ const syncDimensionWithImageResizer = (image, key, value) => {
271
+ if (typeof value === "number") {
272
+ image.style[key] = `${value}px`;
273
+ } else {
274
+ image.style.removeProperty(key);
275
+ }
276
+ };
277
+ export {
278
+ ImageEditor
279
+ };
@@ -0,0 +1,187 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { DecoratorNode } from "lexical";
3
+ import { ImageEditor } from "./ImageEditor.js";
4
+ function convertImageElement(domNode) {
5
+ if (domNode instanceof HTMLImageElement) {
6
+ const { alt: altText, src, title, width, height } = domNode;
7
+ const node = $createImageNode({ altText, src, title, width: width || void 0, height: height || void 0 });
8
+ return { node };
9
+ }
10
+ return null;
11
+ }
12
+ class ImageNode extends DecoratorNode {
13
+ /** @internal */
14
+ __src;
15
+ /** @internal */
16
+ __altText;
17
+ /** @internal */
18
+ __title;
19
+ /** @internal */
20
+ __width;
21
+ /** @internal */
22
+ __height;
23
+ /** @internal */
24
+ __rest;
25
+ /** @internal */
26
+ static getType() {
27
+ return "image";
28
+ }
29
+ /** @internal */
30
+ static clone(node) {
31
+ return new ImageNode(node.__src, node.__altText, node.__title, node.__width, node.__height, node.__rest, node.__key);
32
+ }
33
+ /** @internal */
34
+ afterCloneFrom(prevNode) {
35
+ super.afterCloneFrom(prevNode);
36
+ this.__src = prevNode.__src;
37
+ this.__altText = prevNode.__altText;
38
+ this.__title = prevNode.__title;
39
+ this.__width = prevNode.__width;
40
+ this.__height = prevNode.__height;
41
+ this.__rest = prevNode.__rest;
42
+ }
43
+ /** @internal */
44
+ static importJSON(serializedNode) {
45
+ const { altText, title, src, width, rest, height } = serializedNode;
46
+ const node = $createImageNode({
47
+ altText,
48
+ title,
49
+ src,
50
+ height,
51
+ width,
52
+ rest
53
+ });
54
+ return node;
55
+ }
56
+ /** @internal */
57
+ exportDOM() {
58
+ const element = document.createElement("img");
59
+ element.setAttribute("src", this.__src);
60
+ element.setAttribute("alt", this.__altText);
61
+ if (this.__title) {
62
+ element.setAttribute("title", this.__title);
63
+ }
64
+ if (this.__width !== "inherit" && this.__width) {
65
+ element.setAttribute("width", this.__width.toString());
66
+ }
67
+ if (this.__height !== "inherit" && this.__height) {
68
+ element.setAttribute("height", this.__height.toString());
69
+ }
70
+ return { element };
71
+ }
72
+ /** @internal */
73
+ static importDOM() {
74
+ return {
75
+ img: () => ({
76
+ conversion: convertImageElement,
77
+ priority: 0
78
+ })
79
+ };
80
+ }
81
+ /**
82
+ * Constructs a new {@link ImageNode} with the specified image parameters.
83
+ * Use {@link $createImageNode} to construct one.
84
+ */
85
+ constructor(src, altText, title, width, height, rest, key) {
86
+ super(key);
87
+ this.__src = src;
88
+ this.__title = title;
89
+ this.__altText = altText;
90
+ this.__width = width ?? "inherit";
91
+ this.__height = height ?? "inherit";
92
+ this.__rest = rest ?? [];
93
+ }
94
+ /** @internal */
95
+ exportJSON() {
96
+ return {
97
+ altText: this.getAltText(),
98
+ title: this.getTitle(),
99
+ height: this.__height === "inherit" ? void 0 : this.__height,
100
+ width: this.__width === "inherit" ? void 0 : this.__width,
101
+ src: this.getSrc(),
102
+ rest: this.__rest,
103
+ type: "image",
104
+ version: 1
105
+ };
106
+ }
107
+ /**
108
+ * Sets the image dimensions
109
+ */
110
+ setWidthAndHeight(width, height) {
111
+ const writable = this.getWritable();
112
+ writable.__width = width;
113
+ writable.__height = height;
114
+ }
115
+ /** @internal */
116
+ createDOM(config, _editor) {
117
+ const span = document.createElement("span");
118
+ const theme = config.theme;
119
+ const className = theme.image;
120
+ if (className !== void 0) {
121
+ span.className = className;
122
+ }
123
+ return span;
124
+ }
125
+ /** @internal */
126
+ updateDOM() {
127
+ return false;
128
+ }
129
+ getSrc() {
130
+ return this.__src;
131
+ }
132
+ getAltText() {
133
+ return this.__altText;
134
+ }
135
+ getTitle() {
136
+ return this.__title;
137
+ }
138
+ getHeight() {
139
+ return this.__height;
140
+ }
141
+ getWidth() {
142
+ return this.__width;
143
+ }
144
+ getRest() {
145
+ return this.__rest;
146
+ }
147
+ setTitle(title) {
148
+ this.getWritable().__title = title;
149
+ }
150
+ setSrc(src) {
151
+ this.getWritable().__src = src;
152
+ }
153
+ setAltText(altText) {
154
+ this.getWritable().__altText = altText ?? "";
155
+ }
156
+ /** @internal */
157
+ shouldBeSerializedAsElement() {
158
+ return this.__width !== "inherit" || this.__height !== "inherit" || this.__rest.length > 0;
159
+ }
160
+ /** @internal */
161
+ decorate(_parentEditor) {
162
+ return /* @__PURE__ */ jsx(
163
+ ImageEditor,
164
+ {
165
+ src: this.getSrc(),
166
+ title: this.getTitle(),
167
+ nodeKey: this.getKey(),
168
+ width: this.__width,
169
+ height: this.__height,
170
+ alt: this.__altText,
171
+ rest: this.__rest
172
+ }
173
+ );
174
+ }
175
+ }
176
+ function $createImageNode(params) {
177
+ const { altText, title, src, key, width, height, rest } = params;
178
+ return new ImageNode(src, altText, title, width, height, rest, key);
179
+ }
180
+ function $isImageNode(node) {
181
+ return node instanceof ImageNode;
182
+ }
183
+ export {
184
+ $createImageNode,
185
+ $isImageNode,
186
+ ImageNode
187
+ };
@@ -0,0 +1,9 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ImageIcon } from "@radix-ui/react-icons";
3
+ import styles from "../../styles/ui.module.css.js";
4
+ const ImagePlaceholder = () => {
5
+ return /* @__PURE__ */ jsx("div", { className: styles.imagePlaceholder, children: /* @__PURE__ */ jsx(ImageIcon, {}) });
6
+ };
7
+ export {
8
+ ImagePlaceholder
9
+ };
@@ -0,0 +1,223 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import styles from "../../styles/ui.module.css.js";
4
+ import classNames from "classnames";
5
+ function clamp(value, min, max) {
6
+ return Math.min(Math.max(value, min), max);
7
+ }
8
+ const Direction = {
9
+ east: 1 << 0,
10
+ north: 1 << 3,
11
+ south: 1 << 1,
12
+ west: 1 << 2
13
+ };
14
+ function ImageResizer({
15
+ onResizeStart,
16
+ onResizeEnd,
17
+ imageRef,
18
+ maxWidth,
19
+ editor
20
+ }) {
21
+ const controlWrapperRef = useRef(null);
22
+ const userSelect = useRef({
23
+ priority: "",
24
+ value: "default"
25
+ });
26
+ const positioningRef = useRef({
27
+ currentHeight: 0,
28
+ currentWidth: 0,
29
+ direction: 0,
30
+ isResizing: false,
31
+ ratio: 0,
32
+ startHeight: 0,
33
+ startWidth: 0,
34
+ startX: 0,
35
+ startY: 0
36
+ });
37
+ const editorRootElement = editor.getRootElement();
38
+ const maxWidthContainer = maxWidth ?? (editorRootElement !== null ? editorRootElement.getBoundingClientRect().width - 20 : 100);
39
+ const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 : 100;
40
+ const minWidth = 100;
41
+ const minHeight = 100;
42
+ const setStartCursor = (direction) => {
43
+ const ew = direction === Direction.east || direction === Direction.west;
44
+ const ns = direction === Direction.north || direction === Direction.south;
45
+ const nwse = direction & Direction.north && direction & Direction.west || direction & Direction.south && direction & Direction.east;
46
+ const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw";
47
+ if (editorRootElement !== null) {
48
+ editorRootElement.style.setProperty("cursor", `${cursorDir}-resize`, "important");
49
+ }
50
+ if (document.body !== null) {
51
+ document.body.style.setProperty("cursor", `${cursorDir}-resize`, "important");
52
+ userSelect.current.value = document.body.style.getPropertyValue("-webkit-user-select");
53
+ userSelect.current.priority = document.body.style.getPropertyPriority("-webkit-user-select");
54
+ document.body.style.setProperty("-webkit-user-select", `none`, "important");
55
+ }
56
+ };
57
+ const setEndCursor = () => {
58
+ if (editorRootElement !== null) {
59
+ editorRootElement.style.setProperty("cursor", "text");
60
+ }
61
+ if (document.body !== null) {
62
+ document.body.style.setProperty("cursor", "default");
63
+ document.body.style.setProperty("-webkit-user-select", userSelect.current.value, userSelect.current.priority);
64
+ }
65
+ };
66
+ const handlePointerDown = (event, direction) => {
67
+ if (!editor.isEditable()) {
68
+ return;
69
+ }
70
+ const image = imageRef.current;
71
+ const controlWrapper = controlWrapperRef.current;
72
+ if (image !== null && controlWrapper !== null) {
73
+ event.preventDefault();
74
+ const { width, height } = image.getBoundingClientRect();
75
+ const positioning = positioningRef.current;
76
+ positioning.startWidth = width;
77
+ positioning.startHeight = height;
78
+ positioning.ratio = width / height;
79
+ positioning.currentWidth = width;
80
+ positioning.currentHeight = height;
81
+ positioning.startX = event.clientX;
82
+ positioning.startY = event.clientY;
83
+ positioning.isResizing = true;
84
+ positioning.direction = direction;
85
+ setStartCursor(direction);
86
+ onResizeStart();
87
+ controlWrapper.classList.add(styles.imageControlWrapperResizing);
88
+ image.style.height = `${height}px`;
89
+ image.style.width = `${width}px`;
90
+ document.addEventListener("pointermove", handlePointerMove);
91
+ document.addEventListener("pointerup", handlePointerUp);
92
+ }
93
+ };
94
+ const handlePointerMove = (event) => {
95
+ const image = imageRef.current;
96
+ const positioning = positioningRef.current;
97
+ const isHorizontal = positioning.direction & (Direction.east | Direction.west);
98
+ const isVertical = positioning.direction & (Direction.south | Direction.north);
99
+ if (image !== null && positioning.isResizing) {
100
+ if (isHorizontal && isVertical) {
101
+ let diff = Math.floor(positioning.startX - event.clientX);
102
+ diff = positioning.direction & Direction.east ? -diff : diff;
103
+ const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
104
+ const height = width / positioning.ratio;
105
+ image.style.width = `${width}px`;
106
+ image.style.height = `${height}px`;
107
+ positioning.currentHeight = height;
108
+ positioning.currentWidth = width;
109
+ } else if (isVertical) {
110
+ let diff = Math.floor(positioning.startY - event.clientY);
111
+ diff = positioning.direction & Direction.south ? -diff : diff;
112
+ const height = clamp(positioning.startHeight + diff, minHeight, maxHeightContainer);
113
+ image.style.height = `${height}px`;
114
+ positioning.currentHeight = height;
115
+ } else {
116
+ let diff = Math.floor(positioning.startX - event.clientX);
117
+ diff = positioning.direction & Direction.east ? -diff : diff;
118
+ const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
119
+ image.style.width = `${width}px`;
120
+ positioning.currentWidth = width;
121
+ }
122
+ }
123
+ };
124
+ const handlePointerUp = () => {
125
+ const image = imageRef.current;
126
+ const positioning = positioningRef.current;
127
+ const controlWrapper = controlWrapperRef.current;
128
+ if (image !== null && controlWrapper !== null && positioning.isResizing) {
129
+ const width = positioning.currentWidth;
130
+ const height = positioning.currentHeight;
131
+ positioning.startWidth = 0;
132
+ positioning.startHeight = 0;
133
+ positioning.ratio = 0;
134
+ positioning.startX = 0;
135
+ positioning.startY = 0;
136
+ positioning.currentWidth = 0;
137
+ positioning.currentHeight = 0;
138
+ positioning.isResizing = false;
139
+ controlWrapper.classList.remove(styles.imageControlWrapperResizing);
140
+ setEndCursor();
141
+ onResizeEnd(width, height);
142
+ document.removeEventListener("pointermove", handlePointerMove);
143
+ document.removeEventListener("pointerup", handlePointerUp);
144
+ }
145
+ };
146
+ return /* @__PURE__ */ jsxs("div", { ref: controlWrapperRef, children: [
147
+ /* @__PURE__ */ jsx(
148
+ "div",
149
+ {
150
+ className: classNames(styles.imageResizer, styles.imageResizerN),
151
+ onPointerDown: (event) => {
152
+ handlePointerDown(event, Direction.north);
153
+ }
154
+ }
155
+ ),
156
+ /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ className: classNames(styles.imageResizer, styles.imageResizerNe),
160
+ onPointerDown: (event) => {
161
+ handlePointerDown(event, Direction.north | Direction.east);
162
+ }
163
+ }
164
+ ),
165
+ /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ className: classNames(styles.imageResizer, styles.imageResizerE),
169
+ onPointerDown: (event) => {
170
+ handlePointerDown(event, Direction.east);
171
+ }
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsx(
175
+ "div",
176
+ {
177
+ className: classNames(styles.imageResizer, styles.imageResizerSe),
178
+ onPointerDown: (event) => {
179
+ handlePointerDown(event, Direction.south | Direction.east);
180
+ }
181
+ }
182
+ ),
183
+ /* @__PURE__ */ jsx(
184
+ "div",
185
+ {
186
+ className: classNames(styles.imageResizer, styles.imageResizerS),
187
+ onPointerDown: (event) => {
188
+ handlePointerDown(event, Direction.south);
189
+ }
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsx(
193
+ "div",
194
+ {
195
+ className: classNames(styles.imageResizer, styles.imageResizerSw),
196
+ onPointerDown: (event) => {
197
+ handlePointerDown(event, Direction.south | Direction.west);
198
+ }
199
+ }
200
+ ),
201
+ /* @__PURE__ */ jsx(
202
+ "div",
203
+ {
204
+ className: classNames(styles.imageResizer, styles.imageResizerW),
205
+ onPointerDown: (event) => {
206
+ handlePointerDown(event, Direction.west);
207
+ }
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsx(
211
+ "div",
212
+ {
213
+ className: classNames(styles.imageResizer, styles.imageResizerNw),
214
+ onPointerDown: (event) => {
215
+ handlePointerDown(event, Direction.north | Direction.west);
216
+ }
217
+ }
218
+ )
219
+ ] });
220
+ }
221
+ export {
222
+ ImageResizer as default
223
+ };