@lexical/react 0.1.8 → 0.1.11
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/DEPRECATED_useLexical.dev.js +3 -38
- package/DEPRECATED_useLexical.prod.js +1 -2
- package/DEPRECATED_useLexicalAutoFormatter.dev.js +202 -74
- package/DEPRECATED_useLexicalAutoFormatter.prod.js +21 -16
- package/DEPRECATED_useLexicalCanShowPlaceholder.prod.js +1 -1
- package/DEPRECATED_useLexicalCharacterLimit.dev.js +23 -21
- package/DEPRECATED_useLexicalCharacterLimit.prod.js +8 -8
- package/DEPRECATED_useLexicalDecorators.prod.js +1 -1
- package/DEPRECATED_useLexicalEditor.dev.js +1 -25
- package/DEPRECATED_useLexicalEditor.prod.js +1 -1
- package/DEPRECATED_useLexicalEditorEvents.prod.js +1 -1
- package/DEPRECATED_useLexicalHistory.dev.js +18 -15
- package/DEPRECATED_useLexicalHistory.prod.js +7 -7
- package/DEPRECATED_useLexicalList.dev.js +6 -0
- package/DEPRECATED_useLexicalList.prod.js +1 -1
- package/DEPRECATED_useLexicalPlainText.dev.js +79 -70
- package/DEPRECATED_useLexicalPlainText.prod.js +15 -16
- package/DEPRECATED_useLexicalRichText.dev.js +124 -250
- package/DEPRECATED_useLexicalRichText.prod.js +25 -29
- package/LexicalAutoFormatterPlugin.dev.js +202 -74
- package/LexicalAutoFormatterPlugin.js.flow +10 -0
- package/LexicalAutoFormatterPlugin.prod.js +21 -17
- package/LexicalAutoLinkPlugin.js.flow +23 -0
- package/LexicalAutoLinkPlugin.prod.js +4 -4
- package/LexicalCharacterLimitPlugin.dev.js +23 -21
- package/LexicalCharacterLimitPlugin.js.flow +12 -0
- package/LexicalCharacterLimitPlugin.prod.js +9 -8
- package/LexicalClearEditorPlugin.dev.js +52 -0
- package/LexicalClearEditorPlugin.js +9 -0
- package/LexicalClearEditorPlugin.js.flow +14 -0
- package/LexicalClearEditorPlugin.prod.js +7 -0
- package/LexicalCollaborationPlugin.dev.js +31 -36
- package/LexicalCollaborationPlugin.js.flow +55 -0
- package/LexicalCollaborationPlugin.prod.js +7 -8
- package/LexicalComposer.dev.js +8 -6
- package/LexicalComposer.js.flow +23 -0
- package/LexicalComposer.prod.js +3 -3
- package/LexicalComposerContext.js.flow +27 -0
- package/LexicalComposerContext.prod.js +1 -1
- package/LexicalContentEditable.dev.js +14 -8
- package/LexicalContentEditable.js.flow +35 -0
- package/LexicalContentEditable.prod.js +3 -3
- package/LexicalHashtagPlugin.js.flow +20 -0
- package/LexicalHashtagPlugin.prod.js +1 -1
- package/LexicalHistoryPlugin.dev.js +18 -15
- package/LexicalHistoryPlugin.js.flow +34 -0
- package/LexicalHistoryPlugin.prod.js +7 -7
- package/LexicalHorizontalRuleNode.dev.js +66 -0
- package/LexicalHorizontalRuleNode.js +9 -0
- package/LexicalHorizontalRuleNode.js.flow +25 -0
- package/LexicalHorizontalRuleNode.prod.js +8 -0
- package/LexicalLinkPlugin.dev.js +0 -1
- package/LexicalLinkPlugin.js.flow +10 -0
- package/LexicalLinkPlugin.prod.js +3 -3
- package/LexicalListPlugin.dev.js +6 -0
- package/LexicalListPlugin.js.flow +10 -0
- package/LexicalListPlugin.prod.js +2 -2
- package/LexicalNestedComposer.js.flow +21 -0
- package/LexicalNestedComposer.prod.js +1 -1
- package/LexicalOnChangePlugin.js.flow +14 -0
- package/LexicalOnChangePlugin.prod.js +1 -1
- package/LexicalPlainTextPlugin.dev.js +72 -44
- package/LexicalPlainTextPlugin.js.flow +18 -0
- package/LexicalPlainTextPlugin.prod.js +12 -11
- package/LexicalRichTextPlugin.dev.js +115 -222
- package/LexicalRichTextPlugin.js.flow +18 -0
- package/LexicalRichTextPlugin.prod.js +21 -25
- package/LexicalTablePlugin.dev.js +43 -39
- package/LexicalTablePlugin.js.flow +10 -0
- package/LexicalTablePlugin.prod.js +4 -3
- package/LexicalTreeView.dev.js +10 -2
- package/LexicalTreeView.js.flow +19 -0
- package/LexicalTreeView.prod.js +9 -8
- package/README.md +0 -1
- package/package.json +5 -4
- package/useLexicalDecoratorMap.js.flow +16 -0
- package/useLexicalDecoratorMap.prod.js +1 -1
- package/useLexicalIsTextContentEmpty.js.flow +15 -0
- package/useLexicalIsTextContentEmpty.prod.js +1 -1
- package/useLexicalNodeSelection.dev.js +70 -0
- package/useLexicalNodeSelection.js +9 -0
- package/useLexicalNodeSelection.js.flow +14 -0
- package/useLexicalNodeSelection.prod.js +8 -0
- package/withSubscriptions.js.flow +13 -0
- package/withSubscriptions.prod.js +1 -1
- package/LexicalBootstrapPlugin.dev.js +0 -124
- package/LexicalBootstrapPlugin.js +0 -9
- package/LexicalBootstrapPlugin.prod.js +0 -8
- package/LexicalHorizontalRulePlugin.dev.js +0 -51
- package/LexicalHorizontalRulePlugin.js +0 -9
- package/LexicalHorizontalRulePlugin.prod.js +0 -7
|
@@ -18,35 +18,11 @@ var useLexicalCanShowPlaceholder = require('@lexical/react/DEPRECATED_useLexical
|
|
|
18
18
|
*
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
25
|
-
*
|
|
26
|
-
* This source code is licensed under the MIT license found in the
|
|
27
|
-
* LICENSE file in the root directory of this source tree.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*/
|
|
31
|
-
const useLayoutEffectImpl = CAN_USE_DOM ? react.useLayoutEffect : react.useEffect;
|
|
32
|
-
var useLayoutEffect = useLayoutEffectImpl;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
36
|
-
*
|
|
37
|
-
* This source code is licensed under the MIT license found in the
|
|
38
|
-
* LICENSE file in the root directory of this source tree.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
function useLexicalEditor(editor, onError) {
|
|
21
|
+
function useLexicalEditor(editor) {
|
|
43
22
|
const showPlaceholder = useLexicalCanShowPlaceholder(editor);
|
|
44
23
|
const rootElementRef = react.useCallback(rootElement => {
|
|
45
24
|
editor.setRootElement(rootElement);
|
|
46
25
|
}, [editor]);
|
|
47
|
-
useLayoutEffect(() => {
|
|
48
|
-
return editor.addListener('error', onError);
|
|
49
|
-
}, [editor, onError]);
|
|
50
26
|
return [rootElementRef, showPlaceholder];
|
|
51
27
|
}
|
|
52
28
|
|
|
@@ -58,26 +34,15 @@ function useLexicalEditor(editor, onError) {
|
|
|
58
34
|
*
|
|
59
35
|
*
|
|
60
36
|
*/
|
|
61
|
-
|
|
62
|
-
function defaultOnErrorHandler(e) {
|
|
63
|
-
throw e;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
37
|
function useLexical(editorConfig) {
|
|
67
|
-
const onError = editorConfig !== undefined && editorConfig.onError || defaultOnErrorHandler;
|
|
68
38
|
const editor = react.useMemo(() => {
|
|
69
39
|
if (editorConfig !== undefined) {
|
|
70
|
-
|
|
71
|
-
const {
|
|
72
|
-
onError: _onError,
|
|
73
|
-
...config
|
|
74
|
-
} = editorConfig;
|
|
75
|
-
return lexical.createEditor(config);
|
|
40
|
+
return lexical.createEditor(editorConfig);
|
|
76
41
|
}
|
|
77
42
|
|
|
78
43
|
return lexical.createEditor(editorConfig);
|
|
79
44
|
}, [editorConfig]);
|
|
80
|
-
const [rootElementRef, showPlaceholder] = useLexicalEditor(editor
|
|
45
|
+
const [rootElementRef, showPlaceholder] = useLexicalEditor(editor);
|
|
81
46
|
return [editor, rootElementRef, showPlaceholder];
|
|
82
47
|
}
|
|
83
48
|
|
|
@@ -4,5 +4,4 @@
|
|
|
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
|
-
|
|
8
|
-
module.exports=function(a){const c=void 0!==a&&a.onError||m,d=g.useMemo(()=>{if(void 0!==a){const {onError:p,...n}=a;return b.createEditor(n)}return b.createEditor(a)},[a]),[f,e]=l(d,c);return[d,f,e]};
|
|
7
|
+
var d=require("lexical"),e=require("react"),f=require("@lexical/react/DEPRECATED_useLexicalCanShowPlaceholder");function g(a){const b=f(a);return[e.useCallback(c=>{a.setRootElement(c)},[a]),b]}module.exports=function(a){const b=e.useMemo(()=>d.createEditor(a),[a]),[c,h]=g(b);return[b,c,h]};
|
|
@@ -10,6 +10,7 @@ var list = require('@lexical/list');
|
|
|
10
10
|
var lexical = require('lexical');
|
|
11
11
|
var CodeNode = require('lexical/CodeNode');
|
|
12
12
|
var react = require('react');
|
|
13
|
+
var LexicalHorizontalRuleNode = require('@lexical/react/LexicalHorizontalRuleNode');
|
|
13
14
|
var HeadingNode = require('lexical/HeadingNode');
|
|
14
15
|
var QuoteNode = require('lexical/QuoteNode');
|
|
15
16
|
|
|
@@ -72,17 +73,26 @@ function $findNodeWithOffsetFromJoinedText(elementNode, joinedTextLength, offset
|
|
|
72
73
|
const children = elementNode.getChildren();
|
|
73
74
|
const childrenLength = children.length;
|
|
74
75
|
let runningLength = 0;
|
|
76
|
+
let isPriorNodeTextNode = false;
|
|
75
77
|
|
|
76
78
|
for (let i = 0; i < childrenLength; ++i) {
|
|
77
|
-
|
|
79
|
+
// We must examine the offsetInJoinedText that is located
|
|
80
|
+
// at the length of the string.
|
|
81
|
+
// For example, given "hello", the length is 5, yet
|
|
82
|
+
// the caller still wants the node + offset at the
|
|
83
|
+
// right edge of the "o".
|
|
84
|
+
if (runningLength > joinedTextLength) {
|
|
78
85
|
break;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
const child = children[i];
|
|
82
|
-
const
|
|
89
|
+
const isChildNodeTestNode = lexical.$isTextNode(child);
|
|
90
|
+
const childContentLength = isChildNodeTestNode ? child.getTextContent().length : separatorLength;
|
|
83
91
|
const newRunningLength = runningLength + childContentLength;
|
|
92
|
+
const isJoinedOffsetWithinNode = isPriorNodeTextNode === false && runningLength === offsetInJoinedText || runningLength === 0 && runningLength === offsetInJoinedText || runningLength < offsetInJoinedText && offsetInJoinedText <= newRunningLength;
|
|
84
93
|
|
|
85
|
-
if (
|
|
94
|
+
if (isJoinedOffsetWithinNode && lexical.$isTextNode(child)) {
|
|
95
|
+
// Check isTextNode again for flow.
|
|
86
96
|
return {
|
|
87
97
|
node: child,
|
|
88
98
|
offset: offsetInJoinedText - runningLength
|
|
@@ -90,6 +100,7 @@ function $findNodeWithOffsetFromJoinedText(elementNode, joinedTextLength, offset
|
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
runningLength = newRunningLength;
|
|
103
|
+
isPriorNodeTextNode = isChildNodeTestNode;
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
return null;
|
|
@@ -114,8 +125,6 @@ const SEPARATOR_BETWEEN_TEXT_AND_NON_TEXT_NODES = '\u0004'; // Select an unused
|
|
|
114
125
|
const autoFormatBase = {
|
|
115
126
|
nodeTransformationKind: null,
|
|
116
127
|
regEx: /(?:)/,
|
|
117
|
-
regExCaptureGroupsToDelete: null,
|
|
118
|
-
regExExpectedCaptureGroupCount: 1,
|
|
119
128
|
requiresParagraphStart: false
|
|
120
129
|
};
|
|
121
130
|
const paragraphStartBase = { ...autoFormatBase,
|
|
@@ -151,38 +160,77 @@ const markdownCodeBlock = { ...paragraphStartBase,
|
|
|
151
160
|
};
|
|
152
161
|
const markdownOrderedList = { ...paragraphStartBase,
|
|
153
162
|
nodeTransformationKind: 'paragraphOrderedList',
|
|
154
|
-
regEx: /^(\d+)\.\s
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
regEx: /^(\d+)\.\s/
|
|
164
|
+
};
|
|
165
|
+
const markdownHorizontalRule = { ...paragraphStartBase,
|
|
166
|
+
nodeTransformationKind: 'horizontalRule',
|
|
167
|
+
regEx: /(?:\*\*\* )/
|
|
168
|
+
};
|
|
169
|
+
const markdownHorizontalRuleUsingDashes = { ...paragraphStartBase,
|
|
170
|
+
nodeTransformationKind: 'horizontalRule',
|
|
171
|
+
regEx: /(?:--- )/
|
|
172
|
+
};
|
|
173
|
+
const markdownItalic = { ...autoFormatBase,
|
|
174
|
+
nodeTransformationKind: 'italic',
|
|
175
|
+
regEx: /(\*)(\s*\b)([^\*]*)(\b\s*)(\*\s)$/
|
|
158
176
|
};
|
|
159
177
|
const markdownBold = { ...autoFormatBase,
|
|
160
|
-
nodeTransformationKind: '
|
|
161
|
-
|
|
162
|
-
regEx: /(\*)(\s*\b)([^\*]*)(\b\s*)(\*\s)$/,
|
|
163
|
-
// Remove the first and last capture groups. Remeber, the 0th capture group is the entire string.
|
|
164
|
-
// e.g. "*Hello* " requires removing both "*" as well as bolding "Hello".
|
|
165
|
-
regExCaptureGroupsToDelete: [1, 5],
|
|
166
|
-
// The $ will find the target at the end of the string.
|
|
167
|
-
regExExpectedCaptureGroupCount: 6
|
|
178
|
+
nodeTransformationKind: 'bold',
|
|
179
|
+
regEx: /(\*\*)(\s*\b)([^\*\*]*)(\b\s*)(\*\*\s)$/
|
|
168
180
|
};
|
|
169
|
-
const
|
|
170
|
-
|
|
181
|
+
const markdownBoldWithUnderlines = { ...autoFormatBase,
|
|
182
|
+
nodeTransformationKind: 'bold',
|
|
183
|
+
regEx: /(__)(\s*)([^__]*)(\s*)(__\s)$/
|
|
184
|
+
};
|
|
185
|
+
const markdownBoldItalic = { ...autoFormatBase,
|
|
186
|
+
nodeTransformationKind: 'bold_italic',
|
|
187
|
+
regEx: /(\*\*\*)(\s*\b)([^\*\*\*]*)(\b\s*)(\*\*\*\s)$/
|
|
188
|
+
}; // Markdown does not support underline, but we can allow folks to use
|
|
189
|
+
// the HTML tags for underline.
|
|
190
|
+
|
|
191
|
+
const fakeMarkdownUnderline = { ...autoFormatBase,
|
|
192
|
+
nodeTransformationKind: 'underline',
|
|
193
|
+
regEx: /(\<u\>)(\s*\b)([^\<]*)(\b\s*)(\<\/u\>\s)$/
|
|
194
|
+
};
|
|
195
|
+
const markdownStrikethrough = { ...autoFormatBase,
|
|
196
|
+
nodeTransformationKind: 'strikethrough',
|
|
197
|
+
regEx: /(~~)(\s*\b)([^~~]*)(\b\s*)(~~\s)$/
|
|
198
|
+
};
|
|
199
|
+
const allAutoFormatCriteriaForTextNodes = [markdownBoldItalic, markdownItalic, markdownBold, markdownBoldWithUnderlines, fakeMarkdownUnderline, markdownStrikethrough];
|
|
200
|
+
const allAutoFormatCriteria = [markdownHeader1, markdownHeader2, markdownHeader3, markdownBlockQuote, markdownUnorderedListDash, markdownUnorderedListAsterisk, markdownOrderedList, markdownCodeBlock, markdownHorizontalRule, markdownHorizontalRuleUsingDashes, ...allAutoFormatCriteriaForTextNodes];
|
|
171
201
|
function getAllAutoFormatCriteriaForTextNodes() {
|
|
172
202
|
return allAutoFormatCriteriaForTextNodes;
|
|
173
203
|
}
|
|
174
204
|
function getAllAutoFormatCriteria() {
|
|
175
205
|
return allAutoFormatCriteria;
|
|
176
206
|
}
|
|
207
|
+
function getInitialScanningContext(textNodeWithOffset, triggerState) {
|
|
208
|
+
return {
|
|
209
|
+
autoFormatCriteria: {
|
|
210
|
+
nodeTransformationKind: 'noTransformation',
|
|
211
|
+
regEx: /(?:)/,
|
|
212
|
+
// Empty reg ex will do until the precise criteria is discovered.
|
|
213
|
+
requiresParagraphStart: null
|
|
214
|
+
},
|
|
215
|
+
joinedText: null,
|
|
216
|
+
matchResultContext: {
|
|
217
|
+
offsetInJoinedTextForCollapsedSelection: 0,
|
|
218
|
+
regExCaptureGroups: []
|
|
219
|
+
},
|
|
220
|
+
textNodeWithOffset,
|
|
221
|
+
triggerState
|
|
222
|
+
};
|
|
223
|
+
}
|
|
177
224
|
|
|
178
|
-
function getMatchResultContextWithRegEx(textToSearch, matchMustAppearAtStartOfString, matchMustAppearAtEndOfString, regEx
|
|
225
|
+
function getMatchResultContextWithRegEx(textToSearch, matchMustAppearAtStartOfString, matchMustAppearAtEndOfString, regEx) {
|
|
179
226
|
const matchResultContext = {
|
|
180
|
-
|
|
181
|
-
|
|
227
|
+
offsetInJoinedTextForCollapsedSelection: 0,
|
|
228
|
+
regExCaptureGroups: []
|
|
182
229
|
};
|
|
183
230
|
const regExMatches = textToSearch.match(regEx);
|
|
184
231
|
|
|
185
|
-
if (regExMatches !== null && regExMatches.length > 0 &&
|
|
232
|
+
if (regExMatches !== null && regExMatches.length > 0 && (matchMustAppearAtStartOfString === false || regExMatches.index === 0) && (matchMustAppearAtEndOfString === false || regExMatches.index + regExMatches[0].length === textToSearch.length)) {
|
|
233
|
+
matchResultContext.offsetInJoinedTextForCollapsedSelection = textToSearch.length;
|
|
186
234
|
const captureGroupsCount = regExMatches.length;
|
|
187
235
|
let runningLength = regExMatches.index;
|
|
188
236
|
|
|
@@ -214,7 +262,7 @@ function getMatchResultContextForParagraphs(autoFormatCriteria, scanningContext)
|
|
|
214
262
|
|
|
215
263
|
if (textNodeWithOffset.node.getPreviousSibling() === null) {
|
|
216
264
|
const textToSearch = scanningContext.textNodeWithOffset.node.getTextContent();
|
|
217
|
-
return getMatchResultContextWithRegEx(textToSearch, true, false, autoFormatCriteria.regEx
|
|
265
|
+
return getMatchResultContextWithRegEx(textToSearch, true, false, autoFormatCriteria.regEx);
|
|
218
266
|
}
|
|
219
267
|
|
|
220
268
|
return null;
|
|
@@ -229,17 +277,14 @@ function getMatchResultContextForText(autoFormatCriteria, scanningContext) {
|
|
|
229
277
|
// Lazy calculate the text to search.
|
|
230
278
|
scanningContext.joinedText = $joinTextNodesInElementNode(parentNode, SEPARATOR_BETWEEN_TEXT_AND_NON_TEXT_NODES, scanningContext.textNodeWithOffset);
|
|
231
279
|
}
|
|
232
|
-
|
|
233
|
-
return getMatchResultContextWithRegEx(scanningContext.joinedText, false, true, autoFormatCriteria.regEx, autoFormatCriteria.regExExpectedCaptureGroupCount);
|
|
234
280
|
} else {
|
|
235
281
|
{
|
|
236
282
|
throw Error(`Expected node ${parentNode.__key} to to be a ElementNode.`);
|
|
237
283
|
}
|
|
238
284
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
285
|
+
}
|
|
241
286
|
|
|
242
|
-
return
|
|
287
|
+
return getMatchResultContextWithRegEx(scanningContext.joinedText, false, true, autoFormatCriteria.regEx);
|
|
243
288
|
}
|
|
244
289
|
|
|
245
290
|
function getMatchResultContextForCriteria(autoFormatCriteria, scanningContext) {
|
|
@@ -250,8 +295,11 @@ function getMatchResultContextForCriteria(autoFormatCriteria, scanningContext) {
|
|
|
250
295
|
return getMatchResultContextForText(autoFormatCriteria, scanningContext);
|
|
251
296
|
}
|
|
252
297
|
|
|
253
|
-
function getNewNodeForCriteria(
|
|
298
|
+
function getNewNodeForCriteria(scanningContext, element) {
|
|
254
299
|
let newNode = null;
|
|
300
|
+
const children = element.getChildren();
|
|
301
|
+
const autoFormatCriteria = scanningContext.autoFormatCriteria;
|
|
302
|
+
const matchResultContext = scanningContext.matchResultContext;
|
|
255
303
|
|
|
256
304
|
if (autoFormatCriteria.nodeTransformationKind != null) {
|
|
257
305
|
switch (autoFormatCriteria.nodeTransformationKind) {
|
|
@@ -306,7 +354,7 @@ function getNewNodeForCriteria(autoFormatCriteria, matchResultContext, children)
|
|
|
306
354
|
case 'paragraphCodeBlock':
|
|
307
355
|
{
|
|
308
356
|
// Toggle code and paragraph nodes.
|
|
309
|
-
if (
|
|
357
|
+
if (scanningContext.triggerState != null && scanningContext.triggerState.isCodeBlock) {
|
|
310
358
|
newNode = lexical.$createParagraphNode();
|
|
311
359
|
} else {
|
|
312
360
|
newNode = CodeNode.$createCodeNode();
|
|
@@ -315,6 +363,14 @@ function getNewNodeForCriteria(autoFormatCriteria, matchResultContext, children)
|
|
|
315
363
|
newNode.append(...children);
|
|
316
364
|
return newNode;
|
|
317
365
|
}
|
|
366
|
+
|
|
367
|
+
case 'horizontalRule':
|
|
368
|
+
{
|
|
369
|
+
// return null for newNode. Insert the HR here.
|
|
370
|
+
const horizontalRuleNode = LexicalHorizontalRuleNode.$createHorizontalRuleNode();
|
|
371
|
+
element.insertBefore(horizontalRuleNode);
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
318
374
|
}
|
|
319
375
|
}
|
|
320
376
|
|
|
@@ -330,41 +386,70 @@ function updateTextNode(node, count) {
|
|
|
330
386
|
}
|
|
331
387
|
}
|
|
332
388
|
|
|
333
|
-
function transformTextNodeForAutoFormatCriteria(scanningContext
|
|
334
|
-
if (autoFormatCriteria.requiresParagraphStart) {
|
|
335
|
-
transformTextNodeForParagraphs(scanningContext
|
|
389
|
+
function transformTextNodeForAutoFormatCriteria(scanningContext) {
|
|
390
|
+
if (scanningContext.autoFormatCriteria.requiresParagraphStart) {
|
|
391
|
+
transformTextNodeForParagraphs(scanningContext);
|
|
336
392
|
} else {
|
|
337
|
-
transformTextNodeForText(scanningContext
|
|
393
|
+
transformTextNodeForText(scanningContext);
|
|
338
394
|
}
|
|
339
395
|
}
|
|
340
396
|
|
|
341
|
-
function transformTextNodeForParagraphs(scanningContext
|
|
397
|
+
function transformTextNodeForParagraphs(scanningContext) {
|
|
342
398
|
const textNodeWithOffset = scanningContext.textNodeWithOffset;
|
|
343
399
|
const element = textNodeWithOffset.node.getParentOrThrow();
|
|
344
|
-
const text = matchResultContext.regExCaptureGroups[0].text;
|
|
400
|
+
const text = scanningContext.matchResultContext.regExCaptureGroups[0].text;
|
|
345
401
|
updateTextNode(textNodeWithOffset.node, text.length);
|
|
346
|
-
const elementNode = getNewNodeForCriteria(
|
|
402
|
+
const elementNode = getNewNodeForCriteria(scanningContext, element);
|
|
347
403
|
|
|
348
404
|
if (elementNode !== null) {
|
|
349
405
|
element.replace(elementNode);
|
|
350
406
|
}
|
|
351
407
|
}
|
|
352
408
|
|
|
353
|
-
function
|
|
409
|
+
function getTextFormatType(nodeTransformationKind) {
|
|
410
|
+
switch (nodeTransformationKind) {
|
|
411
|
+
case 'italic':
|
|
412
|
+
case 'bold':
|
|
413
|
+
case 'underline':
|
|
414
|
+
case 'strikethrough':
|
|
415
|
+
return [nodeTransformationKind];
|
|
416
|
+
|
|
417
|
+
case 'bold_italic':
|
|
418
|
+
{
|
|
419
|
+
return ['bold', 'italic'];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function transformTextNodeForText(scanningContext) {
|
|
427
|
+
const autoFormatCriteria = scanningContext.autoFormatCriteria;
|
|
428
|
+
const matchResultContext = scanningContext.matchResultContext;
|
|
429
|
+
|
|
354
430
|
if (autoFormatCriteria.nodeTransformationKind != null) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
431
|
+
if (matchResultContext.regExCaptureGroups.length !== 6) {
|
|
432
|
+
// For BIUS and other formatts which have a pattern + text + pattern,
|
|
433
|
+
// the expected reg ex pattern should have 6 groups.
|
|
434
|
+
// If it does not, then break and fail silently.
|
|
435
|
+
// e2e tests validate the regEx pattern.
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
359
438
|
|
|
360
|
-
|
|
361
|
-
// Remove unwanted text in reg ex patterh.
|
|
362
|
-
removeTextInCaptureGroups(autoFormatCriteria.regExCaptureGroupsToDelete, matchResultContext);
|
|
363
|
-
formatTextInCaptureGroupIndex('bold', 3, matchResultContext);
|
|
364
|
-
}
|
|
439
|
+
const formatting = getTextFormatType(autoFormatCriteria.nodeTransformationKind);
|
|
365
440
|
|
|
366
|
-
|
|
367
|
-
|
|
441
|
+
if (formatting != null) {
|
|
442
|
+
const captureGroupsToDelete = [1, 5];
|
|
443
|
+
const formatCaptureGroup = 3;
|
|
444
|
+
matchResultContext.regExCaptureGroups = getCaptureGroupsByResolvingAllDetails(scanningContext);
|
|
445
|
+
|
|
446
|
+
if (captureGroupsToDelete.length > 0) {
|
|
447
|
+
// Remove unwanted text in reg ex pattern.
|
|
448
|
+
removeTextInCaptureGroups(captureGroupsToDelete, matchResultContext);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
formatTextInCaptureGroupIndex(formatting, formatCaptureGroup, matchResultContext);
|
|
452
|
+
makeCollapsedSelectionAtOffsetInJoinedText(matchResultContext.offsetInJoinedTextForCollapsedSelection, matchResultContext.offsetInJoinedTextForCollapsedSelection + 1, scanningContext.textNodeWithOffset.node.getParentOrThrow());
|
|
368
453
|
}
|
|
369
454
|
}
|
|
370
455
|
} // Some Capture Group Details were left lazily unresolved as their calculation
|
|
@@ -372,7 +457,9 @@ function transformTextNodeForText(scanningContext, autoFormatCriteria, matchResu
|
|
|
372
457
|
// known, the details may be fully resolved without incurring unwasted performance cost.
|
|
373
458
|
|
|
374
459
|
|
|
375
|
-
function getCaptureGroupsByResolvingAllDetails(scanningContext
|
|
460
|
+
function getCaptureGroupsByResolvingAllDetails(scanningContext) {
|
|
461
|
+
const autoFormatCriteria = scanningContext.autoFormatCriteria;
|
|
462
|
+
const matchResultContext = scanningContext.matchResultContext;
|
|
376
463
|
const textNodeWithOffset = scanningContext.textNodeWithOffset;
|
|
377
464
|
const regExCaptureGroups = matchResultContext.regExCaptureGroups;
|
|
378
465
|
const captureGroupsCount = regExCaptureGroups.length;
|
|
@@ -419,7 +506,7 @@ function removeTextInCaptureGroups(regExCaptureGroupsToDelete, matchResultContex
|
|
|
419
506
|
lexical.$setSelection(newSelection);
|
|
420
507
|
const currentSelection = lexical.$getSelection();
|
|
421
508
|
|
|
422
|
-
if (currentSelection
|
|
509
|
+
if (lexical.$isRangeSelection(currentSelection)) {
|
|
423
510
|
currentSelection.removeText(); // Shift offsets for capture groups which are within the same node
|
|
424
511
|
|
|
425
512
|
if (anchorTextNodeWithOffset.node.getKey() === focusTextNodeWithOffset.node.getKey()) {
|
|
@@ -444,6 +531,12 @@ function removeTextInCaptureGroups(regExCaptureGroupsToDelete, matchResultContex
|
|
|
444
531
|
}
|
|
445
532
|
|
|
446
533
|
function shiftCaptureGroupOffsets(delta, applyAtOrAfterOffset, node, startingCaptureGroupIndex, matchResultContext) {
|
|
534
|
+
matchResultContext.offsetInJoinedTextForCollapsedSelection += delta;
|
|
535
|
+
|
|
536
|
+
if (!(matchResultContext.offsetInJoinedTextForCollapsedSelection > 0)) {
|
|
537
|
+
throw Error(`The text content string length does not correlate with insertions/deletions of new text.`);
|
|
538
|
+
}
|
|
539
|
+
|
|
447
540
|
const regExCaptureGroups = matchResultContext.regExCaptureGroups;
|
|
448
541
|
const regExCaptureGroupsCount = regExCaptureGroups.length;
|
|
449
542
|
|
|
@@ -460,7 +553,7 @@ function shiftCaptureGroupOffsets(delta, applyAtOrAfterOffset, node, startingCap
|
|
|
460
553
|
}
|
|
461
554
|
}
|
|
462
555
|
|
|
463
|
-
function formatTextInCaptureGroupIndex(
|
|
556
|
+
function formatTextInCaptureGroupIndex(formatTypes, captureGroupIndex, matchResultContext) {
|
|
464
557
|
const regExCaptureGroups = matchResultContext.regExCaptureGroups;
|
|
465
558
|
const regExCaptureGroupsCount = regExCaptureGroups.length;
|
|
466
559
|
|
|
@@ -479,12 +572,30 @@ function formatTextInCaptureGroupIndex(formatType, captureGroupIndex, matchResul
|
|
|
479
572
|
lexical.$setSelection(newSelection);
|
|
480
573
|
const currentSelection = lexical.$getSelection();
|
|
481
574
|
|
|
482
|
-
if (currentSelection
|
|
483
|
-
|
|
575
|
+
if (lexical.$isRangeSelection(currentSelection)) {
|
|
576
|
+
for (let i = 0; i < formatTypes.length; i++) {
|
|
577
|
+
currentSelection.formatText(formatTypes[i]);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const finalSelection = lexical.$createRangeSelection();
|
|
581
|
+
finalSelection.anchor.set(focusTextNodeWithOffset.node.getKey(), focusTextNodeWithOffset.offset + 1, 'text');
|
|
582
|
+
finalSelection.focus.set(focusTextNodeWithOffset.node.getKey(), focusTextNodeWithOffset.offset + 1, 'text');
|
|
583
|
+
lexical.$setSelection(finalSelection);
|
|
484
584
|
}
|
|
485
585
|
}
|
|
486
586
|
}
|
|
487
587
|
|
|
588
|
+
function makeCollapsedSelectionAtOffsetInJoinedText(offsetInJoinedText, joinedTextLength, parentElementNode) {
|
|
589
|
+
const textNodeWithOffset = $findNodeWithOffsetFromJoinedText(parentElementNode, joinedTextLength, offsetInJoinedText, TRIGGER_STRING_LENGTH);
|
|
590
|
+
|
|
591
|
+
if (textNodeWithOffset != null) {
|
|
592
|
+
const newSelection = lexical.$createRangeSelection();
|
|
593
|
+
newSelection.anchor.set(textNodeWithOffset.node.getKey(), textNodeWithOffset.offset, 'text');
|
|
594
|
+
newSelection.focus.set(textNodeWithOffset.node.getKey(), textNodeWithOffset.offset, 'text');
|
|
595
|
+
lexical.$setSelection(newSelection);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
488
599
|
/**
|
|
489
600
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
490
601
|
*
|
|
@@ -494,17 +605,17 @@ function formatTextInCaptureGroupIndex(formatType, captureGroupIndex, matchResul
|
|
|
494
605
|
*
|
|
495
606
|
*/
|
|
496
607
|
|
|
497
|
-
function getCriteriaWithMatchResultContext(autoFormatCriteriaArray,
|
|
608
|
+
function getCriteriaWithMatchResultContext(autoFormatCriteriaArray, scanningContext) {
|
|
609
|
+
const currentTriggerState = scanningContext.triggerState;
|
|
498
610
|
const count = autoFormatCriteriaArray.length;
|
|
499
611
|
|
|
500
612
|
for (let i = 0; i < count; i++) {
|
|
501
613
|
const autoFormatCriteria = autoFormatCriteriaArray[i]; // Skip code block nodes, unless the nodeTransformationKind calls for toggling the code block.
|
|
502
614
|
|
|
503
|
-
if (currentTriggerState.isCodeBlock === false || autoFormatCriteria.nodeTransformationKind === 'paragraphCodeBlock') {
|
|
615
|
+
if (currentTriggerState != null && currentTriggerState.isCodeBlock === false || autoFormatCriteria.nodeTransformationKind === 'paragraphCodeBlock') {
|
|
504
616
|
const matchResultContext = getMatchResultContextForCriteria(autoFormatCriteria, scanningContext);
|
|
505
617
|
|
|
506
618
|
if (matchResultContext != null) {
|
|
507
|
-
matchResultContext.triggerState = currentTriggerState;
|
|
508
619
|
return {
|
|
509
620
|
autoFormatCriteria: autoFormatCriteria,
|
|
510
621
|
matchResultContext
|
|
@@ -520,7 +631,7 @@ function getCriteriaWithMatchResultContext(autoFormatCriteriaArray, currentTrigg
|
|
|
520
631
|
}
|
|
521
632
|
|
|
522
633
|
function getTextNodeForAutoFormatting(selection) {
|
|
523
|
-
if (selection
|
|
634
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
524
635
|
return null;
|
|
525
636
|
}
|
|
526
637
|
|
|
@@ -536,8 +647,17 @@ function getTextNodeForAutoFormatting(selection) {
|
|
|
536
647
|
};
|
|
537
648
|
}
|
|
538
649
|
|
|
539
|
-
function updateAutoFormatting(editor,
|
|
650
|
+
function updateAutoFormatting(editor, scanningContext) {
|
|
540
651
|
editor.update(() => {
|
|
652
|
+
transformTextNodeForAutoFormatCriteria(scanningContext);
|
|
653
|
+
}, {
|
|
654
|
+
tag: 'history-push'
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function findScanningContextWithValidMatch(editorState, currentTriggerState) {
|
|
659
|
+
let scanningContext = null;
|
|
660
|
+
editorState.read(() => {
|
|
541
661
|
const textNodeWithOffset = getTextNodeForAutoFormatting(lexical.$getSelection());
|
|
542
662
|
|
|
543
663
|
if (textNodeWithOffset === null) {
|
|
@@ -545,24 +665,25 @@ function updateAutoFormatting(editor, currentTriggerState) {
|
|
|
545
665
|
} // Please see the declaration of ScanningContext for a detailed explanation.
|
|
546
666
|
|
|
547
667
|
|
|
548
|
-
const
|
|
549
|
-
joinedText: null,
|
|
550
|
-
textNodeWithOffset
|
|
551
|
-
};
|
|
668
|
+
const initialScanningContext = getInitialScanningContext(textNodeWithOffset, currentTriggerState);
|
|
552
669
|
const criteriaWithMatchResultContext = getCriteriaWithMatchResultContext( // Do not apply paragraph node changes like blockQuote or H1 to listNodes. Also, do not attempt to transform a list into a list using * or -.
|
|
553
|
-
currentTriggerState.isParentAListItemNode === false ? getAllAutoFormatCriteria() : getAllAutoFormatCriteriaForTextNodes(),
|
|
670
|
+
currentTriggerState.isParentAListItemNode === false ? getAllAutoFormatCriteria() : getAllAutoFormatCriteriaForTextNodes(), initialScanningContext);
|
|
554
671
|
|
|
555
672
|
if (criteriaWithMatchResultContext.autoFormatCriteria === null || criteriaWithMatchResultContext.matchResultContext === null) {
|
|
556
673
|
return;
|
|
557
674
|
}
|
|
558
675
|
|
|
559
|
-
|
|
676
|
+
scanningContext = initialScanningContext; // Lazy fill-in the particular format criteria and any matching result information.
|
|
677
|
+
|
|
678
|
+
scanningContext.autoFormatCriteria = criteriaWithMatchResultContext.autoFormatCriteria;
|
|
679
|
+
scanningContext.matchResultContext = criteriaWithMatchResultContext.matchResultContext;
|
|
560
680
|
});
|
|
681
|
+
return scanningContext;
|
|
561
682
|
}
|
|
562
683
|
|
|
563
|
-
function
|
|
684
|
+
function findScanningContext(editorState, currentTriggerState, priorTriggerState) {
|
|
564
685
|
if (currentTriggerState == null || priorTriggerState == null) {
|
|
565
|
-
return
|
|
686
|
+
return null;
|
|
566
687
|
} // The below checks needs to execute relativey quickly, so perform the light-weight ones first.
|
|
567
688
|
// The substr check is a quick way to avoid autoformat parsing in that it looks for the autoformat
|
|
568
689
|
// trigger which is the trigger string (" ").
|
|
@@ -571,7 +692,12 @@ function shouldAttemptToAutoFormat(currentTriggerState, priorTriggerState) {
|
|
|
571
692
|
const triggerStringLength = TRIGGER_STRING.length;
|
|
572
693
|
const currentTextContentLength = currentTriggerState.textContent.length;
|
|
573
694
|
const triggerOffset = currentTriggerState.anchorOffset - triggerStringLength;
|
|
574
|
-
|
|
695
|
+
|
|
696
|
+
if ((currentTriggerState.hasParentNode === true && currentTriggerState.isSimpleText && currentTriggerState.isSelectionCollapsed && currentTriggerState.nodeKey === priorTriggerState.nodeKey && currentTriggerState.anchorOffset !== priorTriggerState.anchorOffset && triggerOffset >= 0 && triggerOffset + triggerStringLength <= currentTextContentLength && currentTriggerState.textContent.substr(triggerOffset, triggerStringLength) === TRIGGER_STRING && currentTriggerState.textContent !== priorTriggerState.textContent) === false) {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return findScanningContextWithValidMatch(editorState, currentTriggerState);
|
|
575
701
|
}
|
|
576
702
|
|
|
577
703
|
function getTriggerState(editorState) {
|
|
@@ -579,7 +705,7 @@ function getTriggerState(editorState) {
|
|
|
579
705
|
editorState.read(() => {
|
|
580
706
|
const selection = lexical.$getSelection();
|
|
581
707
|
|
|
582
|
-
if (selection
|
|
708
|
+
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
583
709
|
return;
|
|
584
710
|
}
|
|
585
711
|
|
|
@@ -608,15 +734,17 @@ function useAutoFormatter(editor) {
|
|
|
608
734
|
// For example, typing "#" and then " ", shoud trigger an format.
|
|
609
735
|
// However, given "#A B", where the user delets "A" should not.
|
|
610
736
|
let priorTriggerState = null;
|
|
611
|
-
editor.addListener('update', ({
|
|
737
|
+
return editor.addListener('update', ({
|
|
612
738
|
tags
|
|
613
739
|
}) => {
|
|
614
740
|
// Examine historic so that we are not running autoformatting within markdown.
|
|
615
741
|
if (tags.has('historic') === false) {
|
|
616
|
-
const
|
|
742
|
+
const editorState = editor.getEditorState();
|
|
743
|
+
const currentTriggerState = getTriggerState(editorState);
|
|
744
|
+
const scanningContext = currentTriggerState == null ? null : findScanningContext(editorState, currentTriggerState, priorTriggerState);
|
|
617
745
|
|
|
618
|
-
if (
|
|
619
|
-
updateAutoFormatting(editor,
|
|
746
|
+
if (scanningContext != null) {
|
|
747
|
+
updateAutoFormatting(editor, scanningContext);
|
|
620
748
|
}
|
|
621
749
|
|
|
622
750
|
priorTriggerState = currentTriggerState;
|