@mdxeditor/editor 0.10.0-alpha.1 → 0.10.0-alpha.2
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.
- package/dist/MDXEditor.js +1 -1
- package/dist/importMarkdownToLexical.js +1 -1
- package/dist/plugins/diff-source/SourceEditor.js +2 -0
- package/dist/plugins/image/ImageEditor.d.ts +3 -1
- package/dist/plugins/image/ImageEditor.js +29 -6
- package/dist/plugins/image/ImageNode.d.ts +11 -1
- package/dist/plugins/image/ImageNode.js +37 -9
- package/dist/plugins/image/ImageResizer.d.ts +18 -0
- package/dist/plugins/image/ImageResizer.js +214 -0
- package/dist/plugins/image/LexicalImageVisitor.js +27 -6
- package/dist/plugins/image/MdastImageVisitor.d.ts +3 -0
- package/dist/plugins/image/MdastImageVisitor.js +72 -1
- package/dist/plugins/image/index.js +3 -1
- package/dist/plugins/jsx/MdastMdxJsxElementVisitor.js +1 -1
- package/dist/plugins/table/TableEditor.js +25 -15
- package/dist/style.css +232 -158
- package/dist/styles/ui.module.css.js +105 -75
- package/dist/utils/lexicalHelpers.js +2 -2
- package/package.json +2 -1
package/dist/MDXEditor.js
CHANGED
|
@@ -62,7 +62,7 @@ const EditorRootElement = ({ children, className }) => {
|
|
|
62
62
|
React__default.useEffect(() => {
|
|
63
63
|
setEditorRootElementRef(editorRootElementRef);
|
|
64
64
|
}, [editorRootElementRef, setEditorRootElementRef]);
|
|
65
|
-
return /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.editorRoot, styles.editorWrapper, className), ref: editorRootElementRef }, children);
|
|
65
|
+
return /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.editorRoot, styles.editorWrapper, className, "mdxeditor"), ref: editorRootElementRef }, children);
|
|
66
66
|
};
|
|
67
67
|
const Methods = ({ mdxRef }) => {
|
|
68
68
|
const realm = corePluginHooks.useRealmContext();
|
|
@@ -28,7 +28,7 @@ function importMdastTreeToLexical({ root, mdastRoot, visitors }) {
|
|
|
28
28
|
return visitor2.testNode(mdastNode);
|
|
29
29
|
});
|
|
30
30
|
if (!visitor) {
|
|
31
|
-
throw new Error(`no MdastImportVisitor found for ${mdastNode.type}`, {
|
|
31
|
+
throw new Error(`no MdastImportVisitor found for ${mdastNode.type} ${JSON.stringify(mdastNode)}`, {
|
|
32
32
|
cause: mdastNode
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -3,6 +3,7 @@ import { SandpackProvider, CodeEditor } from "@codesandbox/sandpack-react";
|
|
|
3
3
|
import React__default from "react";
|
|
4
4
|
import { diffSourcePluginHooks } from "./index.js";
|
|
5
5
|
import { corePluginHooks } from "../core/index.js";
|
|
6
|
+
import { EditorView } from "@codemirror/view";
|
|
6
7
|
const SourceEditor = () => {
|
|
7
8
|
const [markdown$1] = corePluginHooks.useEmitterValues("markdown");
|
|
8
9
|
const updateMarkdown = diffSourcePluginHooks.usePublisher("markdownSourceEditorValue");
|
|
@@ -10,6 +11,7 @@ const SourceEditor = () => {
|
|
|
10
11
|
return /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: null }, /* @__PURE__ */ React__default.createElement(SandpackProvider, null, /* @__PURE__ */ React__default.createElement(
|
|
11
12
|
CodeEditor,
|
|
12
13
|
{
|
|
14
|
+
extensions: [EditorView.lineWrapping],
|
|
13
15
|
showLineNumbers: true,
|
|
14
16
|
additionalLanguages: [{ name: "markdown", extensions: ["md"], language: markdown() }],
|
|
15
17
|
initMode: "lazy",
|
|
@@ -3,5 +3,7 @@ export interface ImageEditorProps {
|
|
|
3
3
|
nodeKey: string;
|
|
4
4
|
src: string;
|
|
5
5
|
title?: string;
|
|
6
|
+
width: number | 'inherit';
|
|
7
|
+
height: number | 'inherit';
|
|
6
8
|
}
|
|
7
|
-
export declare function ImageEditor({ src, title, nodeKey }: ImageEditorProps): JSX.Element;
|
|
9
|
+
export declare function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorProps): JSX.Element;
|
|
@@ -6,6 +6,7 @@ import { $isNodeSelection, $getSelection, $getNodeByKey, $setSelection, SELECTIO
|
|
|
6
6
|
import styles from "../../styles/ui.module.css.js";
|
|
7
7
|
import classNames from "classnames";
|
|
8
8
|
import { $isImageNode } from "./ImageNode.js";
|
|
9
|
+
import ImageResizer from "./ImageResizer.js";
|
|
9
10
|
const imageCache = /* @__PURE__ */ new Set();
|
|
10
11
|
function useSuspenseImage(src) {
|
|
11
12
|
if (!imageCache.has(src)) {
|
|
@@ -23,18 +24,21 @@ function LazyImage({
|
|
|
23
24
|
title,
|
|
24
25
|
className,
|
|
25
26
|
imageRef,
|
|
26
|
-
src
|
|
27
|
+
src,
|
|
28
|
+
width,
|
|
29
|
+
height
|
|
27
30
|
}) {
|
|
28
31
|
useSuspenseImage(src);
|
|
29
|
-
return /* @__PURE__ */ React__default.createElement("img", { className: className || void 0, src, title, ref: imageRef, draggable: "false" });
|
|
32
|
+
return /* @__PURE__ */ React__default.createElement("img", { className: className || void 0, src, title, ref: imageRef, draggable: "false", width, height });
|
|
30
33
|
}
|
|
31
|
-
function ImageEditor({ src, title, nodeKey }) {
|
|
34
|
+
function ImageEditor({ src, title, nodeKey, width, height }) {
|
|
32
35
|
const imageRef = React__default.useRef(null);
|
|
33
36
|
const buttonRef = React__default.useRef(null);
|
|
34
37
|
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
|
|
35
38
|
const [editor] = useLexicalComposerContext();
|
|
36
39
|
const [selection, setSelection] = React__default.useState(null);
|
|
37
40
|
const activeEditorRef = React__default.useRef(null);
|
|
41
|
+
const [isResizing, setIsResizing] = React__default.useState(false);
|
|
38
42
|
const onDelete = React__default.useCallback(
|
|
39
43
|
(payload) => {
|
|
40
44
|
if (isSelected && $isNodeSelection($getSelection())) {
|
|
@@ -101,6 +105,9 @@ function ImageEditor({ src, title, nodeKey }) {
|
|
|
101
105
|
CLICK_COMMAND,
|
|
102
106
|
(payload) => {
|
|
103
107
|
const event = payload;
|
|
108
|
+
if (isResizing) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
104
111
|
if (event.target === imageRef.current) {
|
|
105
112
|
if (event.shiftKey) {
|
|
106
113
|
setSelected(!isSelected);
|
|
@@ -134,12 +141,28 @@ function ImageEditor({ src, title, nodeKey }) {
|
|
|
134
141
|
isMounted = false;
|
|
135
142
|
unregister();
|
|
136
143
|
};
|
|
137
|
-
}, [clearSelection, editor, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
|
|
144
|
+
}, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
|
|
145
|
+
const onResizeEnd = (nextWidth, nextHeight) => {
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
setIsResizing(false);
|
|
148
|
+
}, 200);
|
|
149
|
+
editor.update(() => {
|
|
150
|
+
const node = $getNodeByKey(nodeKey);
|
|
151
|
+
if ($isImageNode(node)) {
|
|
152
|
+
node.setWidthAndHeight(nextWidth, nextHeight);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
const onResizeStart = () => {
|
|
157
|
+
setIsResizing(true);
|
|
158
|
+
};
|
|
138
159
|
const draggable = $isNodeSelection(selection);
|
|
139
160
|
const isFocused = isSelected;
|
|
140
|
-
return /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: null }, /* @__PURE__ */ React__default.createElement(
|
|
161
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: null }, /* @__PURE__ */ React__default.createElement("div", { className: styles.imageWrapper, "data-editor-block-type": "image" }, /* @__PURE__ */ React__default.createElement("div", { draggable }, /* @__PURE__ */ React__default.createElement(
|
|
141
162
|
LazyImage,
|
|
142
163
|
{
|
|
164
|
+
width,
|
|
165
|
+
height,
|
|
143
166
|
className: classNames({
|
|
144
167
|
[styles.focusedImage]: isFocused
|
|
145
168
|
}),
|
|
@@ -147,7 +170,7 @@ function ImageEditor({ src, title, nodeKey }) {
|
|
|
147
170
|
title: title || "",
|
|
148
171
|
imageRef
|
|
149
172
|
}
|
|
150
|
-
))));
|
|
173
|
+
)), draggable && isFocused && /* @__PURE__ */ React__default.createElement(ImageResizer, { editor, imageRef, onResizeStart, onResizeEnd })));
|
|
151
174
|
}
|
|
152
175
|
export {
|
|
153
176
|
ImageEditor
|
|
@@ -7,6 +7,8 @@ import { DecoratorNode } from 'lexical';
|
|
|
7
7
|
export type SerializedImageNode = Spread<{
|
|
8
8
|
altText: string;
|
|
9
9
|
title?: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
10
12
|
src: string;
|
|
11
13
|
type: 'image';
|
|
12
14
|
version: 1;
|
|
@@ -18,18 +20,24 @@ export declare class ImageNode extends DecoratorNode<JSX.Element> {
|
|
|
18
20
|
__src: string;
|
|
19
21
|
__altText: string;
|
|
20
22
|
__title: string | undefined;
|
|
23
|
+
__width: 'inherit' | number;
|
|
24
|
+
__height: 'inherit' | number;
|
|
21
25
|
static getType(): string;
|
|
22
26
|
static clone(node: ImageNode): ImageNode;
|
|
23
27
|
static importJSON(serializedNode: SerializedImageNode): ImageNode;
|
|
24
28
|
exportDOM(): DOMExportOutput;
|
|
25
29
|
static importDOM(): DOMConversionMap | null;
|
|
26
|
-
constructor(src: string, altText: string, title: string | undefined, key?: NodeKey);
|
|
30
|
+
constructor(src: string, altText: string, title: string | undefined, width?: 'inherit' | number, height?: 'inherit' | number, key?: NodeKey);
|
|
27
31
|
exportJSON(): SerializedImageNode;
|
|
32
|
+
setWidthAndHeight(width: 'inherit' | number, height: 'inherit' | number): void;
|
|
28
33
|
createDOM(config: EditorConfig): HTMLElement;
|
|
29
34
|
updateDOM(): false;
|
|
30
35
|
getSrc(): string;
|
|
31
36
|
getAltText(): string;
|
|
32
37
|
getTitle(): string | undefined;
|
|
38
|
+
getHeight(): 'inherit' | number;
|
|
39
|
+
getWidth(): 'inherit' | number;
|
|
40
|
+
hasExplicitDimensions(): boolean;
|
|
33
41
|
setTitle(title: string | undefined): void;
|
|
34
42
|
decorate(_parentEditor: LexicalEditor): JSX.Element;
|
|
35
43
|
}
|
|
@@ -38,6 +46,8 @@ export declare class ImageNode extends DecoratorNode<JSX.Element> {
|
|
|
38
46
|
*/
|
|
39
47
|
export interface CreateImageNodeOptions {
|
|
40
48
|
altText: string;
|
|
49
|
+
width?: number;
|
|
50
|
+
height?: number;
|
|
41
51
|
title?: string;
|
|
42
52
|
key?: NodeKey;
|
|
43
53
|
src: string;
|
|
@@ -9,34 +9,40 @@ import { DecoratorNode } from "lexical";
|
|
|
9
9
|
import { ImageEditor } from "./ImageEditor.js";
|
|
10
10
|
function convertImageElement(domNode) {
|
|
11
11
|
if (domNode instanceof HTMLImageElement) {
|
|
12
|
-
const { alt: altText, src, title } = domNode;
|
|
13
|
-
const node = $createImageNode({ altText, src, title });
|
|
12
|
+
const { alt: altText, src, title, width, height } = domNode;
|
|
13
|
+
const node = $createImageNode({ altText, src, title, width, height });
|
|
14
14
|
return { node };
|
|
15
15
|
}
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
18
|
class ImageNode extends DecoratorNode {
|
|
19
|
-
constructor(src, altText, title, key) {
|
|
19
|
+
constructor(src, altText, title, width, height, key) {
|
|
20
20
|
super(key);
|
|
21
21
|
__publicField(this, "__src");
|
|
22
22
|
__publicField(this, "__altText");
|
|
23
23
|
__publicField(this, "__title");
|
|
24
|
+
__publicField(this, "__width");
|
|
25
|
+
__publicField(this, "__height");
|
|
24
26
|
this.__src = src;
|
|
25
27
|
this.__title = title;
|
|
26
28
|
this.__altText = altText;
|
|
29
|
+
this.__width = width || "inherit";
|
|
30
|
+
this.__height = height || "inherit";
|
|
27
31
|
}
|
|
28
32
|
static getType() {
|
|
29
33
|
return "image";
|
|
30
34
|
}
|
|
31
35
|
static clone(node) {
|
|
32
|
-
return new ImageNode(node.__src, node.__altText, node.__title, node.__key);
|
|
36
|
+
return new ImageNode(node.__src, node.__altText, node.__title, node.__width, node.__height, node.__key);
|
|
33
37
|
}
|
|
34
38
|
static importJSON(serializedNode) {
|
|
35
|
-
const { altText, title, src } = serializedNode;
|
|
39
|
+
const { altText, title, src, width, height } = serializedNode;
|
|
36
40
|
const node = $createImageNode({
|
|
37
41
|
altText,
|
|
38
42
|
title,
|
|
39
|
-
src
|
|
43
|
+
src,
|
|
44
|
+
height,
|
|
45
|
+
width
|
|
40
46
|
});
|
|
41
47
|
return node;
|
|
42
48
|
}
|
|
@@ -47,6 +53,12 @@ class ImageNode extends DecoratorNode {
|
|
|
47
53
|
if (this.__title) {
|
|
48
54
|
element.setAttribute("title", this.__title);
|
|
49
55
|
}
|
|
56
|
+
if (this.__width) {
|
|
57
|
+
element.setAttribute("width", this.__width.toString());
|
|
58
|
+
}
|
|
59
|
+
if (this.__height) {
|
|
60
|
+
element.setAttribute("height", this.__height.toString());
|
|
61
|
+
}
|
|
50
62
|
return { element };
|
|
51
63
|
}
|
|
52
64
|
static importDOM() {
|
|
@@ -61,11 +73,18 @@ class ImageNode extends DecoratorNode {
|
|
|
61
73
|
return {
|
|
62
74
|
altText: this.getAltText(),
|
|
63
75
|
title: this.getTitle(),
|
|
76
|
+
height: this.__height === "inherit" ? 0 : this.__height,
|
|
77
|
+
width: this.__width === "inherit" ? 0 : this.__width,
|
|
64
78
|
src: this.getSrc(),
|
|
65
79
|
type: "image",
|
|
66
80
|
version: 1
|
|
67
81
|
};
|
|
68
82
|
}
|
|
83
|
+
setWidthAndHeight(width, height) {
|
|
84
|
+
const writable = this.getWritable();
|
|
85
|
+
writable.__width = width;
|
|
86
|
+
writable.__height = height;
|
|
87
|
+
}
|
|
69
88
|
createDOM(config) {
|
|
70
89
|
const span = document.createElement("span");
|
|
71
90
|
const theme = config.theme;
|
|
@@ -87,16 +106,25 @@ class ImageNode extends DecoratorNode {
|
|
|
87
106
|
getTitle() {
|
|
88
107
|
return this.__title;
|
|
89
108
|
}
|
|
109
|
+
getHeight() {
|
|
110
|
+
return this.__height;
|
|
111
|
+
}
|
|
112
|
+
getWidth() {
|
|
113
|
+
return this.__width;
|
|
114
|
+
}
|
|
115
|
+
hasExplicitDimensions() {
|
|
116
|
+
return this.__width !== "inherit" || this.__height !== "inherit";
|
|
117
|
+
}
|
|
90
118
|
setTitle(title) {
|
|
91
119
|
this.getWritable().__title = title;
|
|
92
120
|
}
|
|
93
121
|
decorate(_parentEditor) {
|
|
94
|
-
return /* @__PURE__ */ React__default.createElement(ImageEditor, { src: this.getSrc(), title: this.getTitle(), nodeKey: this.getKey() });
|
|
122
|
+
return /* @__PURE__ */ React__default.createElement(ImageEditor, { src: this.getSrc(), title: this.getTitle(), nodeKey: this.getKey(), width: this.__width, height: this.__height });
|
|
95
123
|
}
|
|
96
124
|
}
|
|
97
125
|
function $createImageNode(options) {
|
|
98
|
-
const { altText, title, src, key } = options;
|
|
99
|
-
return new ImageNode(src, altText, title, key);
|
|
126
|
+
const { altText, title, src, key, width, height } = options;
|
|
127
|
+
return new ImageNode(src, altText, title, width, height, key);
|
|
100
128
|
}
|
|
101
129
|
function $isImageNode(node) {
|
|
102
130
|
return node instanceof ImageNode;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
/// <reference types="react" />
|
|
9
|
+
import type { LexicalEditor } from 'lexical';
|
|
10
|
+
export default function ImageResizer({ onResizeStart, onResizeEnd, imageRef, maxWidth, editor }: {
|
|
11
|
+
editor: LexicalEditor;
|
|
12
|
+
imageRef: {
|
|
13
|
+
current: null | HTMLElement;
|
|
14
|
+
};
|
|
15
|
+
maxWidth?: number;
|
|
16
|
+
onResizeEnd: (width: 'inherit' | number, height: 'inherit' | number) => void;
|
|
17
|
+
onResizeStart: () => void;
|
|
18
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import * as React from "react";
|
|
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 ? 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__ */ React.createElement("div", { ref: controlWrapperRef }, /* @__PURE__ */ React.createElement(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
className: classNames(styles.imageResizer, styles.imageResizerN),
|
|
150
|
+
onPointerDown: (event) => {
|
|
151
|
+
handlePointerDown(event, Direction.north);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
), /* @__PURE__ */ React.createElement(
|
|
155
|
+
"div",
|
|
156
|
+
{
|
|
157
|
+
className: classNames(styles.imageResizer, styles.imageResizerNe),
|
|
158
|
+
onPointerDown: (event) => {
|
|
159
|
+
handlePointerDown(event, Direction.north | Direction.east);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
), /* @__PURE__ */ React.createElement(
|
|
163
|
+
"div",
|
|
164
|
+
{
|
|
165
|
+
className: classNames(styles.imageResizer, styles.imageResizerE),
|
|
166
|
+
onPointerDown: (event) => {
|
|
167
|
+
handlePointerDown(event, Direction.east);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
), /* @__PURE__ */ React.createElement(
|
|
171
|
+
"div",
|
|
172
|
+
{
|
|
173
|
+
className: classNames(styles.imageResizer, styles.imageResizerSe),
|
|
174
|
+
onPointerDown: (event) => {
|
|
175
|
+
handlePointerDown(event, Direction.south | Direction.east);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
), /* @__PURE__ */ React.createElement(
|
|
179
|
+
"div",
|
|
180
|
+
{
|
|
181
|
+
className: classNames(styles.imageResizer, styles.imageResizerS),
|
|
182
|
+
onPointerDown: (event) => {
|
|
183
|
+
handlePointerDown(event, Direction.south);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
), /* @__PURE__ */ React.createElement(
|
|
187
|
+
"div",
|
|
188
|
+
{
|
|
189
|
+
className: classNames(styles.imageResizer, styles.imageResizerSw),
|
|
190
|
+
onPointerDown: (event) => {
|
|
191
|
+
handlePointerDown(event, Direction.south | Direction.west);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
), /* @__PURE__ */ React.createElement(
|
|
195
|
+
"div",
|
|
196
|
+
{
|
|
197
|
+
className: classNames(styles.imageResizer, styles.imageResizerW),
|
|
198
|
+
onPointerDown: (event) => {
|
|
199
|
+
handlePointerDown(event, Direction.west);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
), /* @__PURE__ */ React.createElement(
|
|
203
|
+
"div",
|
|
204
|
+
{
|
|
205
|
+
className: classNames(styles.imageResizer, styles.imageResizerNw),
|
|
206
|
+
onPointerDown: (event) => {
|
|
207
|
+
handlePointerDown(event, Direction.north | Direction.west);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
));
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
ImageResizer as default
|
|
214
|
+
};
|
|
@@ -2,12 +2,33 @@ import { $isImageNode } from "./ImageNode.js";
|
|
|
2
2
|
const LexicalImageVisitor = {
|
|
3
3
|
testLexicalNode: $isImageNode,
|
|
4
4
|
visitLexicalNode({ mdastParent, lexicalNode, actions }) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
if (lexicalNode.hasExplicitDimensions()) {
|
|
6
|
+
const img = new Image();
|
|
7
|
+
if (lexicalNode.getHeight() !== "inherit") {
|
|
8
|
+
img.height = lexicalNode.getHeight();
|
|
9
|
+
}
|
|
10
|
+
if (lexicalNode.getWidth() !== "inherit") {
|
|
11
|
+
img.width = lexicalNode.getWidth();
|
|
12
|
+
}
|
|
13
|
+
img.src = lexicalNode.getSrc();
|
|
14
|
+
if (lexicalNode.getAltText()) {
|
|
15
|
+
img.alt = lexicalNode.getAltText();
|
|
16
|
+
}
|
|
17
|
+
if (lexicalNode.getTitle()) {
|
|
18
|
+
img.title = lexicalNode.getTitle();
|
|
19
|
+
}
|
|
20
|
+
actions.appendToParent(mdastParent, {
|
|
21
|
+
type: "html",
|
|
22
|
+
value: img.outerHTML.replace(/>$/, "/>")
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
actions.appendToParent(mdastParent, {
|
|
26
|
+
type: "image",
|
|
27
|
+
url: lexicalNode.getSrc(),
|
|
28
|
+
alt: lexicalNode.getAltText(),
|
|
29
|
+
title: lexicalNode.getTitle()
|
|
30
|
+
});
|
|
31
|
+
}
|
|
11
32
|
}
|
|
12
33
|
};
|
|
13
34
|
export {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import * as Mdast from 'mdast';
|
|
2
2
|
import { MdastImportVisitor } from '../../importMarkdownToLexical';
|
|
3
|
+
import { MdxJsxTextElement, MdxJsxFlowElement } from 'mdast-util-mdx';
|
|
3
4
|
export declare const MdastImageVisitor: MdastImportVisitor<Mdast.Image>;
|
|
5
|
+
export declare const MdastHtmlImageVisitor: MdastImportVisitor<Mdast.HTML>;
|
|
6
|
+
export declare const MdastJsxImageVisitor: MdastImportVisitor<MdxJsxTextElement | MdxJsxFlowElement>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $createImageNode } from "./ImageNode.js";
|
|
2
|
+
import { $createParagraphNode } from "lexical";
|
|
2
3
|
const MdastImageVisitor = {
|
|
3
4
|
testNode: "image",
|
|
4
5
|
visitNode({ mdastNode, actions }) {
|
|
@@ -11,6 +12,76 @@ const MdastImageVisitor = {
|
|
|
11
12
|
);
|
|
12
13
|
}
|
|
13
14
|
};
|
|
15
|
+
const MdastHtmlImageVisitor = {
|
|
16
|
+
testNode: (node) => {
|
|
17
|
+
return node.type === "html" && node.value.trim().startsWith("<img");
|
|
18
|
+
},
|
|
19
|
+
visitNode({ mdastNode, lexicalParent }) {
|
|
20
|
+
const wrapper = document.createElement("div");
|
|
21
|
+
wrapper.innerHTML = mdastNode.value;
|
|
22
|
+
const img = wrapper.querySelector("img");
|
|
23
|
+
if (!img) {
|
|
24
|
+
throw new Error("Invalid HTML image");
|
|
25
|
+
}
|
|
26
|
+
const src = img.src;
|
|
27
|
+
const altText = img.alt;
|
|
28
|
+
const title = img.title;
|
|
29
|
+
const width = img.width;
|
|
30
|
+
const height = img.height;
|
|
31
|
+
const image = $createImageNode({
|
|
32
|
+
src: src || "",
|
|
33
|
+
altText,
|
|
34
|
+
title,
|
|
35
|
+
width,
|
|
36
|
+
height
|
|
37
|
+
});
|
|
38
|
+
if (lexicalParent.getType() === "root") {
|
|
39
|
+
const paragraph = $createParagraphNode();
|
|
40
|
+
paragraph.append(image);
|
|
41
|
+
lexicalParent.append(paragraph);
|
|
42
|
+
} else {
|
|
43
|
+
lexicalParent.append(image);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function getAttributeValue(node, attributeName) {
|
|
48
|
+
const attribute = node.attributes.find((a) => a.type === "mdxJsxAttribute" && a.name === attributeName);
|
|
49
|
+
if (!attribute) {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
return attribute.value;
|
|
53
|
+
}
|
|
54
|
+
const MdastJsxImageVisitor = {
|
|
55
|
+
testNode: (node) => {
|
|
56
|
+
return (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") && node.name === "img";
|
|
57
|
+
},
|
|
58
|
+
visitNode({ mdastNode, lexicalParent }) {
|
|
59
|
+
const src = getAttributeValue(mdastNode, "src");
|
|
60
|
+
if (!src) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const altText = getAttributeValue(mdastNode, "alt") || "";
|
|
64
|
+
const title = getAttributeValue(mdastNode, "title");
|
|
65
|
+
const height = getAttributeValue(mdastNode, "height");
|
|
66
|
+
const width = getAttributeValue(mdastNode, "width");
|
|
67
|
+
const image = $createImageNode({
|
|
68
|
+
src,
|
|
69
|
+
altText,
|
|
70
|
+
title,
|
|
71
|
+
width: width ? parseInt(width, 10) : void 0,
|
|
72
|
+
height: height ? parseInt(height, 10) : void 0
|
|
73
|
+
});
|
|
74
|
+
if (lexicalParent.getType() === "root") {
|
|
75
|
+
const paragraph = $createParagraphNode();
|
|
76
|
+
paragraph.append(image);
|
|
77
|
+
lexicalParent.append(paragraph);
|
|
78
|
+
} else {
|
|
79
|
+
lexicalParent.append(image);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
14
83
|
export {
|
|
15
|
-
|
|
84
|
+
MdastHtmlImageVisitor,
|
|
85
|
+
MdastImageVisitor,
|
|
86
|
+
MdastJsxImageVisitor
|
|
16
87
|
};
|
|
@@ -3,7 +3,7 @@ import { createCommand, $insertNodes, $isRootOrShadowRoot, $createParagraphNode,
|
|
|
3
3
|
import { coreSystem } from "../core/index.js";
|
|
4
4
|
import { $createImageNode, $isImageNode, ImageNode } from "./ImageNode.js";
|
|
5
5
|
import { LexicalImageVisitor } from "./LexicalImageVisitor.js";
|
|
6
|
-
import { MdastImageVisitor } from "./MdastImageVisitor.js";
|
|
6
|
+
import { MdastImageVisitor, MdastHtmlImageVisitor, MdastJsxImageVisitor } from "./MdastImageVisitor.js";
|
|
7
7
|
import { CAN_USE_DOM } from "../../utils/detectMac.js";
|
|
8
8
|
import { system } from "../../gurx/realmFactory.js";
|
|
9
9
|
import { realmPlugin } from "../../gurx/react.js";
|
|
@@ -106,6 +106,8 @@ const [
|
|
|
106
106
|
},
|
|
107
107
|
init: (realm) => {
|
|
108
108
|
realm.pubKey("addImportVisitor", MdastImageVisitor);
|
|
109
|
+
realm.pubKey("addImportVisitor", MdastHtmlImageVisitor);
|
|
110
|
+
realm.pubKey("addImportVisitor", MdastJsxImageVisitor);
|
|
109
111
|
realm.pubKey("addLexicalNode", ImageNode);
|
|
110
112
|
realm.pubKey("addExportVisitor", LexicalImageVisitor);
|
|
111
113
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $createLexicalJsxNode } from "./LexicalJsxNode.js";
|
|
2
2
|
const MdastMdxJsxElementVisitor = {
|
|
3
3
|
testNode: (node) => {
|
|
4
|
-
return node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement";
|
|
4
|
+
return (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") && node.name !== "img";
|
|
5
5
|
},
|
|
6
6
|
visitNode({ lexicalParent, mdastNode }) {
|
|
7
7
|
lexicalParent.append($createLexicalJsxNode(mdastNode));
|