Skip to content

Commit 5a443ca

Browse files
committed
feat: perfect unWrapNodes
1 parent 8097086 commit 5a443ca

File tree

9 files changed

+96
-93
lines changed

9 files changed

+96
-93
lines changed

packages/plugin/src/highlight-block/index.tsx

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,7 @@ import { EDITOR_ELEMENT_TYPE, KEY_EVENT } from "doc-editor-core";
66
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
77
import { isObject } from "doc-editor-utils";
88
import { getBlockNode } from "doc-editor-utils";
9-
import {
10-
isCollapsed,
11-
isFocusLineStart,
12-
isMatchedAttributeNode,
13-
isMatchedEvent,
14-
isWrappedEdgeNode,
15-
isWrappedNode,
16-
} from "doc-editor-utils";
9+
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
1710
import { setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
1811
import { KEYBOARD } from "doc-editor-utils";
1912

@@ -23,21 +16,21 @@ import { COLOR_MAP, HIGHLIGHT_BLOCK_ITEM_KEY, HIGHLIGHT_BLOCK_KEY } from "./type
2316
export const HighlightBlockPlugin = (editor: EditorSuite, readonly: boolean): Plugin => {
2417
const quoteCommand: CommandFn = (editor, key, data) => {
2518
if (isObject(data) && data.path) {
26-
if (!isMatchedAttributeNode(editor, HIGHLIGHT_BLOCK_KEY, null, data.path)) {
27-
if (!isWrappedNode(editor)) {
28-
setWrapNodes(
29-
editor,
30-
{
31-
[HIGHLIGHT_BLOCK_KEY]: {
32-
border: COLOR_MAP[0].border,
33-
background: COLOR_MAP[0].background,
34-
},
19+
if (!isMatchWrapNode(editor, HIGHLIGHT_BLOCK_KEY, HIGHLIGHT_BLOCK_ITEM_KEY, data.path)) {
20+
setWrapNodes(
21+
editor,
22+
{
23+
[HIGHLIGHT_BLOCK_KEY]: {
24+
border: COLOR_MAP[0].border,
25+
background: COLOR_MAP[0].background,
3526
},
36-
{ [HIGHLIGHT_BLOCK_ITEM_KEY]: true }
37-
);
38-
}
27+
},
28+
{ [HIGHLIGHT_BLOCK_ITEM_KEY]: true },
29+
{ at: data.path }
30+
);
3931
} else {
4032
setUnWrapNodes(editor, {
33+
at: data.path,
4134
wrapKey: HIGHLIGHT_BLOCK_KEY,
4235
itemKey: HIGHLIGHT_BLOCK_ITEM_KEY,
4336
});

packages/plugin/src/ordered-list/index.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,7 @@ import { Transforms } from "doc-editor-delta";
88
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
99
import { isObject } from "doc-editor-utils";
1010
import { existKey, getBlockNode } from "doc-editor-utils";
11-
import {
12-
isCollapsed,
13-
isFocusLineStart,
14-
isMatchedAttributeNode,
15-
isMatchedEvent,
16-
isWrappedEdgeNode,
17-
} from "doc-editor-utils";
11+
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
1812
import { setBlockNode, setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
1913
import { KEYBOARD } from "doc-editor-utils";
2014

@@ -23,14 +17,16 @@ import { calcNextOrderListLevels, calcOrderListLevels } from "./utils/serial";
2317

2418
const orderListCommand: CommandFn = (editor, key, data) => {
2519
if (isObject(data) && data.path) {
26-
if (!isMatchedAttributeNode(editor, ORDERED_LIST_KEY, true, data.path)) {
20+
if (!isMatchWrapNode(editor, ORDERED_LIST_KEY, ORDERED_LIST_ITEM_KEY, data.path)) {
2721
setWrapNodes(
2822
editor,
2923
{ [ORDERED_LIST_KEY]: true },
30-
{ [ORDERED_LIST_ITEM_KEY]: { start: 1, level: 1 } }
24+
{ [ORDERED_LIST_ITEM_KEY]: { start: 1, level: 1 } },
25+
{ at: data.path }
3126
);
3227
} else {
3328
setUnWrapNodes(editor, {
29+
at: data.path,
3430
wrapKey: ORDERED_LIST_KEY,
3531
itemKey: ORDERED_LIST_ITEM_KEY,
3632
});
@@ -83,6 +79,8 @@ export const OrderedListPlugin = (editor: Editor): Plugin => {
8379
return KEY_EVENT.STOP;
8480
}
8581

82+
// 相当于匹配`Enter`和`Backspace`的情况下
83+
// 实际上需要严格匹配
8684
if (isFocusLineStart(editor, itemMatch.path)) {
8785
if (level > 1) {
8886
setBlockNode(

packages/plugin/src/quote-block/index.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,24 @@ import { EDITOR_ELEMENT_TYPE, KEY_EVENT } from "doc-editor-core";
66
import type { Editor } from "doc-editor-delta";
77
import { isMatchWrapNode, isObject } from "doc-editor-utils";
88
import { getBlockNode } from "doc-editor-utils";
9-
import {
10-
isCollapsed,
11-
isFocusLineStart,
12-
isMatchedAttributeNode,
13-
isMatchedEvent,
14-
isWrappedEdgeNode,
15-
isWrappedNode,
16-
} from "doc-editor-utils";
9+
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
1710
import { setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
1811
import { KEYBOARD } from "doc-editor-utils";
1912

2013
import { QUOTE_BLOCK_ITEM_KEY, QUOTE_BLOCK_KEY } from "./types";
2114

2215
const quoteCommand: CommandFn = (editor, key, data) => {
2316
if (isObject(data) && data.path) {
24-
if (!isMatchedAttributeNode(editor, QUOTE_BLOCK_KEY, true, data.path)) {
25-
if (!isWrappedNode(editor)) {
26-
setWrapNodes(editor, { [QUOTE_BLOCK_KEY]: true }, { [QUOTE_BLOCK_ITEM_KEY]: true });
27-
}
17+
if (!isMatchWrapNode(editor, QUOTE_BLOCK_KEY, QUOTE_BLOCK_ITEM_KEY, data.path)) {
18+
setWrapNodes(
19+
editor,
20+
{ [QUOTE_BLOCK_KEY]: true },
21+
{ [QUOTE_BLOCK_ITEM_KEY]: true },
22+
{ at: data.path }
23+
);
2824
} else {
2925
setUnWrapNodes(editor, {
26+
at: data.path,
3027
wrapKey: QUOTE_BLOCK_KEY,
3128
itemKey: QUOTE_BLOCK_ITEM_KEY,
3229
});

packages/plugin/src/unordered-list/index.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,24 @@ import type { Editor } from "doc-editor-delta";
77
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
88
import { isObject } from "doc-editor-utils";
99
import { existKey, getBlockNode } from "doc-editor-utils";
10-
import {
11-
isCollapsed,
12-
isFocusLineStart,
13-
isMatchedAttributeNode,
14-
isMatchedEvent,
15-
isWrappedEdgeNode,
16-
} from "doc-editor-utils";
10+
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
1711
import { setBlockNode, setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
1812
import { KEYBOARD } from "doc-editor-utils";
1913

2014
import { UNORDERED_LIST_ITEM_KEY, UNORDERED_LIST_KEY } from "./types";
2115

2216
const orderListCommand: CommandFn = (editor, key, data) => {
2317
if (isObject(data) && data.path) {
24-
if (!isMatchedAttributeNode(editor, UNORDERED_LIST_KEY, true, data.path)) {
18+
if (!isMatchWrapNode(editor, UNORDERED_LIST_KEY, UNORDERED_LIST_ITEM_KEY, data.path)) {
2519
setWrapNodes(
2620
editor,
2721
{ [UNORDERED_LIST_KEY]: true },
28-
{ [UNORDERED_LIST_ITEM_KEY]: { level: 1 } }
22+
{ [UNORDERED_LIST_ITEM_KEY]: { level: 1 } },
23+
{ at: data.path }
2924
);
3025
} else {
3126
setUnWrapNodes(editor, {
27+
at: data.path,
3228
wrapKey: UNORDERED_LIST_KEY,
3329
itemKey: UNORDERED_LIST_ITEM_KEY,
3430
});

packages/utils/src/get.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,6 @@ export const getNextBlockNode = (
4444
return { block, path };
4545
};
4646

47-
export const getBlockAttributes = (
48-
node?: BlockElement,
49-
emit?: string[]
50-
): Record<string, unknown> => {
51-
if (!node) return {};
52-
const emits: string[] = emit ? emit : [];
53-
emits.push("children");
54-
const result: Record<string, unknown> = {};
55-
Object.keys(node)
56-
.filter(item => emits.indexOf(item) === -1)
57-
.forEach(key => (result[key] = node[key]));
58-
return result;
59-
};
60-
6147
export const getOmitAttributes = (
6248
keys: string[],
6349
exclude: string[] = []

packages/utils/src/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export { DEFAULT_PRIORITY, EVENT_ENUM, KEYBOARD } from "./constant";
44
export { omit, pick } from "./filter";
55
export {
66
findNodePath,
7-
getBlockAttributes,
87
getBlockNode,
98
getLineIndex,
109
getNextBlockNode,
@@ -23,7 +22,15 @@ export {
2322
isWrappedEdgeNode,
2423
isWrappedNode,
2524
} from "./is";
26-
export { existKey, isBaseElement, isBlock, isText } from "./ref";
25+
export {
26+
existKey,
27+
getAboveNode,
28+
getBlockAttributes,
29+
getParentNode,
30+
isBaseElement,
31+
isBlock,
32+
isText,
33+
} from "./ref";
2734
export {
2835
setBlockNode,
2936
setTextNode,

packages/utils/src/is.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Editor, Point, Range } from "doc-editor-delta";
1010
import { isEmptyValue, isObject } from "laser-utils";
1111

1212
import { getBlockNode } from "./get";
13-
import { isBlock, isText } from "./ref";
13+
import { getParentNode, isBlock, isText } from "./ref";
1414

1515
export const isWrappedNode = (editor: Editor) => {
1616
const match = getBlockNode(editor);
@@ -129,30 +129,23 @@ export const isMatchWrapNode = (
129129
const location = at || editor.selection;
130130
if (!location) return false;
131131
const current = Editor.node(editor, location);
132+
const currentNode = current && current[0];
132133
// https://github.com/ianstormtaylor/slate/blob/25be3b/packages/slate/src/interfaces/editor.ts#L1062
133-
const parent = Editor.parent(editor, location);
134-
if (current && parent) {
135-
const [node] = current;
136-
const [parentNode] = parent;
137-
if (
138-
isBlock(editor, node) &&
139-
isBlock(editor, parentNode) &&
140-
node[pairKey] &&
141-
parentNode[wrapKey]
142-
) {
134+
const parent = getParentNode(editor, location);
135+
const parentNode = parent && parent[0];
136+
// 如果当前节点即块元素 检查当前块和父级块匹配关系
137+
if (isBlock(editor, currentNode) && isBlock(editor, parentNode)) {
138+
if (currentNode[pairKey] && parentNode[wrapKey]) {
143139
return true;
144140
}
141+
// 在这种情况下应该是只检查
142+
return false;
145143
}
146-
const ancestor = parent && Editor.parent(editor, parent[1]);
147-
if (parent && ancestor) {
148-
const [node] = parent;
149-
const [parentNode] = ancestor;
150-
if (
151-
isBlock(editor, node) &&
152-
isBlock(editor, parentNode) &&
153-
node[pairKey] &&
154-
parentNode[wrapKey]
155-
) {
144+
const ancestor = parent && getParentNode(editor, parent[1]);
145+
const ancestorNode = ancestor && ancestor[0];
146+
// 检查父级块和祖先块匹配关系
147+
if (isBlock(editor, parentNode) && isBlock(editor, ancestorNode)) {
148+
if (parentNode[pairKey] && ancestorNode[wrapKey]) {
156149
return true;
157150
}
158151
}

packages/utils/src/ref.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { BaseNode, BlockElement, Location, Node, TextElement } from "doc-editor-delta";
22
import { Editor, Element, Path, Text } from "doc-editor-delta";
3+
import { isArray } from "laser-utils";
34

45
// 此文件是为了避免循环引用
56

@@ -9,8 +10,10 @@ export const isBaseElement = (block: Node): block is BaseNode => {
910

1011
export const existKey = (node: Node, key: string) => isBaseElement(node) && !!node[key];
1112

12-
export const isBlock = (editor: Editor, node: Node): node is BlockElement =>
13-
Editor.isBlock(editor, node);
13+
export const isBlock = (editor: Editor, node: Node | null): node is BlockElement => {
14+
if (!node) return false;
15+
return Editor.isBlock(editor, node);
16+
};
1417

1518
export const isText = (node: Node): node is TextElement => Text.isText(node);
1619

@@ -41,3 +44,22 @@ export const getAboveNode = (
4144
}
4245
}
4346
};
47+
48+
export const getParentNode = (editor: Editor, at: Location) => {
49+
// fix: 如果是顶层元素则会直接抛异常
50+
if (isArray(at) && !at.length) return null;
51+
return Editor.parent(editor, at);
52+
};
53+
export const getBlockAttributes = (
54+
node?: BlockElement,
55+
emit?: string[]
56+
): Record<string, unknown> => {
57+
if (!node) return {};
58+
const emits: string[] = emit ? emit : [];
59+
emits.push("children");
60+
const result: Record<string, unknown> = {};
61+
Object.keys(node)
62+
.filter(item => emits.indexOf(item) === -1)
63+
.forEach(key => (result[key] = node[key]));
64+
return result;
65+
};

packages/utils/src/set.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { BlockElement, Location, TextElement } from "doc-editor-delta";
2-
import { Editor, Transforms } from "doc-editor-delta";
2+
import { Editor, Path, Transforms } from "doc-editor-delta";
33

4-
import { existKey, getAboveNode, isBlock, isText } from "./ref";
4+
import { existKey, getAboveNode, getBlockAttributes, isBlock, isText } from "./ref";
55

66
export const setBlockNode = (
77
editor: Editor,
@@ -106,11 +106,22 @@ export const setUnWrapNodes = (
106106
itemKey: string;
107107
}
108108
) => {
109+
const { at, wrapKey, itemKey } = options;
110+
const wrap = getAboveNode(editor, { match: n => existKey(n, wrapKey), at });
111+
const pair = getAboveNode(editor, { match: n => existKey(n, itemKey), at });
112+
if (!wrap || !pair) return void 0;
113+
const wrapAttrs = getBlockAttributes(wrap.node, [wrapKey]);
109114
Editor.withoutNormalizing(editor, () => {
110-
setUnBlockNode(editor, [options.itemKey], { key: options.itemKey });
115+
Transforms.setNodes(editor, wrapAttrs, { at: pair.path });
116+
Transforms.unsetNodes(editor, [itemKey], { at: pair.path });
111117
Transforms.unwrapNodes(editor, {
112-
match: n => existKey(n, options.wrapKey),
118+
match: (_, p) => Path.equals(p, wrap.path),
113119
split: true,
120+
// 这里需要注意`at`会变成`range`
121+
// 如果此处传入`wrap.path`会导致所有的子节点都会被`unwrap`
122+
// 即使`match`的结果是一致的 但是变换时会有`rangeRef`来判断相交范围
123+
// https://github.com/ianstormtaylor/slate/blob/25be3b/packages/slate/src/transforms/node.ts#L873
124+
at: pair.path,
114125
});
115126
});
116127
};

0 commit comments

Comments
 (0)