@lexical/react 0.12.1 → 0.12.3
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/LexicalAutoEmbedPlugin.d.ts +3 -2
- package/LexicalAutoEmbedPlugin.dev.js +4 -14
- package/LexicalAutoEmbedPlugin.prod.js +4 -3
- package/LexicalAutoFocusPlugin.dev.js +0 -1
- package/LexicalAutoLinkPlugin.dev.js +126 -73
- package/LexicalAutoLinkPlugin.prod.js +11 -7
- package/LexicalBlockWithAlignableContents.dev.js +0 -10
- package/LexicalCharacterLimitPlugin.dev.js +7 -46
- package/LexicalCheckListPlugin.dev.js +10 -48
- package/LexicalClearEditorPlugin.dev.js +1 -1
- package/LexicalClickableLinkPlugin.dev.js +2 -20
- package/LexicalCollaborationContext.dev.js +0 -3
- package/LexicalCollaborationPlugin.dev.js +8 -37
- package/LexicalComposer.d.ts +3 -7
- package/LexicalComposer.dev.js +9 -11
- package/LexicalComposer.js.flow +4 -4
- package/LexicalComposer.prod.js +2 -1
- package/LexicalComposerContext.dev.js +0 -6
- package/LexicalContentEditable.dev.js +1 -2
- package/LexicalContextMenuPlugin.d.ts +3 -2
- package/LexicalContextMenuPlugin.dev.js +28 -82
- package/LexicalContextMenuPlugin.prod.js +16 -15
- package/LexicalDecoratorBlockNode.dev.js +0 -6
- package/LexicalEditorRefPlugin.dev.js +0 -3
- package/LexicalHashtagPlugin.dev.js +73 -43
- package/LexicalHorizontalRuleNode.dev.js +0 -21
- package/LexicalHorizontalRulePlugin.dev.js +0 -4
- package/LexicalLinkPlugin.dev.js +4 -10
- package/LexicalListPlugin.dev.js +0 -2
- package/LexicalMarkdownShortcutPlugin.dev.js +2 -2
- package/LexicalNestedComposer.d.ts +2 -2
- package/LexicalNestedComposer.dev.js +18 -16
- package/LexicalNestedComposer.js.flow +7 -2
- package/LexicalNestedComposer.prod.js +4 -3
- package/LexicalNodeEventPlugin.dev.js +2 -6
- package/LexicalNodeMenuPlugin.d.ts +3 -2
- package/LexicalNodeMenuPlugin.dev.js +27 -83
- package/LexicalNodeMenuPlugin.prod.js +15 -15
- package/LexicalOnChangePlugin.dev.js +1 -1
- package/LexicalPlainTextPlugin.dev.js +8 -12
- package/LexicalRichTextPlugin.dev.js +8 -12
- package/LexicalTabIndentationPlugin.dev.js +7 -16
- package/LexicalTableOfContents.dev.js +5 -33
- package/LexicalTablePlugin.dev.js +11 -28
- package/LexicalTreeView.dev.js +14 -79
- package/LexicalTypeaheadMenuPlugin.d.ts +4 -3
- package/LexicalTypeaheadMenuPlugin.dev.js +39 -176
- package/LexicalTypeaheadMenuPlugin.prod.js +19 -20
- package/package.json +19 -19
- package/shared/LexicalMenu.d.ts +3 -2
- package/useLexicalEditable.dev.js +1 -5
- package/useLexicalIsTextContentEmpty.dev.js +1 -0
- package/useLexicalNodeSelection.dev.js +0 -7
- package/useLexicalSubscription.dev.js +1 -3
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
|
-
import type { LexicalNode } from 'lexical';
|
|
9
|
+
import type { CommandListenerPriority, LexicalNode } from 'lexical';
|
|
10
10
|
import { MenuOption, MenuRenderFn } from '@lexical/react/LexicalNodeMenuPlugin';
|
|
11
11
|
import { LexicalCommand, LexicalEditor } from 'lexical';
|
|
12
12
|
export type EmbedMatchResult<TEmbedMatchResult = unknown> = {
|
|
@@ -33,6 +33,7 @@ type LexicalAutoEmbedPluginProps<TEmbedConfig extends EmbedConfig> = {
|
|
|
33
33
|
onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void;
|
|
34
34
|
getMenuOptions: (activeEmbedConfig: TEmbedConfig, embedFn: () => void, dismissFn: () => void) => Array<AutoEmbedOption>;
|
|
35
35
|
menuRenderFn: MenuRenderFn<AutoEmbedOption>;
|
|
36
|
+
menuCommandPriority?: CommandListenerPriority;
|
|
36
37
|
};
|
|
37
|
-
export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuRenderFn, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
|
|
38
|
+
export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuRenderFn, menuCommandPriority, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
|
|
38
39
|
export {};
|
|
@@ -28,13 +28,13 @@ class AutoEmbedOption extends LexicalNodeMenuPlugin.MenuOption {
|
|
|
28
28
|
this.title = title;
|
|
29
29
|
this.onSelect = options.onSelect.bind(this);
|
|
30
30
|
}
|
|
31
|
-
|
|
32
31
|
}
|
|
33
32
|
function LexicalAutoEmbedPlugin({
|
|
34
33
|
embedConfigs,
|
|
35
34
|
onOpenEmbedModalForConfig,
|
|
36
35
|
getMenuOptions,
|
|
37
|
-
menuRenderFn
|
|
36
|
+
menuRenderFn,
|
|
37
|
+
menuCommandPriority = lexical.COMMAND_PRIORITY_LOW
|
|
38
38
|
}) {
|
|
39
39
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
40
40
|
const [nodeKey, setNodeKey] = React.useState(null);
|
|
@@ -46,12 +46,10 @@ function LexicalAutoEmbedPlugin({
|
|
|
46
46
|
const checkIfLinkNodeIsEmbeddable = React.useCallback(key => {
|
|
47
47
|
editor.getEditorState().read(async () => {
|
|
48
48
|
const linkNode = lexical.$getNodeByKey(key);
|
|
49
|
-
|
|
50
49
|
if (link.$isLinkNode(linkNode)) {
|
|
51
50
|
for (let i = 0; i < embedConfigs.length; i++) {
|
|
52
51
|
const embedConfig = embedConfigs[i];
|
|
53
52
|
const urlMatch = await Promise.resolve(embedConfig.parseUrl(linkNode.__url));
|
|
54
|
-
|
|
55
53
|
if (urlMatch != null) {
|
|
56
54
|
setActiveEmbedConfig(embedConfig);
|
|
57
55
|
setNodeKey(linkNode.getKey());
|
|
@@ -73,7 +71,6 @@ function LexicalAutoEmbedPlugin({
|
|
|
73
71
|
}
|
|
74
72
|
}
|
|
75
73
|
};
|
|
76
|
-
|
|
77
74
|
return utils.mergeRegister(...[link.LinkNode, link.AutoLinkNode].map(Klass => editor.registerMutationListener(Klass, (...args) => listener(...args))));
|
|
78
75
|
}, [checkIfLinkNodeIsEmbeddable, editor, embedConfigs, nodeKey, reset]);
|
|
79
76
|
React.useEffect(() => {
|
|
@@ -81,12 +78,10 @@ function LexicalAutoEmbedPlugin({
|
|
|
81
78
|
const embedConfig = embedConfigs.find(({
|
|
82
79
|
type
|
|
83
80
|
}) => type === embedConfigType);
|
|
84
|
-
|
|
85
81
|
if (embedConfig) {
|
|
86
82
|
onOpenEmbedModalForConfig(embedConfig);
|
|
87
83
|
return true;
|
|
88
84
|
}
|
|
89
|
-
|
|
90
85
|
return false;
|
|
91
86
|
}, lexical.COMMAND_PRIORITY_EDITOR);
|
|
92
87
|
}, [editor, embedConfigs, onOpenEmbedModalForConfig]);
|
|
@@ -94,25 +89,19 @@ function LexicalAutoEmbedPlugin({
|
|
|
94
89
|
if (activeEmbedConfig != null && nodeKey != null) {
|
|
95
90
|
const linkNode = editor.getEditorState().read(() => {
|
|
96
91
|
const node = lexical.$getNodeByKey(nodeKey);
|
|
97
|
-
|
|
98
92
|
if (link.$isLinkNode(node)) {
|
|
99
93
|
return node;
|
|
100
94
|
}
|
|
101
|
-
|
|
102
95
|
return null;
|
|
103
96
|
});
|
|
104
|
-
|
|
105
97
|
if (link.$isLinkNode(linkNode)) {
|
|
106
98
|
const result = await Promise.resolve(activeEmbedConfig.parseUrl(linkNode.__url));
|
|
107
|
-
|
|
108
99
|
if (result != null) {
|
|
109
100
|
editor.update(() => {
|
|
110
101
|
if (!lexical.$getSelection()) {
|
|
111
102
|
linkNode.selectEnd();
|
|
112
103
|
}
|
|
113
|
-
|
|
114
104
|
activeEmbedConfig.insertNode(editor, result);
|
|
115
|
-
|
|
116
105
|
if (linkNode.isAttached()) {
|
|
117
106
|
linkNode.remove();
|
|
118
107
|
}
|
|
@@ -135,7 +124,8 @@ function LexicalAutoEmbedPlugin({
|
|
|
135
124
|
onClose: reset,
|
|
136
125
|
onSelectOption: onSelectOption,
|
|
137
126
|
options: options,
|
|
138
|
-
menuRenderFn: menuRenderFn
|
|
127
|
+
menuRenderFn: menuRenderFn,
|
|
128
|
+
commandPriority: menuCommandPriority
|
|
139
129
|
}) : null;
|
|
140
130
|
}
|
|
141
131
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
'use strict';var e=require("@lexical/link"),k=require("@lexical/react/LexicalComposerContext"),l=require("@lexical/react/LexicalNodeMenuPlugin"),q=require("@lexical/utils"),r=require("lexical"),z=require("react");let A=r.createCommand("INSERT_EMBED_COMMAND");class B extends l.MenuOption{constructor(f,n){super(f);this.title=f;this.onSelect=n.onSelect.bind(this)}}exports.AutoEmbedOption=B;exports.INSERT_EMBED_COMMAND=A;
|
|
8
|
-
exports.LexicalAutoEmbedPlugin=function({embedConfigs:f,onOpenEmbedModalForConfig:n,getMenuOptions:t,menuRenderFn:C}){let [c]=k.useLexicalComposerContext(),[g,u]=z.useState(null),[h,v]=z.useState(null),m=z.useCallback(()=>{u(null);v(null)},[]),w=z.useCallback(b=>{c.getEditorState().read(async()=>{const a=r.$getNodeByKey(b);if(e.$isLinkNode(a))for(let d=0;d<f.length;d++){const p=f[d];null!=await Promise.resolve(p.parseUrl(a.__url))&&(v(p),u(a.getKey()))}})},
|
|
9
|
-
dirtyLeaves:p})=>{for(const [x,
|
|
10
|
-
await Promise.resolve(h.parseUrl(b.__url));null!=a&&c.update(()=>{r.$getSelection()||b.selectEnd();h.insertNode(c,a);b.isAttached()&&b.remove()})}}},[h,c,g]),
|
|
8
|
+
exports.LexicalAutoEmbedPlugin=function({embedConfigs:f,onOpenEmbedModalForConfig:n,getMenuOptions:t,menuRenderFn:C,menuCommandPriority:D=r.COMMAND_PRIORITY_LOW}){let [c]=k.useLexicalComposerContext(),[g,u]=z.useState(null),[h,v]=z.useState(null),m=z.useCallback(()=>{u(null);v(null)},[]),w=z.useCallback(b=>{c.getEditorState().read(async()=>{const a=r.$getNodeByKey(b);if(e.$isLinkNode(a))for(let d=0;d<f.length;d++){const p=f[d];null!=await Promise.resolve(p.parseUrl(a.__url))&&(v(p),u(a.getKey()))}})},
|
|
9
|
+
[c,f]);z.useEffect(()=>{let b=(a,{updateTags:d,dirtyLeaves:p})=>{for(const [x,E]of a)"created"===E&&d.has("paste")&&3>=p.size?w(x):x===g&&m()};return q.mergeRegister(...[e.LinkNode,e.AutoLinkNode].map(a=>c.registerMutationListener(a,(...d)=>b(...d))))},[w,c,f,g,m]);z.useEffect(()=>c.registerCommand(A,b=>{let a=f.find(({type:d})=>d===b);return a?(n(a),!0):!1},r.COMMAND_PRIORITY_EDITOR),[c,f,n]);let y=z.useCallback(async()=>{if(null!=h&&null!=g){const b=c.getEditorState().read(()=>{const a=r.$getNodeByKey(g);
|
|
10
|
+
return e.$isLinkNode(a)?a:null});if(e.$isLinkNode(b)){const a=await Promise.resolve(h.parseUrl(b.__url));null!=a&&c.update(()=>{r.$getSelection()||b.selectEnd();h.insertNode(c,a);b.isAttached()&&b.remove()})}}},[h,c,g]),F=z.useMemo(()=>null!=h&&null!=g?t(h,y,m):[],[h,y,t,g,m]),G=z.useCallback((b,a,d)=>{c.update(()=>{b.onSelect(a);d()})},[c]);return null!=g?z.createElement(l.LexicalNodeMenuPlugin,{nodeKey:g,onClose:m,onSelectOption:G,options:F,menuRenderFn:C,commandPriority:D}):null};
|
|
11
|
+
exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
|
|
@@ -28,7 +28,6 @@ function AutoFocusPlugin({
|
|
|
28
28
|
// of this plugin, which should preserve focus too.
|
|
29
29
|
const activeElement = document.activeElement;
|
|
30
30
|
const rootElement = editor.getRootElement();
|
|
31
|
-
|
|
32
31
|
if (rootElement !== null && (activeElement === null || !rootElement.contains(activeElement))) {
|
|
33
32
|
// Note: preventScroll won't work in Webkit.
|
|
34
33
|
rootElement.focus({
|
|
@@ -27,194 +27,251 @@ function createLinkMatcherWithRegExp(regExp, urlTransformer = text => text) {
|
|
|
27
27
|
index: match.index,
|
|
28
28
|
length: match[0].length,
|
|
29
29
|
text: match[0],
|
|
30
|
-
url: urlTransformer(
|
|
30
|
+
url: urlTransformer(match[0])
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
|
|
35
34
|
function findFirstMatch(text, matchers) {
|
|
36
35
|
for (let i = 0; i < matchers.length; i++) {
|
|
37
36
|
const match = matchers[i](text);
|
|
38
|
-
|
|
39
37
|
if (match) {
|
|
40
38
|
return match;
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
|
-
|
|
44
41
|
return null;
|
|
45
42
|
}
|
|
46
|
-
|
|
47
43
|
const PUNCTUATION_OR_SPACE = /[.,;\s]/;
|
|
48
|
-
|
|
49
44
|
function isSeparator(char) {
|
|
50
45
|
return PUNCTUATION_OR_SPACE.test(char);
|
|
51
46
|
}
|
|
52
|
-
|
|
53
47
|
function endsWithSeparator(textContent) {
|
|
54
48
|
return isSeparator(textContent[textContent.length - 1]);
|
|
55
49
|
}
|
|
56
|
-
|
|
57
50
|
function startsWithSeparator(textContent) {
|
|
58
51
|
return isSeparator(textContent[0]);
|
|
59
52
|
}
|
|
60
|
-
|
|
61
53
|
function isPreviousNodeValid(node) {
|
|
62
54
|
let previousNode = node.getPreviousSibling();
|
|
63
|
-
|
|
64
55
|
if (lexical.$isElementNode(previousNode)) {
|
|
65
56
|
previousNode = previousNode.getLastDescendant();
|
|
66
57
|
}
|
|
67
|
-
|
|
68
58
|
return previousNode === null || lexical.$isLineBreakNode(previousNode) || lexical.$isTextNode(previousNode) && endsWithSeparator(previousNode.getTextContent());
|
|
69
59
|
}
|
|
70
|
-
|
|
71
60
|
function isNextNodeValid(node) {
|
|
72
61
|
let nextNode = node.getNextSibling();
|
|
73
|
-
|
|
74
62
|
if (lexical.$isElementNode(nextNode)) {
|
|
75
63
|
nextNode = nextNode.getFirstDescendant();
|
|
76
64
|
}
|
|
77
|
-
|
|
78
65
|
return nextNode === null || lexical.$isLineBreakNode(nextNode) || lexical.$isTextNode(nextNode) && startsWithSeparator(nextNode.getTextContent());
|
|
79
66
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const contentBeforeIsValid = matchStart > 0 ? isSeparator(text[matchStart - 1]) : isPreviousNodeValid(node);
|
|
83
|
-
|
|
67
|
+
function isContentAroundIsValid(matchStart, matchEnd, text, nodes) {
|
|
68
|
+
const contentBeforeIsValid = matchStart > 0 ? isSeparator(text[matchStart - 1]) : isPreviousNodeValid(nodes[0]);
|
|
84
69
|
if (!contentBeforeIsValid) {
|
|
85
70
|
return false;
|
|
86
71
|
}
|
|
87
|
-
|
|
88
|
-
const contentAfterIsValid = matchEnd < text.length ? isSeparator(text[matchEnd]) : isNextNodeValid(node);
|
|
72
|
+
const contentAfterIsValid = matchEnd < text.length ? isSeparator(text[matchEnd]) : isNextNodeValid(nodes[nodes.length - 1]);
|
|
89
73
|
return contentAfterIsValid;
|
|
90
74
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
let
|
|
96
|
-
let
|
|
75
|
+
function extractMatchingNodes(nodes, startIndex, endIndex) {
|
|
76
|
+
const unmodifiedBeforeNodes = [];
|
|
77
|
+
const matchingNodes = [];
|
|
78
|
+
const unmodifiedAfterNodes = [];
|
|
79
|
+
let matchingOffset = 0;
|
|
80
|
+
let currentOffset = 0;
|
|
81
|
+
const currentNodes = [...nodes];
|
|
82
|
+
while (currentNodes.length > 0) {
|
|
83
|
+
const currentNode = currentNodes[0];
|
|
84
|
+
const currentNodeText = currentNode.getTextContent();
|
|
85
|
+
const currentNodeLength = currentNodeText.length;
|
|
86
|
+
const currentNodeStart = currentOffset;
|
|
87
|
+
const currentNodeEnd = currentOffset + currentNodeLength;
|
|
88
|
+
if (currentNodeEnd <= startIndex) {
|
|
89
|
+
unmodifiedBeforeNodes.push(currentNode);
|
|
90
|
+
matchingOffset += currentNodeLength;
|
|
91
|
+
} else if (currentNodeStart >= endIndex) {
|
|
92
|
+
unmodifiedAfterNodes.push(currentNode);
|
|
93
|
+
} else {
|
|
94
|
+
matchingNodes.push(currentNode);
|
|
95
|
+
}
|
|
96
|
+
currentOffset += currentNodeLength;
|
|
97
|
+
currentNodes.shift();
|
|
98
|
+
}
|
|
99
|
+
return [matchingOffset, unmodifiedBeforeNodes, matchingNodes, unmodifiedAfterNodes];
|
|
100
|
+
}
|
|
101
|
+
function createAutoLinkNode(nodes, startIndex, endIndex, match) {
|
|
102
|
+
const linkNode = link.$createAutoLinkNode(match.url, match.attributes);
|
|
103
|
+
if (nodes.length === 1) {
|
|
104
|
+
let remainingTextNode = nodes[0];
|
|
105
|
+
let linkTextNode;
|
|
106
|
+
if (startIndex === 0) {
|
|
107
|
+
[linkTextNode, remainingTextNode] = remainingTextNode.splitText(endIndex);
|
|
108
|
+
} else {
|
|
109
|
+
[, linkTextNode, remainingTextNode] = remainingTextNode.splitText(startIndex, endIndex);
|
|
110
|
+
}
|
|
111
|
+
const textNode = lexical.$createTextNode(match.text);
|
|
112
|
+
textNode.setFormat(linkTextNode.getFormat());
|
|
113
|
+
textNode.setDetail(linkTextNode.getDetail());
|
|
114
|
+
linkNode.append(textNode);
|
|
115
|
+
linkTextNode.replace(linkNode);
|
|
116
|
+
return remainingTextNode;
|
|
117
|
+
} else if (nodes.length > 1) {
|
|
118
|
+
const firstTextNode = nodes[0];
|
|
119
|
+
let offset = firstTextNode.getTextContent().length;
|
|
120
|
+
let firstLinkTextNode;
|
|
121
|
+
if (startIndex === 0) {
|
|
122
|
+
firstLinkTextNode = firstTextNode;
|
|
123
|
+
} else {
|
|
124
|
+
[, firstLinkTextNode] = firstTextNode.splitText(startIndex);
|
|
125
|
+
}
|
|
126
|
+
const linkNodes = [];
|
|
127
|
+
let remainingTextNode;
|
|
128
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
129
|
+
const currentNode = nodes[i];
|
|
130
|
+
const currentNodeText = currentNode.getTextContent();
|
|
131
|
+
const currentNodeLength = currentNodeText.length;
|
|
132
|
+
const currentNodeStart = offset;
|
|
133
|
+
const currentNodeEnd = offset + currentNodeLength;
|
|
134
|
+
if (currentNodeStart < endIndex) {
|
|
135
|
+
if (currentNodeEnd <= endIndex) {
|
|
136
|
+
linkNodes.push(currentNode);
|
|
137
|
+
} else {
|
|
138
|
+
const [linkTextNode, endNode] = currentNode.splitText(endIndex - currentNodeStart);
|
|
139
|
+
linkNodes.push(linkTextNode);
|
|
140
|
+
remainingTextNode = endNode;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
offset += currentNodeLength;
|
|
144
|
+
}
|
|
145
|
+
const selection = lexical.$getSelection();
|
|
146
|
+
const selectedTextNode = selection ? selection.getNodes().find(lexical.$isTextNode) : undefined;
|
|
147
|
+
const textNode = lexical.$createTextNode(firstLinkTextNode.getTextContent());
|
|
148
|
+
textNode.setFormat(firstLinkTextNode.getFormat());
|
|
149
|
+
textNode.setDetail(firstLinkTextNode.getDetail());
|
|
150
|
+
linkNode.append(textNode, ...linkNodes);
|
|
151
|
+
// it does not preserve caret position if caret was at the first text node
|
|
152
|
+
// so we need to restore caret position
|
|
153
|
+
if (selectedTextNode && selectedTextNode === firstLinkTextNode) {
|
|
154
|
+
if (lexical.$isRangeSelection(selection)) {
|
|
155
|
+
textNode.select(selection.anchor.offset, selection.focus.offset);
|
|
156
|
+
} else if (lexical.$isNodeSelection(selection)) {
|
|
157
|
+
textNode.select(0, textNode.getTextContent().length);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
firstLinkTextNode.replace(linkNode);
|
|
161
|
+
return remainingTextNode;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
function handleLinkCreation(nodes, matchers, onChange) {
|
|
166
|
+
let currentNodes = [...nodes];
|
|
167
|
+
let text = currentNodes.map(node => node.getTextContent()).join('');
|
|
97
168
|
let match;
|
|
98
|
-
|
|
169
|
+
let invalidMatchEnd = 0;
|
|
99
170
|
while ((match = findFirstMatch(text, matchers)) && match !== null) {
|
|
100
171
|
const matchStart = match.index;
|
|
101
172
|
const matchLength = match.length;
|
|
102
173
|
const matchEnd = matchStart + matchLength;
|
|
103
|
-
const isValid = isContentAroundIsValid(invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd,
|
|
104
|
-
|
|
174
|
+
const isValid = isContentAroundIsValid(invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd, text, currentNodes);
|
|
105
175
|
if (isValid) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
[, linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchStart, invalidMatchEnd + matchStart + matchLength);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const linkNode = link.$createAutoLinkNode(match.url, match.attributes);
|
|
115
|
-
const textNode = lexical.$createTextNode(match.text);
|
|
116
|
-
textNode.setFormat(linkTextNode.getFormat());
|
|
117
|
-
textNode.setDetail(linkTextNode.getDetail());
|
|
118
|
-
linkNode.append(textNode);
|
|
119
|
-
linkTextNode.replace(linkNode);
|
|
176
|
+
const [matchingOffset,, matchingNodes, unmodifiedAfterNodes] = extractMatchingNodes(currentNodes, invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd);
|
|
177
|
+
const actualMatchStart = invalidMatchEnd + matchStart - matchingOffset;
|
|
178
|
+
const actualMatchEnd = invalidMatchEnd + matchEnd - matchingOffset;
|
|
179
|
+
const remainingTextNode = createAutoLinkNode(matchingNodes, actualMatchStart, actualMatchEnd, match);
|
|
180
|
+
currentNodes = remainingTextNode ? [remainingTextNode, ...unmodifiedAfterNodes] : unmodifiedAfterNodes;
|
|
120
181
|
onChange(match.url, null);
|
|
121
182
|
invalidMatchEnd = 0;
|
|
122
183
|
} else {
|
|
123
184
|
invalidMatchEnd += matchEnd;
|
|
124
185
|
}
|
|
125
|
-
|
|
126
186
|
text = text.substring(matchEnd);
|
|
127
187
|
}
|
|
128
188
|
}
|
|
129
|
-
|
|
130
189
|
function handleLinkEdit(linkNode, matchers, onChange) {
|
|
131
190
|
// Check children are simple text
|
|
132
191
|
const children = linkNode.getChildren();
|
|
133
192
|
const childrenLength = children.length;
|
|
134
|
-
|
|
135
193
|
for (let i = 0; i < childrenLength; i++) {
|
|
136
194
|
const child = children[i];
|
|
137
|
-
|
|
138
195
|
if (!lexical.$isTextNode(child) || !child.isSimpleText()) {
|
|
139
196
|
replaceWithChildren(linkNode);
|
|
140
197
|
onChange(null, linkNode.getURL());
|
|
141
198
|
return;
|
|
142
199
|
}
|
|
143
|
-
}
|
|
144
|
-
|
|
200
|
+
}
|
|
145
201
|
|
|
202
|
+
// Check text content fully matches
|
|
146
203
|
const text = linkNode.getTextContent();
|
|
147
204
|
const match = findFirstMatch(text, matchers);
|
|
148
|
-
|
|
149
205
|
if (match === null || match.text !== text) {
|
|
150
206
|
replaceWithChildren(linkNode);
|
|
151
207
|
onChange(null, linkNode.getURL());
|
|
152
208
|
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
209
|
+
}
|
|
155
210
|
|
|
211
|
+
// Check neighbors
|
|
156
212
|
if (!isPreviousNodeValid(linkNode) || !isNextNodeValid(linkNode)) {
|
|
157
213
|
replaceWithChildren(linkNode);
|
|
158
214
|
onChange(null, linkNode.getURL());
|
|
159
215
|
return;
|
|
160
216
|
}
|
|
161
|
-
|
|
162
217
|
const url = linkNode.getURL();
|
|
163
|
-
|
|
164
218
|
if (url !== match.url) {
|
|
165
219
|
linkNode.setURL(match.url);
|
|
166
220
|
onChange(match.url, url);
|
|
167
221
|
}
|
|
168
|
-
|
|
169
222
|
if (match.attributes) {
|
|
170
223
|
const rel = linkNode.getRel();
|
|
171
|
-
|
|
172
224
|
if (rel !== match.attributes.rel) {
|
|
173
225
|
linkNode.setRel(match.attributes.rel || null);
|
|
174
226
|
onChange(match.attributes.rel || null, rel);
|
|
175
227
|
}
|
|
176
|
-
|
|
177
228
|
const target = linkNode.getTarget();
|
|
178
|
-
|
|
179
229
|
if (target !== match.attributes.target) {
|
|
180
230
|
linkNode.setTarget(match.attributes.target || null);
|
|
181
231
|
onChange(match.attributes.target || null, target);
|
|
182
232
|
}
|
|
183
233
|
}
|
|
184
|
-
}
|
|
185
|
-
// Given the creation preconditions, these can only be simple text nodes.
|
|
186
|
-
|
|
234
|
+
}
|
|
187
235
|
|
|
236
|
+
// Bad neighbors are edits in neighbor nodes that make AutoLinks incompatible.
|
|
237
|
+
// Given the creation preconditions, these can only be simple text nodes.
|
|
188
238
|
function handleBadNeighbors(textNode, matchers, onChange) {
|
|
189
239
|
const previousSibling = textNode.getPreviousSibling();
|
|
190
240
|
const nextSibling = textNode.getNextSibling();
|
|
191
241
|
const text = textNode.getTextContent();
|
|
192
|
-
|
|
193
242
|
if (link.$isAutoLinkNode(previousSibling) && !startsWithSeparator(text)) {
|
|
194
243
|
previousSibling.append(textNode);
|
|
195
244
|
handleLinkEdit(previousSibling, matchers, onChange);
|
|
196
245
|
onChange(null, previousSibling.getURL());
|
|
197
246
|
}
|
|
198
|
-
|
|
199
247
|
if (link.$isAutoLinkNode(nextSibling) && !endsWithSeparator(text)) {
|
|
200
248
|
replaceWithChildren(nextSibling);
|
|
201
249
|
handleLinkEdit(nextSibling, matchers, onChange);
|
|
202
250
|
onChange(null, nextSibling.getURL());
|
|
203
251
|
}
|
|
204
252
|
}
|
|
205
|
-
|
|
206
253
|
function replaceWithChildren(node) {
|
|
207
254
|
const children = node.getChildren();
|
|
208
255
|
const childrenLength = children.length;
|
|
209
|
-
|
|
210
256
|
for (let j = childrenLength - 1; j >= 0; j--) {
|
|
211
257
|
node.insertAfter(children[j]);
|
|
212
258
|
}
|
|
213
|
-
|
|
214
259
|
node.remove();
|
|
215
260
|
return children.map(child => child.getLatest());
|
|
216
261
|
}
|
|
217
|
-
|
|
262
|
+
function getTextNodesToMatch(textNode) {
|
|
263
|
+
// check if next siblings are simple text nodes till a node contains a space separator
|
|
264
|
+
const textNodesToMatch = [textNode];
|
|
265
|
+
let nextSibling = textNode.getNextSibling();
|
|
266
|
+
while (nextSibling !== null && lexical.$isTextNode(nextSibling) && nextSibling.isSimpleText()) {
|
|
267
|
+
textNodesToMatch.push(nextSibling);
|
|
268
|
+
if (/[\s]/.test(nextSibling.getTextContent())) {
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
nextSibling = nextSibling.getNextSibling();
|
|
272
|
+
}
|
|
273
|
+
return textNodesToMatch;
|
|
274
|
+
}
|
|
218
275
|
function useAutoLink(editor, matchers, onChange) {
|
|
219
276
|
react.useEffect(() => {
|
|
220
277
|
if (!editor.hasNodes([link.AutoLinkNode])) {
|
|
@@ -222,30 +279,26 @@ function useAutoLink(editor, matchers, onChange) {
|
|
|
222
279
|
throw Error(`LexicalAutoLinkPlugin: AutoLinkNode not registered on editor`);
|
|
223
280
|
}
|
|
224
281
|
}
|
|
225
|
-
|
|
226
282
|
const onChangeWrapped = (url, prevUrl) => {
|
|
227
283
|
if (onChange) {
|
|
228
284
|
onChange(url, prevUrl);
|
|
229
285
|
}
|
|
230
286
|
};
|
|
231
|
-
|
|
232
287
|
return utils.mergeRegister(editor.registerNodeTransform(lexical.TextNode, textNode => {
|
|
233
288
|
const parent = textNode.getParentOrThrow();
|
|
234
289
|
const previous = textNode.getPreviousSibling();
|
|
235
|
-
|
|
236
290
|
if (link.$isAutoLinkNode(parent)) {
|
|
237
291
|
handleLinkEdit(parent, matchers, onChangeWrapped);
|
|
238
292
|
} else if (!link.$isLinkNode(parent)) {
|
|
239
293
|
if (textNode.isSimpleText() && (startsWithSeparator(textNode.getTextContent()) || !link.$isAutoLinkNode(previous))) {
|
|
240
|
-
|
|
294
|
+
const textNodesToMatch = getTextNodesToMatch(textNode);
|
|
295
|
+
handleLinkCreation(textNodesToMatch, matchers, onChangeWrapped);
|
|
241
296
|
}
|
|
242
|
-
|
|
243
297
|
handleBadNeighbors(textNode, matchers, onChangeWrapped);
|
|
244
298
|
}
|
|
245
299
|
}));
|
|
246
300
|
}, [editor, matchers, onChange]);
|
|
247
301
|
}
|
|
248
|
-
|
|
249
302
|
function AutoLinkPlugin({
|
|
250
303
|
matchers,
|
|
251
304
|
onChange
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var
|
|
8
|
-
function
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
(
|
|
7
|
+
'use strict';var k=require("@lexical/link"),n=require("@lexical/react/LexicalComposerContext"),p=require("@lexical/utils"),r=require("lexical"),t=require("react");function u(a){let c=new URLSearchParams;c.append("code",a);for(let b=1;b<arguments.length;b++)c.append("v",arguments[b]);throw Error(`Minified Lexical error #${a}; visit https://lexical.dev/docs/error?${c} for the full message or `+"use the non-minified dev environment for full errors and additional helpful warnings.");}
|
|
8
|
+
function v(a,c){for(let b=0;b<c.length;b++){let d=c[b](a);if(d)return d}return null}let w=/[.,;\s]/;function x(a){a=a.getPreviousSibling();r.$isElementNode(a)&&(a=a.getLastDescendant());var c;!(c=null===a||r.$isLineBreakNode(a))&&(c=r.$isTextNode(a))&&(a=a.getTextContent(),c=w.test(a[a.length-1]));return c}function y(a){a=a.getNextSibling();r.$isElementNode(a)&&(a=a.getFirstDescendant());return null===a||r.$isLineBreakNode(a)||r.$isTextNode(a)&&w.test(a.getTextContent()[0])}
|
|
9
|
+
function z(a,c,b,d){return(0<a?w.test(b[a-1]):x(d[0]))?c<b.length?w.test(b[c]):y(d[d.length-1]):!1}function A(a,c,b){let d=[],f=[],e=[],g=0,h=0;for(a=[...a];0<a.length;){let l=a[0],m=l.getTextContent().length,q=h;h+m<=c?(d.push(l),g+=m):q>=b?e.push(l):f.push(l);h+=m;a.shift()}return[g,d,f,e]}
|
|
10
|
+
function B(a,c,b,d){let f=k.$createAutoLinkNode(d.url,d.attributes);if(1===a.length){var e=a[0];0===c?[h,e]=e.splitText(b):[,h,e]=e.splitText(c,b);var g=r.$createTextNode(d.text);g.setFormat(h.getFormat());g.setDetail(h.getDetail());f.append(g);h.replace(f);return e}if(1<a.length){d=a[0];var h=d.getTextContent().length;0===c?e=d:[,e]=d.splitText(c);c=[];for(d=1;d<a.length;d++){let l=a[d],m=l.getTextContent().length,q=h,E=h+m;if(q<b)if(E<=b)c.push(l);else{let [F,G]=l.splitText(b-q);c.push(F);g=G}h+=
|
|
11
|
+
m}a=(b=r.$getSelection())?b.getNodes().find(r.$isTextNode):void 0;h=r.$createTextNode(e.getTextContent());h.setFormat(e.getFormat());h.setDetail(e.getDetail());f.append(h,...c);a&&a===e&&(r.$isRangeSelection(b)?h.select(b.anchor.offset,b.focus.offset):r.$isNodeSelection(b)&&h.select(0,h.getTextContent().length));e.replace(f);return g}}
|
|
12
|
+
function C(a,c,b){var d=[...a];a=d.map(g=>g.getTextContent()).join("");let f,e=0;for(;(f=v(a,c))&&null!==f;){let g=f.index,h=g+f.length;if(z(e+g,e+h,a,d)){let [l,,m,q]=A(d,e+g,e+h);d=(d=B(m,e+g-l,e+h-l,f))?[d,...q]:q;b(f.url,null);e=0}else e+=h;a=a.substring(h)}}
|
|
13
|
+
function D(a,c,b){var d=a.getChildren();let f=d.length;for(let e=0;e<f;e++){let g=d[e];if(!r.$isTextNode(g)||!g.isSimpleText()){H(a);b(null,a.getURL());return}}d=a.getTextContent();c=v(d,c);null===c||c.text!==d?(H(a),b(null,a.getURL())):x(a)&&y(a)?(d=a.getURL(),d!==c.url&&(a.setURL(c.url),b(c.url,d)),c.attributes&&(d=a.getRel(),d!==c.attributes.rel&&(a.setRel(c.attributes.rel||null),b(c.attributes.rel||null,d)),d=a.getTarget(),d!==c.attributes.target&&(a.setTarget(c.attributes.target||null),b(c.attributes.target||
|
|
14
|
+
null,d)))):(H(a),b(null,a.getURL()))}function H(a){let c=a.getChildren();var b=c.length;for(--b;0<=b;b--)a.insertAfter(c[b]);a.remove();return c.map(d=>d.getLatest())}
|
|
15
|
+
function I(a,c,b){t.useEffect(()=>{a.hasNodes([k.AutoLinkNode])||u(77);let d=(f,e)=>{b&&b(f,e)};return p.mergeRegister(a.registerNodeTransform(r.TextNode,f=>{var e=f.getParentOrThrow(),g=f.getPreviousSibling();if(k.$isAutoLinkNode(e))D(e,c,d);else if(!k.$isLinkNode(e)){if(f.isSimpleText()&&(w.test(f.getTextContent()[0])||!k.$isAutoLinkNode(g))){e=[f];for(g=f.getNextSibling();null!==g&&r.$isTextNode(g)&&g.isSimpleText();){e.push(g);if(/[\s]/.test(g.getTextContent()))break;g=g.getNextSibling()}C(e,
|
|
16
|
+
c,d)}let h=f.getPreviousSibling();e=f.getNextSibling();g=f.getTextContent();k.$isAutoLinkNode(h)&&!w.test(g[0])&&(h.append(f),D(h,c,d),f=h.getURL(),b&&b(null,f));k.$isAutoLinkNode(e)&&!w.test(g[g.length-1])&&(H(e),D(e,c,d),f=e.getURL(),b&&b(null,f))}}))},[a,c,b])}exports.AutoLinkPlugin=function({matchers:a,onChange:c}){let [b]=n.useLexicalComposerContext();I(b,a,c);return null};
|
|
17
|
+
exports.createLinkMatcherWithRegExp=function(a,c=b=>b){return b=>{b=a.exec(b);return null===b?null:{index:b.index,length:b[0].length,text:b[0],url:c(b[0])}}}
|
|
@@ -33,28 +33,23 @@ function BlockWithAlignableContents({
|
|
|
33
33
|
if (isSelected && lexical.$isNodeSelection(lexical.$getSelection())) {
|
|
34
34
|
event.preventDefault();
|
|
35
35
|
const node = lexical.$getNodeByKey(nodeKey);
|
|
36
|
-
|
|
37
36
|
if (lexical.$isDecoratorNode(node)) {
|
|
38
37
|
node.remove();
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
|
-
|
|
42
40
|
return false;
|
|
43
41
|
}, [isSelected, nodeKey]);
|
|
44
42
|
React.useEffect(() => {
|
|
45
43
|
return utils.mergeRegister(editor.registerCommand(lexical.FORMAT_ELEMENT_COMMAND, formatType => {
|
|
46
44
|
if (isSelected) {
|
|
47
45
|
const selection = lexical.$getSelection();
|
|
48
|
-
|
|
49
46
|
if (lexical.$isNodeSelection(selection)) {
|
|
50
47
|
const node = lexical.$getNodeByKey(nodeKey);
|
|
51
|
-
|
|
52
48
|
if (LexicalDecoratorBlockNode.$isDecoratorBlockNode(node)) {
|
|
53
49
|
node.setFormat(formatType);
|
|
54
50
|
}
|
|
55
51
|
} else if (lexical.$isRangeSelection(selection)) {
|
|
56
52
|
const nodes = selection.getNodes();
|
|
57
|
-
|
|
58
53
|
for (const node of nodes) {
|
|
59
54
|
if (LexicalDecoratorBlockNode.$isDecoratorBlockNode(node)) {
|
|
60
55
|
node.setFormat(formatType);
|
|
@@ -64,23 +59,18 @@ function BlockWithAlignableContents({
|
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
}
|
|
67
|
-
|
|
68
62
|
return true;
|
|
69
63
|
}
|
|
70
|
-
|
|
71
64
|
return false;
|
|
72
65
|
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.CLICK_COMMAND, event => {
|
|
73
66
|
if (event.target === ref.current) {
|
|
74
67
|
event.preventDefault();
|
|
75
|
-
|
|
76
68
|
if (!event.shiftKey) {
|
|
77
69
|
clearSelection();
|
|
78
70
|
}
|
|
79
|
-
|
|
80
71
|
setSelected(!isSelected);
|
|
81
72
|
return true;
|
|
82
73
|
}
|
|
83
|
-
|
|
84
74
|
return false;
|
|
85
75
|
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_DELETE_COMMAND, onDelete, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_BACKSPACE_COMMAND, onDelete, lexical.COMMAND_PRIORITY_LOW));
|
|
86
76
|
}, [clearSelection, editor, isSelected, nodeKey, onDelete, setSelected]);
|