@lobehub/editor 4.15.2 → 4.16.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/es/editor-kernel/react/useDecorators.js +14 -8
- package/es/headless/index.d.ts +1 -1
- package/es/headless/index.js +1 -1
- package/es/headless.d.ts +3 -18
- package/es/headless.js +711 -52
- package/es/index.d.ts +5 -5
- package/es/index.js +5 -5
- 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/command/diffCommand.d.ts +2 -17
- package/es/plugins/litexml/command/diffCommand.js +3 -9
- package/es/plugins/litexml/command/index.d.ts +2 -38
- package/es/plugins/litexml/command/index.js +3 -6
- package/es/plugins/litexml/command/symbols.d.ts +67 -0
- package/es/plugins/litexml/command/symbols.js +34 -0
- package/es/plugins/litexml/index.d.ts +1 -2
- package/es/plugins/litexml/plugin/index.js +8 -2
- package/es/plugins/litexml/react/DiffNodeToolbar/index.js +1 -1
- 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/es/symbols-DEEvsKq4.d.ts +67 -0
- package/package.json +5 -1
|
@@ -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";
|
|
@@ -1,14 +1,142 @@
|
|
|
1
|
+
import { getKernelFromEditor, getKernelFromEditorConfig, reconcileDecorator } from "../../../editor-kernel/utils.js";
|
|
1
2
|
import { TableNode, TableNode as TableNode$1 } from "@lexical/table";
|
|
3
|
+
import { setDOMUnmanaged } from "lexical";
|
|
2
4
|
//#region src/plugins/table/node/index.ts
|
|
3
5
|
const OriginalCreateDOM = TableNode.prototype.createDOM;
|
|
6
|
+
const OriginalUpdateDOM = TableNode.prototype.updateDOM;
|
|
7
|
+
const SCROLL_INDICATOR_WIDTH = 24;
|
|
8
|
+
function markTableControllerHost(element, withDecorator = false) {
|
|
9
|
+
setDOMUnmanaged(element);
|
|
10
|
+
if (withDecorator) element.dataset.lexicalDecorator = "true";
|
|
11
|
+
}
|
|
12
|
+
function updateTableScrollIndicators(scrollWrapper) {
|
|
13
|
+
const maxScrollLeft = scrollWrapper.scrollWidth - scrollWrapper.clientWidth;
|
|
14
|
+
const scrollLeft = scrollWrapper.scrollLeft;
|
|
15
|
+
const hasOverflow = maxScrollLeft > 1;
|
|
16
|
+
const showStart = hasOverflow && scrollLeft > 1;
|
|
17
|
+
const showEnd = hasOverflow && scrollLeft < maxScrollLeft - 1;
|
|
18
|
+
const startIndicator = scrollWrapper.querySelector(":scope > .lobe-editor-table-scroll-indicator-start");
|
|
19
|
+
const endIndicator = scrollWrapper.querySelector(":scope > .lobe-editor-table-scroll-indicator-end");
|
|
20
|
+
startIndicator?.classList.toggle("lobe-editor-table-scroll-indicator-visible", showStart);
|
|
21
|
+
endIndicator?.classList.toggle("lobe-editor-table-scroll-indicator-visible", showEnd);
|
|
22
|
+
if (startIndicator) startIndicator.style.transform = `translateX(${scrollLeft}px)`;
|
|
23
|
+
if (endIndicator) endIndicator.style.transform = `translateX(${Math.max(scrollLeft + scrollWrapper.clientWidth - SCROLL_INDICATOR_WIDTH, 0)}px)`;
|
|
24
|
+
}
|
|
25
|
+
function ensureTableScrollIndicators(scrollWrapper) {
|
|
26
|
+
const ensureIndicator = (className) => {
|
|
27
|
+
const existingIndicator = scrollWrapper.querySelector(`:scope > .${className}`);
|
|
28
|
+
if (existingIndicator instanceof HTMLElement) {
|
|
29
|
+
setDOMUnmanaged(existingIndicator);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const indicator = document.createElement("span");
|
|
33
|
+
indicator.className = `lobe-editor-table-scroll-indicator ${className}`;
|
|
34
|
+
setDOMUnmanaged(indicator);
|
|
35
|
+
scrollWrapper.append(indicator);
|
|
36
|
+
};
|
|
37
|
+
ensureIndicator("lobe-editor-table-scroll-indicator-start");
|
|
38
|
+
ensureIndicator("lobe-editor-table-scroll-indicator-end");
|
|
39
|
+
if (scrollWrapper.dataset.scrollIndicatorsReady === "true") {
|
|
40
|
+
updateTableScrollIndicators(scrollWrapper);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
scrollWrapper.dataset.scrollIndicatorsReady = "true";
|
|
44
|
+
scrollWrapper.addEventListener("scroll", () => updateTableScrollIndicators(scrollWrapper), { passive: true });
|
|
45
|
+
const resizeObserver = new ResizeObserver(() => updateTableScrollIndicators(scrollWrapper));
|
|
46
|
+
resizeObserver.observe(scrollWrapper);
|
|
47
|
+
resizeObserver.observe(scrollWrapper.querySelector("table") ?? scrollWrapper);
|
|
48
|
+
requestAnimationFrame(() => updateTableScrollIndicators(scrollWrapper));
|
|
49
|
+
}
|
|
50
|
+
function ensureTableControllerDOM(element) {
|
|
51
|
+
const table = element instanceof HTMLTableElement ? element : element.querySelector("table");
|
|
52
|
+
if (!table) return;
|
|
53
|
+
let scrollWrapper = table.closest(".lobe-editor-table-scroll-wrapper");
|
|
54
|
+
if (!table.closest(".lobe-editor-table-scroll-wrapper")) {
|
|
55
|
+
scrollWrapper = document.createElement("div");
|
|
56
|
+
scrollWrapper.className = "lobe-editor-table-scroll-wrapper";
|
|
57
|
+
table.parentElement?.insertBefore(scrollWrapper, table);
|
|
58
|
+
scrollWrapper.append(table);
|
|
59
|
+
}
|
|
60
|
+
if (!scrollWrapper) return;
|
|
61
|
+
const legacyToolbar = element.querySelector(":scope > .toolbar");
|
|
62
|
+
if (legacyToolbar instanceof HTMLElement) {
|
|
63
|
+
legacyToolbar.className = "toolbar-col";
|
|
64
|
+
markTableControllerHost(legacyToolbar, true);
|
|
65
|
+
scrollWrapper.append(legacyToolbar);
|
|
66
|
+
}
|
|
67
|
+
const ensureToolbar = (parent, className, withDecorator = false) => {
|
|
68
|
+
const existingToolbar = parent.querySelector(`:scope > .${className}`);
|
|
69
|
+
if (existingToolbar instanceof HTMLElement) {
|
|
70
|
+
markTableControllerHost(existingToolbar, withDecorator);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!existingToolbar) {
|
|
74
|
+
const toolbar = document.createElement("div");
|
|
75
|
+
toolbar.className = className;
|
|
76
|
+
markTableControllerHost(toolbar, withDecorator);
|
|
77
|
+
parent.append(toolbar);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const legacyDecoratedToolbar = scrollWrapper.querySelector(":scope > .toolbar[data-lexical-decorator]");
|
|
81
|
+
if (legacyDecoratedToolbar instanceof HTMLElement) {
|
|
82
|
+
legacyDecoratedToolbar.className = "toolbar-col";
|
|
83
|
+
markTableControllerHost(legacyDecoratedToolbar, true);
|
|
84
|
+
}
|
|
85
|
+
const legacyPlainToolbar = scrollWrapper.querySelector(":scope > .toolbar:not([data-lexical-decorator])");
|
|
86
|
+
if (legacyPlainToolbar instanceof HTMLElement) {
|
|
87
|
+
legacyPlainToolbar.className = "toolbar-row";
|
|
88
|
+
markTableControllerHost(legacyPlainToolbar, true);
|
|
89
|
+
element.append(legacyPlainToolbar);
|
|
90
|
+
}
|
|
91
|
+
ensureToolbar(scrollWrapper, "toolbar-col", true);
|
|
92
|
+
ensureToolbar(element, "toolbar-row", true);
|
|
93
|
+
ensureTableScrollIndicators(scrollWrapper);
|
|
94
|
+
}
|
|
95
|
+
function reconcileTableDecorator(editor, node, decorator) {
|
|
96
|
+
if (!decorator) return;
|
|
97
|
+
if (typeof decorator === "function") {
|
|
98
|
+
reconcileDecorator(editor, node.getKey(), decorator(node, editor));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if ("multi" in decorator) {
|
|
102
|
+
const decorators = decorator.multi.map((item) => ({
|
|
103
|
+
queryDOM: item.queryDOM,
|
|
104
|
+
render: item.render(node, editor)
|
|
105
|
+
}));
|
|
106
|
+
reconcileDecorator(editor, node.getKey(), { multi: decorators });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
reconcileDecorator(editor, node.getKey(), {
|
|
110
|
+
queryDOM: decorator.queryDOM,
|
|
111
|
+
render: decorator.render(node, editor)
|
|
112
|
+
});
|
|
113
|
+
}
|
|
4
114
|
function patchTableNode() {
|
|
115
|
+
if (TableNode.prototype.createDOM !== OriginalCreateDOM) return;
|
|
5
116
|
Object.defineProperty(TableNode.prototype, "createDOM", {
|
|
6
117
|
configurable: true,
|
|
7
118
|
enumerable: false,
|
|
8
119
|
value: function(config, editor) {
|
|
9
120
|
const table = OriginalCreateDOM.call(this, config, editor);
|
|
10
|
-
|
|
11
|
-
|
|
121
|
+
ensureTableControllerDOM(table);
|
|
122
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(TableNode.getType()) || null;
|
|
123
|
+
reconcileTableDecorator(editor, this, decorator);
|
|
124
|
+
return table;
|
|
125
|
+
},
|
|
126
|
+
writable: true
|
|
127
|
+
});
|
|
128
|
+
Object.defineProperty(TableNode.prototype, "updateDOM", {
|
|
129
|
+
configurable: true,
|
|
130
|
+
enumerable: false,
|
|
131
|
+
value: function(_prevNode, _dom, _config) {
|
|
132
|
+
const table = OriginalUpdateDOM.call(this, _prevNode, _dom, _config);
|
|
133
|
+
ensureTableControllerDOM(_dom);
|
|
134
|
+
const kernel = getKernelFromEditorConfig(_config);
|
|
135
|
+
const editor = kernel?.getLexicalEditor();
|
|
136
|
+
if (editor) {
|
|
137
|
+
const decorator = kernel?.getDecorator(TableNode.getType()) || null;
|
|
138
|
+
reconcileTableDecorator(editor, this, decorator);
|
|
139
|
+
}
|
|
12
140
|
return table;
|
|
13
141
|
},
|
|
14
142
|
writable: true
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { IEditorPluginConstructor } from "../../../types/kernel.js";
|
|
2
|
+
import { TableNode } from "../node/index.js";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
import { LexicalEditor } from "lexical";
|
|
5
|
+
|
|
2
6
|
//#region src/plugins/table/plugin/index.d.ts
|
|
3
7
|
interface TablePluginOptions {
|
|
8
|
+
decoratorCol?: (node: TableNode, editor: LexicalEditor) => ReactNode;
|
|
9
|
+
decoratorRow?: (node: TableNode, editor: LexicalEditor) => ReactNode;
|
|
4
10
|
theme?: string;
|
|
5
11
|
}
|
|
6
12
|
declare const TablePlugin: IEditorPluginConstructor<TablePluginOptions>;
|