onchain-lexical-instance 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OnchainLexicalInstance.js +11 -0
- package/README.md +1 -0
- package/flow/OnchainLexicalMarkdown.js.flow +24 -0
- package/package.json +36 -0
- package/src/bar/index.tsx +114 -0
- package/src/bar/styles.module.less +19 -0
- package/src/base.module.less +7 -0
- package/src/base.ts +325 -0
- package/src/code/index.ts +293 -0
- package/src/const.ts +41 -0
- package/src/fragment/index.ts +39 -0
- package/src/heading/index.ts +217 -0
- package/src/index.ts +72 -0
- package/src/instancePlugin.ts +34 -0
- package/src/list/formatList.ts +576 -0
- package/src/list/index.ts +231 -0
- package/src/list/item.ts +316 -0
- package/src/list/utils.ts +201 -0
- package/src/number/index.tsx +227 -0
- package/src/number/styles.module.less +17 -0
- package/src/paragraph/index.ts +188 -0
- package/src/paragraph/title.ts +261 -0
- package/src/placeholder/index.ts +123 -0
- package/src/quote/index.ts +112 -0
- package/src/types.d.ts +10 -0
- package/src/utils.ts +123 -0
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
module.exports = require('./dist/OnchainLexicalInstance.js');
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# `@onchain/lexical-instance`
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
* @flow strict
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { $createBarDecoratorNode, $isBarDecoratorNode, BarDecoratorNode } from '../src/bar'
|
|
11
|
+
export { $createInstanceNode, $isInstanceNode, InstanceNode } from '../src/base'
|
|
12
|
+
export { $createInstanceCodeNode, $isInstanceCodeNode, InstanceCodeNode } from '../src/code'
|
|
13
|
+
export { InstanceParagraphType } from '../src/const'
|
|
14
|
+
export { $createInstanceHeadingNode, InstanceHeadingNode } from '../src/heading'
|
|
15
|
+
export { InstancePlugin } from '../src/instancePlugin'
|
|
16
|
+
export { $createInstanceListNode, $isInstanceListNode, InstanceListNode } from '../src/list'
|
|
17
|
+
export * from '../src/list/formatList'
|
|
18
|
+
export { $createInstanceListItemNode, $isInstanceListItemNode, InstanceListItemNode } from '../src/list/item'
|
|
19
|
+
export * from '../src/list/utils'
|
|
20
|
+
export { getInstanceTransformers } from '../src/MarkdownTransformers'
|
|
21
|
+
export { $createNumberDecoratorNode, $isNumberDecoratorNode, NumberDecoratorNode } from '../src/number'
|
|
22
|
+
export { $createInstanceParagraphNode, $isEmptyInstanceParagraphNode, $isEmptyParagraphNode,$isInstanceParagraphNode, InstanceParagraphNode } from '../src/paragraph'
|
|
23
|
+
export { $createInstanceTitleNode, $isInstanceTitleNode, InstanceTitleNode } from '../src/paragraph/title'
|
|
24
|
+
export { $createInstanceQuoteNode, $isInstanceQuoteNode, InstanceQuoteNode } from '../src/quote'
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "onchain-lexical-instance",
|
|
3
|
+
"description": "This package provides the list feature for OnchainLexical.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"lexical",
|
|
6
|
+
"editor",
|
|
7
|
+
"instance"
|
|
8
|
+
],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"version": "0.0.1",
|
|
11
|
+
"types": "index.d.ts",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@lexical/code": "^0.30.0",
|
|
14
|
+
"@lexical/list": "^0.30.0",
|
|
15
|
+
"@lexical/react": "^0.30.0",
|
|
16
|
+
"@lexical/rich-text": "^0.30.0",
|
|
17
|
+
"@lexical/selection": "^0.30.0",
|
|
18
|
+
"@lexical/table": "^0.30.0",
|
|
19
|
+
"@lexical/utils": "^0.30.0",
|
|
20
|
+
"lexical": "0.30.0",
|
|
21
|
+
"onchain-utility": "^0.0.1"
|
|
22
|
+
},
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./src/index.ts",
|
|
28
|
+
"default": "./dist/OnchainLexicalInstance.mjs"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./src/index.ts",
|
|
32
|
+
"default": "./dist/OnchainLexicalInstance.js"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
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 type React from 'react';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
$applyNodeReplacement,
|
|
12
|
+
BaseSelection,
|
|
13
|
+
DecoratorNode,
|
|
14
|
+
EditorConfig,
|
|
15
|
+
LexicalEditor,
|
|
16
|
+
LexicalNode,
|
|
17
|
+
SerializedLexicalNode,
|
|
18
|
+
Spread,
|
|
19
|
+
} from 'lexical';
|
|
20
|
+
|
|
21
|
+
import {$createInstanceNode} from '../base';
|
|
22
|
+
import Styles from './styles.module.less';
|
|
23
|
+
|
|
24
|
+
export type SerializedPlaceholderDecoratorNode = Spread<
|
|
25
|
+
{
|
|
26
|
+
__text: string;
|
|
27
|
+
},
|
|
28
|
+
SerializedLexicalNode
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export class BarDecoratorNode extends DecoratorNode<JSX.Element> {
|
|
32
|
+
__text = 'bar';
|
|
33
|
+
static getType() {
|
|
34
|
+
return 'Bar';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static clone(node: BarDecoratorNode) {
|
|
38
|
+
return new BarDecoratorNode(node.__key);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static importJSON(
|
|
42
|
+
serializedNode: SerializedPlaceholderDecoratorNode,
|
|
43
|
+
): BarDecoratorNode {
|
|
44
|
+
return $createBarDecoratorNode().updateFromJSON(serializedNode);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
constructor(key?: string) {
|
|
48
|
+
super(key);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
createDOM(config: EditorConfig) {
|
|
52
|
+
const div = document.createElement('div');
|
|
53
|
+
div.classList.add(Styles['instance-bar']);
|
|
54
|
+
return div;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
updateDOM() {
|
|
58
|
+
return false; // 静态内容无需更新
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
remove(preserveEmptyParent?: boolean): void {}
|
|
62
|
+
|
|
63
|
+
collapseAtStart(): true {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isSelected(selection?: null | BaseSelection): boolean {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onInsertBlock(e: React.MouseEvent, editor: LexicalEditor) {
|
|
72
|
+
if (!editor) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
editor.update(() => {
|
|
77
|
+
const node = this.getParent();
|
|
78
|
+
if (!node) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const pNode = $createInstanceNode();
|
|
82
|
+
if (e.altKey || e.ctrlKey) {
|
|
83
|
+
node.append(pNode);
|
|
84
|
+
// node.insertBefore(pNode);
|
|
85
|
+
} else {
|
|
86
|
+
node.insertAfter(pNode);
|
|
87
|
+
// node.append(pNode)
|
|
88
|
+
}
|
|
89
|
+
pNode.select();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element {
|
|
94
|
+
return (
|
|
95
|
+
<div>
|
|
96
|
+
<span>⭕</span>
|
|
97
|
+
<span>link</span>
|
|
98
|
+
<button onClick={(e) => this.onInsertBlock(e, editor)}>+</button>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 创建 Bar 装饰节点 */
|
|
105
|
+
export function $createBarDecoratorNode(): BarDecoratorNode {
|
|
106
|
+
return $applyNodeReplacement(new BarDecoratorNode());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 是否是 Bar 装饰节点 */
|
|
110
|
+
export function $isBarDecoratorNode(
|
|
111
|
+
node: LexicalNode | null | undefined,
|
|
112
|
+
): node is BarDecoratorNode {
|
|
113
|
+
return node instanceof BarDecoratorNode;
|
|
114
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@PaddingLeft: 23px;
|
|
2
|
+
.instance-bar {
|
|
3
|
+
user-select: none;
|
|
4
|
+
position: absolute;
|
|
5
|
+
left: -@PaddingLeft * 2;
|
|
6
|
+
padding-left: @PaddingLeft;
|
|
7
|
+
> div {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
> * {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
}
|
|
15
|
+
> span:nth-of-type(1) {
|
|
16
|
+
height: 40px;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/base.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
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 type {InstanceParagraphNode} from './paragraph';
|
|
9
|
+
import type {
|
|
10
|
+
DOMConversionMap,
|
|
11
|
+
DOMConversionOutput,
|
|
12
|
+
DOMExportOutput,
|
|
13
|
+
LexicalNode,
|
|
14
|
+
NodeKey,
|
|
15
|
+
} from 'lexical';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
$applyNodeReplacement,
|
|
19
|
+
EditorConfig,
|
|
20
|
+
ElementFormatType,
|
|
21
|
+
ElementNode,
|
|
22
|
+
isHTMLElement,
|
|
23
|
+
LexicalEditor,
|
|
24
|
+
RangeSelection,
|
|
25
|
+
SerializedElementNode,
|
|
26
|
+
setNodeIndentFromDOM,
|
|
27
|
+
Spread,
|
|
28
|
+
} from 'lexical';
|
|
29
|
+
|
|
30
|
+
import {$createBarDecoratorNode, $isBarDecoratorNode} from './bar';
|
|
31
|
+
import Styles from './base.module.less';
|
|
32
|
+
import {InstanceParagraphType} from './const';
|
|
33
|
+
import {$createNumberDecoratorNode, $isNumberDecoratorNode} from './number';
|
|
34
|
+
import {
|
|
35
|
+
$createInstanceParagraphNode,
|
|
36
|
+
$isEmptyParagraphNode,
|
|
37
|
+
$isInstanceParagraphNode,
|
|
38
|
+
} from './paragraph';
|
|
39
|
+
import {Instance} from './types';
|
|
40
|
+
import {getCachedClassNameArray} from './utils';
|
|
41
|
+
|
|
42
|
+
/** TODO 数据类型 */
|
|
43
|
+
export type SerializedInstanceNode = Spread<
|
|
44
|
+
{
|
|
45
|
+
textFormat: number;
|
|
46
|
+
textStyle: string;
|
|
47
|
+
},
|
|
48
|
+
SerializedElementNode
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
/** @noInheritDoc */
|
|
52
|
+
|
|
53
|
+
export class InstanceNode extends ElementNode {
|
|
54
|
+
// 标记初始段落数量
|
|
55
|
+
static DEFAULT_PARAGRAPHS = 3;
|
|
56
|
+
__instance: Instance | undefined;
|
|
57
|
+
constructor(instance?: Instance, isBase?: boolean, key?: NodeKey) {
|
|
58
|
+
super(key);
|
|
59
|
+
this.__instance = instance;
|
|
60
|
+
// 初始化时自动添加 3 个空段落
|
|
61
|
+
if (!key && this.isEmpty()) {
|
|
62
|
+
this.append(
|
|
63
|
+
$createBarDecoratorNode(),
|
|
64
|
+
$createNumberDecoratorNode(this.__instance),
|
|
65
|
+
...Array(isBase ? 1 : InstanceNode.DEFAULT_PARAGRAPHS)
|
|
66
|
+
.fill(null)
|
|
67
|
+
.map((_i) => $createInstanceParagraphNode()),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static getType(): string {
|
|
73
|
+
return 'Instance';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static clone(node: InstanceNode): InstanceNode {
|
|
77
|
+
return new InstanceNode(node.__instance, false, node.__key);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static canBeEmpty() {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static importDOM(): DOMConversionMap | null {
|
|
85
|
+
return {
|
|
86
|
+
p: (node: Node) => ({
|
|
87
|
+
conversion: $convertInstanceElement,
|
|
88
|
+
priority: 0,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static importJSON(serializedNode: SerializedInstanceNode): InstanceNode {
|
|
94
|
+
return $createInstanceNode().updateFromJSON(serializedNode);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
exportDOM(editor: LexicalEditor): DOMExportOutput {
|
|
98
|
+
const {element} = super.exportDOM(editor);
|
|
99
|
+
|
|
100
|
+
if (isHTMLElement(element)) {
|
|
101
|
+
if (this.isEmpty()) {
|
|
102
|
+
element.append(document.createElement('br'));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const formatType = this.getFormatType();
|
|
106
|
+
element.style.textAlign = formatType;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
element,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exportJSON(): SerializedInstanceNode {
|
|
115
|
+
return {
|
|
116
|
+
...super.exportJSON(),
|
|
117
|
+
// These are included explicitly for backwards compatibility
|
|
118
|
+
textFormat: this.getTextFormat(),
|
|
119
|
+
textStyle: this.getTextStyle(),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// View
|
|
124
|
+
|
|
125
|
+
createDOM(config: EditorConfig): HTMLElement {
|
|
126
|
+
const dom = document.createElement('div');
|
|
127
|
+
const classNames = [
|
|
128
|
+
Styles.instance,
|
|
129
|
+
getCachedClassNameArray(config.theme, 'instance') ?? [],
|
|
130
|
+
];
|
|
131
|
+
if (classNames !== undefined) {
|
|
132
|
+
const domClassList = dom.classList;
|
|
133
|
+
domClassList.add(...classNames.flat(1));
|
|
134
|
+
}
|
|
135
|
+
dom.style.position = 'relative';
|
|
136
|
+
dom.setAttribute('instance', 'true');
|
|
137
|
+
dom.setAttribute('key', this.getKey());
|
|
138
|
+
return dom;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
updateDOM(
|
|
142
|
+
prevNode: InstanceNode,
|
|
143
|
+
dom: HTMLElement,
|
|
144
|
+
config: EditorConfig,
|
|
145
|
+
): boolean {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Mutation
|
|
150
|
+
insertNewAfter(
|
|
151
|
+
rangeSelection: RangeSelection,
|
|
152
|
+
restoreSelection: boolean,
|
|
153
|
+
): InstanceNode {
|
|
154
|
+
const newElement = $createInstanceNode();
|
|
155
|
+
newElement.setTextFormat(rangeSelection.format);
|
|
156
|
+
newElement.setTextStyle(rangeSelection.style);
|
|
157
|
+
const direction = this.getDirection();
|
|
158
|
+
newElement.setDirection(direction);
|
|
159
|
+
newElement.setFormat(this.getFormatType());
|
|
160
|
+
newElement.setStyle(this.getStyle());
|
|
161
|
+
this.insertAfter(newElement, restoreSelection);
|
|
162
|
+
return newElement;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getParagraphChildren<T extends LexicalNode>(): Array<T> {
|
|
166
|
+
return super
|
|
167
|
+
.getChildren<T>()
|
|
168
|
+
.filter(
|
|
169
|
+
(node) =>
|
|
170
|
+
!$isBarDecoratorNode(node) &&
|
|
171
|
+
!$isNumberDecoratorNode(node) &&
|
|
172
|
+
!$isInstanceNode(node),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getSerialNumber(): string {
|
|
177
|
+
const children =
|
|
178
|
+
this.getParent()
|
|
179
|
+
?.getChildren()
|
|
180
|
+
.filter((node) => $isInstanceNode(node)) || [];
|
|
181
|
+
if (children.length) {
|
|
182
|
+
const index = String(children.findIndex((node) => node === this)! + 1);
|
|
183
|
+
const ancestor = this.getParent();
|
|
184
|
+
if ($isInstanceNode(ancestor)) {
|
|
185
|
+
return `${ancestor.getSerialNumber()}-${index}`;
|
|
186
|
+
} else {
|
|
187
|
+
return index;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
collapseAtStart(): boolean {
|
|
194
|
+
const children = this.getParagraphChildren<InstanceParagraphNode>();
|
|
195
|
+
// If we have an empty (trimmed) first paragraph and try and remove it,
|
|
196
|
+
// delete the paragraph as long as we have another sibling to go to
|
|
197
|
+
if (children.every((child) => $isEmptyParagraphNode(child))) {
|
|
198
|
+
const nextSibling = this.getNextSibling();
|
|
199
|
+
const prevSibling = this.getPreviousSibling();
|
|
200
|
+
const result =
|
|
201
|
+
/** 下一个是否存在 */ this.handleRemove(nextSibling) ||
|
|
202
|
+
/** 上一个是否存在 */ this.handleRemove(prevSibling, false);
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
handleRemove(sibling: LexicalNode | null, isNext = true) {
|
|
208
|
+
if (sibling !== null) {
|
|
209
|
+
sibling.selectEnd();
|
|
210
|
+
if ($isInstanceNode(sibling)) {
|
|
211
|
+
const children = sibling.getChildren();
|
|
212
|
+
let anchor = children[0];
|
|
213
|
+
if (!isNext) {
|
|
214
|
+
anchor = children[children.length - 1];
|
|
215
|
+
}
|
|
216
|
+
anchor.selectEnd();
|
|
217
|
+
}
|
|
218
|
+
this.remove();
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
select(_anchorOffset?: number, _focusOffset?: number): RangeSelection {
|
|
225
|
+
const firstChild = this.getChildAtIndex(InstanceParagraphType.Title);
|
|
226
|
+
if (firstChild) {
|
|
227
|
+
return firstChild.selectEnd();
|
|
228
|
+
}
|
|
229
|
+
return super.select(_anchorOffset, _focusOffset);
|
|
230
|
+
}
|
|
231
|
+
isShadowRoot() {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function $convertInstanceElement(element: HTMLElement): DOMConversionOutput {
|
|
237
|
+
const node = $createInstanceNode();
|
|
238
|
+
if (element.style) {
|
|
239
|
+
node.setFormat(element.style.textAlign as ElementFormatType);
|
|
240
|
+
setNodeIndentFromDOM(element, node);
|
|
241
|
+
}
|
|
242
|
+
return {node};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function $createInstanceNode(instance?: Instance): InstanceNode {
|
|
246
|
+
return $applyNodeReplacement(new InstanceNode(instance));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function $createBaseInstanceNode(instance?: Instance): InstanceNode {
|
|
250
|
+
return $applyNodeReplacement(new InstanceNode(instance, true));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function $isInstanceNode(
|
|
254
|
+
node: LexicalNode | null | undefined,
|
|
255
|
+
): node is InstanceNode {
|
|
256
|
+
return node instanceof InstanceNode;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function $remove(node: LexicalNode) {
|
|
260
|
+
const parent = node.getParent();
|
|
261
|
+
if ($isInstanceNode(parent)) {
|
|
262
|
+
const paragraph = parent.getParagraphChildren();
|
|
263
|
+
const removedSize = paragraph.length - 1;
|
|
264
|
+
if (removedSize < InstanceNode.DEFAULT_PARAGRAPHS) {
|
|
265
|
+
if ($isInstanceParagraphNode(node.getPreviousSibling())) {
|
|
266
|
+
node.selectPrevious();
|
|
267
|
+
} else {
|
|
268
|
+
const allEmpty = $checkAllParagraphsEmpty(parent);
|
|
269
|
+
if (allEmpty) {
|
|
270
|
+
const isRemove = parent.collapseAtStart();
|
|
271
|
+
if (!isRemove) {
|
|
272
|
+
node.selectEnd();
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
node.selectEnd();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** 删除某个节点时 需要检测是否需要固定段落 */
|
|
285
|
+
export function $removedFixedParagraph(
|
|
286
|
+
node: ElementNode,
|
|
287
|
+
remove: () => void,
|
|
288
|
+
isKeepPgh: boolean = false,
|
|
289
|
+
) {
|
|
290
|
+
if ($remove(node)) {
|
|
291
|
+
if (isKeepPgh) {
|
|
292
|
+
$keepParagraph(node, remove);
|
|
293
|
+
} else {
|
|
294
|
+
remove();
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
$replaceWithParagraph(node);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** 删除某个节点时 保留当前段落 */
|
|
302
|
+
export function $keepParagraph(node: ElementNode, remove: () => void) {
|
|
303
|
+
const parent = node.getParent();
|
|
304
|
+
if ($isInstanceNode(parent)) {
|
|
305
|
+
$replaceWithParagraph(node).selectEnd();
|
|
306
|
+
} else {
|
|
307
|
+
remove();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** 替换为段落 */
|
|
312
|
+
function $replaceWithParagraph(node: ElementNode) {
|
|
313
|
+
const newElement = $createInstanceParagraphNode();
|
|
314
|
+
const children = node.getChildren();
|
|
315
|
+
children.forEach((child) => newElement.append(child));
|
|
316
|
+
node.replace(newElement);
|
|
317
|
+
return newElement;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** 检查所有段落是否为空 */
|
|
321
|
+
export function $checkAllParagraphsEmpty(parent: InstanceNode) {
|
|
322
|
+
return parent.getParagraphChildren().every((paragraph) => {
|
|
323
|
+
return paragraph.getTextContent().trim() === '';
|
|
324
|
+
});
|
|
325
|
+
}
|