Skip to content

Commit

Permalink
feat: perfect unWrapNodes
Browse files Browse the repository at this point in the history
  • Loading branch information
WindRunnerMax committed May 26, 2024
1 parent 8097086 commit 5a443ca
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 93 deletions.
33 changes: 13 additions & 20 deletions packages/plugin/src/highlight-block/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@ import { EDITOR_ELEMENT_TYPE, KEY_EVENT } from "doc-editor-core";
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
import { isObject } from "doc-editor-utils";
import { getBlockNode } from "doc-editor-utils";
import {
isCollapsed,
isFocusLineStart,
isMatchedAttributeNode,
isMatchedEvent,
isWrappedEdgeNode,
isWrappedNode,
} from "doc-editor-utils";
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
import { setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
import { KEYBOARD } from "doc-editor-utils";

Expand All @@ -23,21 +16,21 @@ import { COLOR_MAP, HIGHLIGHT_BLOCK_ITEM_KEY, HIGHLIGHT_BLOCK_KEY } from "./type
export const HighlightBlockPlugin = (editor: EditorSuite, readonly: boolean): Plugin => {
const quoteCommand: CommandFn = (editor, key, data) => {
if (isObject(data) && data.path) {
if (!isMatchedAttributeNode(editor, HIGHLIGHT_BLOCK_KEY, null, data.path)) {
if (!isWrappedNode(editor)) {
setWrapNodes(
editor,
{
[HIGHLIGHT_BLOCK_KEY]: {
border: COLOR_MAP[0].border,
background: COLOR_MAP[0].background,
},
if (!isMatchWrapNode(editor, HIGHLIGHT_BLOCK_KEY, HIGHLIGHT_BLOCK_ITEM_KEY, data.path)) {
setWrapNodes(
editor,
{
[HIGHLIGHT_BLOCK_KEY]: {
border: COLOR_MAP[0].border,
background: COLOR_MAP[0].background,
},
{ [HIGHLIGHT_BLOCK_ITEM_KEY]: true }
);
}
},
{ [HIGHLIGHT_BLOCK_ITEM_KEY]: true },
{ at: data.path }
);
} else {
setUnWrapNodes(editor, {
at: data.path,
wrapKey: HIGHLIGHT_BLOCK_KEY,
itemKey: HIGHLIGHT_BLOCK_ITEM_KEY,
});
Expand Down
16 changes: 7 additions & 9 deletions packages/plugin/src/ordered-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ import { Transforms } from "doc-editor-delta";
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
import { isObject } from "doc-editor-utils";
import { existKey, getBlockNode } from "doc-editor-utils";
import {
isCollapsed,
isFocusLineStart,
isMatchedAttributeNode,
isMatchedEvent,
isWrappedEdgeNode,
} from "doc-editor-utils";
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
import { setBlockNode, setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
import { KEYBOARD } from "doc-editor-utils";

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

const orderListCommand: CommandFn = (editor, key, data) => {
if (isObject(data) && data.path) {
if (!isMatchedAttributeNode(editor, ORDERED_LIST_KEY, true, data.path)) {
if (!isMatchWrapNode(editor, ORDERED_LIST_KEY, ORDERED_LIST_ITEM_KEY, data.path)) {
setWrapNodes(
editor,
{ [ORDERED_LIST_KEY]: true },
{ [ORDERED_LIST_ITEM_KEY]: { start: 1, level: 1 } }
{ [ORDERED_LIST_ITEM_KEY]: { start: 1, level: 1 } },
{ at: data.path }
);
} else {
setUnWrapNodes(editor, {
at: data.path,
wrapKey: ORDERED_LIST_KEY,
itemKey: ORDERED_LIST_ITEM_KEY,
});
Expand Down Expand Up @@ -83,6 +79,8 @@ export const OrderedListPlugin = (editor: Editor): Plugin => {
return KEY_EVENT.STOP;
}

// 相当于匹配`Enter`和`Backspace`的情况下
// 实际上需要严格匹配
if (isFocusLineStart(editor, itemMatch.path)) {
if (level > 1) {
setBlockNode(
Expand Down
21 changes: 9 additions & 12 deletions packages/plugin/src/quote-block/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,24 @@ import { EDITOR_ELEMENT_TYPE, KEY_EVENT } from "doc-editor-core";
import type { Editor } from "doc-editor-delta";
import { isMatchWrapNode, isObject } from "doc-editor-utils";
import { getBlockNode } from "doc-editor-utils";
import {
isCollapsed,
isFocusLineStart,
isMatchedAttributeNode,
isMatchedEvent,
isWrappedEdgeNode,
isWrappedNode,
} from "doc-editor-utils";
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
import { setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
import { KEYBOARD } from "doc-editor-utils";

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

const quoteCommand: CommandFn = (editor, key, data) => {
if (isObject(data) && data.path) {
if (!isMatchedAttributeNode(editor, QUOTE_BLOCK_KEY, true, data.path)) {
if (!isWrappedNode(editor)) {
setWrapNodes(editor, { [QUOTE_BLOCK_KEY]: true }, { [QUOTE_BLOCK_ITEM_KEY]: true });
}
if (!isMatchWrapNode(editor, QUOTE_BLOCK_KEY, QUOTE_BLOCK_ITEM_KEY, data.path)) {
setWrapNodes(
editor,
{ [QUOTE_BLOCK_KEY]: true },
{ [QUOTE_BLOCK_ITEM_KEY]: true },
{ at: data.path }
);
} else {
setUnWrapNodes(editor, {
at: data.path,
wrapKey: QUOTE_BLOCK_KEY,
itemKey: QUOTE_BLOCK_ITEM_KEY,
});
Expand Down
14 changes: 5 additions & 9 deletions packages/plugin/src/unordered-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,24 @@ import type { Editor } from "doc-editor-delta";
import { assertValue, isMatchWrapNode } from "doc-editor-utils";
import { isObject } from "doc-editor-utils";
import { existKey, getBlockNode } from "doc-editor-utils";
import {
isCollapsed,
isFocusLineStart,
isMatchedAttributeNode,
isMatchedEvent,
isWrappedEdgeNode,
} from "doc-editor-utils";
import { isCollapsed, isFocusLineStart, isMatchedEvent, isWrappedEdgeNode } from "doc-editor-utils";
import { setBlockNode, setUnWrapNodes, setWrapNodes } from "doc-editor-utils";
import { KEYBOARD } from "doc-editor-utils";

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

const orderListCommand: CommandFn = (editor, key, data) => {
if (isObject(data) && data.path) {
if (!isMatchedAttributeNode(editor, UNORDERED_LIST_KEY, true, data.path)) {
if (!isMatchWrapNode(editor, UNORDERED_LIST_KEY, UNORDERED_LIST_ITEM_KEY, data.path)) {
setWrapNodes(
editor,
{ [UNORDERED_LIST_KEY]: true },
{ [UNORDERED_LIST_ITEM_KEY]: { level: 1 } }
{ [UNORDERED_LIST_ITEM_KEY]: { level: 1 } },
{ at: data.path }
);
} else {
setUnWrapNodes(editor, {
at: data.path,
wrapKey: UNORDERED_LIST_KEY,
itemKey: UNORDERED_LIST_ITEM_KEY,
});
Expand Down
14 changes: 0 additions & 14 deletions packages/utils/src/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,6 @@ export const getNextBlockNode = (
return { block, path };
};

export const getBlockAttributes = (
node?: BlockElement,
emit?: string[]
): Record<string, unknown> => {
if (!node) return {};
const emits: string[] = emit ? emit : [];
emits.push("children");
const result: Record<string, unknown> = {};
Object.keys(node)
.filter(item => emits.indexOf(item) === -1)
.forEach(key => (result[key] = node[key]));
return result;
};

export const getOmitAttributes = (
keys: string[],
exclude: string[] = []
Expand Down
11 changes: 9 additions & 2 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export { DEFAULT_PRIORITY, EVENT_ENUM, KEYBOARD } from "./constant";
export { omit, pick } from "./filter";
export {
findNodePath,
getBlockAttributes,
getBlockNode,
getLineIndex,
getNextBlockNode,
Expand All @@ -23,7 +22,15 @@ export {
isWrappedEdgeNode,
isWrappedNode,
} from "./is";
export { existKey, isBaseElement, isBlock, isText } from "./ref";
export {
existKey,
getAboveNode,
getBlockAttributes,
getParentNode,
isBaseElement,
isBlock,
isText,
} from "./ref";
export {
setBlockNode,
setTextNode,
Expand Down
35 changes: 14 additions & 21 deletions packages/utils/src/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Editor, Point, Range } from "doc-editor-delta";
import { isEmptyValue, isObject } from "laser-utils";

import { getBlockNode } from "./get";
import { isBlock, isText } from "./ref";
import { getParentNode, isBlock, isText } from "./ref";

export const isWrappedNode = (editor: Editor) => {
const match = getBlockNode(editor);
Expand Down Expand Up @@ -129,30 +129,23 @@ export const isMatchWrapNode = (
const location = at || editor.selection;
if (!location) return false;
const current = Editor.node(editor, location);
const currentNode = current && current[0];
// https://github.com/ianstormtaylor/slate/blob/25be3b/packages/slate/src/interfaces/editor.ts#L1062
const parent = Editor.parent(editor, location);
if (current && parent) {
const [node] = current;
const [parentNode] = parent;
if (
isBlock(editor, node) &&
isBlock(editor, parentNode) &&
node[pairKey] &&
parentNode[wrapKey]
) {
const parent = getParentNode(editor, location);
const parentNode = parent && parent[0];
// 如果当前节点即块元素 检查当前块和父级块匹配关系
if (isBlock(editor, currentNode) && isBlock(editor, parentNode)) {
if (currentNode[pairKey] && parentNode[wrapKey]) {
return true;
}
// 在这种情况下应该是只检查
return false;
}
const ancestor = parent && Editor.parent(editor, parent[1]);
if (parent && ancestor) {
const [node] = parent;
const [parentNode] = ancestor;
if (
isBlock(editor, node) &&
isBlock(editor, parentNode) &&
node[pairKey] &&
parentNode[wrapKey]
) {
const ancestor = parent && getParentNode(editor, parent[1]);
const ancestorNode = ancestor && ancestor[0];
// 检查父级块和祖先块匹配关系
if (isBlock(editor, parentNode) && isBlock(editor, ancestorNode)) {
if (parentNode[pairKey] && ancestorNode[wrapKey]) {
return true;
}
}
Expand Down
26 changes: 24 additions & 2 deletions packages/utils/src/ref.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { BaseNode, BlockElement, Location, Node, TextElement } from "doc-editor-delta";
import { Editor, Element, Path, Text } from "doc-editor-delta";
import { isArray } from "laser-utils";

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

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

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

export const isBlock = (editor: Editor, node: Node): node is BlockElement =>
Editor.isBlock(editor, node);
export const isBlock = (editor: Editor, node: Node | null): node is BlockElement => {
if (!node) return false;
return Editor.isBlock(editor, node);
};

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

Expand Down Expand Up @@ -41,3 +44,22 @@ export const getAboveNode = (
}
}
};

export const getParentNode = (editor: Editor, at: Location) => {
// fix: 如果是顶层元素则会直接抛异常
if (isArray(at) && !at.length) return null;
return Editor.parent(editor, at);
};
export const getBlockAttributes = (
node?: BlockElement,
emit?: string[]
): Record<string, unknown> => {
if (!node) return {};
const emits: string[] = emit ? emit : [];
emits.push("children");
const result: Record<string, unknown> = {};
Object.keys(node)
.filter(item => emits.indexOf(item) === -1)
.forEach(key => (result[key] = node[key]));
return result;
};
19 changes: 15 additions & 4 deletions packages/utils/src/set.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { BlockElement, Location, TextElement } from "doc-editor-delta";
import { Editor, Transforms } from "doc-editor-delta";
import { Editor, Path, Transforms } from "doc-editor-delta";

import { existKey, getAboveNode, isBlock, isText } from "./ref";
import { existKey, getAboveNode, getBlockAttributes, isBlock, isText } from "./ref";

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

0 comments on commit 5a443ca

Please sign in to comment.