@lobehub/editor 4.24.0 → 4.25.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.
- package/es/codemirror/style.js +42 -0
- package/es/editor-kernel/kernel.js +3 -0
- package/es/editor-kernel/react/useDecorators.js +2 -4
- package/es/headless/collapsible-plugin.js +74 -0
- package/es/headless/index.js +2 -0
- package/es/headless.d.ts +18 -0
- package/es/headless.js +2479 -71
- package/es/index.d.ts +26 -3
- package/es/index.js +17 -2
- package/es/locale/index.d.ts +14 -0
- package/es/locale/index.js +14 -0
- package/es/plugins/block/command/index.js +24 -0
- package/es/plugins/block/plugin/index.js +7 -5
- package/es/plugins/block/react/ReactBlockPlugin.js +43 -1
- package/es/plugins/block/react/core/menu-position.js +20 -0
- package/es/plugins/block/react/drag/drag-session.js +3 -2
- package/es/plugins/block/react/drag/drag-utils.js +13 -1
- package/es/plugins/codemirror-block/index.d.ts +2 -1
- package/es/plugins/codemirror-block/plugin/index.js +2 -0
- package/es/plugins/codemirror-block/react/CodemirrorNode.js +95 -5
- package/es/plugins/codemirror-block/react/useCodemirrorEditLock.js +33 -0
- package/es/plugins/codemirror-block/service/index.d.ts +40 -0
- package/es/plugins/codemirror-block/service/index.js +59 -0
- package/es/plugins/collapsible/command/index.d.ts +17 -0
- package/es/plugins/collapsible/command/index.js +72 -0
- package/es/plugins/collapsible/index.d.ts +7 -0
- package/es/plugins/collapsible/node/CollapsibleNode.d.ts +30 -0
- package/es/plugins/collapsible/node/CollapsibleNode.js +138 -0
- package/es/plugins/collapsible/plugin/index.d.ts +11 -0
- package/es/plugins/collapsible/plugin/index.js +535 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.d.ts +6 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.js +19 -0
- package/es/plugins/collapsible/react/index.d.ts +3 -0
- package/es/plugins/collapsible/react/style.js +67 -0
- package/es/plugins/collapsible/react/type.d.ts +6 -0
- package/es/plugins/common/plugin/index.js +3 -2
- package/es/plugins/common/plugin/progressive-select-all.js +81 -0
- package/es/plugins/common/plugin/register.js +73 -4
- package/es/plugins/link/command/index.js +26 -3
- package/es/plugins/link/conversion/index.js +237 -0
- package/es/plugins/link/index.d.ts +5 -3
- package/es/plugins/link/node/LinkBlockCardNode.d.ts +16 -0
- package/es/plugins/link/node/LinkBlockCardNode.js +66 -0
- package/es/plugins/link/node/LinkCardNode.d.ts +42 -0
- package/es/plugins/link/node/LinkCardNode.js +161 -0
- package/es/plugins/link/node/LinkIframeNode.d.ts +33 -0
- package/es/plugins/link/node/LinkIframeNode.js +131 -0
- package/es/plugins/link/node/LinkNode.d.ts +48 -2
- package/es/plugins/link/node/LinkNode.js +14 -6
- package/es/plugins/link/node/SchemaNode.d.ts +36 -0
- package/es/plugins/link/node/SchemaNode.js +149 -0
- package/es/plugins/link/normalization/index.d.ts +7 -0
- package/es/plugins/link/normalization/index.js +38 -0
- package/es/plugins/link/plugin/index.d.ts +26 -1
- package/es/plugins/link/plugin/index.js +124 -4
- package/es/plugins/link/react/ReactLinkPlugin.js +165 -7
- package/es/plugins/link/react/components/LinkCard.js +235 -0
- package/es/plugins/link/react/components/LinkEdit.js +93 -14
- package/es/plugins/link/react/components/LinkIframe.js +128 -0
- package/es/plugins/link/react/components/LinkToolbar.js +440 -111
- package/es/plugins/link/react/components/SchemaLink.js +49 -0
- package/es/plugins/link/react/index.d.ts +2 -2
- package/es/plugins/link/react/renderer-registry.d.ts +81 -0
- package/es/plugins/link/react/renderer-registry.js +80 -0
- package/es/plugins/link/react/style.js +63 -1
- package/es/plugins/link/react/type.d.ts +24 -1
- package/es/plugins/link/service/i-link-service.d.ts +156 -1
- package/es/plugins/link/service/i-link-service.js +258 -5
- package/es/plugins/list/plugin/index.js +9 -2
- package/es/plugins/litexml/command/diffCommand.js +94 -14
- package/es/plugins/litexml/command/index.js +187 -6
- package/es/plugins/litexml/diff-validation.js +237 -0
- package/es/plugins/litexml/node/DiffContentNode.js +67 -0
- package/es/plugins/litexml/node/DiffNode.d.ts +1 -1
- package/es/plugins/litexml/node/DiffNode.js +5 -2
- package/es/plugins/litexml/node/TableCellDiffNode.js +77 -0
- package/es/plugins/litexml/node/TableRowDiffNode.js +73 -0
- package/es/plugins/litexml/plugin/index.d.ts +2 -0
- package/es/plugins/litexml/plugin/index.js +62 -9
- package/es/plugins/litexml/react/DiffNodeToolbar/index.js +10 -12
- package/es/plugins/litexml/react/TableRowDiffToolbar.js +123 -0
- package/es/plugins/litexml/react/hooks/useHasDiffNode.js +5 -2
- package/es/plugins/litexml/react/index.js +6 -2
- package/es/plugins/litexml/react/style.js +18 -5
- package/es/plugins/litexml/table-cell-diff.js +153 -0
- package/es/plugins/litexml/table-row-diff.js +82 -0
- package/es/plugins/markdown/data-source/markdown/parse.js +25 -1
- package/es/plugins/markdown/plugin/index.js +5 -3
- package/es/plugins/slash/react/ReactSlashPlugin.js +22 -43
- package/es/plugins/slash/react/components/DefaultSlashMenu.js +76 -16
- package/es/plugins/slash/react/menu-utils.js +84 -0
- package/es/plugins/slash/service/i-slash-service.d.ts +3 -0
- package/es/plugins/table/plugin/index.js +4 -1
- package/es/plugins/table/plugin/single-cell-paste.js +79 -0
- package/es/plugins/table/react/TableController/hooks.js +7 -4
- package/es/plugins/table/react/hooks.js +2 -2
- package/es/plugins/table/react/index.js +2 -0
- package/es/plugins/table/react/style.js +15 -5
- package/es/plugins/table/react/useScrollFocusedTableCellIntoView.js +46 -0
- package/es/plugins/toc/index.d.ts +10 -0
- package/es/plugins/toc/plugin/index.d.ts +8 -0
- package/es/plugins/toc/plugin/index.js +56 -0
- package/es/plugins/toc/react/ReactTocPlugin.d.ts +6 -0
- package/es/plugins/toc/react/ReactTocPlugin.js +36 -0
- package/es/plugins/toc/react/getNearestScrollContainer.d.ts +6 -0
- package/es/plugins/toc/react/getNearestScrollContainer.js +18 -0
- package/es/plugins/toc/react/hooks/useActiveHeading.js +59 -0
- package/es/plugins/toc/react/hooks/useTocItems.js +50 -0
- package/es/plugins/toc/react/index.d.ts +5 -0
- package/es/plugins/toc/react/type.d.ts +30 -0
- package/es/plugins/toc/react/useToc.d.ts +5 -0
- package/es/plugins/toc/react/useToc.js +41 -0
- package/es/plugins/toc/service/i-toc-service.d.ts +39 -0
- package/es/plugins/toc/service/i-toc-service.js +125 -0
- package/es/plugins/toc/service/index.d.ts +3 -0
- package/es/plugins/toc/types.d.ts +23 -0
- package/es/plugins/toc/utils/index.d.ts +6 -0
- package/es/plugins/toc/utils/index.js +30 -0
- package/es/plugins/toolbar/react/index.js +26 -12
- package/es/plugins/toolbar/react/style.js +3 -2
- package/es/plugins/virtual-block/plugin/register.js +34 -12
- package/es/plugins/yjs/index.d.ts +6 -0
- package/es/plugins/yjs/plugin/index.d.ts +7 -0
- package/es/plugins/yjs/plugin/index.js +191 -0
- package/es/plugins/yjs/plugin/types.d.ts +16 -0
- package/es/plugins/yjs/plugin/utils/awareness.js +9 -0
- package/es/plugins/yjs/plugin/utils/editor-state.js +47 -0
- package/es/plugins/yjs/plugin/utils/history.js +31 -0
- package/es/plugins/yjs/plugin/utils/node-properties.js +39 -0
- package/es/plugins/yjs/plugin/utils/sync.js +24 -0
- package/es/plugins/yjs/react/codemirrorEditLockProvider.js +59 -0
- package/es/plugins/yjs/react/index.d.ts +23 -0
- package/es/plugins/yjs/react/index.js +200 -0
- package/es/plugins/yjs/service/index.d.ts +47 -0
- package/es/plugins/yjs/service/index.js +84 -0
- package/es/plugins/yjs/websocket-provider.d.ts +78 -0
- package/es/plugins/yjs/websocket-provider.js +263 -0
- package/es/renderer/LexicalRenderer.js +9 -2
- package/es/renderer/engine/render-builtin-node.js +17 -8
- package/es/types/kernel.d.ts +4 -0
- package/es/utils/updatePosition.js +5 -2
- package/package.json +3 -1
- package/patches/@lexical__yjs@0.42.0.patch +42 -0
- package/scripts/postinstall-lexical-patch.cjs +62 -36
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
|
|
2
|
+
import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
|
|
3
|
+
import { $applyNodeReplacement, DecoratorNode, createCommand } from "lexical";
|
|
4
|
+
import { addClassNamesToElement } from "@lexical/utils";
|
|
5
|
+
//#region src/plugins/link/node/LinkCardNode.ts
|
|
6
|
+
init_utils();
|
|
7
|
+
const EDIT_LINK_CARD_COMMAND = createCommand();
|
|
8
|
+
var LinkCardNode = class LinkCardNode extends DecoratorNode {
|
|
9
|
+
static getType() {
|
|
10
|
+
return "link-card";
|
|
11
|
+
}
|
|
12
|
+
static clone(node) {
|
|
13
|
+
return new LinkCardNode(node.__url, node.__title, node.__icon, node.__description, node.__openTarget, node.__key);
|
|
14
|
+
}
|
|
15
|
+
static importJSON(serializedNode) {
|
|
16
|
+
return $createLinkCardNode({
|
|
17
|
+
description: serializedNode.description,
|
|
18
|
+
icon: serializedNode.icon,
|
|
19
|
+
openTarget: serializedNode.openTarget,
|
|
20
|
+
title: serializedNode.title,
|
|
21
|
+
url: serializedNode.url
|
|
22
|
+
}).updateFromJSON(serializedNode);
|
|
23
|
+
}
|
|
24
|
+
static importDOM() {
|
|
25
|
+
return { a: (node) => {
|
|
26
|
+
if (node instanceof HTMLAnchorElement && node.dataset.linkCard === "true" && node.dataset.linkCardLayout !== "block") return {
|
|
27
|
+
conversion: $convertLinkCardElement,
|
|
28
|
+
priority: 2
|
|
29
|
+
};
|
|
30
|
+
return null;
|
|
31
|
+
} };
|
|
32
|
+
}
|
|
33
|
+
constructor(url, title, icon, description, openTarget, key) {
|
|
34
|
+
super(key);
|
|
35
|
+
this.__url = url;
|
|
36
|
+
this.__title = title || url;
|
|
37
|
+
this.__icon = icon || "";
|
|
38
|
+
this.__description = description || "";
|
|
39
|
+
this.__openTarget = openTarget ?? "_blank";
|
|
40
|
+
}
|
|
41
|
+
createDOM(config, editor) {
|
|
42
|
+
const element = document.createElement("span");
|
|
43
|
+
element.dataset.linkCard = "true";
|
|
44
|
+
addClassNamesToElement(element, config.theme.linkCard);
|
|
45
|
+
element.addEventListener("mouseenter", (event) => {
|
|
46
|
+
if (event.target instanceof HTMLElement) {
|
|
47
|
+
event.target.classList.add("hover");
|
|
48
|
+
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
49
|
+
event,
|
|
50
|
+
node: this
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
element.addEventListener("mouseleave", (event) => {
|
|
55
|
+
if (event.target instanceof HTMLElement) {
|
|
56
|
+
event.target.classList.remove("hover");
|
|
57
|
+
editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return element;
|
|
61
|
+
}
|
|
62
|
+
updateDOM() {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
isInline() {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
isKeyboardSelectable() {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
exportDOM() {
|
|
72
|
+
const element = document.createElement("a");
|
|
73
|
+
element.dataset.linkCard = "true";
|
|
74
|
+
element.dataset.icon = this.getIcon();
|
|
75
|
+
element.dataset.description = this.getDescription();
|
|
76
|
+
if (this.getOpenTarget()) element.target = this.getOpenTarget();
|
|
77
|
+
element.href = this.getURL();
|
|
78
|
+
element.textContent = this.getTitle();
|
|
79
|
+
return { element };
|
|
80
|
+
}
|
|
81
|
+
exportJSON() {
|
|
82
|
+
return {
|
|
83
|
+
...super.exportJSON(),
|
|
84
|
+
description: this.getDescription(),
|
|
85
|
+
icon: this.getIcon(),
|
|
86
|
+
openTarget: this.getOpenTarget(),
|
|
87
|
+
title: this.getTitle(),
|
|
88
|
+
url: this.getURL()
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
updateFromJSON(serializedNode) {
|
|
92
|
+
return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setTitle(serializedNode.title).setIcon(serializedNode.icon).setDescription(serializedNode.description).setOpenTarget(serializedNode.openTarget ?? "_blank");
|
|
93
|
+
}
|
|
94
|
+
getTextContent() {
|
|
95
|
+
return this.getTitle();
|
|
96
|
+
}
|
|
97
|
+
getURL() {
|
|
98
|
+
return this.getLatest().__url;
|
|
99
|
+
}
|
|
100
|
+
setURL(url) {
|
|
101
|
+
const writable = this.getWritable();
|
|
102
|
+
writable.__url = url;
|
|
103
|
+
return writable;
|
|
104
|
+
}
|
|
105
|
+
getTitle() {
|
|
106
|
+
return this.getLatest().__title || this.getURL();
|
|
107
|
+
}
|
|
108
|
+
setTitle(title) {
|
|
109
|
+
const writable = this.getWritable();
|
|
110
|
+
writable.__title = title || this.getURL();
|
|
111
|
+
return writable;
|
|
112
|
+
}
|
|
113
|
+
getIcon() {
|
|
114
|
+
return this.getLatest().__icon;
|
|
115
|
+
}
|
|
116
|
+
setIcon(icon) {
|
|
117
|
+
const writable = this.getWritable();
|
|
118
|
+
writable.__icon = icon || "";
|
|
119
|
+
return writable;
|
|
120
|
+
}
|
|
121
|
+
getDescription() {
|
|
122
|
+
return this.getLatest().__description;
|
|
123
|
+
}
|
|
124
|
+
setDescription(description) {
|
|
125
|
+
const writable = this.getWritable();
|
|
126
|
+
writable.__description = description || "";
|
|
127
|
+
return writable;
|
|
128
|
+
}
|
|
129
|
+
getOpenTarget() {
|
|
130
|
+
return this.getLatest().__openTarget;
|
|
131
|
+
}
|
|
132
|
+
setOpenTarget(openTarget) {
|
|
133
|
+
const writable = this.getWritable();
|
|
134
|
+
writable.__openTarget = openTarget ?? "_blank";
|
|
135
|
+
return writable;
|
|
136
|
+
}
|
|
137
|
+
decorate(editor) {
|
|
138
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(LinkCardNode.getType());
|
|
139
|
+
if (!decorator) return null;
|
|
140
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
141
|
+
return decorator.render(this, editor);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
function $createLinkCardNode(payload) {
|
|
145
|
+
return $applyNodeReplacement(new LinkCardNode(payload.url, payload.title, payload.icon, payload.description, payload.openTarget));
|
|
146
|
+
}
|
|
147
|
+
function $convertLinkCardElement(domNode) {
|
|
148
|
+
const element = domNode;
|
|
149
|
+
return { node: $createLinkCardNode({
|
|
150
|
+
description: element.dataset.description,
|
|
151
|
+
icon: element.dataset.icon,
|
|
152
|
+
openTarget: element.getAttribute("target"),
|
|
153
|
+
title: element.textContent || element.href,
|
|
154
|
+
url: element.getAttribute("href") || ""
|
|
155
|
+
}) };
|
|
156
|
+
}
|
|
157
|
+
function $isLinkCardNode(node) {
|
|
158
|
+
return node instanceof LinkCardNode || node?.getType?.() === LinkCardNode.getType();
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { $createLinkCardNode, $isLinkCardNode, EDIT_LINK_CARD_COMMAND, LinkCardNode };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DOMConversionMap, DOMExportOutput, DecoratorNode, EditorConfig, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
|
+
//#region src/plugins/link/node/LinkIframeNode.d.ts
|
|
3
|
+
type SerializedLinkIframeNode = Spread<{
|
|
4
|
+
src: string;
|
|
5
|
+
title: string;
|
|
6
|
+
url: string;
|
|
7
|
+
}, SerializedLexicalNode>;
|
|
8
|
+
declare class LinkIframeNode extends DecoratorNode<unknown> {
|
|
9
|
+
__url: string;
|
|
10
|
+
__src: string;
|
|
11
|
+
__title: string;
|
|
12
|
+
static getType(): string;
|
|
13
|
+
static clone(node: LinkIframeNode): LinkIframeNode;
|
|
14
|
+
static importJSON(serializedNode: SerializedLinkIframeNode): LinkIframeNode;
|
|
15
|
+
static importDOM(): DOMConversionMap | null;
|
|
16
|
+
constructor(url: string, src?: string, title?: string, key?: NodeKey);
|
|
17
|
+
createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
|
|
18
|
+
updateDOM(): false;
|
|
19
|
+
isInline(): false;
|
|
20
|
+
exportDOM(): DOMExportOutput;
|
|
21
|
+
exportJSON(): SerializedLinkIframeNode;
|
|
22
|
+
updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkIframeNode>): this;
|
|
23
|
+
getTextContent(): string;
|
|
24
|
+
getURL(): string;
|
|
25
|
+
setURL(url: string): this;
|
|
26
|
+
getSrc(): string;
|
|
27
|
+
setSrc(src?: string): this;
|
|
28
|
+
getTitle(): string;
|
|
29
|
+
setTitle(title?: string): this;
|
|
30
|
+
decorate(editor: LexicalEditor): unknown;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { LinkIframeNode, SerializedLinkIframeNode };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
|
|
2
|
+
import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
|
|
3
|
+
import { $applyNodeReplacement, DecoratorNode } from "lexical";
|
|
4
|
+
import { addClassNamesToElement } from "@lexical/utils";
|
|
5
|
+
//#region src/plugins/link/node/LinkIframeNode.ts
|
|
6
|
+
init_utils();
|
|
7
|
+
var LinkIframeNode = class LinkIframeNode extends DecoratorNode {
|
|
8
|
+
static getType() {
|
|
9
|
+
return "link-iframe";
|
|
10
|
+
}
|
|
11
|
+
static clone(node) {
|
|
12
|
+
return new LinkIframeNode(node.__url, node.__src, node.__title, node.__key);
|
|
13
|
+
}
|
|
14
|
+
static importJSON(serializedNode) {
|
|
15
|
+
return $createLinkIframeNode({
|
|
16
|
+
src: serializedNode.src,
|
|
17
|
+
title: serializedNode.title,
|
|
18
|
+
url: serializedNode.url
|
|
19
|
+
}).updateFromJSON(serializedNode);
|
|
20
|
+
}
|
|
21
|
+
static importDOM() {
|
|
22
|
+
return { a: (node) => {
|
|
23
|
+
if (node instanceof HTMLAnchorElement && node.dataset.linkIframe === "true") return {
|
|
24
|
+
conversion: $convertLinkIframeElement,
|
|
25
|
+
priority: 2
|
|
26
|
+
};
|
|
27
|
+
return null;
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
constructor(url, src, title, key) {
|
|
31
|
+
super(key);
|
|
32
|
+
this.__url = url;
|
|
33
|
+
this.__src = src || url;
|
|
34
|
+
this.__title = title || url;
|
|
35
|
+
}
|
|
36
|
+
createDOM(config, editor) {
|
|
37
|
+
const element = document.createElement("div");
|
|
38
|
+
element.dataset.linkIframe = "true";
|
|
39
|
+
addClassNamesToElement(element, config.theme.linkIframe);
|
|
40
|
+
element.addEventListener("mouseenter", (event) => {
|
|
41
|
+
if (event.target instanceof HTMLElement) {
|
|
42
|
+
event.target.classList.add("hover");
|
|
43
|
+
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
44
|
+
event,
|
|
45
|
+
node: this
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
element.addEventListener("mouseleave", (event) => {
|
|
50
|
+
if (event.target instanceof HTMLElement) {
|
|
51
|
+
event.target.classList.remove("hover");
|
|
52
|
+
editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return element;
|
|
56
|
+
}
|
|
57
|
+
updateDOM() {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
isInline() {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
exportDOM() {
|
|
64
|
+
const element = document.createElement("a");
|
|
65
|
+
element.dataset.linkIframe = "true";
|
|
66
|
+
element.dataset.src = this.getSrc();
|
|
67
|
+
element.href = this.getURL();
|
|
68
|
+
element.textContent = this.getTitle();
|
|
69
|
+
return { element };
|
|
70
|
+
}
|
|
71
|
+
exportJSON() {
|
|
72
|
+
return {
|
|
73
|
+
...super.exportJSON(),
|
|
74
|
+
src: this.getSrc(),
|
|
75
|
+
title: this.getTitle(),
|
|
76
|
+
url: this.getURL()
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
updateFromJSON(serializedNode) {
|
|
80
|
+
return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setSrc(serializedNode.src).setTitle(serializedNode.title);
|
|
81
|
+
}
|
|
82
|
+
getTextContent() {
|
|
83
|
+
return this.getTitle();
|
|
84
|
+
}
|
|
85
|
+
getURL() {
|
|
86
|
+
return this.getLatest().__url;
|
|
87
|
+
}
|
|
88
|
+
setURL(url) {
|
|
89
|
+
const writable = this.getWritable();
|
|
90
|
+
writable.__url = url;
|
|
91
|
+
return writable;
|
|
92
|
+
}
|
|
93
|
+
getSrc() {
|
|
94
|
+
return this.getLatest().__src || this.getURL();
|
|
95
|
+
}
|
|
96
|
+
setSrc(src) {
|
|
97
|
+
const writable = this.getWritable();
|
|
98
|
+
writable.__src = src || this.getURL();
|
|
99
|
+
return writable;
|
|
100
|
+
}
|
|
101
|
+
getTitle() {
|
|
102
|
+
return this.getLatest().__title || this.getURL();
|
|
103
|
+
}
|
|
104
|
+
setTitle(title) {
|
|
105
|
+
const writable = this.getWritable();
|
|
106
|
+
writable.__title = title || this.getURL();
|
|
107
|
+
return writable;
|
|
108
|
+
}
|
|
109
|
+
decorate(editor) {
|
|
110
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(LinkIframeNode.getType());
|
|
111
|
+
if (!decorator) return null;
|
|
112
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
113
|
+
return decorator.render(this, editor);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
function $createLinkIframeNode(payload) {
|
|
117
|
+
return $applyNodeReplacement(new LinkIframeNode(payload.url, payload.src, payload.title));
|
|
118
|
+
}
|
|
119
|
+
function $convertLinkIframeElement(domNode) {
|
|
120
|
+
const element = domNode;
|
|
121
|
+
return { node: $createLinkIframeNode({
|
|
122
|
+
src: element.dataset.src,
|
|
123
|
+
title: element.textContent || element.href,
|
|
124
|
+
url: element.getAttribute("href") || ""
|
|
125
|
+
}) };
|
|
126
|
+
}
|
|
127
|
+
function $isLinkIframeNode(node) {
|
|
128
|
+
return node instanceof LinkIframeNode || node?.getType?.() === LinkIframeNode.getType();
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { $createLinkIframeNode, $isLinkIframeNode, LinkIframeNode };
|
|
@@ -1,9 +1,55 @@
|
|
|
1
|
-
import { BaseSelection, DOMConversionMap, EditorConfig, ElementNode, LexicalCommand, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, RangeSelection, SerializedElementNode, Spread } from "lexical";
|
|
1
|
+
import { BaseSelection, DOMConversionMap, EditorConfig, ElementDOMSlot, ElementNode, LexicalCommand, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, RangeSelection, SerializedElementNode, Spread } from "lexical";
|
|
2
2
|
//#region src/plugins/link/node/LinkNode.d.ts
|
|
3
3
|
type LinkAttributes = {
|
|
4
4
|
rel?: null | string;
|
|
5
5
|
target?: null | string;
|
|
6
6
|
title?: null | string;
|
|
7
7
|
};
|
|
8
|
+
type SerializedLinkNode = Spread<{
|
|
9
|
+
url: string;
|
|
10
|
+
}, Spread<LinkAttributes, SerializedElementNode>>;
|
|
11
|
+
type LinkHTMLElementType = HTMLAnchorElement | HTMLSpanElement;
|
|
12
|
+
/** @noInheritDoc */
|
|
13
|
+
declare class LinkNode extends ElementNode {
|
|
14
|
+
/** @internal */
|
|
15
|
+
__url: string;
|
|
16
|
+
/** @internal */
|
|
17
|
+
__target: null | string;
|
|
18
|
+
/** @internal */
|
|
19
|
+
__rel: null | string;
|
|
20
|
+
/** @internal */
|
|
21
|
+
__title: null | string;
|
|
22
|
+
static getType(): string;
|
|
23
|
+
static clone(node: LinkNode): LinkNode;
|
|
24
|
+
constructor(url?: string, attributes?: LinkAttributes, key?: NodeKey);
|
|
25
|
+
createDOM(config: EditorConfig, editor: LexicalEditor): LinkHTMLElementType;
|
|
26
|
+
updateLinkDOM(prevNode: this | null, anchor: LinkHTMLElementType, _config: EditorConfig): void;
|
|
27
|
+
updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
|
|
28
|
+
static importDOM(): DOMConversionMap | null;
|
|
29
|
+
static importJSON(serializedNode: SerializedLinkNode): LinkNode;
|
|
30
|
+
updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkNode>): this;
|
|
31
|
+
sanitizeUrl(url: string, allowedProtocols?: Set<string>): string;
|
|
32
|
+
exportJSON(): SerializedLinkNode | SerializedAutoLinkNode;
|
|
33
|
+
getURL(): string;
|
|
34
|
+
setURL(url: string): this;
|
|
35
|
+
getTarget(): null | string;
|
|
36
|
+
setTarget(target: null | string): this;
|
|
37
|
+
getRel(): null | string;
|
|
38
|
+
setRel(rel: null | string): this;
|
|
39
|
+
getTitle(): null | string;
|
|
40
|
+
setTitle(title: null | string): this;
|
|
41
|
+
insertNewAfter(_: RangeSelection, restoreSelection?: boolean): null | ElementNode;
|
|
42
|
+
canInsertTextBefore(): false;
|
|
43
|
+
canInsertTextAfter(): false;
|
|
44
|
+
canBeEmpty(): false;
|
|
45
|
+
isInline(): true;
|
|
46
|
+
extractWithChild(child: LexicalNode, selection: BaseSelection): boolean;
|
|
47
|
+
isEmailURI(): boolean;
|
|
48
|
+
isWebSiteURI(): boolean;
|
|
49
|
+
getDOMSlot(element: HTMLElement): ElementDOMSlot<HTMLElement>;
|
|
50
|
+
}
|
|
51
|
+
type SerializedAutoLinkNode = Spread<{
|
|
52
|
+
isUnlinked: boolean;
|
|
53
|
+
}, SerializedLinkNode>;
|
|
8
54
|
//#endregion
|
|
9
|
-
export { LinkAttributes };
|
|
55
|
+
export { LinkAttributes, LinkNode, SerializedAutoLinkNode, SerializedLinkNode };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createDebugLogger, init_debug } from "../../../utils/debug.js";
|
|
2
|
-
import { assert, init_utils } from "../../../editor-kernel/utils.js";
|
|
2
|
+
import { assert, getKernelFromEditorConfig, init_utils } from "../../../editor-kernel/utils.js";
|
|
3
|
+
import { ILinkService } from "../service/i-link-service.js";
|
|
3
4
|
import { $applyNodeReplacement, $getSelection, $isElementNode, $isNodeSelection, $isRangeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, ElementNode, createCommand } from "lexical";
|
|
4
5
|
import { $findMatchingParent, addClassNamesToElement, isHTMLAnchorElement } from "@lexical/utils";
|
|
5
6
|
//#region src/plugins/link/node/LinkNode.ts
|
|
@@ -45,7 +46,7 @@ var LinkNode = class LinkNode extends ElementNode {
|
|
|
45
46
|
event.target.classList.add("hover");
|
|
46
47
|
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
47
48
|
event,
|
|
48
|
-
|
|
49
|
+
node: this
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
});
|
|
@@ -59,7 +60,8 @@ var LinkNode = class LinkNode extends ElementNode {
|
|
|
59
60
|
}
|
|
60
61
|
updateLinkDOM(prevNode, anchor, _config) {
|
|
61
62
|
if (isHTMLAnchorElement(anchor)) {
|
|
62
|
-
|
|
63
|
+
const linkService = getLinkServiceFromConfig(_config);
|
|
64
|
+
if (!prevNode || prevNode.__url !== this.__url) anchor.href = this.sanitizeUrl(this.__url, linkService?.getAllowedProtocols());
|
|
63
65
|
for (const attr of [
|
|
64
66
|
"target",
|
|
65
67
|
"rel",
|
|
@@ -90,11 +92,11 @@ var LinkNode = class LinkNode extends ElementNode {
|
|
|
90
92
|
updateFromJSON(serializedNode) {
|
|
91
93
|
return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setRel(serializedNode.rel || null).setTarget(serializedNode.target || null).setTitle(serializedNode.title || null);
|
|
92
94
|
}
|
|
93
|
-
sanitizeUrl(url) {
|
|
95
|
+
sanitizeUrl(url, allowedProtocols = SUPPORTED_URL_PROTOCOLS) {
|
|
94
96
|
url = formatUrl(url);
|
|
95
97
|
try {
|
|
96
98
|
const parsedUrl = new URL(formatUrl(url));
|
|
97
|
-
if (!
|
|
99
|
+
if (!allowedProtocols.has(parsedUrl.protocol)) return "about:blank";
|
|
98
100
|
} catch {
|
|
99
101
|
return url;
|
|
100
102
|
}
|
|
@@ -174,7 +176,13 @@ var LinkNode = class LinkNode extends ElementNode {
|
|
|
174
176
|
isWebSiteURI() {
|
|
175
177
|
return this.__url.startsWith("https://") || this.__url.startsWith("http://");
|
|
176
178
|
}
|
|
179
|
+
getDOMSlot(element) {
|
|
180
|
+
return super.getDOMSlot(element);
|
|
181
|
+
}
|
|
177
182
|
};
|
|
183
|
+
function getLinkServiceFromConfig(config) {
|
|
184
|
+
return getKernelFromEditorConfig(config)?.requireService(ILinkService) || null;
|
|
185
|
+
}
|
|
178
186
|
function $convertAnchorElement(domNode) {
|
|
179
187
|
let node = null;
|
|
180
188
|
if (isHTMLAnchorElement(domNode)) {
|
|
@@ -202,7 +210,7 @@ function $createLinkNode(url = "", attributes) {
|
|
|
202
210
|
* @returns true if node is a LinkNode, false otherwise.
|
|
203
211
|
*/
|
|
204
212
|
function $isLinkNode(node) {
|
|
205
|
-
return node instanceof LinkNode;
|
|
213
|
+
return node instanceof LinkNode || node?.getType?.() === "link" || node?.getType?.() === "autolink";
|
|
206
214
|
}
|
|
207
215
|
var AutoLinkNode = class AutoLinkNode extends LinkNode {
|
|
208
216
|
constructor(url = "", attributes = {}, key) {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { DOMConversionMap, DOMExportOutput, DecoratorNode, EditorConfig, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
|
+
//#region src/plugins/link/node/SchemaNode.d.ts
|
|
3
|
+
type SerializedSchemaNode = Spread<{
|
|
4
|
+
payload?: unknown;
|
|
5
|
+
schemaType: string;
|
|
6
|
+
title: string;
|
|
7
|
+
url: string;
|
|
8
|
+
}, SerializedLexicalNode>;
|
|
9
|
+
declare class SchemaNode extends DecoratorNode<unknown> {
|
|
10
|
+
__url: string;
|
|
11
|
+
__schemaType: string;
|
|
12
|
+
__payload: unknown;
|
|
13
|
+
__title: string;
|
|
14
|
+
static getType(): string;
|
|
15
|
+
static clone(node: SchemaNode): SchemaNode;
|
|
16
|
+
static importJSON(serializedNode: SerializedSchemaNode): SchemaNode;
|
|
17
|
+
static importDOM(): DOMConversionMap | null;
|
|
18
|
+
constructor(url: string, schemaType?: string, payload?: unknown, title?: string, key?: NodeKey);
|
|
19
|
+
createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
|
|
20
|
+
updateDOM(): false;
|
|
21
|
+
exportDOM(): DOMExportOutput;
|
|
22
|
+
exportJSON(): SerializedSchemaNode;
|
|
23
|
+
updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedSchemaNode>): this;
|
|
24
|
+
getTextContent(): string;
|
|
25
|
+
getURL(): string;
|
|
26
|
+
setURL(url: string): this;
|
|
27
|
+
getSchemaType(): string;
|
|
28
|
+
setSchemaType(schemaType?: string): this;
|
|
29
|
+
getPayload(): unknown;
|
|
30
|
+
setPayload(payload: unknown): this;
|
|
31
|
+
getTitle(): string;
|
|
32
|
+
setTitle(title?: string): this;
|
|
33
|
+
decorate(editor: LexicalEditor): unknown;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { SchemaNode, SerializedSchemaNode };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
|
|
2
|
+
import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
|
|
3
|
+
import { $applyNodeReplacement, DecoratorNode } from "lexical";
|
|
4
|
+
import { addClassNamesToElement } from "@lexical/utils";
|
|
5
|
+
//#region src/plugins/link/node/SchemaNode.ts
|
|
6
|
+
init_utils();
|
|
7
|
+
var SchemaNode = class SchemaNode extends DecoratorNode {
|
|
8
|
+
static getType() {
|
|
9
|
+
return "schema-link";
|
|
10
|
+
}
|
|
11
|
+
static clone(node) {
|
|
12
|
+
return new SchemaNode(node.__url, node.__schemaType, node.__payload, node.__title, node.__key);
|
|
13
|
+
}
|
|
14
|
+
static importJSON(serializedNode) {
|
|
15
|
+
return $createSchemaNode({
|
|
16
|
+
payload: serializedNode.payload,
|
|
17
|
+
schemaType: serializedNode.schemaType,
|
|
18
|
+
title: serializedNode.title,
|
|
19
|
+
url: serializedNode.url
|
|
20
|
+
}).updateFromJSON(serializedNode);
|
|
21
|
+
}
|
|
22
|
+
static importDOM() {
|
|
23
|
+
return { a: (node) => {
|
|
24
|
+
if (node instanceof HTMLAnchorElement && node.dataset.schemaLink === "true") return {
|
|
25
|
+
conversion: $convertSchemaElement,
|
|
26
|
+
priority: 2
|
|
27
|
+
};
|
|
28
|
+
return null;
|
|
29
|
+
} };
|
|
30
|
+
}
|
|
31
|
+
constructor(url, schemaType, payload, title, key) {
|
|
32
|
+
super(key);
|
|
33
|
+
this.__url = url;
|
|
34
|
+
this.__schemaType = schemaType || "";
|
|
35
|
+
this.__payload = payload;
|
|
36
|
+
this.__title = title || url;
|
|
37
|
+
}
|
|
38
|
+
createDOM(config, editor) {
|
|
39
|
+
const element = document.createElement("div");
|
|
40
|
+
element.dataset.schemaLink = "true";
|
|
41
|
+
addClassNamesToElement(element, config.theme.schemaLink);
|
|
42
|
+
element.addEventListener("mouseenter", (event) => {
|
|
43
|
+
if (event.target instanceof HTMLElement) {
|
|
44
|
+
event.target.classList.add("hover");
|
|
45
|
+
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
46
|
+
event,
|
|
47
|
+
node: this
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
element.addEventListener("mouseleave", (event) => {
|
|
52
|
+
if (event.target instanceof HTMLElement) {
|
|
53
|
+
event.target.classList.remove("hover");
|
|
54
|
+
editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return element;
|
|
58
|
+
}
|
|
59
|
+
updateDOM() {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
exportDOM() {
|
|
63
|
+
const element = document.createElement("a");
|
|
64
|
+
element.dataset.schemaLink = "true";
|
|
65
|
+
element.dataset.schemaType = this.getSchemaType();
|
|
66
|
+
element.dataset.payload = JSON.stringify(this.getPayload() ?? null);
|
|
67
|
+
element.href = this.getURL();
|
|
68
|
+
element.textContent = this.getTitle();
|
|
69
|
+
return { element };
|
|
70
|
+
}
|
|
71
|
+
exportJSON() {
|
|
72
|
+
return {
|
|
73
|
+
...super.exportJSON(),
|
|
74
|
+
payload: this.getPayload(),
|
|
75
|
+
schemaType: this.getSchemaType(),
|
|
76
|
+
title: this.getTitle(),
|
|
77
|
+
url: this.getURL()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
updateFromJSON(serializedNode) {
|
|
81
|
+
return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setSchemaType(serializedNode.schemaType).setPayload(serializedNode.payload).setTitle(serializedNode.title);
|
|
82
|
+
}
|
|
83
|
+
getTextContent() {
|
|
84
|
+
return this.getTitle();
|
|
85
|
+
}
|
|
86
|
+
getURL() {
|
|
87
|
+
return this.getLatest().__url;
|
|
88
|
+
}
|
|
89
|
+
setURL(url) {
|
|
90
|
+
const writable = this.getWritable();
|
|
91
|
+
writable.__url = url;
|
|
92
|
+
return writable;
|
|
93
|
+
}
|
|
94
|
+
getSchemaType() {
|
|
95
|
+
return this.getLatest().__schemaType;
|
|
96
|
+
}
|
|
97
|
+
setSchemaType(schemaType) {
|
|
98
|
+
const writable = this.getWritable();
|
|
99
|
+
writable.__schemaType = schemaType || "";
|
|
100
|
+
return writable;
|
|
101
|
+
}
|
|
102
|
+
getPayload() {
|
|
103
|
+
return this.getLatest().__payload;
|
|
104
|
+
}
|
|
105
|
+
setPayload(payload) {
|
|
106
|
+
const writable = this.getWritable();
|
|
107
|
+
writable.__payload = payload;
|
|
108
|
+
return writable;
|
|
109
|
+
}
|
|
110
|
+
getTitle() {
|
|
111
|
+
return this.getLatest().__title || this.getURL();
|
|
112
|
+
}
|
|
113
|
+
setTitle(title) {
|
|
114
|
+
const writable = this.getWritable();
|
|
115
|
+
writable.__title = title || this.getURL();
|
|
116
|
+
return writable;
|
|
117
|
+
}
|
|
118
|
+
decorate(editor) {
|
|
119
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(SchemaNode.getType());
|
|
120
|
+
if (!decorator) return null;
|
|
121
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
122
|
+
return decorator.render(this, editor);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
function $createSchemaNode(payload) {
|
|
126
|
+
return $applyNodeReplacement(new SchemaNode(payload.url, payload.schemaType, payload.payload, payload.title));
|
|
127
|
+
}
|
|
128
|
+
function $convertSchemaElement(domNode) {
|
|
129
|
+
const element = domNode;
|
|
130
|
+
return { node: $createSchemaNode({
|
|
131
|
+
payload: parsePayload(element.dataset.payload),
|
|
132
|
+
schemaType: element.dataset.schemaType,
|
|
133
|
+
title: element.textContent || element.href,
|
|
134
|
+
url: element.getAttribute("href") || ""
|
|
135
|
+
}) };
|
|
136
|
+
}
|
|
137
|
+
function parsePayload(value) {
|
|
138
|
+
if (!value) return void 0;
|
|
139
|
+
try {
|
|
140
|
+
return JSON.parse(value);
|
|
141
|
+
} catch {
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function $isSchemaNode(node) {
|
|
146
|
+
return node instanceof SchemaNode || node?.getType?.() === SchemaNode.getType();
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { $createSchemaNode, $isSchemaNode, SchemaNode };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LinkNode } from "../node/LinkNode.js";
|
|
2
|
+
import { LinkService } from "../service/i-link-service.js";
|
|
3
|
+
import { LexicalEditor } from "lexical";
|
|
4
|
+
//#region src/plugins/link/normalization/index.d.ts
|
|
5
|
+
declare function normalizeSchemaLinkNode(node: LinkNode, editor: LexicalEditor, linkService: LinkService): boolean;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { normalizeSchemaLinkNode };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { $createSchemaNode } from "../node/SchemaNode.js";
|
|
2
|
+
//#region src/plugins/link/normalization/index.ts
|
|
3
|
+
function normalizeSchemaLinkNode(node, editor, linkService) {
|
|
4
|
+
const url = node.getURL();
|
|
5
|
+
const title = node.getTitle() || node.getTextContent() || url;
|
|
6
|
+
const schema = linkService.parseSchemaUrl(url);
|
|
7
|
+
const rule = linkService.getSchemaRule(url, {
|
|
8
|
+
editor,
|
|
9
|
+
schema,
|
|
10
|
+
text: node.getTextContent(),
|
|
11
|
+
title
|
|
12
|
+
});
|
|
13
|
+
if (!rule) return false;
|
|
14
|
+
const parsed = rule.parse?.(url, schema);
|
|
15
|
+
const payload = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? normalizeSchemaPayload(parsed) : { payload: parsed };
|
|
16
|
+
if (linkService.hasSchemaLinkRenderer(url)) payload.payload = {
|
|
17
|
+
...payload.payload && typeof payload.payload === "object" ? payload.payload : {},
|
|
18
|
+
__legacyLink: {
|
|
19
|
+
rel: node.getRel(),
|
|
20
|
+
target: node.getTarget(),
|
|
21
|
+
text: node.getTextContent(),
|
|
22
|
+
title: node.getTitle()
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
node.replace($createSchemaNode({
|
|
26
|
+
payload: payload.payload,
|
|
27
|
+
schemaType: payload.schemaType || rule.id,
|
|
28
|
+
title: payload.title || title,
|
|
29
|
+
url: payload.url || url
|
|
30
|
+
}));
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
function normalizeSchemaPayload(payload) {
|
|
34
|
+
if ("payload" in payload || "schemaType" in payload || "title" in payload || "url" in payload) return payload;
|
|
35
|
+
return { payload };
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { normalizeSchemaLinkNode };
|