@block-kit/react 1.0.0

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.
Files changed (93) hide show
  1. package/dist/es/hooks/use-editor.d.ts +21 -0
  2. package/dist/es/hooks/use-editor.js +26 -0
  3. package/dist/es/hooks/use-readonly.d.ts +5 -0
  4. package/dist/es/hooks/use-readonly.js +6 -0
  5. package/dist/es/index.d.ts +18 -0
  6. package/dist/es/index.js +17 -0
  7. package/dist/es/model/block.d.ts +7 -0
  8. package/dist/es/model/block.js +142 -0
  9. package/dist/es/model/eol.d.ts +6 -0
  10. package/dist/es/model/eol.js +18 -0
  11. package/dist/es/model/leaf.d.ts +7 -0
  12. package/dist/es/model/leaf.js +38 -0
  13. package/dist/es/model/line.d.ts +7 -0
  14. package/dist/es/model/line.js +152 -0
  15. package/dist/es/plugin/index.d.ts +25 -0
  16. package/dist/es/plugin/index.js +3 -0
  17. package/dist/es/plugin/modules/priority.d.ts +13 -0
  18. package/dist/es/plugin/modules/priority.js +26 -0
  19. package/dist/es/plugin/modules/wrap.d.ts +26 -0
  20. package/dist/es/plugin/modules/wrap.js +72 -0
  21. package/dist/es/plugin/types/index.d.ts +35 -0
  22. package/dist/es/plugin/types/index.js +7 -0
  23. package/dist/es/preset/block-kit.d.ts +6 -0
  24. package/dist/es/preset/block-kit.js +17 -0
  25. package/dist/es/preset/editable.d.ts +16 -0
  26. package/dist/es/preset/editable.js +42 -0
  27. package/dist/es/preset/embed.d.ts +14 -0
  28. package/dist/es/preset/embed.js +23 -0
  29. package/dist/es/preset/isolate.d.ts +12 -0
  30. package/dist/es/preset/isolate.js +10 -0
  31. package/dist/es/preset/text.d.ts +9 -0
  32. package/dist/es/preset/text.js +34 -0
  33. package/dist/es/preset/void.d.ts +14 -0
  34. package/dist/es/preset/void.js +23 -0
  35. package/dist/es/preset/zero.d.ts +23 -0
  36. package/dist/es/preset/zero.js +20 -0
  37. package/dist/es/utils/constant.d.ts +2 -0
  38. package/dist/es/utils/constant.js +6 -0
  39. package/dist/es/utils/event.d.ts +2 -0
  40. package/dist/es/utils/event.js +7 -0
  41. package/dist/es/utils/is.d.ts +2 -0
  42. package/dist/es/utils/is.js +12 -0
  43. package/dist/es/utils/weak-map.d.ts +17 -0
  44. package/dist/es/utils/weak-map.js +15 -0
  45. package/dist/es/utils/wrapper.d.ts +7 -0
  46. package/dist/es/utils/wrapper.js +25 -0
  47. package/dist/lib/hooks/use-editor.d.ts +21 -0
  48. package/dist/lib/hooks/use-editor.js +53 -0
  49. package/dist/lib/hooks/use-readonly.d.ts +5 -0
  50. package/dist/lib/hooks/use-readonly.js +33 -0
  51. package/dist/lib/index.d.ts +18 -0
  52. package/dist/lib/index.js +40 -0
  53. package/dist/lib/model/block.d.ts +7 -0
  54. package/dist/lib/model/block.js +168 -0
  55. package/dist/lib/model/eol.d.ts +6 -0
  56. package/dist/lib/model/eol.js +24 -0
  57. package/dist/lib/model/leaf.d.ts +7 -0
  58. package/dist/lib/model/leaf.js +64 -0
  59. package/dist/lib/model/line.d.ts +7 -0
  60. package/dist/lib/model/line.js +178 -0
  61. package/dist/lib/plugin/index.d.ts +25 -0
  62. package/dist/lib/plugin/index.js +7 -0
  63. package/dist/lib/plugin/modules/priority.d.ts +13 -0
  64. package/dist/lib/plugin/modules/priority.js +31 -0
  65. package/dist/lib/plugin/modules/wrap.d.ts +26 -0
  66. package/dist/lib/plugin/modules/wrap.js +78 -0
  67. package/dist/lib/plugin/types/index.d.ts +35 -0
  68. package/dist/lib/plugin/types/index.js +10 -0
  69. package/dist/lib/preset/block-kit.d.ts +6 -0
  70. package/dist/lib/preset/block-kit.js +21 -0
  71. package/dist/lib/preset/editable.d.ts +16 -0
  72. package/dist/lib/preset/editable.js +46 -0
  73. package/dist/lib/preset/embed.d.ts +14 -0
  74. package/dist/lib/preset/embed.js +30 -0
  75. package/dist/lib/preset/isolate.d.ts +12 -0
  76. package/dist/lib/preset/isolate.js +14 -0
  77. package/dist/lib/preset/text.d.ts +9 -0
  78. package/dist/lib/preset/text.js +37 -0
  79. package/dist/lib/preset/void.d.ts +14 -0
  80. package/dist/lib/preset/void.js +30 -0
  81. package/dist/lib/preset/zero.d.ts +23 -0
  82. package/dist/lib/preset/zero.js +23 -0
  83. package/dist/lib/utils/constant.d.ts +2 -0
  84. package/dist/lib/utils/constant.js +9 -0
  85. package/dist/lib/utils/event.d.ts +2 -0
  86. package/dist/lib/utils/event.js +12 -0
  87. package/dist/lib/utils/is.d.ts +2 -0
  88. package/dist/lib/utils/is.js +16 -0
  89. package/dist/lib/utils/weak-map.d.ts +17 -0
  90. package/dist/lib/utils/weak-map.js +18 -0
  91. package/dist/lib/utils/wrapper.d.ts +7 -0
  92. package/dist/lib/utils/wrapper.js +29 -0
  93. package/package.json +38 -0
@@ -0,0 +1,21 @@
1
+ import type { Editor } from "@block-kit/core";
2
+ import React from "react";
3
+ export declare const BlockKitContext: React.Context<Editor | null>;
4
+ export declare const useEditorStatic: () => {
5
+ editor: Editor;
6
+ ref: import("@block-kit/core").Ref;
7
+ rect: import("@block-kit/core/dist/es/rect").Rect;
8
+ state: import("@block-kit/core").EditorState;
9
+ event: import("@block-kit/core").Event;
10
+ input: import("@block-kit/core").Input;
11
+ model: import("@block-kit/core").Model;
12
+ plugin: import("@block-kit/core").Plugin;
13
+ schema: import("@block-kit/core").Schema;
14
+ logger: import("@block-kit/core").Logger;
15
+ collect: import("@block-kit/core").Collect;
16
+ command: import("@block-kit/core").Command;
17
+ history: import("@block-kit/core").History;
18
+ perform: import("@block-kit/core").Perform;
19
+ clipboard: import("@block-kit/core").Clipboard;
20
+ selection: import("@block-kit/core").Selection;
21
+ };
@@ -0,0 +1,26 @@
1
+ import React, { createContext } from "react";
2
+ export const BlockKitContext = createContext(null);
3
+ export const useEditorStatic = () => {
4
+ const editor = React.useContext(BlockKitContext);
5
+ if (!editor) {
6
+ throw new Error("UseEditor must be used within a EditorContext");
7
+ }
8
+ return {
9
+ editor,
10
+ ref: editor.ref,
11
+ rect: editor.rect,
12
+ state: editor.state,
13
+ event: editor.event,
14
+ input: editor.input,
15
+ model: editor.model,
16
+ plugin: editor.plugin,
17
+ schema: editor.schema,
18
+ logger: editor.logger,
19
+ collect: editor.collect,
20
+ command: editor.command,
21
+ history: editor.history,
22
+ perform: editor.perform,
23
+ clipboard: editor.clipboard,
24
+ selection: editor.selection,
25
+ };
26
+ };
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ export declare const ReadonlyContext: React.Context<boolean>;
3
+ export declare const useReadonly: () => {
4
+ readonly: boolean;
5
+ };
@@ -0,0 +1,6 @@
1
+ import React, { createContext } from "react";
2
+ export const ReadonlyContext = createContext(false);
3
+ export const useReadonly = () => {
4
+ const readonly = React.useContext(ReadonlyContext);
5
+ return { readonly };
6
+ };
@@ -0,0 +1,18 @@
1
+ export { BlockKitContext, useEditorStatic } from "./hooks/use-editor";
2
+ export { ReadonlyContext, useReadonly } from "./hooks/use-readonly";
3
+ export { BlockModel } from "./model/block";
4
+ export { EOLModel } from "./model/eol";
5
+ export { LeafModel } from "./model/leaf";
6
+ export { LineModel } from "./model/line";
7
+ export { EditorPlugin } from "./plugin/index";
8
+ export { Priority } from "./plugin/modules/priority";
9
+ export { InjectWrapKeys } from "./plugin/modules/wrap";
10
+ export type { ReactLeafContext, ReactLineContext, ReactWrapLeafContext, ReactWrapLineContext, } from "./plugin/types";
11
+ export { BlockKit } from "./preset/block-kit";
12
+ export { Editable } from "./preset/editable";
13
+ export { Embed } from "./preset/embed";
14
+ export { Isolate } from "./preset/isolate";
15
+ export { Text } from "./preset/text";
16
+ export { Void } from "./preset/void";
17
+ export { ZeroSpace } from "./preset/zero";
18
+ export { preventNativeEvent, preventReactEvent } from "./utils/event";
@@ -0,0 +1,17 @@
1
+ export { BlockKitContext, useEditorStatic } from "./hooks/use-editor";
2
+ export { ReadonlyContext, useReadonly } from "./hooks/use-readonly";
3
+ export { BlockModel } from "./model/block";
4
+ export { EOLModel } from "./model/eol";
5
+ export { LeafModel } from "./model/leaf";
6
+ export { LineModel } from "./model/line";
7
+ export { EditorPlugin } from "./plugin/index";
8
+ export { Priority } from "./plugin/modules/priority";
9
+ export { InjectWrapKeys } from "./plugin/modules/wrap";
10
+ export { BlockKit } from "./preset/block-kit";
11
+ export { Editable } from "./preset/editable";
12
+ export { Embed } from "./preset/embed";
13
+ export { Isolate } from "./preset/isolate";
14
+ export { Text } from "./preset/text";
15
+ export { Void } from "./preset/void";
16
+ export { ZeroSpace } from "./preset/zero";
17
+ export { preventNativeEvent, preventReactEvent } from "./utils/event";
@@ -0,0 +1,7 @@
1
+ import type { BlockState, Editor } from "@block-kit/core";
2
+ import React from "react";
3
+ export declare const BlockModel: React.NamedExoticComponent<{
4
+ editor: Editor;
5
+ state: BlockState;
6
+ placeholder?: React.ReactNode;
7
+ }>;
@@ -0,0 +1,142 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { BLOCK_ID_KEY, BLOCK_KEY, EDITOR_EVENT, EDITOR_STATE, PLACEHOLDER_KEY, } from "@block-kit/core";
3
+ import { useMemoFn } from "@block-kit/utils/dist/es/hooks";
4
+ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
+ import { EDITOR_TO_WRAP_LINE_KEYS, EDITOR_TO_WRAP_LINE_PLUGINS } from "../plugin/modules/wrap";
6
+ import { isStrictEmptyLine } from "../utils/is";
7
+ import { JSX_TO_STATE } from "../utils/weak-map";
8
+ import { getWrapSymbol } from "../utils/wrapper";
9
+ import { LineModel } from "./line";
10
+ /**
11
+ * Block Model
12
+ * @param props
13
+ */
14
+ const BlockView = props => {
15
+ const { editor, state } = props;
16
+ const flushing = useRef(false);
17
+ const [lines, setLines] = useState(() => state.getLines());
18
+ /**
19
+ * 设置行 DOM 节点
20
+ */
21
+ const setModel = (ref) => {
22
+ if (ref) {
23
+ editor.model.setBlockModel(ref, state);
24
+ }
25
+ };
26
+ /**
27
+ * 数据同步变更, 异步批量绘制变更
28
+ */
29
+ const onContentChange = useMemoFn(() => {
30
+ // 举个例子: 同步等待刷新的队列 => ||||||||
31
+ // 进入更新行为后, 异步行为等待, 同步的队列由于 !flushing 全部被守卫
32
+ // 主线程执行完毕后, 异步队列开始执行, 此时拿到的是最新数据, 以此批量重新渲染
33
+ if (flushing.current)
34
+ return void 0;
35
+ flushing.current = true;
36
+ Promise.resolve().then(() => {
37
+ flushing.current = false;
38
+ setLines(state.getLines());
39
+ editor.state.set(EDITOR_STATE.PAINTING, true);
40
+ });
41
+ });
42
+ /**
43
+ * 监听内容变更事件, 更新当前块视图
44
+ */
45
+ useLayoutEffect(() => {
46
+ editor.event.on(EDITOR_EVENT.CONTENT_CHANGE, onContentChange);
47
+ return () => {
48
+ editor.event.off(EDITOR_EVENT.CONTENT_CHANGE, onContentChange);
49
+ };
50
+ }, [editor.event, onContentChange]);
51
+ /**
52
+ * 视图更新需要重新设置选区 无依赖数组
53
+ */
54
+ useLayoutEffect(() => {
55
+ const selection = editor.selection.get();
56
+ if (!editor.state.get(EDITOR_STATE.COMPOSING) &&
57
+ editor.state.get(EDITOR_STATE.FOCUS) &&
58
+ selection) {
59
+ // 更新浏览器选区
60
+ editor.logger.debug("UpdateDOMSelection");
61
+ editor.selection.updateDOMSelection(true);
62
+ }
63
+ });
64
+ /**
65
+ * 视图更新需要触发视图绘制完成事件 无依赖数组
66
+ * state -> parent -> node -> child ->|
67
+ * effect <- parent <- node <- child <-|
68
+ */
69
+ useEffect(() => {
70
+ editor.logger.debug("OnPaint");
71
+ editor.state.set(EDITOR_STATE.PAINTING, false);
72
+ Promise.resolve().then(() => {
73
+ editor.event.trigger(EDITOR_EVENT.PAINT, {});
74
+ });
75
+ });
76
+ /**
77
+ * 处理行节点
78
+ */
79
+ const elements = useMemo(() => {
80
+ return lines.map((line, index) => {
81
+ const node = (_jsx(LineModel, { editor: editor, lineState: line, index: index }, line.key));
82
+ JSX_TO_STATE.set(node, line);
83
+ return node;
84
+ });
85
+ }, [editor, lines]);
86
+ /**
87
+ * 将行包装组合 O(N)
88
+ */
89
+ const children = useMemo(() => {
90
+ const wrapped = [];
91
+ const keys = EDITOR_TO_WRAP_LINE_KEYS.get(editor);
92
+ const plugins = EDITOR_TO_WRAP_LINE_PLUGINS.get(editor);
93
+ if (!keys || !plugins)
94
+ return elements;
95
+ const len = elements.length;
96
+ for (let i = 0; i < len; ++i) {
97
+ const element = elements[i];
98
+ const symbol = getWrapSymbol(keys, element);
99
+ const line = JSX_TO_STATE.get(element);
100
+ if (!element || !line || !symbol) {
101
+ wrapped.push(element);
102
+ continue;
103
+ }
104
+ // 执行到此处说明需要包装相关节点(即使仅单个节点)
105
+ const nodes = [element];
106
+ for (let k = i + 1; k < len; ++k) {
107
+ const next = elements[k];
108
+ const nextSymbol = getWrapSymbol(keys, next);
109
+ if (!next || !nextSymbol || nextSymbol !== symbol) {
110
+ // 回退到上一个值, 以便下次循环时重新检查
111
+ i = k - 1;
112
+ break;
113
+ }
114
+ nodes.push(next);
115
+ i = k;
116
+ }
117
+ // 通过插件渲染包装节点
118
+ let wrapper = nodes;
119
+ const op = line.op;
120
+ for (const plugin of plugins) {
121
+ // 这里的状态以首个节点为准
122
+ const context = {
123
+ lineState: line,
124
+ children: wrapper,
125
+ };
126
+ if (plugin.match(line.op.attributes || {}, op) && plugin.wrapLine) {
127
+ wrapper = plugin.wrapLine(context);
128
+ }
129
+ }
130
+ const key = `${i - nodes.length + 1}-${i}`;
131
+ wrapped.push(_jsx(React.Fragment, { children: wrapper }, key));
132
+ }
133
+ return wrapped;
134
+ }, [editor, elements]);
135
+ return (_jsxs("div", { [BLOCK_KEY]: true, [BLOCK_ID_KEY]: state.key, ref: setModel, children: [props.placeholder && lines.length === 1 && isStrictEmptyLine(lines[0]) && (_jsx("div", { [PLACEHOLDER_KEY]: true, style: {
136
+ position: "absolute",
137
+ opacity: "0.3",
138
+ userSelect: "none",
139
+ pointerEvents: "none",
140
+ }, children: props.placeholder })), children] }));
141
+ };
142
+ export const BlockModel = React.memo(BlockView);
@@ -0,0 +1,6 @@
1
+ import type { Editor, LeafState } from "@block-kit/core";
2
+ import React from "react";
3
+ export declare const EOLModel: React.NamedExoticComponent<{
4
+ editor: Editor;
5
+ leafState: LeafState;
6
+ }>;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { LEAF_KEY } from "@block-kit/core";
3
+ import React from "react";
4
+ import { ZeroSpace } from "../preset/zero";
5
+ /**
6
+ * EOL Model
7
+ * @param props
8
+ */
9
+ const EOLView = props => {
10
+ const { editor, leafState } = props;
11
+ const setModel = (ref) => {
12
+ if (ref) {
13
+ editor.model.setLeafModel(ref, leafState);
14
+ }
15
+ };
16
+ return (_jsx("span", { [LEAF_KEY]: true, ref: setModel, children: _jsx(ZeroSpace, { enter: true }) }));
17
+ };
18
+ export const EOLModel = React.memo(EOLView);
@@ -0,0 +1,7 @@
1
+ import type { Editor, LeafState } from "@block-kit/core";
2
+ import React from "react";
3
+ export declare const LeafModel: React.NamedExoticComponent<{
4
+ editor: Editor;
5
+ index: number;
6
+ leafState: LeafState;
7
+ }>;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { LEAF_KEY, PLUGIN_TYPE } from "@block-kit/core";
3
+ import React, { useMemo } from "react";
4
+ import { Text } from "../preset/text";
5
+ import { LEAF_TO_TEXT as LT } from "../utils/weak-map";
6
+ /**
7
+ * Leaf Model
8
+ * @param props
9
+ */
10
+ const LeafView = props => {
11
+ const { editor, leafState } = props;
12
+ const setModel = (ref) => {
13
+ if (ref) {
14
+ editor.model.setLeafModel(ref, leafState);
15
+ }
16
+ };
17
+ const runtime = useMemo(() => {
18
+ const text = leafState.getText();
19
+ const context = {
20
+ op: leafState.op,
21
+ classList: [],
22
+ lineState: leafState.parent,
23
+ leafState: leafState,
24
+ attributes: leafState.op.attributes,
25
+ style: {},
26
+ children: _jsx(Text, { ref: el => LT.set(leafState, el), children: text }),
27
+ };
28
+ const plugins = editor.plugin.getPriorityPlugins(PLUGIN_TYPE.RENDER_LEAF);
29
+ for (const plugin of plugins) {
30
+ if (plugin.match(context.attributes || {}, context.op)) {
31
+ context.children = plugin.renderLeaf(context);
32
+ }
33
+ }
34
+ return context;
35
+ }, [editor, leafState]);
36
+ return (_jsx("span", { [LEAF_KEY]: true, ref: setModel, className: runtime.classList.join(" "), style: runtime.style, children: runtime.children }));
37
+ };
38
+ export const LeafModel = React.memo(LeafView);
@@ -0,0 +1,7 @@
1
+ import type { Editor, LineState } from "@block-kit/core";
2
+ import React from "react";
3
+ export declare const LineModel: React.NamedExoticComponent<{
4
+ editor: Editor;
5
+ index: number;
6
+ lineState: LineState;
7
+ }>;
@@ -0,0 +1,152 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { NODE_KEY, PLUGIN_TYPE } from "@block-kit/core";
3
+ import { EOL, EOL_OP } from "@block-kit/delta";
4
+ import { cs, isDOMText } from "@block-kit/utils";
5
+ import { useUpdateLayoutEffect } from "@block-kit/utils/dist/es/hooks";
6
+ import React, { useMemo } from "react";
7
+ import { EDITOR_TO_WRAP_LEAF_KEYS, EDITOR_TO_WRAP_LEAF_PLUGINS } from "../plugin/modules/wrap";
8
+ import { JSX_TO_STATE, LEAF_TO_TEXT } from "../utils/weak-map";
9
+ import { getWrapSymbol } from "../utils/wrapper";
10
+ import { EOLModel } from "./eol";
11
+ import { LeafModel } from "./leaf";
12
+ /**
13
+ * Line Model
14
+ * @param props
15
+ */
16
+ const LineView = props => {
17
+ const { editor, lineState } = props;
18
+ /**
19
+ * 设置行 DOM 节点
20
+ */
21
+ const setModel = (ref) => {
22
+ if (ref) {
23
+ editor.model.setLineModel(ref, lineState);
24
+ }
25
+ };
26
+ /**
27
+ * 首次处理会将所有 DOM 渲染, 不需要执行脏数据检查
28
+ * 需要 LayoutEffect 以保证 DOM -> Sel 的执行顺序
29
+ */
30
+ useUpdateLayoutEffect(() => {
31
+ const leaves = lineState.getLeaves();
32
+ for (const leaf of leaves) {
33
+ const dom = LEAF_TO_TEXT.get(leaf);
34
+ if (!dom)
35
+ continue;
36
+ const text = leaf.getText();
37
+ // 避免 React 非受控与 IME 造成的 DOM 内容问题
38
+ if (text === dom.textContent)
39
+ continue;
40
+ editor.logger.debug("Correct Text Node", dom);
41
+ const nodes = dom.childNodes;
42
+ for (let i = 1; i < nodes.length; ++i) {
43
+ const node = nodes[i];
44
+ node && node.remove();
45
+ }
46
+ if (isDOMText(dom.firstChild)) {
47
+ dom.firstChild.nodeValue = text;
48
+ }
49
+ }
50
+ }, [lineState]);
51
+ /**
52
+ * 处理行内的节点
53
+ */
54
+ const elements = useMemo(() => {
55
+ const leaves = lineState.getLeaves();
56
+ const textLeaves = leaves.slice(0, -1);
57
+ const nodes = textLeaves.map((n, i) => {
58
+ const node = _jsx(LeafModel, { editor: editor, index: i, leafState: n }, i);
59
+ JSX_TO_STATE.set(node, n);
60
+ return node;
61
+ });
62
+ // 空行则仅存在一个 Leaf, 此时需要渲染空的占位节点
63
+ if (!nodes.length && leaves[0]) {
64
+ const leaf = leaves[0];
65
+ const node = _jsx(EOLModel, { editor: editor, leafState: leaf }, EOL);
66
+ JSX_TO_STATE.set(node, leaf);
67
+ nodes.push(node);
68
+ return nodes;
69
+ }
70
+ // inline-void(embed) 在行未时需要预设零宽字符来放置光标
71
+ const eolLeaf = leaves[leaves.length - 1];
72
+ const lastLeaf = textLeaves[textLeaves.length - 1];
73
+ if (lastLeaf && eolLeaf && lastLeaf.embed) {
74
+ const node = _jsx(EOLModel, { editor: editor, leafState: eolLeaf }, EOL);
75
+ JSX_TO_STATE.set(node, eolLeaf);
76
+ nodes.push(node);
77
+ return nodes;
78
+ }
79
+ return nodes;
80
+ }, [editor, lineState]);
81
+ /**
82
+ * 将行内节点包装组合 O(N)
83
+ */
84
+ const children = useMemo(() => {
85
+ const wrapped = [];
86
+ const keys = EDITOR_TO_WRAP_LEAF_KEYS.get(editor);
87
+ const plugins = EDITOR_TO_WRAP_LEAF_PLUGINS.get(editor);
88
+ if (!keys || !plugins)
89
+ return elements;
90
+ const len = elements.length;
91
+ for (let i = 0; i < len; ++i) {
92
+ const element = elements[i];
93
+ const symbol = getWrapSymbol(keys, element);
94
+ const leaf = JSX_TO_STATE.get(element);
95
+ if (!element || !leaf || !symbol) {
96
+ wrapped.push(element);
97
+ continue;
98
+ }
99
+ // 执行到此处说明需要包装相关节点(即使仅单个节点)
100
+ const nodes = [element];
101
+ for (let k = i + 1; k < len; ++k) {
102
+ const next = elements[k];
103
+ const nextSymbol = getWrapSymbol(keys, next);
104
+ if (!next || !nextSymbol || nextSymbol !== symbol) {
105
+ // 回退到上一个值, 以便下次循环时重新检查
106
+ i = k - 1;
107
+ break;
108
+ }
109
+ nodes.push(next);
110
+ i = k;
111
+ }
112
+ // 通过插件渲染包装节点
113
+ let wrapper = nodes;
114
+ const op = leaf.op;
115
+ for (const plugin of plugins) {
116
+ // 这里的状态以首个节点为准
117
+ const context = {
118
+ leafState: leaf,
119
+ children: wrapper,
120
+ };
121
+ if (plugin.match(leaf.op.attributes || {}, op) && plugin.wrapLeaf) {
122
+ wrapper = plugin.wrapLeaf(context);
123
+ }
124
+ }
125
+ const key = `${i - nodes.length + 1}-${i}`;
126
+ wrapped.push(_jsx(React.Fragment, { children: wrapper }, key));
127
+ }
128
+ return wrapped;
129
+ }, [editor, elements]);
130
+ /**
131
+ * 处理行级节点的渲染
132
+ */
133
+ const runtime = useMemo(() => {
134
+ const context = {
135
+ classList: [],
136
+ lineState: lineState,
137
+ attributes: lineState.attributes,
138
+ style: {},
139
+ children,
140
+ };
141
+ const plugins = editor.plugin.getPriorityPlugins(PLUGIN_TYPE.RENDER_LINE);
142
+ for (const plugin of plugins) {
143
+ const op = Object.assign(Object.assign({}, EOL_OP), { attributes: context.attributes });
144
+ if (plugin.match(context.attributes, op)) {
145
+ context.children = plugin.renderLine(context);
146
+ }
147
+ }
148
+ return context;
149
+ }, [children, editor.plugin, lineState]);
150
+ return (_jsx("div", { [NODE_KEY]: true, ref: setModel, dir: "auto", className: cs(runtime.classList), style: runtime.style, children: runtime.children }));
151
+ };
152
+ export const LineModel = React.memo(LineView);
@@ -0,0 +1,25 @@
1
+ /// <reference types="react" />
2
+ import { CorePlugin } from "@block-kit/core";
3
+ import type { ReactLeafContext, ReactLineContext, ReactWrapLeafContext, ReactWrapLineContext } from "./types";
4
+ export declare abstract class EditorPlugin extends CorePlugin {
5
+ /**
6
+ * 渲染包装行节点
7
+ * - 调度优先级值越大 DOM 结构在越外层
8
+ */
9
+ wrapLine?(children: ReactWrapLineContext): React.ReactNode;
10
+ /**
11
+ * 渲染包装叶子节点
12
+ * - 调度优先级值越大 DOM 结构在越外层
13
+ */
14
+ wrapLeaf?(context: ReactWrapLeafContext): React.ReactNode;
15
+ /**
16
+ * 渲染行节点
17
+ * - 调度优先级值越大 DOM 结构在越外层
18
+ */
19
+ renderLine?(context: ReactLineContext): React.ReactNode;
20
+ /**
21
+ * 渲染块级子节点
22
+ * - 调度优先级值越大 DOM 结构在越外层
23
+ */
24
+ renderLeaf?(context: ReactLeafContext): React.ReactNode;
25
+ }
@@ -0,0 +1,3 @@
1
+ import { CorePlugin } from "@block-kit/core";
2
+ export class EditorPlugin extends CorePlugin {
3
+ }
@@ -0,0 +1,13 @@
1
+ import type { CorePlugin } from "@block-kit/core";
2
+ /**
3
+ * 获取插件的优先级
4
+ * @param key
5
+ * @param plugin
6
+ */
7
+ export declare const getPluginPriority: (key: string, plugin: CorePlugin) => number;
8
+ /**
9
+ * 优先级定义装饰器
10
+ * - 兼容性实现, 非强制类型检查
11
+ * @param priority
12
+ */
13
+ export declare function Priority<T>(priority: number): (target: T, key: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -0,0 +1,26 @@
1
+ import { PRIORITY_KEY } from "@block-kit/core";
2
+ import { DEFAULT_PRIORITY, isNumber } from "@block-kit/utils";
3
+ /**
4
+ * 获取插件的优先级
5
+ * @param key
6
+ * @param plugin
7
+ */
8
+ export const getPluginPriority = (key, plugin) => {
9
+ const priorityKey = `${PRIORITY_KEY}${key}`;
10
+ const priorityPlugin = plugin;
11
+ const priority = priorityPlugin[priorityKey];
12
+ return isNumber(priority) ? priority : DEFAULT_PRIORITY;
13
+ };
14
+ /**
15
+ * 优先级定义装饰器
16
+ * - 兼容性实现, 非强制类型检查
17
+ * @param priority
18
+ */
19
+ export function Priority(priority) {
20
+ return function (target, key, descriptor) {
21
+ const priorityKey = `${PRIORITY_KEY}${key}`;
22
+ const plugin = target;
23
+ plugin[priorityKey] = priority;
24
+ return descriptor;
25
+ };
26
+ }
@@ -0,0 +1,26 @@
1
+ import type { CorePlugin, Editor } from "@block-kit/core";
2
+ import type { O, P } from "@block-kit/utils/dist/es/types";
3
+ import type { EditorPlugin } from "../index";
4
+ import { WRAP_TYPE } from "../types";
5
+ export declare const EDITOR_TO_WRAP_LINE_KEYS: WeakMap<Editor, string[]>;
6
+ export declare const EDITOR_TO_WRAP_LEAF_KEYS: WeakMap<Editor, string[]>;
7
+ export declare const EDITOR_TO_WRAP_LINE_PLUGINS: WeakMap<Editor, EditorPlugin[]>;
8
+ export declare const EDITOR_TO_WRAP_LEAF_PLUGINS: WeakMap<Editor, EditorPlugin[]>;
9
+ /**
10
+ * 为 WrapNode 定义 Key
11
+ * @param ...keys
12
+ */
13
+ export declare function InjectWrapKeys<T>(...keys: string[]): (target: T, key: O.Values<typeof WRAP_TYPE>, descriptor: PropertyDescriptor) => PropertyDescriptor;
14
+ /**
15
+ * 获取插件的 WrapKeys
16
+ * @param key
17
+ * @param plugin
18
+ */
19
+ export declare const getWrapKeys: (key: string, plugin: CorePlugin) => string[] | P.Undef;
20
+ /**
21
+ * 初始化 Wrap 模式的插件
22
+ * - Wrap 模式的插件化在 React 层面渲染时实现
23
+ * - 其是渲染时调度且不存在 WrapState 的概念
24
+ * @param editor
25
+ */
26
+ export declare const initWrapPlugins: (editor: Editor) => void;
@@ -0,0 +1,72 @@
1
+ import { WRAP_TYPE } from "../types";
2
+ import { getPluginPriority } from "./priority";
3
+ export const EDITOR_TO_WRAP_LINE_KEYS = new WeakMap();
4
+ export const EDITOR_TO_WRAP_LEAF_KEYS = new WeakMap();
5
+ export const EDITOR_TO_WRAP_LINE_PLUGINS = new WeakMap();
6
+ export const EDITOR_TO_WRAP_LEAF_PLUGINS = new WeakMap();
7
+ /**
8
+ * 为 WrapNode 定义 Key
9
+ * @param ...keys
10
+ */
11
+ export function InjectWrapKeys(...keys) {
12
+ return function (target, key, descriptor) {
13
+ const wrapPluginKey = `${key}Keys`;
14
+ const plugin = target;
15
+ plugin[wrapPluginKey] = keys;
16
+ return descriptor;
17
+ };
18
+ }
19
+ /**
20
+ * 获取插件的 WrapKeys
21
+ * @param key
22
+ * @param plugin
23
+ */
24
+ export const getWrapKeys = (key, plugin) => {
25
+ const wrapPluginKey = `${key}Keys`;
26
+ const wrapPlugin = plugin;
27
+ const keys = wrapPlugin[wrapPluginKey];
28
+ return keys;
29
+ };
30
+ /**
31
+ * 初始化 Wrap 模式的插件
32
+ * - Wrap 模式的插件化在 React 层面渲染时实现
33
+ * - 其是渲染时调度且不存在 WrapState 的概念
34
+ * @param editor
35
+ */
36
+ export const initWrapPlugins = (editor) => {
37
+ const plugins = editor.plugin.current;
38
+ const wrapLineKeys = [];
39
+ const wrapLeafKeys = [];
40
+ const wrapLinePlugins = [];
41
+ const wrapLeafPlugins = [];
42
+ for (const plugin of plugins) {
43
+ const lineKeys = getWrapKeys(WRAP_TYPE.LINE, plugin);
44
+ if (lineKeys) {
45
+ wrapLineKeys.push(...lineKeys);
46
+ }
47
+ const leafKeys = getWrapKeys(WRAP_TYPE.LEAF, plugin);
48
+ if (leafKeys) {
49
+ wrapLeafKeys.push(...leafKeys);
50
+ }
51
+ if (plugin.wrapLine) {
52
+ wrapLinePlugins.push(plugin);
53
+ }
54
+ if (plugin.wrapLeaf) {
55
+ wrapLeafPlugins.push(plugin);
56
+ }
57
+ }
58
+ wrapLinePlugins.sort((a, b) => {
59
+ const priorityA = getPluginPriority(WRAP_TYPE.LINE, a);
60
+ const priorityB = getPluginPriority(WRAP_TYPE.LINE, b);
61
+ return priorityA - priorityB;
62
+ });
63
+ wrapLeafPlugins.sort((a, b) => {
64
+ const priorityA = getPluginPriority(WRAP_TYPE.LEAF, a);
65
+ const priorityB = getPluginPriority(WRAP_TYPE.LEAF, b);
66
+ return priorityA - priorityB;
67
+ });
68
+ wrapLineKeys.length && EDITOR_TO_WRAP_LINE_KEYS.set(editor, wrapLineKeys);
69
+ wrapLeafKeys.length && EDITOR_TO_WRAP_LEAF_KEYS.set(editor, wrapLeafKeys);
70
+ wrapLinePlugins.length && EDITOR_TO_WRAP_LINE_PLUGINS.set(editor, wrapLinePlugins);
71
+ wrapLeafPlugins.length && EDITOR_TO_WRAP_LEAF_PLUGINS.set(editor, wrapLeafPlugins);
72
+ };