@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.
Files changed (54) hide show
  1. package/LexicalAutoEmbedPlugin.d.ts +3 -2
  2. package/LexicalAutoEmbedPlugin.dev.js +4 -14
  3. package/LexicalAutoEmbedPlugin.prod.js +4 -3
  4. package/LexicalAutoFocusPlugin.dev.js +0 -1
  5. package/LexicalAutoLinkPlugin.dev.js +126 -73
  6. package/LexicalAutoLinkPlugin.prod.js +11 -7
  7. package/LexicalBlockWithAlignableContents.dev.js +0 -10
  8. package/LexicalCharacterLimitPlugin.dev.js +7 -46
  9. package/LexicalCheckListPlugin.dev.js +10 -48
  10. package/LexicalClearEditorPlugin.dev.js +1 -1
  11. package/LexicalClickableLinkPlugin.dev.js +2 -20
  12. package/LexicalCollaborationContext.dev.js +0 -3
  13. package/LexicalCollaborationPlugin.dev.js +8 -37
  14. package/LexicalComposer.d.ts +3 -7
  15. package/LexicalComposer.dev.js +9 -11
  16. package/LexicalComposer.js.flow +4 -4
  17. package/LexicalComposer.prod.js +2 -1
  18. package/LexicalComposerContext.dev.js +0 -6
  19. package/LexicalContentEditable.dev.js +1 -2
  20. package/LexicalContextMenuPlugin.d.ts +3 -2
  21. package/LexicalContextMenuPlugin.dev.js +28 -82
  22. package/LexicalContextMenuPlugin.prod.js +16 -15
  23. package/LexicalDecoratorBlockNode.dev.js +0 -6
  24. package/LexicalEditorRefPlugin.dev.js +0 -3
  25. package/LexicalHashtagPlugin.dev.js +73 -43
  26. package/LexicalHorizontalRuleNode.dev.js +0 -21
  27. package/LexicalHorizontalRulePlugin.dev.js +0 -4
  28. package/LexicalLinkPlugin.dev.js +4 -10
  29. package/LexicalListPlugin.dev.js +0 -2
  30. package/LexicalMarkdownShortcutPlugin.dev.js +2 -2
  31. package/LexicalNestedComposer.d.ts +2 -2
  32. package/LexicalNestedComposer.dev.js +18 -16
  33. package/LexicalNestedComposer.js.flow +7 -2
  34. package/LexicalNestedComposer.prod.js +4 -3
  35. package/LexicalNodeEventPlugin.dev.js +2 -6
  36. package/LexicalNodeMenuPlugin.d.ts +3 -2
  37. package/LexicalNodeMenuPlugin.dev.js +27 -83
  38. package/LexicalNodeMenuPlugin.prod.js +15 -15
  39. package/LexicalOnChangePlugin.dev.js +1 -1
  40. package/LexicalPlainTextPlugin.dev.js +8 -12
  41. package/LexicalRichTextPlugin.dev.js +8 -12
  42. package/LexicalTabIndentationPlugin.dev.js +7 -16
  43. package/LexicalTableOfContents.dev.js +5 -33
  44. package/LexicalTablePlugin.dev.js +11 -28
  45. package/LexicalTreeView.dev.js +14 -79
  46. package/LexicalTypeaheadMenuPlugin.d.ts +4 -3
  47. package/LexicalTypeaheadMenuPlugin.dev.js +39 -176
  48. package/LexicalTypeaheadMenuPlugin.prod.js +19 -20
  49. package/package.json +19 -19
  50. package/shared/LexicalMenu.d.ts +3 -2
  51. package/useLexicalEditable.dev.js +1 -5
  52. package/useLexicalIsTextContentEmpty.dev.js +1 -0
  53. package/useLexicalNodeSelection.dev.js +0 -7
  54. 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()))}})},[c,f]);z.useEffect(()=>{let b=(a,{updateTags:d,
9
- dirtyLeaves:p})=>{for(const [x,D]of a)"created"===D&&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);return e.$isLinkNode(a)?a:null});if(e.$isLinkNode(b)){const a=
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]),E=z.useMemo(()=>null!=h&&null!=g?t(h,y,m):[],[h,y,t,g,m]),F=z.useCallback((b,a,d)=>{c.update(()=>{b.onSelect(a);d()})},[c]);return null!=g?z.createElement(l.LexicalNodeMenuPlugin,{nodeKey:g,onClose:m,onSelectOption:F,options:E,menuRenderFn:C}):null};exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
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(text)
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
- function isContentAroundIsValid(matchStart, matchEnd, text, node) {
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
- function handleLinkCreation(node, matchers, onChange) {
93
- const nodeText = node.getTextContent();
94
- let text = nodeText;
95
- let invalidMatchEnd = 0;
96
- let remainingTextNode = node;
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, nodeText, node);
104
-
174
+ const isValid = isContentAroundIsValid(invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd, text, currentNodes);
105
175
  if (isValid) {
106
- let linkTextNode;
107
-
108
- if (invalidMatchEnd + matchStart === 0) {
109
- [linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchLength);
110
- } else {
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
- } // Check text content fully matches
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
- } // Check neighbors
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
- } // Bad neighbours are edits in neighbor nodes that make AutoLinks incompatible.
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
- handleLinkCreation(textNode, matchers, onChangeWrapped);
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 h=require("@lexical/link"),n=require("@lexical/react/LexicalComposerContext"),p=require("@lexical/utils"),u=require("lexical"),w=require("react");function A(a){let b=new URLSearchParams;b.append("code",a);for(let c=1;c<arguments.length;c++)b.append("v",arguments[c]);throw Error(`Minified Lexical error #${a}; visit https://lexical.dev/docs/error?${b} for the full message or `+"use the non-minified dev environment for full errors and additional helpful warnings.");}
8
- function B(a,b){for(let c=0;c<b.length;c++){let d=b[c](a);if(d)return d}return null}let C=/[.,;\s]/;function E(a){a=a.getPreviousSibling();u.$isElementNode(a)&&(a=a.getLastDescendant());var b;!(b=null===a||u.$isLineBreakNode(a))&&(b=u.$isTextNode(a))&&(a=a.getTextContent(),b=C.test(a[a.length-1]));return b}function F(a){a=a.getNextSibling();u.$isElementNode(a)&&(a=a.getFirstDescendant());return null===a||u.$isLineBreakNode(a)||u.$isTextNode(a)&&C.test(a.getTextContent()[0])}
9
- function G(a,b,c){var d=a.getChildren();let e=d.length;for(let f=0;f<e;f++){let l=d[f];if(!u.$isTextNode(l)||!l.isSimpleText()){H(a);c(null,a.getURL());return}}d=a.getTextContent();b=B(d,b);null===b||b.text!==d?(H(a),c(null,a.getURL())):E(a)&&F(a)?(d=a.getURL(),d!==b.url&&(a.setURL(b.url),c(b.url,d)),b.attributes&&(d=a.getRel(),d!==b.attributes.rel&&(a.setRel(b.attributes.rel||null),c(b.attributes.rel||null,d)),d=a.getTarget(),d!==b.attributes.target&&(a.setTarget(b.attributes.target||null),c(b.attributes.target||
10
- null,d)))):(H(a),c(null,a.getURL()))}function H(a){let b=a.getChildren();var c=b.length;for(--c;0<=c;c--)a.insertAfter(b[c]);a.remove();return b.map(d=>d.getLatest())}
11
- function I(a,b,c){w.useEffect(()=>{a.hasNodes([h.AutoLinkNode])||A(77);let d=(e,f)=>{c&&c(e,f)};return p.mergeRegister(a.registerNodeTransform(u.TextNode,e=>{var f=e.getParentOrThrow(),l=e.getPreviousSibling();if(h.$isAutoLinkNode(f))G(f,b,d);else if(!h.$isLinkNode(f)){if(e.isSimpleText()&&(C.test(e.getTextContent()[0])||!h.$isAutoLinkNode(l))){l=f=e.getTextContent();let m=0,v=e;for(var g;(g=B(l,b))&&null!==g;){let r=g.index,x=g.length,y=r+x;var t=m+r,q=m+y,z=f,D=e;if((0<t?C.test(z[t-1]):E(D))&&(q<
12
- z.length?C.test(z[q]):F(D))){var k=void 0;0===m+r?[k,v]=v.splitText(m+x):[,k,v]=v.splitText(m+r,m+r+x);t=h.$createAutoLinkNode(g.url,g.attributes);q=u.$createTextNode(g.text);q.setFormat(k.getFormat());q.setDetail(k.getDetail());t.append(q);k.replace(t);c&&c(g.url,null);m=0}else m+=y;l=l.substring(y)}}f=e.getPreviousSibling();g=e.getNextSibling();k=e.getTextContent();h.$isAutoLinkNode(f)&&!C.test(k[0])&&(f.append(e),G(f,b,d),e=f.getURL(),c&&c(null,e));h.$isAutoLinkNode(g)&&!C.test(k[k.length-1])&&
13
- (H(g),G(g,b,d),e=g.getURL(),c&&c(null,e))}}))},[a,b,c])}exports.AutoLinkPlugin=function({matchers:a,onChange:b}){let [c]=n.useLexicalComposerContext();I(c,a,b);return null};exports.createLinkMatcherWithRegExp=function(a,b=c=>c){return c=>{let d=a.exec(c);return null===d?null:{index:d.index,length:d[0].length,text:d[0],url:b(c)}}}
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]);