@lobehub/editor 4.15.2 → 4.16.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.
- package/es/editor-kernel/react/useDecorators.js +14 -8
- package/es/headless.d.ts +2 -0
- package/es/headless.js +710 -46
- package/es/index.d.ts +4 -3
- package/es/index.js +4 -3
- package/es/locale/index.d.ts +2 -0
- package/es/locale/index.js +5 -1
- package/es/plugins/auto-complete/plugin/index.js +3 -3
- package/es/plugins/block/index.d.ts +1 -1
- package/es/plugins/block/plugin/index.js +78 -2
- package/es/plugins/block/react/ReactBlockPlugin.js +172 -16
- package/es/plugins/block/react/drag/drag-utils.js +37 -10
- package/es/plugins/block/service/i-block-menu-service.d.ts +18 -1
- package/es/plugins/block/service/i-block-menu-service.js +24 -0
- package/es/plugins/block/service/index.d.ts +1 -1
- package/es/plugins/codeblock/plugin/index.js +25 -1
- package/es/plugins/common/plugin/register.js +2 -2
- package/es/plugins/litexml/plugin/index.js +8 -2
- package/es/plugins/slash/plugin/index.js +1 -1
- package/es/plugins/slash/react/ReactSlashPlugin.js +4 -4
- package/es/plugins/table/command/index.d.ts +13 -1
- package/es/plugins/table/command/index.js +220 -39
- package/es/plugins/table/index.d.ts +3 -2
- package/es/plugins/table/node/index.d.ts +2 -0
- package/es/plugins/table/node/index.js +130 -2
- package/es/plugins/table/plugin/index.d.ts +6 -0
- package/es/plugins/table/plugin/index.js +193 -4
- package/es/plugins/table/react/TableActionMenu/ActionMenu.js +82 -6
- package/es/plugins/table/react/TableActionMenu/index.js +9 -4
- package/es/plugins/table/react/TableColController.js +354 -0
- package/es/plugins/table/react/TableController/hooks.js +201 -0
- package/es/plugins/table/react/TableController/style.js +264 -0
- package/es/plugins/table/react/TableController/utils.js +25 -0
- package/es/plugins/table/react/TableControllerButton.js +81 -0
- package/es/plugins/table/react/TableControllerMenu.js +123 -0
- package/es/plugins/table/react/TableInsertButton.js +25 -0
- package/es/plugins/table/react/TableResize/index.js +153 -78
- package/es/plugins/table/react/TableRowController.js +349 -0
- package/es/plugins/table/react/hooks.js +77 -0
- package/es/plugins/table/react/index.js +139 -16
- package/es/plugins/table/react/style.js +89 -8
- package/es/plugins/table/react/type.d.ts +2 -0
- package/es/plugins/table/react/useAutoFitPastedTable.js +189 -0
- package/es/plugins/table/service/i-table-controller-menu-service.d.ts +44 -0
- package/es/plugins/table/service/i-table-controller-menu-service.js +31 -0
- package/es/plugins/table/service/index.d.ts +1 -0
- package/es/plugins/table/utils/autoFitColumnWidth.js +87 -0
- package/es/plugins/table/utils/distributeColumnWidth.js +37 -0
- package/es/plugins/table/utils/index.js +102 -2
- package/es/react/EditorProvider/index.d.ts +2 -2
- package/es/renderer/LexicalDiff.d.ts +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { IEditor, IServiceID } from "../../../types/kernel.js";
|
|
2
|
+
import { LexicalNode } from "lexical";
|
|
3
|
+
|
|
2
4
|
//#region src/plugins/block/service/i-block-menu-service.d.ts
|
|
3
5
|
interface IBlockMenuRenderContext {
|
|
4
6
|
blockElement: HTMLElement;
|
|
@@ -21,11 +23,20 @@ interface IBlockActionButton {
|
|
|
21
23
|
title: string | ((context: IBlockMenuRenderContext) => string);
|
|
22
24
|
when?: (context: IBlockMenuRenderContext) => boolean;
|
|
23
25
|
}
|
|
26
|
+
interface IBlockSelectHandler {
|
|
27
|
+
key: string;
|
|
28
|
+
onSelect: (node: LexicalNode) => boolean | void;
|
|
29
|
+
order?: number;
|
|
30
|
+
}
|
|
24
31
|
interface IBlockMenuService {
|
|
25
32
|
getActionButtons(context: IBlockMenuRenderContext): IBlockActionButton[];
|
|
26
33
|
getMenus(context: IBlockMenuRenderContext): IBlockMenuItem[];
|
|
34
|
+
isMenuSuppressed(): boolean;
|
|
27
35
|
registerActionButton(item: IBlockActionButton): () => void;
|
|
28
36
|
registerMenu(item: IBlockMenuItem): () => void;
|
|
37
|
+
registerSelectHandler(item: IBlockSelectHandler): () => void;
|
|
38
|
+
selectNode(node: LexicalNode): boolean;
|
|
39
|
+
setMenuSuppressed(key: string, suppressed: boolean): void;
|
|
29
40
|
subscribe(listener: () => void): () => void;
|
|
30
41
|
}
|
|
31
42
|
declare const IBlockMenuService: IServiceID<IBlockMenuService>;
|
|
@@ -33,12 +44,18 @@ declare class BlockMenuService implements IBlockMenuService {
|
|
|
33
44
|
private actionButtons;
|
|
34
45
|
private items;
|
|
35
46
|
private listeners;
|
|
47
|
+
private selectHandlers;
|
|
48
|
+
private suppressors;
|
|
36
49
|
getActionButtons(context: IBlockMenuRenderContext): IBlockActionButton[];
|
|
37
50
|
getMenus(context: IBlockMenuRenderContext): IBlockMenuItem[];
|
|
51
|
+
isMenuSuppressed(): boolean;
|
|
38
52
|
registerActionButton(item: IBlockActionButton): () => void;
|
|
39
53
|
registerMenu(item: IBlockMenuItem): () => void;
|
|
54
|
+
registerSelectHandler(item: IBlockSelectHandler): () => void;
|
|
55
|
+
selectNode(node: LexicalNode): boolean;
|
|
56
|
+
setMenuSuppressed(key: string, suppressed: boolean): void;
|
|
40
57
|
subscribe(listener: () => void): () => void;
|
|
41
58
|
private notify;
|
|
42
59
|
}
|
|
43
60
|
//#endregion
|
|
44
|
-
export { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService };
|
|
61
|
+
export { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler };
|
|
@@ -6,6 +6,8 @@ var BlockMenuService = class {
|
|
|
6
6
|
this.actionButtons = /* @__PURE__ */ new Map();
|
|
7
7
|
this.items = /* @__PURE__ */ new Map();
|
|
8
8
|
this.listeners = /* @__PURE__ */ new Set();
|
|
9
|
+
this.selectHandlers = /* @__PURE__ */ new Map();
|
|
10
|
+
this.suppressors = /* @__PURE__ */ new Set();
|
|
9
11
|
}
|
|
10
12
|
getActionButtons(context) {
|
|
11
13
|
return Array.from(this.actionButtons.values()).filter((item) => item.when ? item.when(context) : true).sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
@@ -13,6 +15,9 @@ var BlockMenuService = class {
|
|
|
13
15
|
getMenus(context) {
|
|
14
16
|
return Array.from(this.items.values()).filter((item) => item.when ? item.when(context) : true).sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
15
17
|
}
|
|
18
|
+
isMenuSuppressed() {
|
|
19
|
+
return this.suppressors.size > 0;
|
|
20
|
+
}
|
|
16
21
|
registerActionButton(item) {
|
|
17
22
|
this.actionButtons.set(item.key, item);
|
|
18
23
|
this.notify();
|
|
@@ -29,6 +34,25 @@ var BlockMenuService = class {
|
|
|
29
34
|
this.notify();
|
|
30
35
|
};
|
|
31
36
|
}
|
|
37
|
+
registerSelectHandler(item) {
|
|
38
|
+
this.selectHandlers.set(item.key, item);
|
|
39
|
+
this.notify();
|
|
40
|
+
return () => {
|
|
41
|
+
this.selectHandlers.delete(item.key);
|
|
42
|
+
this.notify();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
selectNode(node) {
|
|
46
|
+
const handlers = Array.from(this.selectHandlers.values()).sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
47
|
+
for (const handler of handlers) if (handler.onSelect(node)) return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
setMenuSuppressed(key, suppressed) {
|
|
51
|
+
const size = this.suppressors.size;
|
|
52
|
+
if (suppressed) this.suppressors.add(key);
|
|
53
|
+
else this.suppressors.delete(key);
|
|
54
|
+
if (this.suppressors.size !== size) this.notify();
|
|
55
|
+
}
|
|
32
56
|
subscribe(listener) {
|
|
33
57
|
this.listeners.add(listener);
|
|
34
58
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService } from "./i-block-menu-service.js";
|
|
1
|
+
import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler } from "./i-block-menu-service.js";
|
|
@@ -2,10 +2,11 @@ import { INodeHelper, init_helper } from "../../../editor-kernel/inode/helper.js
|
|
|
2
2
|
import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
|
|
3
3
|
import { ILitexmlService } from "../../litexml/service/litexml-service.js";
|
|
4
4
|
import { IMarkdownShortCutService } from "../../markdown/service/shortcut.js";
|
|
5
|
+
import { IBlockMenuService } from "../../block/service/i-block-menu-service.js";
|
|
5
6
|
import { getCodeLanguageByInput } from "../utils/language.js";
|
|
6
7
|
import { registerCodeHighlighting, toCodeTheme } from "./CodeHighlighterShiki.js";
|
|
7
8
|
import { CustomShikiTokenizer, registerCodeCommand } from "../command/index.js";
|
|
8
|
-
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, TabNode } from "lexical";
|
|
9
|
+
import { $createRangeSelection, $getSelection, $isRangeSelection, $setSelection, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, TabNode } from "lexical";
|
|
9
10
|
import { $createCodeNode, $isCodeHighlightNode, $isCodeNode, CodeHighlightNode, CodeNode } from "@lexical/code-core";
|
|
10
11
|
//#region src/plugins/codeblock/plugin/index.ts
|
|
11
12
|
init_helper();
|
|
@@ -75,6 +76,29 @@ const CodeblockPlugin = class extends KernelPlugin {
|
|
|
75
76
|
}, COMMAND_PRIORITY_EDITOR));
|
|
76
77
|
this.registerMarkdown();
|
|
77
78
|
this.registerLiteXml();
|
|
79
|
+
this.registerBlockSelect();
|
|
80
|
+
}
|
|
81
|
+
registerBlockSelect() {
|
|
82
|
+
const blockMenuService = this.kernel.requireService(IBlockMenuService);
|
|
83
|
+
if (!blockMenuService) return;
|
|
84
|
+
this.register(blockMenuService.registerSelectHandler({
|
|
85
|
+
key: "__codeblock_block_select_handler",
|
|
86
|
+
onSelect: (node) => {
|
|
87
|
+
if (!$isCodeNode(node)) return false;
|
|
88
|
+
const textNodes = node.getAllTextNodes();
|
|
89
|
+
const firstTextNode = textNodes[0];
|
|
90
|
+
const lastTextNode = textNodes.at(-1);
|
|
91
|
+
if (!firstTextNode || !lastTextNode) {
|
|
92
|
+
node.select(0, node.getChildrenSize());
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
const selection = $createRangeSelection();
|
|
96
|
+
selection.setTextNodeRange(firstTextNode, 0, lastTextNode, lastTextNode.getTextContentSize());
|
|
97
|
+
$setSelection(selection);
|
|
98
|
+
return true;
|
|
99
|
+
},
|
|
100
|
+
order: 100
|
|
101
|
+
}));
|
|
78
102
|
}
|
|
79
103
|
registerLiteXml() {
|
|
80
104
|
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createDebugLogger, init_debug } from "../../../utils/debug.js";
|
|
2
2
|
import { HotkeyEnum, init_hotkey } from "../../../types/hotkey.js";
|
|
3
3
|
import { $closest } from "../../../editor-kernel/utils.js";
|
|
4
|
-
import { $createNodeSelection, $createParagraphNode, $getRoot, $getSelection, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, $setSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, FORMAT_TEXT_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, REDO_COMMAND, UNDO_COMMAND } from "lexical";
|
|
4
|
+
import { $createNodeSelection, $createParagraphNode, $getRoot, $getSelection, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, $setSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, FORMAT_TEXT_COMMAND, HISTORIC_TAG, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, REDO_COMMAND, UNDO_COMMAND } from "lexical";
|
|
5
5
|
import { $isHeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
6
6
|
import { mergeRegister } from "@lexical/utils";
|
|
7
7
|
import { $isCodeHighlightNode, $isCodeNode } from "@lexical/code-core";
|
|
@@ -297,7 +297,7 @@ function registerLastElement(editor) {
|
|
|
297
297
|
root.append(paragraph);
|
|
298
298
|
}
|
|
299
299
|
isProcessing = false;
|
|
300
|
-
});
|
|
300
|
+
}, { tag: HISTORIC_TAG });
|
|
301
301
|
});
|
|
302
302
|
}
|
|
303
303
|
});
|
|
@@ -5,7 +5,7 @@ import { registerLiteXMLDiffCommand } from "../command/diffCommand.js";
|
|
|
5
5
|
import { ILitexmlService, LitexmlService } from "../service/litexml-service.js";
|
|
6
6
|
import LitexmlDataSource from "../data-source/litexml-data-source.js";
|
|
7
7
|
import { IMarkdownShortCutService } from "../../markdown/service/shortcut.js";
|
|
8
|
-
import { $nodesOfType } from "lexical";
|
|
8
|
+
import { $nodesOfType, HISTORIC_TAG } from "lexical";
|
|
9
9
|
//#region src/plugins/litexml/plugin/index.ts
|
|
10
10
|
init_plugin();
|
|
11
11
|
/**
|
|
@@ -42,8 +42,14 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
42
42
|
this.register(editor.registerNodeTransform(DiffNode, (node) => {
|
|
43
43
|
if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
44
44
|
}));
|
|
45
|
-
this.register(editor.registerUpdateListener(({ editorState, prevEditorState }) => {
|
|
45
|
+
this.register(editor.registerUpdateListener(({ editorState, prevEditorState, tags }) => {
|
|
46
46
|
if (editorState === prevEditorState) return;
|
|
47
|
+
if (tags.has(HISTORIC_TAG)) return;
|
|
48
|
+
let shouldNormalize = false;
|
|
49
|
+
editorState.read(() => {
|
|
50
|
+
shouldNormalize = $nodesOfType(DiffNode).some((node) => node.diffType === "modify" && node.getChildrenSize() === 1);
|
|
51
|
+
});
|
|
52
|
+
if (!shouldNormalize) return;
|
|
47
53
|
editor.update(() => {
|
|
48
54
|
const diffNodes = $nodesOfType(DiffNode);
|
|
49
55
|
for (const node of diffNodes) if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
@@ -26,7 +26,7 @@ const SlashPlugin = class extends KernelPlugin {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
triggerClose() {
|
|
29
|
-
this.config?.triggerClose?.();
|
|
29
|
+
if (this.currentSlashTrigger !== null || this.manualOpen || !this.suppressOpen) this.config?.triggerClose?.();
|
|
30
30
|
this.currentSlashTrigger = null;
|
|
31
31
|
this.currentSlashTriggerIndex = -1;
|
|
32
32
|
this.manualOpen = false;
|
|
@@ -26,10 +26,10 @@ const ReactSlashPlugin = ({ children, anchorClassName, getPopupContainer, placem
|
|
|
26
26
|
const cancelRef = useRef({ cancel: noop });
|
|
27
27
|
const triggerMapRef = useRef(/* @__PURE__ */ new Map());
|
|
28
28
|
const close = useCallback(() => {
|
|
29
|
-
setIsOpen(false);
|
|
30
|
-
setOptions([]);
|
|
31
|
-
setResolution(null);
|
|
32
|
-
setActiveKey(null);
|
|
29
|
+
setIsOpen((open) => open ? false : open);
|
|
30
|
+
setOptions((currentOptions) => currentOptions.length > 0 ? [] : currentOptions);
|
|
31
|
+
setResolution((currentResolution) => currentResolution ? null : currentResolution);
|
|
32
|
+
setActiveKey((currentActiveKey) => currentActiveKey ? null : currentActiveKey);
|
|
33
33
|
}, []);
|
|
34
34
|
useEffect(() => close, [close]);
|
|
35
35
|
const handleActiveKeyChange = useCallback((key) => {
|
|
@@ -9,9 +9,21 @@ declare const INSERT_TABLE_COMMAND: _$lexical.LexicalCommand<{
|
|
|
9
9
|
rows: string;
|
|
10
10
|
}>;
|
|
11
11
|
declare const SELECT_TABLE_COMMAND: _$lexical.LexicalCommand<{
|
|
12
|
+
anchorIndex?: number;
|
|
12
13
|
columnIndex?: number;
|
|
14
|
+
extend?: boolean;
|
|
13
15
|
rowIndex?: number;
|
|
14
16
|
table: string;
|
|
15
17
|
}>;
|
|
18
|
+
declare const INSERT_TABLE_COLUMN_COMMAND: _$lexical.LexicalCommand<{
|
|
19
|
+
columnIndex: number;
|
|
20
|
+
insertAfter?: boolean;
|
|
21
|
+
table: string;
|
|
22
|
+
}>;
|
|
23
|
+
declare const INSERT_TABLE_ROW_COMMAND: _$lexical.LexicalCommand<{
|
|
24
|
+
insertAfter?: boolean;
|
|
25
|
+
rowIndex: number;
|
|
26
|
+
table: string;
|
|
27
|
+
}>;
|
|
16
28
|
//#endregion
|
|
17
|
-
export { INSERT_TABLE_COMMAND, SELECT_TABLE_COMMAND };
|
|
29
|
+
export { INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, SELECT_TABLE_COMMAND };
|
|
@@ -1,57 +1,238 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDefaultTableColWidths, syncTableWidthDOM } from "../utils/index.js";
|
|
2
|
+
import { getAutoFitTableColumnWidths } from "../utils/autoFitColumnWidth.js";
|
|
3
|
+
import { getDistributedTableColumnWidths } from "../utils/distributeColumnWidth.js";
|
|
4
|
+
import { $computeTableMapSkipCellCheck, $createTableNodeWithDimensions, $createTableSelection, $findTableNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isSimpleTable, $isTableNode, $isTableRowNode, $isTableSelection } from "@lexical/table";
|
|
2
5
|
import { $getNodeByKey, $getPreviousSelection, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_EDITOR, createCommand } from "lexical";
|
|
3
6
|
import { $insertNodeToNearestRoot, mergeRegister } from "@lexical/utils";
|
|
4
7
|
//#region src/plugins/table/command/index.ts
|
|
5
8
|
const INSERT_TABLE_COMMAND = createCommand();
|
|
6
9
|
const SELECT_TABLE_COMMAND = createCommand();
|
|
10
|
+
const INSERT_TABLE_COLUMN_COMMAND = createCommand();
|
|
11
|
+
const INSERT_TABLE_ROW_COMMAND = createCommand();
|
|
12
|
+
const SYNC_TABLE_COLUMN_WIDTH_COMMAND = createCommand();
|
|
13
|
+
const AUTO_FIT_TABLE_COLUMN_WIDTH_COMMAND = createCommand();
|
|
14
|
+
const DISTRIBUTE_TABLE_COLUMN_WIDTH_COMMAND = createCommand();
|
|
15
|
+
const MOVE_TABLE_COLUMN_COMMAND = createCommand();
|
|
16
|
+
const MOVE_TABLE_ROW_COMMAND = createCommand();
|
|
17
|
+
const resetTableScrollLeft = (editor, tableKey) => {
|
|
18
|
+
const tableElement = editor.getElementByKey(tableKey);
|
|
19
|
+
const scrollWrapper = (tableElement instanceof HTMLTableElement ? tableElement : tableElement?.querySelector("table.editor_table, table"))?.closest(".lobe-editor-table-scroll-wrapper");
|
|
20
|
+
if (scrollWrapper) scrollWrapper.scrollLeft = 0;
|
|
21
|
+
};
|
|
22
|
+
const $selectFirstDescendant = (node) => {
|
|
23
|
+
const firstDescendant = node.getFirstDescendant();
|
|
24
|
+
if ($isTextNode(firstDescendant)) firstDescendant.select();
|
|
25
|
+
else node.selectStart();
|
|
26
|
+
};
|
|
27
|
+
const getMoveRange = (selectedIndexes, targetIndex, insertAfter = false) => {
|
|
28
|
+
if (selectedIndexes.length === 0) return null;
|
|
29
|
+
const sortedIndexes = [...selectedIndexes].sort((a, b) => a - b);
|
|
30
|
+
const from = sortedIndexes[0];
|
|
31
|
+
const to = sortedIndexes.at(-1) ?? from;
|
|
32
|
+
const count = to - from + 1;
|
|
33
|
+
const insertionIndex = insertAfter ? targetIndex + 1 : targetIndex;
|
|
34
|
+
if (insertionIndex >= from && insertionIndex <= to + 1) return null;
|
|
35
|
+
return {
|
|
36
|
+
count,
|
|
37
|
+
from,
|
|
38
|
+
target: insertionIndex > to ? insertionIndex - count : insertionIndex,
|
|
39
|
+
to
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const $selectTableRows = (tableNode, from, to) => {
|
|
43
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
44
|
+
const firstRow = tableMap[from];
|
|
45
|
+
const lastRow = tableMap[to];
|
|
46
|
+
const firstCell = firstRow?.[0]?.cell;
|
|
47
|
+
const lastCell = lastRow?.[lastRow.length - 1]?.cell;
|
|
48
|
+
if (!firstCell || !lastCell) return false;
|
|
49
|
+
const tableSelection = $createTableSelection();
|
|
50
|
+
tableSelection.set(tableNode.getKey(), firstCell.getKey(), lastCell.getKey());
|
|
51
|
+
$setSelection(tableSelection);
|
|
52
|
+
return true;
|
|
53
|
+
};
|
|
54
|
+
const $selectTableColumns = (tableNode, from, to) => {
|
|
55
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
56
|
+
const firstCell = tableMap.find((row) => row[from])?.[from]?.cell;
|
|
57
|
+
const lastCell = [...tableMap].reverse().find((row) => row[to])?.[to]?.cell;
|
|
58
|
+
if (!firstCell || !lastCell) return false;
|
|
59
|
+
const tableSelection = $createTableSelection();
|
|
60
|
+
tableSelection.set(tableNode.getKey(), firstCell.getKey(), lastCell.getKey());
|
|
61
|
+
$setSelection(tableSelection);
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
const getRangeFromSelection = (selection, table, targetIndex, direction, crossAxisLength, anchorIndex) => {
|
|
65
|
+
if (anchorIndex !== void 0) return {
|
|
66
|
+
from: Math.min(anchorIndex, targetIndex),
|
|
67
|
+
to: Math.max(anchorIndex, targetIndex)
|
|
68
|
+
};
|
|
69
|
+
if (!$isTableSelection(selection) || selection.tableKey !== table) return {
|
|
70
|
+
from: 0,
|
|
71
|
+
to: targetIndex
|
|
72
|
+
};
|
|
73
|
+
const shape = selection.getShape();
|
|
74
|
+
if (!(direction === "row" ? shape.fromX === 0 && shape.toX === crossAxisLength - 1 : shape.fromY === 0 && shape.toY === crossAxisLength - 1)) return {
|
|
75
|
+
from: 0,
|
|
76
|
+
to: targetIndex
|
|
77
|
+
};
|
|
78
|
+
const from = direction === "row" ? shape.fromY : shape.fromX;
|
|
79
|
+
const to = direction === "row" ? shape.toY : shape.toX;
|
|
80
|
+
return {
|
|
81
|
+
from: Math.min(from, targetIndex),
|
|
82
|
+
to: Math.max(to, targetIndex)
|
|
83
|
+
};
|
|
84
|
+
};
|
|
7
85
|
function registerTableCommand(editor) {
|
|
8
86
|
return mergeRegister(editor.registerCommand(INSERT_TABLE_COMMAND, ({ rows, columns, includeHeaders }) => {
|
|
9
87
|
const selection = $getSelection() || $getPreviousSelection();
|
|
10
88
|
if (!selection || !$isRangeSelection(selection)) return false;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
89
|
+
const anchorNode = $getNodeByKey(selection.anchor.key);
|
|
90
|
+
if (!anchorNode) return false;
|
|
91
|
+
if ($findTableNode(anchorNode)) return false;
|
|
92
|
+
const rowCount = Number(rows);
|
|
93
|
+
const columnCount = Number(columns);
|
|
94
|
+
const tableNode = $createTableNodeWithDimensions(rowCount, columnCount, includeHeaders);
|
|
95
|
+
tableNode.setColWidths(createDefaultTableColWidths(columnCount));
|
|
14
96
|
if ($isElementNode(anchorNode) && anchorNode.isEmpty()) anchorNode.replace(tableNode);
|
|
15
97
|
else $insertNodeToNearestRoot(tableNode);
|
|
16
98
|
const firstDescendant = tableNode.getFirstDescendant();
|
|
17
99
|
if ($isTextNode(firstDescendant)) firstDescendant.select();
|
|
18
100
|
return true;
|
|
19
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
101
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(INSERT_TABLE_COLUMN_COMMAND, ({ table, columnIndex, insertAfter = true }) => {
|
|
102
|
+
const tableNode = $getNodeByKey(table);
|
|
103
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
104
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
105
|
+
const firstCell = tableMap.find((row) => row[columnIndex])?.[columnIndex]?.cell;
|
|
106
|
+
const lastCell = [...tableMap].reverse().find((row) => row[columnIndex])?.[columnIndex]?.cell;
|
|
107
|
+
if (!firstCell || !lastCell) return false;
|
|
108
|
+
const tableSelection = $createTableSelection();
|
|
109
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
110
|
+
$setSelection(tableSelection);
|
|
111
|
+
$insertTableColumnAtSelection(insertAfter);
|
|
112
|
+
return true;
|
|
113
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(INSERT_TABLE_ROW_COMMAND, ({ table, rowIndex, insertAfter = true }) => {
|
|
114
|
+
const tableNode = $getNodeByKey(table);
|
|
115
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
116
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
117
|
+
const row = tableMap[rowIndex];
|
|
118
|
+
const firstCell = row?.[0]?.cell;
|
|
119
|
+
const lastCell = row?.[row.length - 1]?.cell;
|
|
120
|
+
if (!firstCell || !lastCell) return false;
|
|
121
|
+
const tableSelection = $createTableSelection();
|
|
122
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
123
|
+
$setSelection(tableSelection);
|
|
124
|
+
const insertedRow = $insertTableRowAtSelection(insertAfter);
|
|
125
|
+
if (insertedRow) $selectFirstDescendant(insertedRow);
|
|
126
|
+
return true;
|
|
127
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SYNC_TABLE_COLUMN_WIDTH_COMMAND, ({ table, columnIndex }) => {
|
|
128
|
+
const tableNode = $getNodeByKey(table);
|
|
129
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
130
|
+
const columnCount = tableNode.getColumnCount();
|
|
131
|
+
const selectedWidth = (tableNode.getColWidths() || createDefaultTableColWidths(columnCount))[columnIndex];
|
|
132
|
+
if (selectedWidth === void 0) return false;
|
|
133
|
+
const nextColWidths = Array.from({ length: columnCount }, () => selectedWidth);
|
|
134
|
+
tableNode.setColWidths(nextColWidths);
|
|
135
|
+
requestAnimationFrame(() => {
|
|
136
|
+
syncTableWidthDOM(editor, table, nextColWidths);
|
|
137
|
+
});
|
|
138
|
+
return true;
|
|
139
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(AUTO_FIT_TABLE_COLUMN_WIDTH_COMMAND, ({ table, columnIndexes }) => {
|
|
140
|
+
const tableNode = $getNodeByKey(table);
|
|
141
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
142
|
+
const nextColWidths = getAutoFitTableColumnWidths(editor, tableNode, columnIndexes);
|
|
143
|
+
if (!nextColWidths) return false;
|
|
144
|
+
tableNode.setColWidths(nextColWidths);
|
|
145
|
+
requestAnimationFrame(() => {
|
|
146
|
+
syncTableWidthDOM(editor, table, nextColWidths);
|
|
147
|
+
});
|
|
148
|
+
return true;
|
|
149
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(DISTRIBUTE_TABLE_COLUMN_WIDTH_COMMAND, ({ table }) => {
|
|
150
|
+
const tableNode = $getNodeByKey(table);
|
|
151
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
152
|
+
const nextColWidths = getDistributedTableColumnWidths(editor, tableNode);
|
|
153
|
+
if (!nextColWidths) return false;
|
|
154
|
+
tableNode.setColWidths(nextColWidths);
|
|
155
|
+
requestAnimationFrame(() => {
|
|
156
|
+
syncTableWidthDOM(editor, table, nextColWidths);
|
|
157
|
+
resetTableScrollLeft(editor, table);
|
|
52
158
|
});
|
|
53
|
-
return
|
|
159
|
+
return true;
|
|
160
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(MOVE_TABLE_COLUMN_COMMAND, ({ table, selectedColumns, columnIndex, insertAfter = false }) => {
|
|
161
|
+
const tableNode = $getNodeByKey(table);
|
|
162
|
+
if (!tableNode || !$isTableNode(tableNode) || !$isSimpleTable(tableNode)) return false;
|
|
163
|
+
const moveRange = getMoveRange(selectedColumns, columnIndex, insertAfter);
|
|
164
|
+
if (!moveRange) return false;
|
|
165
|
+
const { count, from, target, to } = moveRange;
|
|
166
|
+
tableNode.getChildren().filter($isTableRowNode).forEach((row) => {
|
|
167
|
+
const cells = row.getChildren();
|
|
168
|
+
const movedCells = cells.slice(from, to + 1);
|
|
169
|
+
const nextCells = [...cells.slice(0, from), ...cells.slice(to + 1)];
|
|
170
|
+
nextCells.splice(target, 0, ...movedCells);
|
|
171
|
+
row.splice(0, cells.length, nextCells);
|
|
172
|
+
});
|
|
173
|
+
const colWidths = tableNode.getColWidths();
|
|
174
|
+
if (colWidths && colWidths.length === tableNode.getColumnCount()) {
|
|
175
|
+
const movedWidths = colWidths.slice(from, to + 1);
|
|
176
|
+
const nextWidths = [...colWidths.slice(0, from), ...colWidths.slice(to + 1)];
|
|
177
|
+
nextWidths.splice(target, 0, ...movedWidths);
|
|
178
|
+
tableNode.setColWidths(nextWidths);
|
|
179
|
+
}
|
|
180
|
+
$selectTableColumns(tableNode, target, target + count - 1);
|
|
181
|
+
return true;
|
|
182
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(MOVE_TABLE_ROW_COMMAND, ({ table, selectedRows, rowIndex, insertAfter = false }) => {
|
|
183
|
+
const tableNode = $getNodeByKey(table);
|
|
184
|
+
if (!tableNode || !$isTableNode(tableNode) || !$isSimpleTable(tableNode)) return false;
|
|
185
|
+
const moveRange = getMoveRange(selectedRows, rowIndex, insertAfter);
|
|
186
|
+
if (!moveRange) return false;
|
|
187
|
+
const { count, from, target, to } = moveRange;
|
|
188
|
+
const rows = tableNode.getChildren();
|
|
189
|
+
const movedRows = rows.slice(from, to + 1);
|
|
190
|
+
const nextRows = [...rows.slice(0, from), ...rows.slice(to + 1)];
|
|
191
|
+
nextRows.splice(target, 0, ...movedRows);
|
|
192
|
+
tableNode.splice(0, rows.length, nextRows);
|
|
193
|
+
$selectTableRows(tableNode, target, target + count - 1);
|
|
194
|
+
return true;
|
|
195
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SELECT_TABLE_COMMAND, ({ table, anchorIndex, columnIndex, extend, rowIndex }) => {
|
|
196
|
+
const prevSelection = $getSelection();
|
|
197
|
+
const tableNode = $getNodeByKey(table);
|
|
198
|
+
if (!tableNode || !$isTableNode(tableNode)) return false;
|
|
199
|
+
const tableSelection = $isTableSelection(prevSelection) ? prevSelection : $createTableSelection();
|
|
200
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
201
|
+
if (rowIndex !== void 0) {
|
|
202
|
+
const { from, to } = extend ? getRangeFromSelection(prevSelection, table, rowIndex, "row", tableNode.getColumnCount(), anchorIndex) : {
|
|
203
|
+
from: rowIndex,
|
|
204
|
+
to: rowIndex
|
|
205
|
+
};
|
|
206
|
+
const firstRow = tableMap[from];
|
|
207
|
+
const lastRow = tableMap[to];
|
|
208
|
+
const firstCell = firstRow?.[0]?.cell;
|
|
209
|
+
const lastCell = lastRow?.[lastRow.length - 1]?.cell;
|
|
210
|
+
if (!firstCell || !lastCell) return false;
|
|
211
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
212
|
+
$setSelection(tableSelection);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (columnIndex !== void 0) {
|
|
216
|
+
const { from, to } = extend ? getRangeFromSelection(prevSelection, table, columnIndex, "column", tableMap.length, anchorIndex) : {
|
|
217
|
+
from: columnIndex,
|
|
218
|
+
to: columnIndex
|
|
219
|
+
};
|
|
220
|
+
const firstCell = tableMap.find((row) => row[from])?.[from]?.cell;
|
|
221
|
+
const lastCell = [...tableMap].reverse().find((row) => row[to])?.[to]?.cell;
|
|
222
|
+
if (!firstCell || !lastCell) return false;
|
|
223
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
224
|
+
$setSelection(tableSelection);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
const firstRow = tableMap[0];
|
|
228
|
+
const lastRow = tableMap.at(-1);
|
|
229
|
+
const firstCell = firstRow?.[0]?.cell;
|
|
230
|
+
const lastCell = lastRow?.[lastRow.length - 1]?.cell;
|
|
231
|
+
if (!firstCell || !lastCell) return false;
|
|
232
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
233
|
+
$setSelection(tableSelection);
|
|
234
|
+
return true;
|
|
54
235
|
}, COMMAND_PRIORITY_EDITOR));
|
|
55
236
|
}
|
|
56
237
|
//#endregion
|
|
57
|
-
export { INSERT_TABLE_COMMAND, SELECT_TABLE_COMMAND, registerTableCommand };
|
|
238
|
+
export { AUTO_FIT_TABLE_COLUMN_WIDTH_COMMAND, DISTRIBUTE_TABLE_COLUMN_WIDTH_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, MOVE_TABLE_COLUMN_COMMAND, MOVE_TABLE_ROW_COMMAND, SELECT_TABLE_COMMAND, SYNC_TABLE_COLUMN_WIDTH_COMMAND, registerTableCommand };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { INSERT_TABLE_COMMAND, SELECT_TABLE_COMMAND } from "./command/index.js";
|
|
1
|
+
import { INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, SELECT_TABLE_COMMAND } from "./command/index.js";
|
|
2
2
|
import { TablePlugin, TablePluginOptions } from "./plugin/index.js";
|
|
3
|
-
import { ReactTablePlugin } from "./react/index.js";
|
|
3
|
+
import { ReactTablePlugin } from "./react/index.js";
|
|
4
|
+
import { ITableControllerMenuActionItem, ITableControllerMenuItem, ITableControllerMenuRenderContext, ITableControllerMenuSeparatorItem, ITableControllerMenuService, TableControllerMenuAxis, TableControllerMenuService } from "./service/i-table-controller-menu-service.js";
|