onchain-lexical-instance 0.0.13 → 0.0.14

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.
@@ -1,427 +1,465 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- */
8
- import {$isListItemNode} from '@lexical/list';
9
- import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
10
- import {
11
- $createTableCellNode,
12
- $createTableRowNode,
13
- $isTableCellNode,
14
- $isTableSelection,
15
- TableCellNode,
16
- TableRowNode,
17
- } from '@lexical/table';
18
- import {$findMatchingParent, mergeRegister} from '@lexical/utils';
19
- import {
20
- $createRangeSelection,
21
- $createTextNode,
22
- $getNodeByKey,
23
- $getSelection,
24
- $isElementNode,
25
- $isRangeSelection,
26
- $setSelection,
27
- COMMAND_PRIORITY_CRITICAL,
28
- COMMAND_PRIORITY_LOW,
29
- DELETE_CHARACTER_COMMAND,
30
- ElementNode,
31
- exportNodeToJSON,
32
- INSERT_PARAGRAPH_COMMAND,
33
- LexicalNode,
34
- SerializedLexicalNode,
35
- } from 'lexical';
36
- import {getStorageSerializedString} from 'onchain-lexical-markdown';
37
- import {dfs} from 'onchain-utility/traversal';
38
- import React, {useEffect} from 'react';
39
-
40
- import {$isInstanceNode} from './base';
41
- import {
42
- ADD_NEW_INSTANCE_NODE,
43
- DisableSelector,
44
- PluginProps,
45
- SPLIT_INSTANCE_NODE,
46
- } from './const';
47
- import {HorizontalRulePlugin} from './horizontal/horizontalPlugin';
48
- import {$createInstanceListNode, $isInstanceListNode} from './list';
49
- import {
50
- $isInstanceListItemNode,
51
- $registerInstanceListItemInsertParagraph,
52
- } from './list/item';
53
- import {
54
- $registerNumberDecoratorDomUpdate,
55
- $registerNumberDecoratorNodeUpdate,
56
- } from './number';
57
- import {
58
- $createInstanceParagraphNode,
59
- $registerInstanceParagraphNodeTransform,
60
- } from './paragraph';
61
- import {$registerInstanceHeadingNodeTransform} from './paragraph/title';
62
- import {
63
- $createInstanceTableNode,
64
- $isInstanceTableNode,
65
- $registerTableCommand,
66
- } from './table';
67
- import {$addInstancesNode, clearCache, setTemporaryContentText} from './utils';
68
-
69
- export const InstancePlugin: React.FC<PluginProps> = (props) => {
70
- const {placeholder} = props;
71
- const [editor] = useLexicalComposerContext();
72
- // const {setSelectedInstance} = useInstanceConfig();
73
- useEffect(() => {
74
- return mergeRegister(
75
- $registerInstanceParagraphNodeTransform(editor, {placeholder}),
76
- $registerInstanceHeadingNodeTransform(editor),
77
- $registerInstanceListItemInsertParagraph(editor),
78
- $registerNumberDecoratorNodeUpdate(editor),
79
- $registerNumberDecoratorDomUpdate(editor),
80
- $registerTableCommand(editor),
81
- // $selectionChange(editor, setSelectedInstance),
82
- editor.registerCommand(
83
- DELETE_CHARACTER_COMMAND,
84
- (event) => {
85
- const selection = $getSelection();
86
- if (selection) {
87
- return selection.getNodes().some((node) => {
88
- return editor
89
- .getElementByKey(node.getKey())
90
- ?.closest(DisableSelector);
91
- });
92
- }
93
- return false;
94
- },
95
- COMMAND_PRIORITY_CRITICAL,
96
- ),
97
- editor.registerCommand(
98
- INSERT_PARAGRAPH_COMMAND,
99
- (event) => {
100
- const selection = $getSelection();
101
- const [start, end] = selection?.getStartEndPoints() || [];
102
- if (start && end && start.key === end.key) {
103
- return $isInstanceNode($getNodeByKey(start.key));
104
- }
105
- return false;
106
- },
107
- COMMAND_PRIORITY_CRITICAL,
108
- ),
109
- editor.registerCommand(
110
- INSERT_PARAGRAPH_COMMAND,
111
- (event) => {
112
- const selection = $getSelection();
113
- const [start, end] = selection?.getStartEndPoints() || [];
114
- if (start && end && start.key === end.key) {
115
- return $isInstanceNode($getNodeByKey(start.key));
116
- }
117
- return false;
118
- },
119
- COMMAND_PRIORITY_CRITICAL,
120
- ),
121
- editor.registerCommand(
122
- ADD_NEW_INSTANCE_NODE,
123
- ({insNodeKey, instances, isAddChildLevel}) => {
124
- $addInstancesNode({insNodeKey, instances, isAddChildLevel});
125
- return true;
126
- },
127
- COMMAND_PRIORITY_LOW,
128
- ),
129
- /** 需求拆分 */
130
- editor.registerCommand(
131
- SPLIT_INSTANCE_NODE,
132
- ({selection, insNodeKey, instance, isAddChildLevel}) => {
133
- $setSelection(null);
134
- if ($isRangeSelection(selection) || $isTableSelection(selection)) {
135
- const forwardSelection = $createRangeSelection();
136
- const [startPoint, endPoint] = selection.isBackward()
137
- ? [selection.focus, selection.anchor]
138
- : [selection.anchor, selection.focus];
139
- forwardSelection.anchor.set(
140
- startPoint.key,
141
- startPoint.offset,
142
- startPoint.type,
143
- );
144
- forwardSelection.focus.set(
145
- endPoint.key,
146
- endPoint.offset,
147
- endPoint.type,
148
- );
149
- const nodes = forwardSelection.extract();
150
-
151
- const hasElementNode = nodes.some((node) => $isElementNode(node));
152
- const elementNodes: LexicalNode[] = [];
153
- if (!hasElementNode) {
154
- const paragraph = $createInstanceParagraphNode();
155
- nodes.forEach((node) => node.remove());
156
- paragraph.append(...nodes);
157
- elementNodes.push(paragraph);
158
- } else {
159
- if ($isTableSelection(selection)) {
160
- const size = {
161
- cell: 0,
162
- row: 0,
163
- rowIndex: Infinity,
164
- };
165
- const newRowMap = new Map<number, TableRowNode>();
166
- const configs = selection
167
- .getNodes()
168
- .filter((node) => $isTableCellNode(node))
169
- .map((node) => {
170
- const row = node.getParent()!;
171
- const rowIndex = row.getIndexWithinParent();
172
- if (size.rowIndex !== rowIndex) {
173
- size.rowIndex = rowIndex;
174
- size.cell = 1;
175
- size.row++;
176
- } else {
177
- size.cell++;
178
- }
179
- if (!newRowMap.has(rowIndex)) {
180
- newRowMap.set(rowIndex, $createTableRowNode());
181
- }
182
- return {
183
- col: node.getIndexWithinParent(),
184
- node,
185
- rowIndex: rowIndex,
186
- rowNode: row,
187
- };
188
- });
189
- const tableNode = $createInstanceTableNode();
190
- const maxCellSize = configs[0].rowNode.getChildrenSize();
191
- const maxRowSize = configs[0].rowNode
192
- .getParent()
193
- ?.getChildrenSize();
194
- if (size.cell === maxCellSize) {
195
- tableNode.append(...configs.map((config) => config.rowNode));
196
- } else {
197
- configs.forEach((config) => {
198
- const rowNode =
199
- newRowMap.get(config.rowIndex) ||
200
- newRowMap
201
- .set(config.rowIndex, $createTableRowNode())
202
- .get(config.rowIndex)!;
203
- if (size.row !== maxRowSize) {
204
- config.node.replace($createTableCellNode());
205
- }
206
- rowNode.append(config.node);
207
- });
208
- newRowMap.values().forEach((row) => {
209
- tableNode.append(row);
210
- });
211
- }
212
- elementNodes.push(tableNode);
213
- } else {
214
- const [startNode, endNode] = [
215
- startPoint.getNode(),
216
- endPoint.getNode(),
217
- ];
218
-
219
- const startAncestors = getAncestors(startNode);
220
- const endAncestors = getAncestors(endNode);
221
-
222
- const sameLevel = new Map<string, LexicalNode>();
223
- startAncestors.some((sa, idx) => {
224
- const eaIdx = endAncestors.findIndex(
225
- (ea, idx) => ea.getKey() === sa.getKey(),
226
- );
227
- const isSame = eaIdx > -1;
228
- if (isSame) {
229
- sameLevel.set('start', startAncestors[idx - 1]);
230
- sameLevel.set('end', endAncestors[eaIdx - 1]);
231
- }
232
- return isSame;
233
- });
234
-
235
- const next = $getFollowUpNode({
236
- node: startNode,
237
- terminate: sameLevel.get('start'),
238
- type: 'getNextSiblings',
239
- });
240
- const previous = $getFollowUpNode({
241
- node: endNode.getNextSibling() || endNode,
242
- terminate: sameLevel.get('end'),
243
- type: 'getPreviousSiblings',
244
- });
245
- let node = next.original.getNextSibling();
246
- while (node && node.getKey() !== previous.original.getKey()) {
247
- elementNodes.push(node);
248
- node = node.getNextSibling();
249
- }
250
- elementNodes.unshift(...next.nodes);
251
- elementNodes.push(...previous.nodes);
252
- // 添加嵌套Item父List
253
- if (elementNodes.every((node) => $isListItemNode(node))) {
254
- const originalListNode = $findMatchingParent(
255
- next.original,
256
- (node) => $isInstanceListNode(node),
257
- );
258
- if (originalListNode) {
259
- const listNode = $createInstanceListNode(
260
- originalListNode.getListType(),
261
- );
262
- listNode.append(...elementNodes);
263
- elementNodes.length = 0;
264
- (elementNodes as LexicalNode[]).push(listNode);
265
- }
266
- }
267
- }
268
- }
269
-
270
- const paragraph = $createInstanceParagraphNode();
271
- paragraph.append(...elementNodes);
272
- const json = exportNodeToJSON<
273
- SerializedLexicalNode & {children: SerializedLexicalNode[]}
274
- >(paragraph);
275
- setTemporaryContentText(
276
- instance,
277
- getStorageSerializedString(json.children),
278
- );
279
-
280
- $addInstancesNode({
281
- insNodeKey,
282
- instances: [instance],
283
- isAddChildLevel,
284
- });
285
- }
286
- return true;
287
- },
288
- COMMAND_PRIORITY_LOW,
289
- ),
290
- );
291
- }, [editor, placeholder]);
292
- useEffect(() => {
293
- return () => {
294
- clearCache();
295
- };
296
- }, []);
297
- return React.createElement(
298
- React.Fragment,
299
- {},
300
- React.createElement(HorizontalRulePlugin),
301
- );
302
- };
303
-
304
- function getDescendants(node: ElementNode) {
305
- if ($isElementNode(node)) {
306
- return dfs(node.getChildren(), (node) => {
307
- if ($isElementNode(node)) {
308
- return node.getChildren();
309
- } else {
310
- return [];
311
- }
312
- });
313
- } else {
314
- return [];
315
- }
316
- }
317
-
318
- function getAncestors(node: LexicalNode) {
319
- const parent = node.getParent();
320
- if (parent) {
321
- return dfs([parent], (node) => {
322
- const parent = node.getParent();
323
- if (parent) {
324
- return [parent];
325
- } else {
326
- return [];
327
- }
328
- });
329
- } else {
330
- return [];
331
- }
332
- }
333
-
334
- function $getFollowUpNode({
335
- node,
336
- terminate,
337
- type,
338
- }: {
339
- node: LexicalNode;
340
- terminate?: LexicalNode;
341
- type: 'getNextSiblings' | 'getPreviousSiblings';
342
- }) {
343
- const isTableCellNode = $isTableCellNode(node);
344
- if (isTableCellNode) {
345
- const descendants = getDescendants(node as TableCellNode);
346
- const text = $createTextNode('');
347
- node = text;
348
- if (type === 'getNextSiblings') {
349
- descendants[0].insertBefore(text);
350
- } else {
351
- descendants[descendants.length - 1].insertAfter(text);
352
- }
353
- }
354
-
355
- const ancestors = terminate ? [terminate] : getAncestors(node);
356
- const index = ancestors.findIndex((node) => $isInstanceNode(node));
357
- const term = ancestors[index - 1] || ancestors[ancestors.length - 1];
358
- const original = term;
359
-
360
- // console.log(
361
- // {
362
- // node,
363
- // original,
364
- // terminate,
365
- // type,
366
- // },
367
- // 'getFollowUpNode',
368
- // );
369
-
370
- const set = new Set<LexicalNode>();
371
- const termKey = term.getKey();
372
- dfs([node], (node) => {
373
- const parent = node.getParent();
374
- const parentKey = parent?.getKey();
375
- const isStop = parentKey === termKey;
376
- const siblings = node[type]();
377
- siblings.forEach((sibling) => {
378
- if ($isInstanceListItemNode(sibling)) {
379
- sibling.defaultRemove();
380
- return;
381
- }
382
- sibling.remove();
383
- });
384
- if (parent) {
385
- const constructor = parent.constructor;
386
- const newParent = constructor.importJSON(
387
- parent.exportJSON(),
388
- ) as ElementNode;
389
- newParent.append(...siblings);
390
- if (set.size) {
391
- const children = Array.from(set)[0];
392
- if (type === 'getNextSiblings') {
393
- if (siblings.length) {
394
- siblings[0].insertBefore(children);
395
- } else {
396
- newParent.append(children);
397
- }
398
- } else {
399
- if (siblings.length) {
400
- siblings[siblings.length - 1].insertAfter(children);
401
- } else {
402
- newParent.append(children);
403
- }
404
- }
405
- set.clear();
406
- }
407
- set.add(newParent);
408
- }
409
- if (isStop || !parent) {
410
- return [];
411
- } else {
412
- return [parent].filter(Boolean);
413
- }
414
- });
415
- if (isTableCellNode) {
416
- const tableNode = $findMatchingParent(node, (node) =>
417
- $isInstanceTableNode(node),
418
- );
419
- if (tableNode) {
420
- tableNode.remove();
421
- }
422
- }
423
- return {
424
- nodes: Array.from(set.values()),
425
- original,
426
- };
427
- }
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import {$isListItemNode} from '@lexical/list';
9
+ import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
10
+ import {
11
+ $createTableCellNode,
12
+ $createTableRowNode,
13
+ $isTableCellNode,
14
+ $isTableSelection,
15
+ TableCellNode,
16
+ TableRowNode,
17
+ } from '@lexical/table';
18
+ import {$findMatchingParent, mergeRegister} from '@lexical/utils';
19
+ import {
20
+ $createRangeSelection,
21
+ $createTextNode,
22
+ $getNodeByKey,
23
+ $getSelection,
24
+ $isDecoratorNode,
25
+ $isElementNode,
26
+ $isNodeSelection,
27
+ $isRangeSelection,
28
+ $setSelection,
29
+ CLICK_COMMAND,
30
+ COMMAND_PRIORITY_CRITICAL,
31
+ COMMAND_PRIORITY_LOW,
32
+ DELETE_CHARACTER_COMMAND,
33
+ ElementNode,
34
+ exportNodeToJSON,
35
+ INSERT_PARAGRAPH_COMMAND,
36
+ LexicalNode,
37
+ SELECTION_CHANGE_COMMAND,
38
+ SerializedLexicalNode,
39
+ } from 'lexical';
40
+ import {getStorageSerializedString} from 'onchain-lexical-markdown';
41
+ import {dfs} from 'onchain-utility/traversal';
42
+ import React, {useEffect} from 'react';
43
+
44
+ import {$isInstanceNode} from './base';
45
+ import {
46
+ ADD_NEW_INSTANCE_NODE,
47
+ DisableSelector,
48
+ PluginProps,
49
+ SPLIT_INSTANCE_NODE,
50
+ } from './const';
51
+ import {HorizontalRulePlugin} from './horizontal/horizontalPlugin';
52
+ import {$createInstanceListNode, $isInstanceListNode} from './list';
53
+ import {
54
+ $isInstanceListItemNode,
55
+ $registerInstanceListItemInsertParagraph,
56
+ } from './list/item';
57
+ import {
58
+ $registerNumberDecoratorDomUpdate,
59
+ $registerNumberDecoratorNodeUpdate,
60
+ } from './number';
61
+ import {
62
+ $createInstanceParagraphNode,
63
+ $registerInstanceParagraphNodeTransform,
64
+ } from './paragraph';
65
+ import {$registerInstanceHeadingNodeTransform} from './paragraph/title';
66
+ import {ParametersNode} from './parameters';
67
+ import {
68
+ $createInstanceTableNode,
69
+ $isInstanceTableNode,
70
+ $registerTableCommand,
71
+ } from './table';
72
+ import {$addInstancesNode, $selectDecoratorNode, clearCache, setTemporaryContentText} from './utils';
73
+
74
+ export const InstancePlugin: React.FC<PluginProps> = (props) => {
75
+ const {placeholder} = props;
76
+ const [editor] = useLexicalComposerContext();
77
+ // const {setSelectedInstance} = useInstanceConfig();
78
+ useEffect(() => {
79
+ return mergeRegister(
80
+ $registerInstanceParagraphNodeTransform(editor, {placeholder}),
81
+ $registerInstanceHeadingNodeTransform(editor),
82
+ $registerInstanceListItemInsertParagraph(editor),
83
+ $registerNumberDecoratorNodeUpdate(editor),
84
+ $registerNumberDecoratorDomUpdate(editor),
85
+ $registerTableCommand(editor),
86
+ // $selectionChange(editor, setSelectedInstance),
87
+ editor.registerCommand(
88
+ SELECTION_CHANGE_COMMAND,
89
+ () => {
90
+ const selection = $getSelection();
91
+ if ($isNodeSelection(selection)) {
92
+ const [node] = selection.getNodes();
93
+ if ($isDecoratorNode(node)) {
94
+ $selectDecoratorNode(node);
95
+ }
96
+ }
97
+ return false;
98
+ },
99
+ COMMAND_PRIORITY_CRITICAL,
100
+ ),
101
+ editor.registerCommand(
102
+ CLICK_COMMAND,
103
+ (event: MouseEvent) => {
104
+ if (event.target instanceof HTMLElement) {
105
+ const decoratorRootEle = event.target.closest(
106
+ '[data-lexical-decorator="true"]',
107
+ );
108
+ if (decoratorRootEle) {
109
+ const key = decoratorRootEle.getAttribute('key');
110
+ if (key) {
111
+ const node = $getNodeByKey<ParametersNode>(key);
112
+ $selectDecoratorNode(node);
113
+ }
114
+ }
115
+ }
116
+ return false;
117
+ },
118
+ COMMAND_PRIORITY_LOW,
119
+ ),
120
+ editor.registerCommand(
121
+ DELETE_CHARACTER_COMMAND,
122
+ (event) => {
123
+ const selection = $getSelection();
124
+ if (selection) {
125
+ return selection.getNodes().some((node) => {
126
+ return editor
127
+ .getElementByKey(node.getKey())
128
+ ?.closest(DisableSelector);
129
+ });
130
+ }
131
+ return false;
132
+ },
133
+ COMMAND_PRIORITY_CRITICAL,
134
+ ),
135
+ editor.registerCommand(
136
+ INSERT_PARAGRAPH_COMMAND,
137
+ (event) => {
138
+ const selection = $getSelection();
139
+ const [start, end] = selection?.getStartEndPoints() || [];
140
+ if (start && end && start.key === end.key) {
141
+ return $isInstanceNode($getNodeByKey(start.key));
142
+ }
143
+ return false;
144
+ },
145
+ COMMAND_PRIORITY_CRITICAL,
146
+ ),
147
+ editor.registerCommand(
148
+ INSERT_PARAGRAPH_COMMAND,
149
+ (event) => {
150
+ const selection = $getSelection();
151
+ const [start, end] = selection?.getStartEndPoints() || [];
152
+ if (start && end && start.key === end.key) {
153
+ return $isInstanceNode($getNodeByKey(start.key));
154
+ }
155
+ return false;
156
+ },
157
+ COMMAND_PRIORITY_CRITICAL,
158
+ ),
159
+ editor.registerCommand(
160
+ ADD_NEW_INSTANCE_NODE,
161
+ ({insNodeKey, instances, isAddChildLevel}) => {
162
+ $addInstancesNode({insNodeKey, instances, isAddChildLevel});
163
+ return true;
164
+ },
165
+ COMMAND_PRIORITY_LOW,
166
+ ),
167
+ /** 需求拆分 */
168
+ editor.registerCommand(
169
+ SPLIT_INSTANCE_NODE,
170
+ ({selection, insNodeKey, instance, isAddChildLevel}) => {
171
+ $setSelection(null);
172
+ if ($isRangeSelection(selection) || $isTableSelection(selection)) {
173
+ const forwardSelection = $createRangeSelection();
174
+ const [startPoint, endPoint] = selection.isBackward()
175
+ ? [selection.focus, selection.anchor]
176
+ : [selection.anchor, selection.focus];
177
+ forwardSelection.anchor.set(
178
+ startPoint.key,
179
+ startPoint.offset,
180
+ startPoint.type,
181
+ );
182
+ forwardSelection.focus.set(
183
+ endPoint.key,
184
+ endPoint.offset,
185
+ endPoint.type,
186
+ );
187
+ const nodes = forwardSelection.extract();
188
+
189
+ const hasElementNode = nodes.some((node) => $isElementNode(node));
190
+ const elementNodes: LexicalNode[] = [];
191
+ if (!hasElementNode) {
192
+ const paragraph = $createInstanceParagraphNode();
193
+ nodes.forEach((node) => node.remove());
194
+ paragraph.append(...nodes);
195
+ elementNodes.push(paragraph);
196
+ } else {
197
+ if ($isTableSelection(selection)) {
198
+ const size = {
199
+ cell: 0,
200
+ row: 0,
201
+ rowIndex: Infinity,
202
+ };
203
+ const newRowMap = new Map<number, TableRowNode>();
204
+ const configs = selection
205
+ .getNodes()
206
+ .filter((node) => $isTableCellNode(node))
207
+ .map((node) => {
208
+ const row = node.getParent()!;
209
+ const rowIndex = row.getIndexWithinParent();
210
+ if (size.rowIndex !== rowIndex) {
211
+ size.rowIndex = rowIndex;
212
+ size.cell = 1;
213
+ size.row++;
214
+ } else {
215
+ size.cell++;
216
+ }
217
+ if (!newRowMap.has(rowIndex)) {
218
+ newRowMap.set(rowIndex, $createTableRowNode());
219
+ }
220
+ return {
221
+ col: node.getIndexWithinParent(),
222
+ node,
223
+ rowIndex: rowIndex,
224
+ rowNode: row,
225
+ };
226
+ });
227
+ const tableNode = $createInstanceTableNode();
228
+ const maxCellSize = configs[0].rowNode.getChildrenSize();
229
+ const maxRowSize = configs[0].rowNode
230
+ .getParent()
231
+ ?.getChildrenSize();
232
+ if (size.cell === maxCellSize) {
233
+ tableNode.append(...configs.map((config) => config.rowNode));
234
+ } else {
235
+ configs.forEach((config) => {
236
+ const rowNode =
237
+ newRowMap.get(config.rowIndex) ||
238
+ newRowMap
239
+ .set(config.rowIndex, $createTableRowNode())
240
+ .get(config.rowIndex)!;
241
+ if (size.row !== maxRowSize) {
242
+ config.node.replace($createTableCellNode());
243
+ }
244
+ rowNode.append(config.node);
245
+ });
246
+ newRowMap.values().forEach((row) => {
247
+ tableNode.append(row);
248
+ });
249
+ }
250
+ elementNodes.push(tableNode);
251
+ } else {
252
+ const [startNode, endNode] = [
253
+ startPoint.getNode(),
254
+ endPoint.getNode(),
255
+ ];
256
+
257
+ const startAncestors = getAncestors(startNode);
258
+ const endAncestors = getAncestors(endNode);
259
+
260
+ const sameLevel = new Map<string, LexicalNode>();
261
+ startAncestors.some((sa, idx) => {
262
+ const eaIdx = endAncestors.findIndex(
263
+ (ea, idx) => ea.getKey() === sa.getKey(),
264
+ );
265
+ const isSame = eaIdx > -1;
266
+ if (isSame) {
267
+ sameLevel.set('start', startAncestors[idx - 1]);
268
+ sameLevel.set('end', endAncestors[eaIdx - 1]);
269
+ }
270
+ return isSame;
271
+ });
272
+
273
+ const next = $getFollowUpNode({
274
+ node: startNode,
275
+ terminate: sameLevel.get('start'),
276
+ type: 'getNextSiblings',
277
+ });
278
+ const previous = $getFollowUpNode({
279
+ node: endNode.getNextSibling() || endNode,
280
+ terminate: sameLevel.get('end'),
281
+ type: 'getPreviousSiblings',
282
+ });
283
+ let node = next.original.getNextSibling();
284
+ while (node && node.getKey() !== previous.original.getKey()) {
285
+ elementNodes.push(node);
286
+ node = node.getNextSibling();
287
+ }
288
+ elementNodes.unshift(...next.nodes);
289
+ elementNodes.push(...previous.nodes);
290
+ // 添加嵌套Item父List
291
+ if (elementNodes.every((node) => $isListItemNode(node))) {
292
+ const originalListNode = $findMatchingParent(
293
+ next.original,
294
+ (node) => $isInstanceListNode(node),
295
+ );
296
+ if (originalListNode) {
297
+ const listNode = $createInstanceListNode(
298
+ originalListNode.getListType(),
299
+ );
300
+ listNode.append(...elementNodes);
301
+ elementNodes.length = 0;
302
+ (elementNodes as LexicalNode[]).push(listNode);
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ const paragraph = $createInstanceParagraphNode();
309
+ paragraph.append(...elementNodes);
310
+ const json = exportNodeToJSON<
311
+ SerializedLexicalNode & {children: SerializedLexicalNode[]}
312
+ >(paragraph);
313
+ setTemporaryContentText(
314
+ instance,
315
+ getStorageSerializedString(json.children),
316
+ );
317
+
318
+ $addInstancesNode({
319
+ insNodeKey,
320
+ instances: [instance],
321
+ isAddChildLevel,
322
+ });
323
+ }
324
+ return true;
325
+ },
326
+ COMMAND_PRIORITY_LOW,
327
+ ),
328
+ );
329
+ }, [editor, placeholder]);
330
+ useEffect(() => {
331
+ return () => {
332
+ clearCache();
333
+ };
334
+ }, []);
335
+ return React.createElement(
336
+ React.Fragment,
337
+ {},
338
+ React.createElement(HorizontalRulePlugin),
339
+ );
340
+ };
341
+
342
+ function getDescendants(node: ElementNode) {
343
+ if ($isElementNode(node)) {
344
+ return dfs(node.getChildren(), (node) => {
345
+ if ($isElementNode(node)) {
346
+ return node.getChildren();
347
+ } else {
348
+ return [];
349
+ }
350
+ });
351
+ } else {
352
+ return [];
353
+ }
354
+ }
355
+
356
+ function getAncestors(node: LexicalNode) {
357
+ const parent = node.getParent();
358
+ if (parent) {
359
+ return dfs([parent], (node) => {
360
+ const parent = node.getParent();
361
+ if (parent) {
362
+ return [parent];
363
+ } else {
364
+ return [];
365
+ }
366
+ });
367
+ } else {
368
+ return [];
369
+ }
370
+ }
371
+
372
+ function $getFollowUpNode({
373
+ node,
374
+ terminate,
375
+ type,
376
+ }: {
377
+ node: LexicalNode;
378
+ terminate?: LexicalNode;
379
+ type: 'getNextSiblings' | 'getPreviousSiblings';
380
+ }) {
381
+ const isTableCellNode = $isTableCellNode(node);
382
+ if (isTableCellNode) {
383
+ const descendants = getDescendants(node as TableCellNode);
384
+ const text = $createTextNode('');
385
+ node = text;
386
+ if (type === 'getNextSiblings') {
387
+ descendants[0].insertBefore(text);
388
+ } else {
389
+ descendants[descendants.length - 1].insertAfter(text);
390
+ }
391
+ }
392
+
393
+ const ancestors = terminate ? [terminate] : getAncestors(node);
394
+ const index = ancestors.findIndex((node) => $isInstanceNode(node));
395
+ const term = ancestors[index - 1] || ancestors[ancestors.length - 1];
396
+ const original = term;
397
+
398
+ // console.log(
399
+ // {
400
+ // node,
401
+ // original,
402
+ // terminate,
403
+ // type,
404
+ // },
405
+ // 'getFollowUpNode',
406
+ // );
407
+
408
+ const set = new Set<LexicalNode>();
409
+ const termKey = term.getKey();
410
+ dfs([node], (node) => {
411
+ const parent = node.getParent();
412
+ const parentKey = parent?.getKey();
413
+ const isStop = parentKey === termKey;
414
+ const siblings = node[type]();
415
+ siblings.forEach((sibling) => {
416
+ if ($isInstanceListItemNode(sibling)) {
417
+ sibling.defaultRemove();
418
+ return;
419
+ }
420
+ sibling.remove();
421
+ });
422
+ if (parent) {
423
+ const constructor = parent.constructor;
424
+ const newParent = constructor.importJSON(
425
+ parent.exportJSON(),
426
+ ) as ElementNode;
427
+ newParent.append(...siblings);
428
+ if (set.size) {
429
+ const children = Array.from(set)[0];
430
+ if (type === 'getNextSiblings') {
431
+ if (siblings.length) {
432
+ siblings[0].insertBefore(children);
433
+ } else {
434
+ newParent.append(children);
435
+ }
436
+ } else {
437
+ if (siblings.length) {
438
+ siblings[siblings.length - 1].insertAfter(children);
439
+ } else {
440
+ newParent.append(children);
441
+ }
442
+ }
443
+ set.clear();
444
+ }
445
+ set.add(newParent);
446
+ }
447
+ if (isStop || !parent) {
448
+ return [];
449
+ } else {
450
+ return [parent].filter(Boolean);
451
+ }
452
+ });
453
+ if (isTableCellNode) {
454
+ const tableNode = $findMatchingParent(node, (node) =>
455
+ $isInstanceTableNode(node),
456
+ );
457
+ if (tableNode) {
458
+ tableNode.remove();
459
+ }
460
+ }
461
+ return {
462
+ nodes: Array.from(set.values()),
463
+ original,
464
+ };
465
+ }