@lobehub/editor 4.24.1 → 4.25.1
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.d.ts +9 -0
- package/es/editor-kernel/kernel.js +74 -11
- 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 +30 -0
- package/es/headless.js +2618 -139
- 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 +14 -4
- package/es/plugins/common/plugin/progressive-select-all.js +81 -0
- package/es/plugins/common/plugin/register.js +73 -4
- package/es/plugins/common/react/ReactPlainText.js +27 -9
- 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/react/hooks/useEditor.d.ts +24 -2
- package/es/react/hooks/useEditor.js +13 -3
- package/es/react.d.ts +2 -2
- package/es/renderer/LexicalRenderer.js +9 -2
- package/es/types/kernel.d.ts +16 -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,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 };
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import { IEditorPluginConstructor } from "../../../types/kernel.js";
|
|
2
2
|
import "../../../types/index.js";
|
|
3
|
+
import { LinkCardNode } from "../node/LinkCardNode.js";
|
|
4
|
+
import { LinkIframeNode } from "../node/LinkIframeNode.js";
|
|
3
5
|
import { LinkAttributes } from "../node/LinkNode.js";
|
|
6
|
+
import { SchemaNode } from "../node/SchemaNode.js";
|
|
7
|
+
import { LinkEmbedRule, LinkLabels, LinkToolbarAction, SchemaLinkRendererConfig, SchemaRule } from "../service/i-link-service.js";
|
|
8
|
+
import { normalizeSchemaLinkNode } from "../normalization/index.js";
|
|
9
|
+
import { LexicalEditor } from "lexical";
|
|
4
10
|
//#region src/plugins/link/plugin/index.d.ts
|
|
5
11
|
interface LinkPluginOptions {
|
|
12
|
+
allowedProtocols?: string[];
|
|
13
|
+
/** Registration-time option. Recreate LinkPlugin to apply changes. */
|
|
6
14
|
attributes?: LinkAttributes;
|
|
15
|
+
decoratorCard?: (node: LinkCardNode, editor: LexicalEditor) => any;
|
|
16
|
+
decoratorIframe?: (node: LinkIframeNode, editor: LexicalEditor) => any;
|
|
17
|
+
decoratorSchema?: (node: SchemaNode, editor: LexicalEditor) => any;
|
|
18
|
+
/** Registration-time option. Recreate LinkPlugin to apply changes. */
|
|
7
19
|
enableHotkey?: boolean;
|
|
20
|
+
labels?: Partial<LinkLabels>;
|
|
21
|
+
linkEmbedRules?: LinkEmbedRule[];
|
|
22
|
+
/** Registration-time option. Recreate LinkPlugin to apply changes. */
|
|
8
23
|
linkRegex?: RegExp;
|
|
24
|
+
/** Registration-time normalization strategy. Defaults to true for legacy compatibility. */
|
|
25
|
+
normalizeSchemaLinks?: boolean;
|
|
26
|
+
schemaLinkRenderers?: SchemaLinkRendererConfig[];
|
|
27
|
+
schemaRules?: SchemaRule[];
|
|
28
|
+
/** Registration-time option. Recreate LinkPlugin to apply changes. */
|
|
9
29
|
theme?: {
|
|
10
30
|
link?: string;
|
|
31
|
+
linkCard?: string;
|
|
32
|
+
linkIframe?: string;
|
|
33
|
+
schemaLink?: string;
|
|
11
34
|
};
|
|
35
|
+
toolbarActions?: LinkToolbarAction[];
|
|
36
|
+
/** Registration-time option. Recreate LinkPlugin to apply changes. */
|
|
12
37
|
validateUrl?: (url: string) => boolean;
|
|
13
38
|
}
|
|
14
39
|
declare const LinkPlugin: IEditorPluginConstructor<LinkPluginOptions>;
|
|
15
40
|
//#endregion
|
|
16
|
-
export { LinkPlugin, LinkPluginOptions };
|
|
41
|
+
export { LinkPlugin, LinkPluginOptions, normalizeSchemaLinkNode };
|
|
@@ -2,9 +2,14 @@ import { INodeHelper, init_helper } from "../../../editor-kernel/inode/helper.js
|
|
|
2
2
|
import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
|
|
3
3
|
import { ILitexmlService } from "../../litexml/service/litexml-service.js";
|
|
4
4
|
import { IMarkdownShortCutService } from "../../markdown/service/shortcut.js";
|
|
5
|
+
import { ILinkService, LinkService } from "../service/i-link-service.js";
|
|
5
6
|
import { $createLinkNode, $isLinkNode, AutoLinkNode, LinkNode } from "../node/LinkNode.js";
|
|
6
7
|
import { INSERT_LINK_COMMAND, registerLinkCommand } from "../command/index.js";
|
|
7
|
-
import {
|
|
8
|
+
import { $isLinkCardNode, LinkCardNode } from "../node/LinkCardNode.js";
|
|
9
|
+
import { $isLinkBlockCardNode, LinkBlockCardNode } from "../node/LinkBlockCardNode.js";
|
|
10
|
+
import { $isLinkIframeNode, LinkIframeNode } from "../node/LinkIframeNode.js";
|
|
11
|
+
import { $isSchemaNode, SchemaNode } from "../node/SchemaNode.js";
|
|
12
|
+
import { normalizeSchemaLinkNode } from "../normalization/index.js";
|
|
8
13
|
import { registerLinkCommands } from "./registry.js";
|
|
9
14
|
import { $createTextNode, COMMAND_PRIORITY_NORMAL, PASTE_COMMAND } from "lexical";
|
|
10
15
|
//#region src/plugins/link/plugin/index.ts
|
|
@@ -18,10 +23,22 @@ const LinkPlugin = class extends KernelPlugin {
|
|
|
18
23
|
super();
|
|
19
24
|
this.kernel = kernel;
|
|
20
25
|
this.config = config;
|
|
21
|
-
this.linkRegex = /^
|
|
26
|
+
this.linkRegex = /^[a-z][\d+.a-z-]*:\/\/\S+$/i;
|
|
22
27
|
this.service = new LinkService();
|
|
23
|
-
kernel.registerNodes([
|
|
28
|
+
kernel.registerNodes([
|
|
29
|
+
LinkNode,
|
|
30
|
+
AutoLinkNode,
|
|
31
|
+
LinkCardNode,
|
|
32
|
+
LinkBlockCardNode,
|
|
33
|
+
LinkIframeNode,
|
|
34
|
+
SchemaNode
|
|
35
|
+
]);
|
|
36
|
+
this.service.updateConfig(config);
|
|
24
37
|
kernel.registerService(ILinkService, this.service);
|
|
38
|
+
this.registerDecorator(kernel, LinkCardNode.getType(), (node, editor) => config?.decoratorCard ? config.decoratorCard(node, editor) : null);
|
|
39
|
+
this.registerDecorator(kernel, LinkBlockCardNode.getType(), (node, editor) => config?.decoratorCard ? config.decoratorCard(node, editor) : null);
|
|
40
|
+
this.registerDecorator(kernel, LinkIframeNode.getType(), (node, editor) => config?.decoratorIframe ? config.decoratorIframe(node, editor) : null);
|
|
41
|
+
this.registerDecorator(kernel, SchemaNode.getType(), (node, editor) => config?.decoratorSchema ? config.decoratorSchema(node, editor) : null);
|
|
25
42
|
if (config?.theme) kernel.registerThemes(config.theme);
|
|
26
43
|
if (config?.linkRegex) this.linkRegex = config.linkRegex;
|
|
27
44
|
}
|
|
@@ -45,6 +62,9 @@ const LinkPlugin = class extends KernelPlugin {
|
|
|
45
62
|
}
|
|
46
63
|
return false;
|
|
47
64
|
}, COMMAND_PRIORITY_NORMAL));
|
|
65
|
+
if (this.config?.normalizeSchemaLinks !== false) this.register(editor.registerNodeTransform(LinkNode, (node) => {
|
|
66
|
+
normalizeSchemaLinkNode(node, editor, this.service);
|
|
67
|
+
}));
|
|
48
68
|
this.registerMarkdown();
|
|
49
69
|
this.registerLiteXml();
|
|
50
70
|
}
|
|
@@ -69,6 +89,74 @@ const LinkPlugin = class extends KernelPlugin {
|
|
|
69
89
|
version: 1
|
|
70
90
|
})];
|
|
71
91
|
});
|
|
92
|
+
litexmlService.registerXMLWriter(LinkCardNode.getType(), (node, ctx) => {
|
|
93
|
+
if (!$isLinkCardNode(node)) return false;
|
|
94
|
+
return ctx.createXmlNode("link-card", {
|
|
95
|
+
description: node.getDescription(),
|
|
96
|
+
href: node.getURL(),
|
|
97
|
+
icon: node.getIcon(),
|
|
98
|
+
openTarget: node.getOpenTarget() || "_blank",
|
|
99
|
+
title: node.getTitle()
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
litexmlService.registerXMLWriter(LinkBlockCardNode.getType(), (node, ctx) => {
|
|
103
|
+
if (!$isLinkBlockCardNode(node)) return false;
|
|
104
|
+
return ctx.createXmlNode("link-card", {
|
|
105
|
+
description: node.getDescription(),
|
|
106
|
+
href: node.getURL(),
|
|
107
|
+
icon: node.getIcon(),
|
|
108
|
+
layout: "block",
|
|
109
|
+
openTarget: node.getOpenTarget() || "_blank",
|
|
110
|
+
title: node.getTitle()
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
litexmlService.registerXMLReader("link-card", (xmlNode) => {
|
|
114
|
+
return [INodeHelper.createTypeNode(xmlNode.getAttribute("layout") === "block" ? LinkBlockCardNode.getType() : LinkCardNode.getType(), {
|
|
115
|
+
description: xmlNode.getAttribute("description") || "",
|
|
116
|
+
icon: xmlNode.getAttribute("icon") || "",
|
|
117
|
+
openTarget: xmlNode.getAttribute("openTarget") || "_blank",
|
|
118
|
+
title: xmlNode.getAttribute("title") || xmlNode.getAttribute("href") || "",
|
|
119
|
+
type: xmlNode.getAttribute("layout") === "block" ? LinkBlockCardNode.getType() : LinkCardNode.getType(),
|
|
120
|
+
url: xmlNode.getAttribute("href") || "",
|
|
121
|
+
version: 1
|
|
122
|
+
})];
|
|
123
|
+
});
|
|
124
|
+
litexmlService.registerXMLWriter(LinkIframeNode.getType(), (node, ctx) => {
|
|
125
|
+
if (!$isLinkIframeNode(node)) return false;
|
|
126
|
+
return ctx.createXmlNode("link-iframe", {
|
|
127
|
+
href: node.getURL(),
|
|
128
|
+
src: node.getSrc(),
|
|
129
|
+
title: node.getTitle()
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
litexmlService.registerXMLReader("link-iframe", (xmlNode) => {
|
|
133
|
+
return [INodeHelper.createTypeNode(LinkIframeNode.getType(), {
|
|
134
|
+
src: xmlNode.getAttribute("src") || xmlNode.getAttribute("href") || "",
|
|
135
|
+
title: xmlNode.getAttribute("title") || xmlNode.getAttribute("href") || "",
|
|
136
|
+
type: LinkIframeNode.getType(),
|
|
137
|
+
url: xmlNode.getAttribute("href") || "",
|
|
138
|
+
version: 1
|
|
139
|
+
})];
|
|
140
|
+
});
|
|
141
|
+
litexmlService.registerXMLWriter(SchemaNode.getType(), (node, ctx) => {
|
|
142
|
+
if (!$isSchemaNode(node)) return false;
|
|
143
|
+
return ctx.createXmlNode("schema-link", {
|
|
144
|
+
href: node.getURL(),
|
|
145
|
+
payload: JSON.stringify(node.getPayload() ?? null),
|
|
146
|
+
schemaType: node.getSchemaType(),
|
|
147
|
+
title: node.getTitle()
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
litexmlService.registerXMLReader("schema-link", (xmlNode) => {
|
|
151
|
+
return [INodeHelper.createTypeNode(SchemaNode.getType(), {
|
|
152
|
+
payload: parseJsonAttribute(xmlNode.getAttribute("payload")),
|
|
153
|
+
schemaType: xmlNode.getAttribute("schemaType") || "",
|
|
154
|
+
title: xmlNode.getAttribute("title") || xmlNode.getAttribute("href") || "",
|
|
155
|
+
type: SchemaNode.getType(),
|
|
156
|
+
url: xmlNode.getAttribute("href") || "",
|
|
157
|
+
version: 1
|
|
158
|
+
})];
|
|
159
|
+
});
|
|
72
160
|
}
|
|
73
161
|
registerMarkdown() {
|
|
74
162
|
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
@@ -88,6 +176,30 @@ const LinkPlugin = class extends KernelPlugin {
|
|
|
88
176
|
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(LinkNode.getType(), (ctx, node) => {
|
|
89
177
|
if ($isLinkNode(node)) ctx.wrap("[", `](${node.getURL()})`);
|
|
90
178
|
});
|
|
179
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(LinkCardNode.getType(), (ctx, node) => {
|
|
180
|
+
if ($isLinkCardNode(node)) {
|
|
181
|
+
ctx.appendLine(`[${node.getTitle()}](${node.getURL()})\n`);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(LinkBlockCardNode.getType(), (ctx, node) => {
|
|
186
|
+
if ($isLinkBlockCardNode(node)) {
|
|
187
|
+
ctx.appendLine(`[${node.getTitle()}](${node.getURL()})\n`);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(LinkIframeNode.getType(), (ctx, node) => {
|
|
192
|
+
if ($isLinkIframeNode(node)) {
|
|
193
|
+
ctx.appendLine(`[${node.getTitle()}](${node.getURL()})\n`);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(SchemaNode.getType(), (ctx, node) => {
|
|
198
|
+
if ($isSchemaNode(node)) {
|
|
199
|
+
ctx.appendLine(`[${node.getTitle()}](${node.getURL()})\n`);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
91
203
|
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("link", (node, children) => {
|
|
92
204
|
return [INodeHelper.createElementNode("link", {
|
|
93
205
|
children,
|
|
@@ -102,5 +214,13 @@ const LinkPlugin = class extends KernelPlugin {
|
|
|
102
214
|
});
|
|
103
215
|
}
|
|
104
216
|
};
|
|
217
|
+
function parseJsonAttribute(value) {
|
|
218
|
+
if (!value) return void 0;
|
|
219
|
+
try {
|
|
220
|
+
return JSON.parse(value);
|
|
221
|
+
} catch {
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
105
225
|
//#endregion
|
|
106
|
-
export { LinkPlugin };
|
|
226
|
+
export { LinkPlugin, normalizeSchemaLinkNode };
|
|
@@ -2,49 +2,207 @@
|
|
|
2
2
|
import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context.js";
|
|
3
3
|
import { useLexicalEditor } from "../../../editor-kernel/react/useLexicalEditor.js";
|
|
4
4
|
import { MarkdownPlugin } from "../../markdown/plugin/index.js";
|
|
5
|
+
import { useTranslation } from "../../../editor-kernel/react/useTranslation.js";
|
|
5
6
|
import { ILinkService } from "../service/i-link-service.js";
|
|
7
|
+
import { UNLINK_LINK_COMMAND } from "../command/index.js";
|
|
6
8
|
import { LinkPlugin } from "../plugin/index.js";
|
|
9
|
+
import { useEditable } from "../../../editor-kernel/react/useEditable.js";
|
|
7
10
|
import PortalAnchor from "../../../editor-kernel/react/PortalAnchor.js";
|
|
11
|
+
import LinkCard from "./components/LinkCard.js";
|
|
8
12
|
import { styles } from "./style.js";
|
|
9
|
-
import LinkEdit from "./components/LinkEdit.js";
|
|
13
|
+
import LinkEdit, { EDIT_LINK_COMMAND } from "./components/LinkEdit.js";
|
|
14
|
+
import LinkIframe from "./components/LinkIframe.js";
|
|
10
15
|
import LinkToolbar from "./components/LinkToolbar.js";
|
|
11
|
-
import
|
|
16
|
+
import SchemaLink from "./components/SchemaLink.js";
|
|
17
|
+
import { LinkReactRendererRegistry, splitReactSchemaRules } from "./renderer-registry.js";
|
|
18
|
+
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
12
19
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
import { message } from "antd";
|
|
13
21
|
//#region src/plugins/link/react/ReactLinkPlugin.tsx
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
async function copyTextToClipboard(text) {
|
|
23
|
+
try {
|
|
24
|
+
await navigator.clipboard.writeText(text);
|
|
25
|
+
} catch {
|
|
26
|
+
const textarea = document.createElement("textarea");
|
|
27
|
+
textarea.value = text;
|
|
28
|
+
document.body.append(textarea);
|
|
29
|
+
textarea.select();
|
|
30
|
+
document.execCommand("copy");
|
|
31
|
+
textarea.remove();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function isDefaultToolbarItemEnabled(defaultToolbarItems, key) {
|
|
35
|
+
if (defaultToolbarItems === false) return false;
|
|
36
|
+
if (defaultToolbarItems === true || defaultToolbarItems === void 0) return true;
|
|
37
|
+
return defaultToolbarItems[key] !== false;
|
|
38
|
+
}
|
|
39
|
+
const ReactLinkPlugin = ({ allowedProtocols, theme, enableHotkey = true, validateUrl, attributes, defaultToolbarItems, labels, linkEmbedRules, normalizeSchemaLinks, renderLinkCard, renderLinkIframe, renderSchema, schemaLinkRenderers, schemaRules, toolbarActions }) => {
|
|
40
|
+
const [enableToolbar, setEnableToolbar] = useState(true);
|
|
41
|
+
const [linkService, setLinkService] = useState(null);
|
|
16
42
|
const [editor] = useLexicalComposerContext();
|
|
43
|
+
const { editable } = useEditable();
|
|
44
|
+
const t = useTranslation();
|
|
45
|
+
const registeredRef = useRef(false);
|
|
46
|
+
const rendererRegistryRef = useRef(new LinkReactRendererRegistry());
|
|
47
|
+
const splitSchemaRules = useMemo(() => splitReactSchemaRules(schemaRules), [schemaRules]);
|
|
17
48
|
useLayoutEffect(() => {
|
|
49
|
+
if (registeredRef.current) return;
|
|
50
|
+
registeredRef.current = true;
|
|
18
51
|
editor.registerPlugin(MarkdownPlugin);
|
|
19
52
|
editor.registerPlugin(LinkPlugin, {
|
|
53
|
+
allowedProtocols,
|
|
20
54
|
attributes,
|
|
55
|
+
decoratorCard: (node, editor) => /* @__PURE__ */ jsx(LinkCard, {
|
|
56
|
+
description: node.getDescription(),
|
|
57
|
+
editor,
|
|
58
|
+
icon: node.getIcon(),
|
|
59
|
+
node,
|
|
60
|
+
openTarget: node.getOpenTarget(),
|
|
61
|
+
rendererRegistry: rendererRegistryRef.current,
|
|
62
|
+
title: node.getTitle(),
|
|
63
|
+
url: node.getURL()
|
|
64
|
+
}),
|
|
65
|
+
decoratorIframe: (node, editor) => /* @__PURE__ */ jsx(LinkIframe, {
|
|
66
|
+
editor,
|
|
67
|
+
node,
|
|
68
|
+
rendererRegistry: rendererRegistryRef.current,
|
|
69
|
+
src: node.getSrc(),
|
|
70
|
+
title: node.getTitle(),
|
|
71
|
+
url: node.getURL()
|
|
72
|
+
}),
|
|
73
|
+
decoratorSchema: (node, editor) => /* @__PURE__ */ jsx(SchemaLink, {
|
|
74
|
+
editor,
|
|
75
|
+
node,
|
|
76
|
+
rendererRegistry: rendererRegistryRef.current
|
|
77
|
+
}),
|
|
21
78
|
enableHotkey,
|
|
79
|
+
labels,
|
|
80
|
+
linkEmbedRules,
|
|
81
|
+
normalizeSchemaLinks,
|
|
82
|
+
schemaLinkRenderers: getSchemaLinkRendererProtocols(schemaLinkRenderers),
|
|
83
|
+
schemaRules: splitSchemaRules.coreRules,
|
|
22
84
|
theme: theme || styles,
|
|
85
|
+
toolbarActions,
|
|
23
86
|
validateUrl
|
|
24
87
|
});
|
|
25
88
|
}, [
|
|
89
|
+
allowedProtocols,
|
|
26
90
|
attributes,
|
|
27
91
|
enableHotkey,
|
|
92
|
+
labels,
|
|
93
|
+
linkEmbedRules,
|
|
94
|
+
normalizeSchemaLinks,
|
|
95
|
+
schemaLinkRenderers,
|
|
96
|
+
schemaRules,
|
|
28
97
|
styles,
|
|
29
98
|
theme,
|
|
99
|
+
toolbarActions,
|
|
30
100
|
validateUrl
|
|
31
101
|
]);
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
rendererRegistryRef.current.update({
|
|
104
|
+
renderLinkCard,
|
|
105
|
+
renderLinkIframe,
|
|
106
|
+
renderSchema,
|
|
107
|
+
schemaLinkRenderers,
|
|
108
|
+
schemaRenderers: splitSchemaRules.schemaRenderers
|
|
109
|
+
});
|
|
110
|
+
}, [
|
|
111
|
+
renderLinkCard,
|
|
112
|
+
renderLinkIframe,
|
|
113
|
+
renderSchema,
|
|
114
|
+
schemaLinkRenderers,
|
|
115
|
+
splitSchemaRules.schemaRenderers
|
|
116
|
+
]);
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
if (!registeredRef.current) return;
|
|
119
|
+
editor.requireService(ILinkService)?.updateConfig({
|
|
120
|
+
allowedProtocols,
|
|
121
|
+
labels,
|
|
122
|
+
linkEmbedRules,
|
|
123
|
+
schemaLinkRenderers: getSchemaLinkRendererProtocols(schemaLinkRenderers),
|
|
124
|
+
schemaRules: splitSchemaRules.coreRules,
|
|
125
|
+
toolbarActions
|
|
126
|
+
});
|
|
127
|
+
}, [
|
|
128
|
+
allowedProtocols,
|
|
129
|
+
editor,
|
|
130
|
+
labels,
|
|
131
|
+
linkEmbedRules,
|
|
132
|
+
schemaLinkRenderers,
|
|
133
|
+
splitSchemaRules.coreRules,
|
|
134
|
+
toolbarActions
|
|
135
|
+
]);
|
|
32
136
|
useLexicalEditor(() => {
|
|
33
137
|
const linkService = editor.requireService(ILinkService);
|
|
138
|
+
setLinkService(linkService);
|
|
34
139
|
setEnableToolbar(linkService.enableLinkToolbar);
|
|
35
140
|
const handleChange = () => {
|
|
36
141
|
setEnableToolbar(linkService.enableLinkToolbar);
|
|
37
142
|
};
|
|
38
143
|
linkService.on("linkToolbarChange", handleChange);
|
|
144
|
+
const unregisterItems = [];
|
|
145
|
+
const registerDefaultToolbarItem = (key, item) => {
|
|
146
|
+
if (!isDefaultToolbarItemEnabled(defaultToolbarItems, key)) return;
|
|
147
|
+
unregisterItems.push(linkService.registerToolbarItem(item));
|
|
148
|
+
};
|
|
149
|
+
registerDefaultToolbarItem("open", {
|
|
150
|
+
icon: "open",
|
|
151
|
+
key: "open",
|
|
152
|
+
label: "link.open",
|
|
153
|
+
onClick: ({ editor, linkNode }) => {
|
|
154
|
+
const linkUrl = editor.getEditorState().read(() => linkNode.getURL());
|
|
155
|
+
window.open(linkUrl, "_blank");
|
|
156
|
+
},
|
|
157
|
+
order: 10
|
|
158
|
+
});
|
|
159
|
+
registerDefaultToolbarItem("edit", {
|
|
160
|
+
icon: "edit",
|
|
161
|
+
key: "edit",
|
|
162
|
+
label: "link.edit",
|
|
163
|
+
onClick: ({ close, editor, linkDom, linkNode }) => {
|
|
164
|
+
close();
|
|
165
|
+
editor.dispatchCommand(EDIT_LINK_COMMAND, {
|
|
166
|
+
linkNode,
|
|
167
|
+
linkNodeDOM: linkDom
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
order: 20
|
|
171
|
+
});
|
|
172
|
+
registerDefaultToolbarItem("copy", {
|
|
173
|
+
icon: "copy",
|
|
174
|
+
key: "copy",
|
|
175
|
+
label: "link.copy",
|
|
176
|
+
onClick: async ({ editor, linkNode }) => {
|
|
177
|
+
await copyTextToClipboard(editor.getEditorState().read(() => linkNode.getURL()));
|
|
178
|
+
message.success(t("link.copySuccess"));
|
|
179
|
+
},
|
|
180
|
+
order: 30
|
|
181
|
+
});
|
|
182
|
+
registerDefaultToolbarItem("unlink", {
|
|
183
|
+
icon: "unlink",
|
|
184
|
+
key: "unlink",
|
|
185
|
+
label: "link.unlink",
|
|
186
|
+
onClick: ({ close, editor, linkNode }) => {
|
|
187
|
+
editor.dispatchCommand(UNLINK_LINK_COMMAND, { key: linkNode.getKey() });
|
|
188
|
+
close();
|
|
189
|
+
},
|
|
190
|
+
order: 40
|
|
191
|
+
});
|
|
39
192
|
return () => {
|
|
40
193
|
linkService.off("linkToolbarChange", handleChange);
|
|
194
|
+
unregisterItems.forEach((unregister) => unregister());
|
|
41
195
|
};
|
|
42
|
-
}, []);
|
|
196
|
+
}, [defaultToolbarItems, t]);
|
|
43
197
|
return /* @__PURE__ */ jsxs(PortalAnchor, { children: [/* @__PURE__ */ jsx(LinkToolbar, {
|
|
44
198
|
editor: editor.getLexicalEditor(),
|
|
45
|
-
enable: enableToolbar
|
|
46
|
-
|
|
199
|
+
enable: enableToolbar && editable,
|
|
200
|
+
linkService
|
|
201
|
+
}), editable && /* @__PURE__ */ jsx(LinkEdit, { editor: editor.getLexicalEditor() })] });
|
|
47
202
|
};
|
|
48
203
|
ReactLinkPlugin.displayName = "ReactLinkPlugin";
|
|
204
|
+
function getSchemaLinkRendererProtocols(renderers) {
|
|
205
|
+
return renderers?.map(({ protocol }) => ({ protocol }));
|
|
206
|
+
}
|
|
49
207
|
//#endregion
|
|
50
208
|
export { ReactLinkPlugin, ReactLinkPlugin as default };
|