@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
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createDefaultTableColWidths } from "./index.js";
|
|
2
|
+
import { $computeTableMapSkipCellCheck } from "@lexical/table";
|
|
3
|
+
//#region src/plugins/table/utils/autoFitColumnWidth.ts
|
|
4
|
+
const AUTO_FIT_MIN_COLUMN_WIDTH = 75;
|
|
5
|
+
const MEASURE_CONTAINER_CLASS = "lobe-editor-table-auto-fit-measure";
|
|
6
|
+
const getTableElement$1 = (editor, tableKey) => {
|
|
7
|
+
const tableElement = editor.getElementByKey(tableKey);
|
|
8
|
+
return tableElement instanceof HTMLTableElement ? tableElement : tableElement?.querySelector("table.editor_table, table");
|
|
9
|
+
};
|
|
10
|
+
const applyNoWrapMeasureStyle = (element) => {
|
|
11
|
+
element.style.whiteSpace = "nowrap";
|
|
12
|
+
element.style.wordBreak = "normal";
|
|
13
|
+
element.style.overflowWrap = "normal";
|
|
14
|
+
element.style.setProperty("text-wrap", "nowrap");
|
|
15
|
+
};
|
|
16
|
+
const createMeasureContainer = (tableElement) => {
|
|
17
|
+
const container = document.createElement("div");
|
|
18
|
+
container.className = MEASURE_CONTAINER_CLASS;
|
|
19
|
+
container.style.insetBlockStart = "0";
|
|
20
|
+
container.style.insetInlineStart = "-100000px";
|
|
21
|
+
container.style.pointerEvents = "none";
|
|
22
|
+
container.style.position = "absolute";
|
|
23
|
+
container.style.visibility = "hidden";
|
|
24
|
+
container.style.zIndex = "-1";
|
|
25
|
+
(tableElement.parentElement ?? document.body).append(container);
|
|
26
|
+
return container;
|
|
27
|
+
};
|
|
28
|
+
const measureCellNoWrapWidth = (cellElement, measureContainer) => {
|
|
29
|
+
const table = document.createElement("table");
|
|
30
|
+
const tbody = document.createElement("tbody");
|
|
31
|
+
const row = document.createElement("tr");
|
|
32
|
+
const clone = cellElement.cloneNode(true);
|
|
33
|
+
table.className = "editor_table";
|
|
34
|
+
table.style.borderCollapse = "collapse";
|
|
35
|
+
table.style.borderSpacing = "0";
|
|
36
|
+
table.style.tableLayout = "auto";
|
|
37
|
+
table.style.width = "max-content";
|
|
38
|
+
delete clone.dataset.lexicalKey;
|
|
39
|
+
clone.style.blockSize = "auto";
|
|
40
|
+
clone.style.display = "table-cell";
|
|
41
|
+
clone.style.inlineSize = "max-content";
|
|
42
|
+
clone.style.maxInlineSize = "none";
|
|
43
|
+
clone.style.minInlineSize = "0";
|
|
44
|
+
clone.style.overflow = "visible";
|
|
45
|
+
clone.style.position = "static";
|
|
46
|
+
clone.style.width = "max-content";
|
|
47
|
+
applyNoWrapMeasureStyle(table);
|
|
48
|
+
applyNoWrapMeasureStyle(clone);
|
|
49
|
+
clone.querySelectorAll("*").forEach(applyNoWrapMeasureStyle);
|
|
50
|
+
row.append(clone);
|
|
51
|
+
tbody.append(row);
|
|
52
|
+
table.append(tbody);
|
|
53
|
+
measureContainer.append(table);
|
|
54
|
+
const width = Math.ceil(Math.max(table.getBoundingClientRect().width, clone.getBoundingClientRect().width));
|
|
55
|
+
table.remove();
|
|
56
|
+
return width;
|
|
57
|
+
};
|
|
58
|
+
const getAutoFitTableColumnWidths = (editor, tableNode, columnIndexes) => {
|
|
59
|
+
const tableElement = getTableElement$1(editor, tableNode.getKey());
|
|
60
|
+
if (!tableElement || columnIndexes.length === 0) return null;
|
|
61
|
+
const columnCount = tableNode.getColumnCount();
|
|
62
|
+
const targetColumnIndexes = [...new Set(columnIndexes)].filter((index) => index >= 0 && index < columnCount);
|
|
63
|
+
if (targetColumnIndexes.length === 0) return null;
|
|
64
|
+
const measureContainer = createMeasureContainer(tableElement);
|
|
65
|
+
try {
|
|
66
|
+
const [tableMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
67
|
+
const nextColWidths = [...tableNode.getColWidths() || createDefaultTableColWidths(columnCount)];
|
|
68
|
+
for (const columnIndex of targetColumnIndexes) {
|
|
69
|
+
const measuredCellKeys = /* @__PURE__ */ new Set();
|
|
70
|
+
let columnWidth = AUTO_FIT_MIN_COLUMN_WIDTH;
|
|
71
|
+
for (const row of tableMap) {
|
|
72
|
+
const cell = row[columnIndex]?.cell;
|
|
73
|
+
if (!cell || cell.getColSpan() > 1 || measuredCellKeys.has(cell.getKey())) continue;
|
|
74
|
+
const cellElement = editor.getElementByKey(cell.getKey());
|
|
75
|
+
if (!(cellElement instanceof HTMLElement)) continue;
|
|
76
|
+
measuredCellKeys.add(cell.getKey());
|
|
77
|
+
columnWidth = Math.max(columnWidth, measureCellNoWrapWidth(cellElement, measureContainer));
|
|
78
|
+
}
|
|
79
|
+
nextColWidths[columnIndex] = columnWidth;
|
|
80
|
+
}
|
|
81
|
+
return nextColWidths;
|
|
82
|
+
} finally {
|
|
83
|
+
measureContainer.remove();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
export { getAutoFitTableColumnWidths };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/plugins/table/utils/distributeColumnWidth.ts
|
|
2
|
+
const DISTRIBUTE_MIN_COLUMN_WIDTH = 75;
|
|
3
|
+
const getTableElement = (editor, tableKey) => {
|
|
4
|
+
const tableElement = editor.getElementByKey(tableKey);
|
|
5
|
+
return tableElement instanceof HTMLTableElement ? tableElement : tableElement?.querySelector("table.editor_table, table");
|
|
6
|
+
};
|
|
7
|
+
const getScrollContainerWidth = (tableElement) => {
|
|
8
|
+
const container = tableElement.closest(".lobe-editor-table-scroll-wrapper") ?? tableElement.parentElement;
|
|
9
|
+
if (!container) return 0;
|
|
10
|
+
return container.clientWidth || container.getBoundingClientRect().width;
|
|
11
|
+
};
|
|
12
|
+
const getHorizontalBorderWidth = (tableElement) => {
|
|
13
|
+
const firstRow = tableElement.rows[0];
|
|
14
|
+
if (!firstRow) return 0;
|
|
15
|
+
const cells = Array.from(firstRow.cells);
|
|
16
|
+
if (cells.length === 0) return 0;
|
|
17
|
+
return cells.reduce((total, cell, index) => {
|
|
18
|
+
const style = getComputedStyle(cell);
|
|
19
|
+
const borderInlineStartWidth = index === 0 ? Number.parseFloat(style.borderInlineStartWidth) || 0 : 0;
|
|
20
|
+
const borderInlineEndWidth = Number.parseFloat(style.borderInlineEndWidth) || 0;
|
|
21
|
+
return total + borderInlineStartWidth + borderInlineEndWidth;
|
|
22
|
+
}, 0);
|
|
23
|
+
};
|
|
24
|
+
const getDistributedTableColumnWidths = (editor, tableNode) => {
|
|
25
|
+
const columnCount = tableNode.getColumnCount();
|
|
26
|
+
if (columnCount === 0) return null;
|
|
27
|
+
const tableElement = getTableElement(editor, tableNode.getKey());
|
|
28
|
+
if (!tableElement) return null;
|
|
29
|
+
const minTableWidth = DISTRIBUTE_MIN_COLUMN_WIDTH * columnCount;
|
|
30
|
+
const targetTableWidth = Math.max(getScrollContainerWidth(tableElement) - getHorizontalBorderWidth(tableElement), minTableWidth);
|
|
31
|
+
const columnWidth = Math.floor(targetTableWidth / columnCount);
|
|
32
|
+
const colWidths = Array.from({ length: columnCount }, () => columnWidth);
|
|
33
|
+
colWidths[columnCount - 1] = targetTableWidth - columnWidth * (columnCount - 1);
|
|
34
|
+
return colWidths;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { getDistributedTableColumnWidths };
|
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
import { assert, init_utils } from "../../../editor-kernel/utils.js";
|
|
2
|
-
import { $isTableCellNode } from "@lexical/table";
|
|
2
|
+
import { $isTableCellNode, $isTableSelection } from "@lexical/table";
|
|
3
3
|
import { $getNearestNodeFromDOMNode } from "lexical";
|
|
4
4
|
import { addClassNamesToElement, removeClassNamesFromElement } from "@lexical/utils";
|
|
5
5
|
//#region src/plugins/table/utils/index.ts
|
|
6
6
|
init_utils();
|
|
7
|
+
const EMPTY_TABLE_SELECTION_INDEXES = {
|
|
8
|
+
selectedColumns: [],
|
|
9
|
+
selectedRows: []
|
|
10
|
+
};
|
|
11
|
+
function createDefaultTableColWidths(columnCount, tableWidth = 750) {
|
|
12
|
+
const safeColumnCount = Math.max(1, columnCount);
|
|
13
|
+
const columnWidth = Math.floor(tableWidth / safeColumnCount);
|
|
14
|
+
const colWidths = Array.from({ length: safeColumnCount }, () => columnWidth);
|
|
15
|
+
colWidths[safeColumnCount - 1] = tableWidth - columnWidth * (safeColumnCount - 1);
|
|
16
|
+
return colWidths;
|
|
17
|
+
}
|
|
18
|
+
function syncTableWidthDOM(editor, tableKey, colWidths) {
|
|
19
|
+
const tableElement = editor.getElementByKey(tableKey);
|
|
20
|
+
const table = tableElement instanceof HTMLTableElement ? tableElement : tableElement?.querySelector("table.editor_table, table");
|
|
21
|
+
if (!(table instanceof HTMLTableElement)) return;
|
|
22
|
+
table.style.width = `${colWidths.reduce((total, width) => total + width, 0)}px`;
|
|
23
|
+
}
|
|
24
|
+
const range = (from, to) => {
|
|
25
|
+
return Array.from({ length: to - from + 1 }, (_, index) => from + index);
|
|
26
|
+
};
|
|
27
|
+
function getSelectedTableColumnIndexes(selection, tableKey, rowCount) {
|
|
28
|
+
if (!$isTableSelection(selection) || selection.tableKey !== tableKey) return [];
|
|
29
|
+
const shape = selection.getShape();
|
|
30
|
+
return shape.fromY === 0 && shape.toY === rowCount - 1 ? range(shape.fromX, shape.toX) : [];
|
|
31
|
+
}
|
|
32
|
+
function getSelectedTableRowIndexes(selection, tableKey, columnCount) {
|
|
33
|
+
if (!$isTableSelection(selection) || selection.tableKey !== tableKey) return [];
|
|
34
|
+
const shape = selection.getShape();
|
|
35
|
+
return shape.fromX === 0 && shape.toX === columnCount - 1 ? range(shape.fromY, shape.toY) : [];
|
|
36
|
+
}
|
|
37
|
+
function isTableFullySelected(selection, tableKey, columnCount, rowCount) {
|
|
38
|
+
if (!$isTableSelection(selection) || selection.tableKey !== tableKey) return false;
|
|
39
|
+
const shape = selection.getShape();
|
|
40
|
+
return shape.fromX === 0 && shape.toX === columnCount - 1 && shape.fromY === 0 && shape.toY === rowCount - 1;
|
|
41
|
+
}
|
|
42
|
+
function getTableSelectionIndexes(selection, tableKey, columnCount, rowCount) {
|
|
43
|
+
if (!$isTableSelection(selection) || selection.tableKey !== tableKey) return EMPTY_TABLE_SELECTION_INDEXES;
|
|
44
|
+
return {
|
|
45
|
+
selectedColumns: getSelectedTableColumnIndexes(selection, tableKey, rowCount),
|
|
46
|
+
selectedRows: getSelectedTableRowIndexes(selection, tableKey, columnCount)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
7
49
|
function $forEachTableCell(grid, cb) {
|
|
8
50
|
const { domRows } = grid;
|
|
9
51
|
for (const [y, row] of domRows.entries()) {
|
|
@@ -30,12 +72,69 @@ function $removeHighlightFromDOM(editor, cell) {
|
|
|
30
72
|
const editorThemeClasses = editor._config.theme;
|
|
31
73
|
removeClassNamesFromElement(element, editorThemeClasses.tableCellSelected);
|
|
32
74
|
}
|
|
75
|
+
function getTableSelectionOutlineRect(selectedCells, table, selection) {
|
|
76
|
+
const outlineElements = [...selectedCells];
|
|
77
|
+
if (outlineElements.length === 0) return null;
|
|
78
|
+
const shape = selection.getShape();
|
|
79
|
+
const tableElement = outlineElements[0].closest("table.editor_table, table");
|
|
80
|
+
const scrollWrapper = tableElement?.closest(".lobe-editor-table-scroll-wrapper");
|
|
81
|
+
const tableRoot = scrollWrapper?.parentElement ?? tableElement?.parentElement;
|
|
82
|
+
const isColumnSelection = shape.fromY === 0 && shape.toY === table.rows - 1;
|
|
83
|
+
const isRowSelection = shape.fromX === 0 && shape.toX === table.columns - 1;
|
|
84
|
+
const selectedRowControllers = [];
|
|
85
|
+
if (isColumnSelection && isRowSelection) return null;
|
|
86
|
+
if (isColumnSelection) {
|
|
87
|
+
const columnControllers = scrollWrapper?.querySelectorAll(".table-controller-col .col");
|
|
88
|
+
for (let index = shape.fromX; index <= shape.toX; index++) {
|
|
89
|
+
const element = columnControllers?.[index];
|
|
90
|
+
if (element) outlineElements.push(element);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (isRowSelection) {
|
|
94
|
+
const rowControllers = tableRoot?.querySelectorAll(".table-controller-row .row");
|
|
95
|
+
for (let index = shape.fromY; index <= shape.toY; index++) {
|
|
96
|
+
const element = rowControllers?.[index];
|
|
97
|
+
if (element) {
|
|
98
|
+
selectedRowControllers.push(element);
|
|
99
|
+
outlineElements.push(element);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
let left = Number.POSITIVE_INFINITY;
|
|
104
|
+
let top = Number.POSITIVE_INFINITY;
|
|
105
|
+
let right = Number.NEGATIVE_INFINITY;
|
|
106
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
107
|
+
for (const element of outlineElements) {
|
|
108
|
+
if (!element.isConnected) continue;
|
|
109
|
+
const rect = element.getBoundingClientRect();
|
|
110
|
+
left = Math.min(left, rect.left);
|
|
111
|
+
top = Math.min(top, rect.top);
|
|
112
|
+
right = Math.max(right, rect.right);
|
|
113
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
114
|
+
}
|
|
115
|
+
if (isRowSelection && scrollWrapper) {
|
|
116
|
+
const scrollWrapperRect = scrollWrapper.getBoundingClientRect();
|
|
117
|
+
const tableRect = tableElement?.getBoundingClientRect();
|
|
118
|
+
const rowControllerLeft = selectedRowControllers.reduce((currentLeft, element) => Math.min(currentLeft, element.getBoundingClientRect().left), Number.POSITIVE_INFINITY);
|
|
119
|
+
left = rowControllerLeft === Number.POSITIVE_INFINITY ? scrollWrapperRect.left : rowControllerLeft;
|
|
120
|
+
right = Math.min(tableRect?.right ?? scrollWrapperRect.right, scrollWrapperRect.right);
|
|
121
|
+
}
|
|
122
|
+
if (left === Number.POSITIVE_INFINITY || top === Number.POSITIVE_INFINITY || right === Number.NEGATIVE_INFINITY || bottom === Number.NEGATIVE_INFINITY) return null;
|
|
123
|
+
return {
|
|
124
|
+
height: bottom - top,
|
|
125
|
+
left,
|
|
126
|
+
top,
|
|
127
|
+
width: right - left
|
|
128
|
+
};
|
|
129
|
+
}
|
|
33
130
|
function $updateDOMForSelection(editor, table, selection) {
|
|
34
131
|
const selectedCellNodes = new Set(selection ? selection.getNodes() : []);
|
|
132
|
+
const selectedCells = /* @__PURE__ */ new Set();
|
|
35
133
|
$forEachTableCell(table, (cell, lexicalNode) => {
|
|
36
134
|
const elem = cell.elem;
|
|
37
135
|
if (selectedCellNodes.has(lexicalNode)) {
|
|
38
136
|
cell.highlighted = true;
|
|
137
|
+
selectedCells.add(elem);
|
|
39
138
|
$addHighlightToDOM(editor, cell);
|
|
40
139
|
} else {
|
|
41
140
|
cell.highlighted = false;
|
|
@@ -43,6 +142,7 @@ function $updateDOMForSelection(editor, table, selection) {
|
|
|
43
142
|
if (!elem.getAttribute("style")) elem.removeAttribute("style");
|
|
44
143
|
}
|
|
45
144
|
});
|
|
145
|
+
return $isTableSelection(selection) ? getTableSelectionOutlineRect([...selectedCells], table, selection) : null;
|
|
46
146
|
}
|
|
47
147
|
//#endregion
|
|
48
|
-
export { $updateDOMForSelection };
|
|
148
|
+
export { $updateDOMForSelection, createDefaultTableColWidths, getTableSelectionIndexes, isTableFullySelected, syncTableWidthDOM };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _default } from "../../locale/index.js";
|
|
2
|
+
import * as _$react from "react";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/EditorProvider/index.d.ts
|
|
6
6
|
type LocaleType = typeof _default;
|
|
@@ -20,7 +20,7 @@ declare const EditorProvider: {
|
|
|
20
20
|
({
|
|
21
21
|
children,
|
|
22
22
|
config
|
|
23
|
-
}: EditorProviderProps): _$
|
|
23
|
+
}: EditorProviderProps): _$react.JSX.Element;
|
|
24
24
|
displayName: string;
|
|
25
25
|
};
|
|
26
26
|
declare const useEditorContent: () => EditorContextValue;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DiffAppearance } from "./diff/style.js";
|
|
2
2
|
import { LexicalDiffBlockRenderContext, LexicalDiffBlockRenderer, LexicalDiffCell, LexicalDiffRow, LexicalDiffRowKind } from "./diff/types.js";
|
|
3
3
|
import { LexicalRendererProps } from "./types.js";
|
|
4
|
+
import * as _$react from "react";
|
|
4
5
|
import { CSSProperties, ReactNode } from "react";
|
|
5
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
6
6
|
import { SerializedEditorState } from "lexical";
|
|
7
7
|
|
|
8
8
|
//#region src/renderer/LexicalDiff.d.ts
|
|
@@ -36,6 +36,6 @@ declare function LexicalDiff({
|
|
|
36
36
|
renderBlockDiff,
|
|
37
37
|
className,
|
|
38
38
|
style
|
|
39
|
-
}: LexicalDiffProps): _$
|
|
39
|
+
}: LexicalDiffProps): _$react.JSX.Element;
|
|
40
40
|
//#endregion
|
|
41
41
|
export { LexicalDiff, LexicalDiffProps };
|
package/package.json
CHANGED