@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.
Files changed (52) hide show
  1. package/es/editor-kernel/react/useDecorators.js +14 -8
  2. package/es/headless.d.ts +2 -0
  3. package/es/headless.js +710 -46
  4. package/es/index.d.ts +4 -3
  5. package/es/index.js +4 -3
  6. package/es/locale/index.d.ts +2 -0
  7. package/es/locale/index.js +5 -1
  8. package/es/plugins/auto-complete/plugin/index.js +3 -3
  9. package/es/plugins/block/index.d.ts +1 -1
  10. package/es/plugins/block/plugin/index.js +78 -2
  11. package/es/plugins/block/react/ReactBlockPlugin.js +172 -16
  12. package/es/plugins/block/react/drag/drag-utils.js +37 -10
  13. package/es/plugins/block/service/i-block-menu-service.d.ts +18 -1
  14. package/es/plugins/block/service/i-block-menu-service.js +24 -0
  15. package/es/plugins/block/service/index.d.ts +1 -1
  16. package/es/plugins/codeblock/plugin/index.js +25 -1
  17. package/es/plugins/common/plugin/register.js +2 -2
  18. package/es/plugins/litexml/plugin/index.js +8 -2
  19. package/es/plugins/slash/plugin/index.js +1 -1
  20. package/es/plugins/slash/react/ReactSlashPlugin.js +4 -4
  21. package/es/plugins/table/command/index.d.ts +13 -1
  22. package/es/plugins/table/command/index.js +220 -39
  23. package/es/plugins/table/index.d.ts +3 -2
  24. package/es/plugins/table/node/index.d.ts +2 -0
  25. package/es/plugins/table/node/index.js +130 -2
  26. package/es/plugins/table/plugin/index.d.ts +6 -0
  27. package/es/plugins/table/plugin/index.js +193 -4
  28. package/es/plugins/table/react/TableActionMenu/ActionMenu.js +82 -6
  29. package/es/plugins/table/react/TableActionMenu/index.js +9 -4
  30. package/es/plugins/table/react/TableColController.js +354 -0
  31. package/es/plugins/table/react/TableController/hooks.js +201 -0
  32. package/es/plugins/table/react/TableController/style.js +264 -0
  33. package/es/plugins/table/react/TableController/utils.js +25 -0
  34. package/es/plugins/table/react/TableControllerButton.js +81 -0
  35. package/es/plugins/table/react/TableControllerMenu.js +123 -0
  36. package/es/plugins/table/react/TableInsertButton.js +25 -0
  37. package/es/plugins/table/react/TableResize/index.js +153 -78
  38. package/es/plugins/table/react/TableRowController.js +349 -0
  39. package/es/plugins/table/react/hooks.js +77 -0
  40. package/es/plugins/table/react/index.js +139 -16
  41. package/es/plugins/table/react/style.js +89 -8
  42. package/es/plugins/table/react/type.d.ts +2 -0
  43. package/es/plugins/table/react/useAutoFitPastedTable.js +189 -0
  44. package/es/plugins/table/service/i-table-controller-menu-service.d.ts +44 -0
  45. package/es/plugins/table/service/i-table-controller-menu-service.js +31 -0
  46. package/es/plugins/table/service/index.d.ts +1 -0
  47. package/es/plugins/table/utils/autoFitColumnWidth.js +87 -0
  48. package/es/plugins/table/utils/distributeColumnWidth.js +37 -0
  49. package/es/plugins/table/utils/index.js +102 -2
  50. package/es/react/EditorProvider/index.d.ts +2 -2
  51. package/es/renderer/LexicalDiff.d.ts +2 -2
  52. 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): _$react_jsx_runtime0.JSX.Element;
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): _$react_jsx_runtime0.JSX.Element;
39
+ }: LexicalDiffProps): _$react.JSX.Element;
40
40
  //#endregion
41
41
  export { LexicalDiff, LexicalDiffProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "4.15.2",
3
+ "version": "4.16.0",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",