@mx-space/cli 0.14.1 → 0.15.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 (56) hide show
  1. package/README.md +35 -11
  2. package/dist/{Lexical.dev-DTxuYHmG.mjs → Lexical.dev-Cck4n26y.mjs} +998 -584
  3. package/dist/{Lexical.node-BlCLnfA6.mjs → Lexical.node-D6wf2D5M.mjs} +15 -5
  4. package/dist/Lexical.prod-DCwS1oTq.mjs +7170 -0
  5. package/dist/{LexicalClipboard.dev-CdFkLR_D.mjs → LexicalClipboard.dev-CMkPHV7O.mjs} +6 -6
  6. package/dist/{LexicalClipboard.node-ktUajxsv.mjs → LexicalClipboard.node-m4nZ7IYw.mjs} +2 -2
  7. package/dist/{LexicalClipboard.prod-DliyLofW.mjs → LexicalClipboard.prod-B_rN1vE1.mjs} +10 -8
  8. package/dist/{LexicalCodeCore.dev-CL9tHWJX.mjs → LexicalCodeCore.dev-DyJw3i8V.mjs} +62 -72
  9. package/dist/{LexicalCodeCore.prod-BoMSJc29.mjs → LexicalCodeCore.prod-DPXLHQLF.mjs} +212 -213
  10. package/dist/{LexicalDragon.dev-CqW7Aq5H.mjs → LexicalDragon.dev-ZkNfMwrE.mjs} +3 -3
  11. package/dist/{LexicalDragon.node-DLXctJyy.mjs → LexicalDragon.node-Ds9oLUFh.mjs} +2 -2
  12. package/dist/{LexicalDragon.prod-DHI3T6n5.mjs → LexicalDragon.prod-B58ic2av.mjs} +5 -4
  13. package/dist/{LexicalExtension.dev-BP-18zns.mjs → LexicalExtension.dev-DvwHs1Xv.mjs} +79 -67
  14. package/dist/{LexicalExtension.node-BWH6xbzG.mjs → LexicalExtension.node-5OymZQfy.mjs} +4 -2
  15. package/dist/{LexicalExtension.prod-Cs6OgrDn.mjs → LexicalExtension.prod-Ce0T5smQ.mjs} +372 -358
  16. package/dist/{LexicalHeadless.dev-DJp__UcP.mjs → LexicalHeadless.dev-DA0WldSR.mjs} +2 -2
  17. package/dist/{LexicalHeadless.prod-o0v9cIu5.mjs → LexicalHeadless.prod-1kQgKB6c.mjs} +2 -2
  18. package/dist/{LexicalHtml.dev-DoymwFpG.mjs → LexicalHtml.dev-BDB7CUrx.mjs} +14 -12
  19. package/dist/{LexicalHtml.node-CVkbWtUA.mjs → LexicalHtml.node-DpVNt7It.mjs} +4 -4
  20. package/dist/{LexicalHtml.prod-C2b3z99k.mjs → LexicalHtml.prod-HkxM5scw.mjs} +331 -300
  21. package/dist/{LexicalLink.dev-7cgLOLIe.mjs → LexicalLink.dev-C5zNJlrm.mjs} +65 -59
  22. package/dist/{LexicalLink.prod-DWFgoXHR.mjs → LexicalLink.prod-Bkt9hQAt.mjs} +196 -201
  23. package/dist/{LexicalList.dev-Ci2iOxFx.mjs → LexicalList.dev-B7VgME-m.mjs} +55 -82
  24. package/dist/LexicalList.prod-B8c7TDjH.mjs +951 -0
  25. package/dist/{LexicalMarkdown.dev-C0ZYCyuR.mjs → LexicalMarkdown.dev-B5z9kLbV.mjs} +62 -42
  26. package/dist/{LexicalMarkdown.prod-0M-Nc-rh.mjs → LexicalMarkdown.prod-wKzw7FFH.mjs} +255 -202
  27. package/dist/{LexicalRichText.dev-Cnr1kDym.mjs → LexicalRichText.dev-CUd6Li5F.mjs} +399 -120
  28. package/dist/{LexicalRichText.node-BUJZAiap.mjs → LexicalRichText.node-BKfaQ-CH.mjs} +12 -8
  29. package/dist/LexicalRichText.prod-SBewjJHx.mjs +708 -0
  30. package/dist/{LexicalSelection.dev-C4L7l8OB.mjs → LexicalSelection.dev-D27JOCqj.mjs} +48 -17
  31. package/dist/{LexicalSelection.node-CLKroUMQ.mjs → LexicalSelection.node-DBEwPjBu.mjs} +5 -4
  32. package/dist/{LexicalSelection.prod-DzFqpTlw.mjs → LexicalSelection.prod-OaYhXHOL.mjs} +101 -92
  33. package/dist/{LexicalTable.dev-Cc9-zqKa.mjs → LexicalTable.dev-6IFFOAE0.mjs} +590 -259
  34. package/dist/LexicalTable.prod-2CaLZ7fH.mjs +2524 -0
  35. package/dist/{LexicalUtils.dev-BRgK0L6G.mjs → LexicalUtils.dev-DJA7J_7v.mjs} +15 -19
  36. package/dist/{LexicalUtils.node-BVAwOkL4.mjs → LexicalUtils.node-Df9oPTlA.mjs} +6 -6
  37. package/dist/{LexicalUtils.prod-C1gtvVDP.mjs → LexicalUtils.prod-Bb7V4Ewg.mjs} +155 -159
  38. package/dist/bin/mxs.mjs +1 -1
  39. package/dist/esm-CDcJmHkG.mjs +2 -0
  40. package/dist/{esm-BNu4sUWV.mjs → esm-DLzrv7gD.mjs} +1837 -6015
  41. package/dist/index.d.mts +57 -62
  42. package/dist/index.mjs +1 -1
  43. package/dist/{mxs-CMX2yklg.mjs → mxs-BIRW5Axj.mjs} +13579 -8682
  44. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  45. package/dist/vendor/litexml/cli.mjs +7043 -6207
  46. package/dist/vendor/litexml/litexml-html-preview-client.css +80 -47
  47. package/dist/vendor/litexml/litexml-html-preview-client.js +14422 -10838
  48. package/dist/vendor/litexml/style.css +6 -2
  49. package/package.json +23 -23
  50. package/skills/commands-ai.md +45 -12
  51. package/skills/overview.md +1 -1
  52. package/dist/Lexical.prod-CeB9UQuo.mjs +0 -6816
  53. package/dist/LexicalList.prod-D0xX5d3G.mjs +0 -944
  54. package/dist/LexicalRichText.prod-BfbMQ9uM.mjs +0 -538
  55. package/dist/LexicalTable.prod-BgO4nCxy.mjs +0 -2336
  56. package/dist/esm-jbwcVZth.mjs +0 -2
@@ -1,9 +1,9 @@
1
- import { f as namedSignals, l as effect, p as signal } from "./LexicalExtension.node-BWH6xbzG.mjs";
2
- import { $t as CONTROLLED_TEXT_INSERTION_COMMAND, A as $getChildCaret, An as IS_FIREFOX, B as $getNodeByKeyOrThrow, Bn as KEY_ARROW_LEFT_COMMAND, Br as isDOMDocumentNode, C as $getAdjacentChildCaret, Ct as $normalizeCaret, Dn as IS_BOLD, Dr as getDOMSelectionRange, Er as getDOMSelectionPoints, Fn as IS_STRIKETHROUGH, G as $getSiblingCaret, H as $getPreviousSelection, Hn as KEY_ARROW_UP_COMMAND, Hr as isDOMShadowRoot, Ht as CLICK_COMMAND, I as $getEditor, Jn as KEY_TAB_COMMAND, Jr as isHTMLTableRowElement, Jt as COMMAND_PRIORITY_HIGH, Kn as KEY_ESCAPE_COMMAND, Mt as $setSelection, Nn as IS_ITALIC, R as $getNearestNodeFromDOMNode, Rn as IS_UNDERLINE, Tr as getDOMSelection, U as $getRoot, Un as KEY_BACKSPACE_COMMAND, Vn as KEY_ARROW_RIGHT_COMMAND, Vr as isDOMNode, W as $getSelection, Wn as KEY_DELETE_COMMAND, Yt as COMMAND_PRIORITY_LOW, _t as $isSiblingCaret, ai as safeCast, an as DELETE_LINE_COMMAND, ar as SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, at as $isElementNode, b as $findMatchingParent, bn as INSERT_PARAGRAPH_COMMAND, br as defineExtension, bt as $isTextNode, dn as ElementNode, fn as FOCUS_COMMAND, ft as $isParagraphNode, g as $createRangeSelectionFromDom, gr as createCommand, h as $createRangeSelection, hr as configExtension, ht as $isRootOrShadowRoot, i as $caretFromPoint, ii as removeClassNamesFromElement, in as DELETE_CHARACTER_COMMAND, ir as SELECTION_CHANGE_COMMAND, jt as $setPointFromCaret, m as $createPoint, mn as FORMAT_TEXT_COMMAND, mt as $isRootNode, n as $applyNodeReplacement, ni as mergeRegister, nt as $isChildCaret, oi as setDOMStyleFromCSS, on as DELETE_WORD_COMMAND, or as SELECT_ALL_COMMAND, ot as $isExtendableTextPointCaret, p as $createParagraphNode, pn as FORMAT_ELEMENT_COMMAND, pr as addClassNamesToElement, pt as $isRangeSelection, qr as isHTMLElement, qt as COMMAND_PRIORITY_EDITOR, si as setDOMUnmanaged, st as $isInlineElementOrDecoratorNode, tn as CUT_COMMAND, ur as TEXT_TYPE_TO_FORMAT, ut as $isLineBreakNode, v as $createTextNode, wr as getComposedEventTarget, wt as $normalizeSelection__EXPERIMENTAL, x as $fullReconcile, y as $extendCaretToRange, z as $getNodeByKey, zn as KEY_ARROW_DOWN_COMMAND, zr as isCurrentlyReadOnlyMode } from "./Lexical.node-BlCLnfA6.mjs";
3
- import { c as DOMImportExtension, f as ImportTextFormat, h as defineImportRule, m as contextValue, o as $propagateTextAlignToBlockChildren, p as ImportTextStyle, s as CoreImportExtension, v as sel } from "./LexicalHtml.node-CVkbWtUA.mjs";
4
- import { d as $unwrapAndFilterDescendants, h as objectKlassEquals, n as $dfsWithSlots, o as $insertFirst$1, s as $insertNodeToNearestRoot, t as $descendantsMatching } from "./LexicalUtils.node-BVAwOkL4.mjs";
5
- import { o as copyToClipboard, t as $getClipboardDataFromSelection } from "./LexicalClipboard.node-ktUajxsv.mjs";
6
- //#region ../../node_modules/.pnpm/@lexical+table@0.46.0_typescript@6.0.3/node_modules/@lexical/table/dist/LexicalTable.dev.mjs
1
+ import { d as getPeerDependencyFromEditor, f as namedSignals, l as effect, p as signal } from "./LexicalExtension.node-5OymZQfy.mjs";
2
+ import { $r as isHTMLElement, $t as COMMAND_PRIORITY_LOW, A as $getChildCaret, Ar as getComposedEventTarget, B as $getNodeByKey, Dt as $normalizeSelection__EXPERIMENTAL, Et as $normalizeCaret, Ft as $setSelection, G as $getSelection, Gn as KEY_ARROW_RIGHT_COMMAND, Gr as isCurrentlyReadOnlyMode, Hn as IS_UNDERLINE, I as $getDocument, Jn as KEY_DELETE_COMMAND, Jr as isDOMShadowRoot, K as $getSiblingCaret, Kn as KEY_ARROW_UP_COMMAND, Kr as isDOMDocumentNode, Kt as CLICK_COMMAND, L as $getEditor, Ln as IS_ITALIC, Mr as getDOMSelectionPoints, Nr as getDOMSelectionRange, Or as getActiveElement, Pn as IS_FIREFOX, Pt as $setPointFromCaret, Qn as KEY_TAB_COMMAND, Qt as COMMAND_PRIORITY_HIGH, S as $fullReconcile, St as $isTextNode, Tr as defineExtension, U as $getPreviousSelection, Un as KEY_ARROW_DOWN_COMMAND, V as $getNodeByKeyOrThrow, W as $getRoot, Wn as KEY_ARROW_LEFT_COMMAND, Xn as KEY_ESCAPE_COMMAND, Zt as COMMAND_PRIORITY_EDITOR, _n as FORMAT_ELEMENT_COMMAND, an as CUT_COMMAND, b as $findMatchingParent, br as configExtension, ci as mergeRegister, cn as DELETE_CHARACTER_COMMAND, cr as SELECTION_CHANGE_COMMAND, ct as $isInlineElementOrDecoratorNode, dt as $isLineBreakNode, ei as isHTMLTableRowElement, fi as removeClassNamesFromElement, g as $createRangeSelectionFromDom, gn as FOCUS_COMMAND, gt as $isRootOrShadowRoot, h as $createRangeSelection, hi as setDOMUnmanaged, hn as ElementNode, hr as TEXT_TYPE_TO_FORMAT, ht as $isRootNode, i as $caretFromPoint, in as COPY_COMMAND, jn as IS_BOLD, jr as getDOMSelection, ln as DELETE_LINE_COMMAND, lr as SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, m as $createPoint, mi as setDOMStyleFromCSS, mt as $isRangeSelection, n as $applyNodeReplacement, ot as $isElementNode, p as $createParagraphNode, pi as safeCast, pt as $isParagraphNode, qn as KEY_BACKSPACE_COMMAND, qr as isDOMNode, rn as CONTROLLED_TEXT_INSERTION_COMMAND, rr as PASTE_COMMAND, rt as $isChildCaret, st as $isExtendableTextPointCaret, ui as registerEventListener, un as DELETE_WORD_COMMAND, ur as SELECT_ALL_COMMAND, v as $createTextNode, vn as FORMAT_TEXT_COMMAND, vr as addClassNamesToElement, w as $getAdjacentChildCaret, wn as INSERT_PARAGRAPH_COMMAND, xr as createCommand, y as $extendCaretToRange, yt as $isSiblingCaret, z as $getNearestNodeFromDOMNode, zn as IS_STRIKETHROUGH } from "./Lexical.node-D6wf2D5M.mjs";
3
+ import { c as CoreImportExtension, g as defineImportRule, h as contextValue, l as DOMImportExtension, m as ImportTextStyle, o as $propagateTextAlignToBlockChildren, p as ImportTextFormat, y as sel } from "./LexicalHtml.node-DpVNt7It.mjs";
4
+ import { d as $unwrapAndFilterDescendants, m as objectKlassEquals, n as $dfs, o as $insertFirst$1, s as $insertNodeToNearestRoot, t as $descendantsMatching } from "./LexicalUtils.node-Df9oPTlA.mjs";
5
+ import { o as copyToClipboard, t as $getClipboardDataFromSelection } from "./LexicalClipboard.node-m4nZ7IYw.mjs";
6
+ //#region ../../node_modules/.pnpm/@lexical+table@0.49.0_typescript@6.0.3/node_modules/@lexical/table/dist/LexicalTable.dev.mjs
7
7
  /**
8
8
  * Copyright (c) Meta Platforms, Inc. and affiliates.
9
9
  *
@@ -34,7 +34,7 @@ const TableCellHeaderStates = {
34
34
  ROW: 1
35
35
  };
36
36
  /** @noInheritDoc */
37
- var TableCellNode = class TableCellNode extends ElementNode {
37
+ var TableCellNode = class extends ElementNode {
38
38
  /** @internal */
39
39
  __colSpan;
40
40
  /** @internal */
@@ -47,11 +47,20 @@ var TableCellNode = class TableCellNode extends ElementNode {
47
47
  __backgroundColor;
48
48
  /** @internal */
49
49
  __verticalAlign;
50
- static getType() {
51
- return "tablecell";
52
- }
53
- static clone(node) {
54
- return new TableCellNode(node.__headerState, node.__colSpan, node.__width, node.__key);
50
+ $config() {
51
+ return this.config("tablecell", {
52
+ extends: ElementNode,
53
+ importDOM: {
54
+ td: () => ({
55
+ conversion: $convertTableCellNodeElement,
56
+ priority: 0
57
+ }),
58
+ th: () => ({
59
+ conversion: $convertTableCellNodeElement,
60
+ priority: 0
61
+ })
62
+ }
63
+ });
55
64
  }
56
65
  afterCloneFrom(node) {
57
66
  super.afterCloneFrom(node);
@@ -62,21 +71,6 @@ var TableCellNode = class TableCellNode extends ElementNode {
62
71
  this.__headerState = node.__headerState;
63
72
  this.__width = node.__width;
64
73
  }
65
- static importDOM() {
66
- return {
67
- td: (node) => ({
68
- conversion: $convertTableCellNodeElement,
69
- priority: 0
70
- }),
71
- th: (node) => ({
72
- conversion: $convertTableCellNodeElement,
73
- priority: 0
74
- })
75
- };
76
- }
77
- static importJSON(serializedNode) {
78
- return $createTableCellNode().updateFromJSON(serializedNode);
79
- }
80
74
  updateFromJSON(serializedNode) {
81
75
  return super.updateFromJSON(serializedNode).setHeaderStyles(serializedNode.headerState).setColSpan(serializedNode.colSpan || 1).setRowSpan(serializedNode.rowSpan || 1).setWidth(serializedNode.width || void 0).setBackgroundColor(serializedNode.backgroundColor || null).setVerticalAlign(serializedNode.verticalAlign || void 0);
82
76
  }
@@ -90,7 +84,7 @@ var TableCellNode = class TableCellNode extends ElementNode {
90
84
  this.__verticalAlign = void 0;
91
85
  }
92
86
  createDOM(config) {
93
- const element = document.createElement(this.getTag());
87
+ const element = $getDocument().createElement(this.getTag());
94
88
  if (this.__width) element.style.width = `${this.__width}px`;
95
89
  if (this.__colSpan > 1) element.colSpan = this.__colSpan;
96
90
  if (this.__rowSpan > 1) element.rowSpan = this.__rowSpan;
@@ -309,32 +303,26 @@ function formatDevErrorMessage(message) {
309
303
  *
310
304
  */
311
305
  /** @noInheritDoc */
312
- var TableRowNode = class TableRowNode extends ElementNode {
306
+ var TableRowNode = class extends ElementNode {
313
307
  /** @internal */
314
308
  __height;
315
- static getType() {
316
- return "tablerow";
317
- }
318
- static clone(node) {
319
- return new TableRowNode(node.__height, node.__key);
309
+ $config() {
310
+ return this.config("tablerow", {
311
+ extends: ElementNode,
312
+ importDOM: { tr: () => ({
313
+ conversion: $convertTableRowElement,
314
+ priority: 0
315
+ }) }
316
+ });
320
317
  }
321
318
  afterCloneFrom(prevNode) {
322
319
  super.afterCloneFrom(prevNode);
323
320
  this.__height = prevNode.__height;
324
321
  }
325
- static importDOM() {
326
- return { tr: (node) => ({
327
- conversion: $convertTableRowElement,
328
- priority: 0
329
- }) };
330
- }
331
- static importJSON(serializedNode) {
332
- return $createTableRowNode().updateFromJSON(serializedNode);
333
- }
334
322
  updateFromJSON(serializedNode) {
335
323
  return super.updateFromJSON(serializedNode).setHeight(serializedNode.height);
336
324
  }
337
- constructor(height, key) {
325
+ constructor(height = void 0, key) {
338
326
  super(key);
339
327
  this.__height = height;
340
328
  }
@@ -346,7 +334,7 @@ var TableRowNode = class TableRowNode extends ElementNode {
346
334
  };
347
335
  }
348
336
  createDOM(config) {
349
- const element = document.createElement("tr");
337
+ const element = $getDocument().createElement("tr");
350
338
  if (this.__height) element.style.height = `${this.__height}px`;
351
339
  addClassNamesToElement(element, config.theme.tableRow);
352
340
  return element;
@@ -860,6 +848,13 @@ function $unmergeCell() {
860
848
  if (!$isTableCellNode(cellNode)) formatDevErrorMessage(`Expected to find a parent TableCellNode`);
861
849
  return $unmergeCellNode(cellNode);
862
850
  }
851
+ /**
852
+ * Unmerges the given cell, splitting it back into individual cells.
853
+ * Unlike {@link $unmergeCell}, this does not depend on the current
854
+ * selection. No-op if the cell is not merged.
855
+ *
856
+ * @param cellNode The merged cell to split.
857
+ */
863
858
  function $unmergeCellNode(cellNode) {
864
859
  const [cell, row, grid] = $getNodeTriplet(cellNode);
865
860
  const colSpan = cell.__colSpan;
@@ -996,6 +991,10 @@ function $computeTableCellRectSpans(map, boundary) {
996
991
  topSpan
997
992
  };
998
993
  }
994
+ /**
995
+ * Compute the bounding rectangle of two table cells in a table map,
996
+ * expanding iteratively to include any merged cells that overlap the boundary.
997
+ */
999
998
  function $computeTableCellRectBoundary(map, cellAMap, cellBMap) {
1000
999
  let minColumn = Math.min(cellAMap.startColumn, cellBMap.startColumn);
1001
1000
  let minRow = Math.min(cellAMap.startRow, cellBMap.startRow);
@@ -1077,6 +1076,24 @@ function $moveTableColumn(tableNode, originColumn, targetColumn) {
1077
1076
  tableNode.setColWidths(newWidths);
1078
1077
  }
1079
1078
  }
1079
+ /**
1080
+ * Moves a row from one position to another within a simple (non-merged) table.
1081
+ *
1082
+ * @param tableNode The table node to modify.
1083
+ * @param originRow The index of the row to move.
1084
+ * @param targetRow The index to move the row to.
1085
+ */
1086
+ function $moveTableRow(tableNode, originRow, targetRow) {
1087
+ if (originRow === targetRow) return;
1088
+ const rows = tableNode.getChildren().filter($isTableRowNode);
1089
+ const rowCount = rows.length;
1090
+ if (originRow < 0 || originRow >= rowCount || targetRow < 0 || targetRow >= rowCount) return;
1091
+ if (!$isSimpleTable(tableNode)) return;
1092
+ const originRowNode = rows[originRow];
1093
+ const targetRowNode = rows[targetRow];
1094
+ if (targetRow > originRow) targetRowNode.insertAfter(originRowNode);
1095
+ else targetRowNode.insertBefore(originRowNode);
1096
+ }
1080
1097
  function $getTableCellNodeRect(tableCellNode) {
1081
1098
  const [cellNode, , gridNode] = $getNodeTriplet(tableCellNode);
1082
1099
  const rows = gridNode.getChildren().filter($isTableRowNode);
@@ -1104,6 +1121,119 @@ function $getTableCellNodeRect(tableCellNode) {
1104
1121
  }
1105
1122
  return null;
1106
1123
  }
1124
+ function $insertTableIntoGrid(tableNode, selection) {
1125
+ const anchorAndFocus = selection.getStartEndPoints();
1126
+ const isTableSel = $isTableSelection(selection);
1127
+ if (anchorAndFocus === null) return false;
1128
+ const [anchor, focus] = anchorAndFocus;
1129
+ const [anchorCellNode, anchorRowNode, gridNode] = $getNodeTriplet(anchor);
1130
+ const focusCellNode = $findMatchingParent(focus.getNode(), (n) => $isTableCellNode(n));
1131
+ if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableRowNode(anchorRowNode) || !$isTableNode(gridNode)) return false;
1132
+ const [initialGridMap, anchorCellMap, focusCellMap] = $computeTableMap(gridNode, anchorCellNode, focusCellNode);
1133
+ const [templateGridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
1134
+ const initialRowCount = initialGridMap.length;
1135
+ const initialColCount = initialRowCount > 0 ? initialGridMap[0].length : 0;
1136
+ let startRow = anchorCellMap.startRow;
1137
+ let startCol = anchorCellMap.startColumn;
1138
+ let affectedRowCount = templateGridMap.length;
1139
+ let affectedColCount = affectedRowCount > 0 ? templateGridMap[0].length : 0;
1140
+ if (isTableSel) {
1141
+ const selectionBoundary = $computeTableCellRectBoundary(initialGridMap, anchorCellMap, focusCellMap);
1142
+ const selectionRowCount = selectionBoundary.maxRow - selectionBoundary.minRow + 1;
1143
+ const selectionColCount = selectionBoundary.maxColumn - selectionBoundary.minColumn + 1;
1144
+ startRow = selectionBoundary.minRow;
1145
+ startCol = selectionBoundary.minColumn;
1146
+ affectedRowCount = Math.min(affectedRowCount, selectionRowCount);
1147
+ affectedColCount = Math.min(affectedColCount, selectionColCount);
1148
+ }
1149
+ let didPerformMergeOperations = false;
1150
+ const lastRowForUnmerge = Math.min(initialRowCount, startRow + affectedRowCount) - 1;
1151
+ const lastColForUnmerge = Math.min(initialColCount, startCol + affectedColCount) - 1;
1152
+ const unmergedKeys = /* @__PURE__ */ new Set();
1153
+ for (let row = startRow; row <= lastRowForUnmerge; row++) for (let col = startCol; col <= lastColForUnmerge; col++) {
1154
+ const cellMap = initialGridMap[row][col];
1155
+ if (unmergedKeys.has(cellMap.cell.getKey())) continue;
1156
+ if (cellMap.cell.__rowSpan === 1 && cellMap.cell.__colSpan === 1) continue;
1157
+ $unmergeCellNode(cellMap.cell);
1158
+ unmergedKeys.add(cellMap.cell.getKey());
1159
+ didPerformMergeOperations = true;
1160
+ }
1161
+ let [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
1162
+ const rowsToInsert = affectedRowCount - initialRowCount + startRow;
1163
+ for (let i = 0; i < rowsToInsert; i++) {
1164
+ const cellMap = interimGridMap[initialRowCount - 1][0];
1165
+ $insertTableRowAtNode(cellMap.cell);
1166
+ }
1167
+ const colsToInsert = affectedColCount - initialColCount + startCol;
1168
+ for (let i = 0; i < colsToInsert; i++) {
1169
+ const cellMap = interimGridMap[0][initialColCount - 1];
1170
+ $insertTableColumnAtNode(cellMap.cell, true, false);
1171
+ }
1172
+ [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
1173
+ for (let row = startRow; row < startRow + affectedRowCount; row++) for (let col = startCol; col < startCol + affectedColCount; col++) {
1174
+ const templateRow = row - startRow;
1175
+ const templateCol = col - startCol;
1176
+ const templateCellMap = templateGridMap[templateRow][templateCol];
1177
+ if (templateCellMap.startRow !== templateRow || templateCellMap.startColumn !== templateCol) continue;
1178
+ const templateCell = templateCellMap.cell;
1179
+ if (templateCell.__rowSpan !== 1 || templateCell.__colSpan !== 1) {
1180
+ const cellsToMerge = [];
1181
+ const lastRowForMerge = Math.min(row + templateCell.__rowSpan, startRow + affectedRowCount) - 1;
1182
+ const lastColForMerge = Math.min(col + templateCell.__colSpan, startCol + affectedColCount) - 1;
1183
+ for (let r = row; r <= lastRowForMerge; r++) for (let c = col; c <= lastColForMerge; c++) {
1184
+ const cellMap = interimGridMap[r][c];
1185
+ cellsToMerge.push(cellMap.cell);
1186
+ }
1187
+ $mergeCells(cellsToMerge);
1188
+ didPerformMergeOperations = true;
1189
+ }
1190
+ const { cell } = interimGridMap[row][col];
1191
+ const backgroundColor = templateCell.getBackgroundColor();
1192
+ if (backgroundColor !== null && backgroundColor !== void 0) cell.setBackgroundColor(backgroundColor);
1193
+ const originalChildren = cell.getChildren();
1194
+ templateCell.getChildren().forEach((child) => {
1195
+ if ($isTextNode(child)) {
1196
+ $createParagraphNode().append(child);
1197
+ cell.append(child);
1198
+ } else cell.append(child);
1199
+ });
1200
+ originalChildren.forEach((n) => n.remove());
1201
+ }
1202
+ if (isTableSel && didPerformMergeOperations) {
1203
+ const [finalGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
1204
+ finalGridMap[anchorCellMap.startRow][anchorCellMap.startColumn].cell.selectEnd();
1205
+ }
1206
+ return true;
1207
+ }
1208
+ function $setTableRowIsHeader(tableNode, rowIndex, isHeader) {
1209
+ const [gridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
1210
+ if (!(rowIndex >= 0 && rowIndex < gridMap.length)) formatDevErrorMessage(`Table row index ${String(rowIndex)} out of range`);
1211
+ const rowMap = gridMap[rowIndex];
1212
+ const headerState = isHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
1213
+ const visited = /* @__PURE__ */ new Set();
1214
+ for (let col = 0; col < rowMap.length; col++) {
1215
+ const mapCell = rowMap[col];
1216
+ if (mapCell == null) continue;
1217
+ if (!visited.has(mapCell.cell)) {
1218
+ visited.add(mapCell.cell);
1219
+ mapCell.cell.setHeaderStyles(headerState, TableCellHeaderStates.ROW);
1220
+ }
1221
+ }
1222
+ }
1223
+ function $setTableColumnIsHeader(tableNode, columnIndex, isHeader) {
1224
+ const [gridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
1225
+ if (!(gridMap.length > 0 && columnIndex >= 0 && columnIndex < gridMap[0].length)) formatDevErrorMessage(`Table column index ${String(columnIndex)} out of range`);
1226
+ const headerState = isHeader ? TableCellHeaderStates.COLUMN : TableCellHeaderStates.NO_STATUS;
1227
+ const visited = /* @__PURE__ */ new Set();
1228
+ for (let row = 0; row < gridMap.length; row++) {
1229
+ const mapCell = gridMap[row][columnIndex];
1230
+ if (mapCell == null) continue;
1231
+ if (!visited.has(mapCell.cell)) {
1232
+ visited.add(mapCell.cell);
1233
+ mapCell.cell.setHeaderStyles(headerState, TableCellHeaderStates.COLUMN);
1234
+ }
1235
+ }
1236
+ }
1107
1237
  function $getCellNodes(tableSelection) {
1108
1238
  const [[anchorNode, anchorCell, anchorRow, anchorTable], [focusNode, focusCell, focusRow, focusTable]] = ["anchor", "focus"].map((k) => {
1109
1239
  const node = tableSelection[k].getNode();
@@ -1197,14 +1327,31 @@ var TableSelection = class TableSelection {
1197
1327
  extract() {
1198
1328
  return this.getNodes();
1199
1329
  }
1200
- insertRawText(text) {}
1330
+ insertRawText(text) {
1331
+ if (text === "") return;
1332
+ const tsvGrid = (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n").map((line) => line.split(" "));
1333
+ const tableNode = $createTableNode();
1334
+ for (const row of tsvGrid) {
1335
+ const rowNode = $createTableRowNode();
1336
+ for (const cellText of row) {
1337
+ const cellNode = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
1338
+ const paragraph = $createParagraphNode();
1339
+ if (cellText) paragraph.append($createTextNode(cellText));
1340
+ cellNode.append(paragraph);
1341
+ rowNode.append(cellNode);
1342
+ }
1343
+ tableNode.append(rowNode);
1344
+ }
1345
+ const { anchorCell } = $getCellNodes(this);
1346
+ $insertTableIntoGrid(tableNode, anchorCell.select(0, anchorCell.getChildrenSize()));
1347
+ }
1201
1348
  insertText() {}
1202
1349
  /**
1203
1350
  * Returns whether the provided TextFormatType is present on the Selection.
1204
1351
  * This will be true if any paragraph in table cells has the specified format.
1205
1352
  *
1206
1353
  * @param type the TextFormatType to check for.
1207
- * @returns true if the provided format is currently toggled on on the Selection, false otherwise.
1354
+ * @returns true if the provided format is currently toggled on the Selection, false otherwise.
1208
1355
  */
1209
1356
  hasFormat(type) {
1210
1357
  let format = 0;
@@ -1220,6 +1367,7 @@ var TableSelection = class TableSelection {
1220
1367
  if (!$isElementNode(focusNode)) formatDevErrorMessage(`Expected TableSelection focus to be an ElementNode`);
1221
1368
  $normalizeSelection__EXPERIMENTAL(focusNode.select(0, focusNode.getChildrenSize())).insertNodes(nodes);
1222
1369
  }
1370
+ /** @deprecated Use {@link $computeTableMap} and {@link $computeTableCellRectBoundary} directly. */
1223
1371
  getShape() {
1224
1372
  const { anchorCell, focusCell } = $getCellNodes(this);
1225
1373
  const anchorCellNodeRect = $getTableCellNodeRect(anchorCell);
@@ -1287,12 +1435,23 @@ var TableSelection = class TableSelection {
1287
1435
  return textContent;
1288
1436
  }
1289
1437
  };
1438
+ /** Type guard for {@link TableSelection}. */
1290
1439
  function $isTableSelection(x) {
1291
1440
  return x instanceof TableSelection;
1292
1441
  }
1442
+ /** @deprecated Use {@link $createTableSelectionFrom} instead. */
1293
1443
  function $createTableSelection() {
1294
1444
  return new TableSelection("root", $createPoint("root", 0, "element"), $createPoint("root", 0, "element"));
1295
1445
  }
1446
+ /**
1447
+ * Creates a {@link TableSelection} spanning from `anchorCell` to `focusCell`
1448
+ * within `tableNode`. In `__DEV__` mode, validates that both cells belong to
1449
+ * the given table and that the table is attached to the editor state.
1450
+ *
1451
+ * If the current selection is already a TableSelection, it clones and
1452
+ * re-targets it (preserving identity for dirty-checking); otherwise it
1453
+ * constructs a fresh one.
1454
+ */
1296
1455
  function $createTableSelectionFrom(tableNode, anchorCell, focusCell) {
1297
1456
  const tableNodeKey = tableNode.getKey();
1298
1457
  const anchorCellKey = anchorCell.getKey();
@@ -1301,7 +1460,7 @@ function $createTableSelectionFrom(tableNode, anchorCell, focusCell) {
1301
1460
  if (!tableNode.is($findTableNode(anchorCell))) formatDevErrorMessage(`$createTableSelectionFrom: anchorCell ${anchorCellKey} is not in table ${tableNodeKey}`);
1302
1461
  if (!tableNode.is($findTableNode(focusCell))) formatDevErrorMessage(`$createTableSelectionFrom: focusCell ${focusCellKey} is not in table ${tableNodeKey}`);
1303
1462
  const prevSelection = $getSelection();
1304
- const nextSelection = $isTableSelection(prevSelection) ? prevSelection.clone() : $createTableSelection();
1463
+ const nextSelection = $isTableSelection(prevSelection) ? prevSelection.clone() : new TableSelection("root", $createPoint("root", 0, "element"), $createPoint("root", 0, "element"));
1305
1464
  nextSelection.set(tableNode.getKey(), anchorCell.getKey(), focusCell.getKey());
1306
1465
  return nextSelection;
1307
1466
  }
@@ -1525,7 +1684,7 @@ var TableObserver = class {
1525
1684
  $updateDOMForSelection(editor, getTable(tableNode, tableElement), null);
1526
1685
  if (setEmptySelection && $getSelection() !== null) {
1527
1686
  $setSelection(null);
1528
- editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1687
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1529
1688
  }
1530
1689
  }
1531
1690
  $enableHighlightStyle() {
@@ -1583,7 +1742,7 @@ var TableObserver = class {
1583
1742
  this.focusCellNodeKey = targetCellNode.getKey();
1584
1743
  this.tableSelection = $createTableSelectionFrom(tableNode, anchorTableCell, targetCellNode);
1585
1744
  $setSelection(this.tableSelection);
1586
- editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1745
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1587
1746
  $updateDOMForSelection(editor, this.table, this.tableSelection);
1588
1747
  return true;
1589
1748
  }
@@ -1645,7 +1804,7 @@ var TableObserver = class {
1645
1804
  formatSelection.formatText(type, alignFormatWith);
1646
1805
  });
1647
1806
  $setSelection(selection);
1648
- this.editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1807
+ this.editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1649
1808
  }
1650
1809
  $clearText() {
1651
1810
  const { editor } = this;
@@ -1660,7 +1819,7 @@ var TableObserver = class {
1660
1819
  tableNode.selectPrevious();
1661
1820
  const parent = tableNode.getParent();
1662
1821
  tableNode.remove();
1663
- if ($isRootNode(parent) && parent.isEmpty()) editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, void 0);
1822
+ if ($isRootNode(parent) && parent.isEmpty()) editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND);
1664
1823
  return;
1665
1824
  }
1666
1825
  selectedNodes.forEach((cellNode) => {
@@ -1676,7 +1835,7 @@ var TableObserver = class {
1676
1835
  });
1677
1836
  $updateDOMForSelection(editor, this.table, null);
1678
1837
  $setSelection(null);
1679
- editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1838
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1680
1839
  }
1681
1840
  };
1682
1841
  const LEXICAL_ELEMENT_KEY = "__lexicalTableSelection";
@@ -1688,6 +1847,14 @@ function $getTableNodeByKeyOrThrow(key) {
1688
1847
  const isPointerDownOnEvent = (event) => {
1689
1848
  return (event.buttons & 1) === 1;
1690
1849
  };
1850
+ const AUTO_SCROLL_EDGE_ZONE = 40;
1851
+ const AUTO_SCROLL_MAX_STEP = 18;
1852
+ function autoScrollStep(pos, start, end) {
1853
+ const speed = (depth) => Math.max(1, Math.ceil(Math.min(AUTO_SCROLL_EDGE_ZONE, depth) / AUTO_SCROLL_EDGE_ZONE * AUTO_SCROLL_MAX_STEP));
1854
+ if (pos <= start + AUTO_SCROLL_EDGE_ZONE) return -speed(start + AUTO_SCROLL_EDGE_ZONE - pos);
1855
+ if (pos >= end - AUTO_SCROLL_EDGE_ZONE) return speed(pos - (end - AUTO_SCROLL_EDGE_ZONE));
1856
+ return 0;
1857
+ }
1691
1858
  function isHTMLTableElement(el) {
1692
1859
  return isHTMLElement(el) && el.nodeName === "TABLE";
1693
1860
  }
@@ -1730,17 +1897,14 @@ function registerTableWindowHandlers(editor, tableObservers) {
1730
1897
  if ($isTableSelection($getSelection())) {
1731
1898
  for (const [observer] of tableObservers.observers.values()) observer.$clearHighlight(false);
1732
1899
  $setSelection(null);
1733
- editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1900
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1734
1901
  }
1735
1902
  if (!selectionInfo) return;
1736
1903
  const { tableObserver, tableElement, cellElement } = selectionInfo;
1737
1904
  $handleTableClick(editor, event, cellElement, tableElement, tableObserver, tableObservers);
1738
1905
  });
1739
1906
  };
1740
- editorWindow.addEventListener("pointerdown", pointerDownCallback);
1741
- return () => {
1742
- editorWindow.removeEventListener("pointerdown", pointerDownCallback);
1743
- };
1907
+ return registerEventListener(editorWindow, "pointerdown", pointerDownCallback);
1744
1908
  });
1745
1909
  }
1746
1910
  function $handleTableClick(editor, event, selectedDOMCell, tableElement, tableObserver, tableObservers) {
@@ -1752,45 +1916,137 @@ function $handleTableClick(editor, event, selectedDOMCell, tableElement, tableOb
1752
1916
  if (startingCell !== null && tableObserver.anchorCell === null) editor.update(() => {
1753
1917
  tableObserver.$setAnchorCellForSelection(startingCell);
1754
1918
  });
1755
- const onPointerUp = () => {
1919
+ let lastClientX = event.clientX;
1920
+ let lastClientY = event.clientY;
1921
+ let autoScrollRafId = null;
1922
+ const stopSelecting = () => {
1756
1923
  tableObserver.isSelecting = false;
1924
+ if (autoScrollRafId !== null) {
1925
+ editorWindow.cancelAnimationFrame(autoScrollRafId);
1926
+ autoScrollRafId = null;
1927
+ }
1757
1928
  editorWindow.removeEventListener("pointerup", onPointerUp);
1758
1929
  editorWindow.removeEventListener("pointermove", onPointerMove);
1759
1930
  };
1931
+ const resolveFocusCellFromPoint = (clientX, clientY) => {
1932
+ const tableRoot = tableElement.getRootNode();
1933
+ if (!isDOMDocumentNode(tableRoot) && !isDOMShadowRoot(tableRoot)) return null;
1934
+ for (const el of tableRoot.elementsFromPoint(clientX, clientY)) {
1935
+ const cell = getDOMCellInTableFromTarget(tableElement, el);
1936
+ if (cell) return cell;
1937
+ }
1938
+ return null;
1939
+ };
1940
+ const applyFocusCell = (focusCell, override) => {
1941
+ if (tableObserver.anchorCell === null) editor.update(() => {
1942
+ tableObserver.$setAnchorCellForSelection(focusCell);
1943
+ });
1944
+ if (tableObserver.focusCell === null || focusCell.elem !== tableObserver.focusCell.elem) {
1945
+ tableObservers.setNextFocus({
1946
+ focusCell,
1947
+ override,
1948
+ tableKey: tableObserver.tableNodeKey
1949
+ });
1950
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
1951
+ }
1952
+ };
1953
+ const findScrollContainer = (axis) => {
1954
+ for (let el = tableElement.parentElement; el; el = el.parentElement) if (axis === "x" ? el.scrollWidth > el.clientWidth : el.scrollHeight > el.clientHeight) {
1955
+ const style = editorWindow.getComputedStyle(el);
1956
+ const overflow = axis === "x" ? style.overflowX : style.overflowY;
1957
+ if (overflow === "auto" || overflow === "scroll") return el;
1958
+ }
1959
+ return null;
1960
+ };
1961
+ const scrollAxis = (container, pos, axis) => {
1962
+ let start;
1963
+ let end;
1964
+ if (container === null) {
1965
+ start = 0;
1966
+ end = axis === "x" ? editorWindow.innerWidth : editorWindow.innerHeight;
1967
+ } else {
1968
+ const rect = container.getBoundingClientRect();
1969
+ start = axis === "x" ? rect.left : rect.top;
1970
+ end = axis === "x" ? rect.right : rect.bottom;
1971
+ }
1972
+ const step = autoScrollStep(pos, start, end);
1973
+ if (step === 0) return false;
1974
+ if (container === null) {
1975
+ const before = axis === "x" ? editorWindow.scrollX : editorWindow.scrollY;
1976
+ editorWindow.scrollBy(axis === "x" ? step : 0, axis === "x" ? 0 : step);
1977
+ return (axis === "x" ? editorWindow.scrollX : editorWindow.scrollY) !== before;
1978
+ }
1979
+ if (axis === "x") {
1980
+ const before = container.scrollLeft;
1981
+ container.scrollLeft += step;
1982
+ return container.scrollLeft !== before;
1983
+ }
1984
+ const before = container.scrollTop;
1985
+ container.scrollTop += step;
1986
+ return container.scrollTop !== before;
1987
+ };
1988
+ const clampHitPoint = (hContainer, vContainer) => {
1989
+ let x = lastClientX;
1990
+ let y = lastClientY;
1991
+ if (hContainer === null) x = Math.min(Math.max(x, 1), editorWindow.innerWidth - 1);
1992
+ else {
1993
+ const rect = hContainer.getBoundingClientRect();
1994
+ x = Math.min(Math.max(x, rect.left + 1), rect.right - 1);
1995
+ }
1996
+ if (vContainer === null) y = Math.min(Math.max(y, 1), editorWindow.innerHeight - 1);
1997
+ else {
1998
+ const rect = vContainer.getBoundingClientRect();
1999
+ y = Math.min(Math.max(y, rect.top + 1), rect.bottom - 1);
2000
+ }
2001
+ return [x, y];
2002
+ };
2003
+ const isNearScrollEdge = () => {
2004
+ const hContainer = findScrollContainer("x");
2005
+ if (hContainer !== null) {
2006
+ const rect = hContainer.getBoundingClientRect();
2007
+ if (autoScrollStep(lastClientX, rect.left, rect.right) !== 0) return true;
2008
+ }
2009
+ const vContainer = findScrollContainer("y");
2010
+ const vStart = vContainer === null ? 0 : vContainer.getBoundingClientRect().top;
2011
+ const vEnd = vContainer === null ? editorWindow.innerHeight : vContainer.getBoundingClientRect().bottom;
2012
+ return autoScrollStep(lastClientY, vStart, vEnd) !== 0;
2013
+ };
2014
+ const tickAutoScroll = () => {
2015
+ autoScrollRafId = null;
2016
+ if (!tableObserver.isSelecting) return;
2017
+ const hContainer = findScrollContainer("x");
2018
+ const vContainer = findScrollContainer("y");
2019
+ const scrolledX = hContainer !== null && scrollAxis(hContainer, lastClientX, "x");
2020
+ const scrolledY = scrollAxis(vContainer, lastClientY, "y");
2021
+ if (scrolledX || scrolledY) {
2022
+ const [hitX, hitY] = clampHitPoint(hContainer, vContainer);
2023
+ const focusCell = resolveFocusCellFromPoint(hitX, hitY);
2024
+ if (focusCell) applyFocusCell(focusCell, false);
2025
+ autoScrollRafId = editorWindow.requestAnimationFrame(tickAutoScroll);
2026
+ }
2027
+ };
2028
+ const maybeStartAutoScroll = () => {
2029
+ if (autoScrollRafId !== null || tableObserver.pointerType === "touch" || !isNearScrollEdge()) return;
2030
+ autoScrollRafId = editorWindow.requestAnimationFrame(tickAutoScroll);
2031
+ };
2032
+ const onPointerUp = () => {
2033
+ stopSelecting();
2034
+ };
1760
2035
  const onPointerMove = (moveEvent) => {
1761
2036
  if (!isPointerDownOnEvent(moveEvent) && tableObserver.isSelecting) {
1762
- tableObserver.isSelecting = false;
1763
- editorWindow.removeEventListener("pointerup", onPointerUp);
1764
- editorWindow.removeEventListener("pointermove", onPointerMove);
2037
+ stopSelecting();
1765
2038
  return;
1766
2039
  }
1767
2040
  const moveTarget = getComposedEventTarget(moveEvent);
1768
2041
  if (!isDOMNode(moveTarget)) return;
2042
+ lastClientX = moveEvent.clientX;
2043
+ lastClientY = moveEvent.clientY;
1769
2044
  let focusCell = null;
1770
2045
  const override = !(IS_FIREFOX || tableElement.contains(moveTarget));
1771
2046
  if (override) focusCell = getDOMCellInTableFromTarget(tableElement, moveTarget);
1772
- else {
1773
- const tableRoot = tableElement.getRootNode();
1774
- if (!isDOMDocumentNode(tableRoot) && !isDOMShadowRoot(tableRoot)) return;
1775
- for (const el of tableRoot.elementsFromPoint(moveEvent.clientX, moveEvent.clientY)) {
1776
- focusCell = getDOMCellInTableFromTarget(tableElement, el);
1777
- if (focusCell) break;
1778
- }
1779
- }
1780
- if (focusCell) {
1781
- const anchorCell = focusCell;
1782
- if (tableObserver.anchorCell === null) editor.update(() => {
1783
- tableObserver.$setAnchorCellForSelection(anchorCell);
1784
- });
1785
- if (tableObserver.focusCell === null || focusCell.elem !== tableObserver.focusCell.elem) {
1786
- tableObservers.setNextFocus({
1787
- focusCell,
1788
- override,
1789
- tableKey: tableObserver.tableNodeKey
1790
- });
1791
- editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
1792
- }
1793
- }
2047
+ else focusCell = resolveFocusCellFromPoint(moveEvent.clientX, moveEvent.clientY);
2048
+ if (focusCell) applyFocusCell(focusCell, override);
2049
+ maybeStartAutoScroll();
1794
2050
  };
1795
2051
  editorWindow.addEventListener("pointerup", onPointerUp, tableObserver.listenerOptions);
1796
2052
  editorWindow.addEventListener("pointermove", onPointerMove, tableObserver.listenerOptions);
@@ -1823,10 +2079,7 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler, tableObse
1823
2079
  if (getDOMCellFromTarget(target) !== null) event.preventDefault();
1824
2080
  }
1825
2081
  };
1826
- tableElement.addEventListener("mousedown", onTripleClick, tableObserver.listenerOptions);
1827
- tableObserver.listenersToRemove.add(() => {
1828
- tableElement.removeEventListener("mousedown", onTripleClick);
1829
- });
2082
+ tableObserver.listenersToRemove.add(registerEventListener(tableElement, "mousedown", onTripleClick, tableObserver.listenerOptions));
1830
2083
  for (const [command, direction] of ARROW_KEY_COMMANDS_WITH_DIRECTION) tableObserver.listenersToRemove.add(editor.registerCommand(command, (event) => $handleArrowKey(editor, event, direction, tableNode, tableObserver, tableObservers), COMMAND_PRIORITY_HIGH));
1831
2084
  tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ESCAPE_COMMAND, (event) => {
1832
2085
  const selection = $getSelection();
@@ -1840,30 +2093,16 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler, tableObse
1840
2093
  }
1841
2094
  return false;
1842
2095
  }, COMMAND_PRIORITY_HIGH));
1843
- const deleteTextHandler = (command) => () => {
2096
+ const $deleteTextHandler = () => {
1844
2097
  const selection = $getSelection();
1845
2098
  if (!$isSelectionInTable(selection, tableNode)) return false;
1846
2099
  if ($isTableSelection(selection)) {
1847
2100
  tableObserver.$clearText();
1848
2101
  return true;
1849
- } else if ($isRangeSelection(selection)) {
1850
- if (!$isTableCellNode($findParentTableCellNodeInTable(tableNode, selection.anchor.getNode()))) return false;
1851
- const anchorNode = selection.anchor.getNode();
1852
- const focusNode = selection.focus.getNode();
1853
- const isAnchorInside = tableNode.isParentOf(anchorNode);
1854
- const isFocusInside = tableNode.isParentOf(focusNode);
1855
- if (isAnchorInside && !isFocusInside || isFocusInside && !isAnchorInside) {
1856
- tableObserver.$clearText();
1857
- return true;
1858
- }
1859
- const nearestElementNode = $findMatchingParent(selection.anchor.getNode(), (n) => $isElementNode(n));
1860
- const topLevelCellElementNode = nearestElementNode && $findMatchingParent(nearestElementNode, (n) => $isElementNode(n) && $isTableCellNode(n.getParent()));
1861
- if (!$isElementNode(topLevelCellElementNode) || !$isElementNode(nearestElementNode)) return false;
1862
- if (command === DELETE_LINE_COMMAND && topLevelCellElementNode.getPreviousSibling() === null) return true;
1863
2102
  }
1864
2103
  return false;
1865
2104
  };
1866
- for (const command of DELETE_TEXT_COMMANDS) tableObserver.listenersToRemove.add(editor.registerCommand(command, deleteTextHandler(command), COMMAND_PRIORITY_HIGH));
2105
+ for (const command of DELETE_TEXT_COMMANDS) tableObserver.listenersToRemove.add(editor.registerCommand(command, $deleteTextHandler, COMMAND_PRIORITY_HIGH));
1867
2106
  const $deleteCellHandler = (event) => {
1868
2107
  const selection = $getSelection();
1869
2108
  if (!($isTableSelection(selection) || $isRangeSelection(selection))) return false;
@@ -1901,14 +2140,35 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler, tableObse
1901
2140
  }
1902
2141
  return false;
1903
2142
  }, COMMAND_PRIORITY_HIGH));
2143
+ const doc = rootElement.ownerDocument;
2144
+ tableObserver.listenersToRemove.add(registerEventListener(doc, "paste", (event) => {
2145
+ if (event.defaultPrevented) return;
2146
+ if (editor.read("latest", () => {
2147
+ const selection = $getSelection();
2148
+ return rootElement.contains(doc.activeElement) && $isTableSelection(selection) && $isSelectionInTable(selection, tableNode);
2149
+ })) {
2150
+ event.preventDefault();
2151
+ editor.dispatchCommand(PASTE_COMMAND, event);
2152
+ }
2153
+ }));
2154
+ tableObserver.listenersToRemove.add(registerEventListener(doc, "copy", (event) => {
2155
+ if (event.defaultPrevented) return;
2156
+ const target = getComposedEventTarget(event);
2157
+ if (target === rootElement || isDOMNode(target) && rootElement.contains(target)) return;
2158
+ if (editor.read("latest", () => {
2159
+ const selection = $getSelection();
2160
+ return rootElement.contains(getActiveElement(rootElement)) && $isTableSelection(selection) && $isSelectionInTable(selection, tableNode);
2161
+ })) {
2162
+ event.preventDefault();
2163
+ editor.dispatchCommand(COPY_COMMAND, event);
2164
+ }
2165
+ }));
1904
2166
  tableObserver.listenersToRemove.add(editor.registerCommand(FORMAT_TEXT_COMMAND, (payload) => {
1905
2167
  const selection = $getSelection();
1906
2168
  if (!$isSelectionInTable(selection, tableNode)) return false;
1907
2169
  if ($isTableSelection(selection)) {
1908
2170
  tableObserver.$formatCells(payload);
1909
2171
  return true;
1910
- } else if ($isRangeSelection(selection)) {
1911
- if (!$isTableCellNode($findMatchingParent(selection.anchor.getNode(), (n) => $isTableCellNode(n)))) return false;
1912
2172
  }
1913
2173
  return false;
1914
2174
  }, COMMAND_PRIORITY_HIGH));
@@ -1918,15 +2178,15 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler, tableObse
1918
2178
  const anchorNode = selection.anchor.getNode();
1919
2179
  const focusNode = selection.focus.getNode();
1920
2180
  if (!$isTableCellNode(anchorNode) || !$isTableCellNode(focusNode)) return false;
1921
- if ($isFullTableSelection(selection, tableNode)) {
1922
- tableNode.setFormat(formatType);
1923
- return true;
1924
- }
1925
2181
  const [tableMap, anchorCell, focusCell] = $computeTableMap(tableNode, anchorNode, focusNode);
1926
2182
  const maxRow = Math.max(anchorCell.startRow + anchorCell.cell.__rowSpan - 1, focusCell.startRow + focusCell.cell.__rowSpan - 1);
1927
2183
  const maxColumn = Math.max(anchorCell.startColumn + anchorCell.cell.__colSpan - 1, focusCell.startColumn + focusCell.cell.__colSpan - 1);
1928
2184
  const minRow = Math.min(anchorCell.startRow, focusCell.startRow);
1929
2185
  const minColumn = Math.min(anchorCell.startColumn, focusCell.startColumn);
2186
+ if (minRow === 0 && minColumn === 0 && maxRow === tableMap.length - 1 && maxColumn === tableMap[0].length - 1) {
2187
+ tableNode.setFormat(formatType);
2188
+ return true;
2189
+ }
1930
2190
  const visited = /* @__PURE__ */ new Set();
1931
2191
  for (let i = minRow; i <= maxRow; i++) for (let j = minColumn; j <= maxColumn; j++) {
1932
2192
  const cell = tableMap[i][j].cell;
@@ -2355,17 +2615,6 @@ function $isSelectionInTable(selection, tableNode) {
2355
2615
  }
2356
2616
  return false;
2357
2617
  }
2358
- function $isFullTableSelection(selection, tableNode) {
2359
- if ($isTableSelection(selection)) {
2360
- const anchorNode = selection.anchor.getNode();
2361
- const focusNode = selection.focus.getNode();
2362
- if (tableNode && anchorNode && focusNode) {
2363
- const [map] = $computeTableMap(tableNode, anchorNode, focusNode);
2364
- return anchorNode.getKey() === map[0][0].cell.getKey() && focusNode.getKey() === map[map.length - 1].at(-1).cell.getKey();
2365
- }
2366
- }
2367
- return false;
2368
- }
2369
2618
  function selectTableCellNode(tableCell, fromStart) {
2370
2619
  if (fromStart) tableCell.selectStart();
2371
2620
  else tableCell.selectEnd();
@@ -2650,13 +2899,21 @@ const __DEV__ = true;
2650
2899
  function isHTMLDivElement(element) {
2651
2900
  return isHTMLElement(element) && element.nodeName === "DIV";
2652
2901
  }
2653
- function updateColgroup(dom, colCount, colWidths) {
2654
- const colGroup = dom.querySelector("colgroup");
2655
- if (!colGroup) return;
2902
+ function $updateColgroup(dom, colCount, colWidths) {
2903
+ let colGroup = dom.querySelector(":scope > colgroup");
2904
+ if (!colWidths) {
2905
+ if (colGroup) colGroup.remove();
2906
+ return;
2907
+ }
2908
+ if (!colGroup) {
2909
+ colGroup = $getDocument().createElement("colgroup");
2910
+ setDOMUnmanaged(colGroup);
2911
+ dom.insertBefore(colGroup, dom.firstChild);
2912
+ }
2656
2913
  const cols = [];
2657
2914
  for (let i = 0; i < colCount; i++) {
2658
- const col = document.createElement("col");
2659
- const width = colWidths && colWidths[i];
2915
+ const col = $getDocument().createElement("col");
2916
+ const width = colWidths[i];
2660
2917
  if (width) col.style.width = `${width}px`;
2661
2918
  cols.push(col);
2662
2919
  }
@@ -2701,10 +2958,115 @@ function alignTableElement(dom, config, formatType) {
2701
2958
  removeClassNamesFromElement(dom, ...removeClasses);
2702
2959
  addClassNamesToElement(dom, ...addClasses);
2703
2960
  }
2961
+ function $createScrollableWrapper(tableElement, config, hideNativeScrollbar) {
2962
+ const wrapper = $getDocument().createElement("div");
2963
+ const classes = config.theme.tableScrollableWrapper;
2964
+ if (classes) addClassNamesToElement(wrapper, classes);
2965
+ else wrapper.style.overflowX = "auto";
2966
+ if (hideNativeScrollbar) wrapper.style.scrollbarWidth = "none";
2967
+ wrapper.appendChild(tableElement);
2968
+ return wrapper;
2969
+ }
2970
+ function $createStickyScrollbar(config) {
2971
+ const doc = $getDocument();
2972
+ const scrollbar = doc.createElement("div");
2973
+ const classes = config.theme.tableStickyScrollbar;
2974
+ if (classes) addClassNamesToElement(scrollbar, classes);
2975
+ else {
2976
+ scrollbar.style.position = "sticky";
2977
+ scrollbar.style.bottom = "0";
2978
+ scrollbar.style.overflowX = "scroll";
2979
+ scrollbar.style.overflowY = "hidden";
2980
+ }
2981
+ scrollbar.style.display = "none";
2982
+ scrollbar.setAttribute("aria-hidden", "true");
2983
+ scrollbar.tabIndex = -1;
2984
+ const spacer = doc.createElement("div");
2985
+ spacer.style.height = "1px";
2986
+ spacer.style.width = "0px";
2987
+ scrollbar.appendChild(spacer);
2988
+ return scrollbar;
2989
+ }
2990
+ const overlayStickyScrollbars = /* @__PURE__ */ new WeakSet();
2991
+ function measureScrollbarThickness(scrollbar) {
2992
+ const prevDisplay = scrollbar.style.display;
2993
+ scrollbar.style.display = "";
2994
+ const thickness = scrollbar.offsetHeight - scrollbar.clientHeight;
2995
+ scrollbar.style.display = prevDisplay;
2996
+ return thickness;
2997
+ }
2998
+ function attachStickyScrollbarListeners(elements) {
2999
+ const { scrollable, scrollbar, tableElement } = elements;
3000
+ if (scrollbar.classList.length === 0 && measureScrollbarThickness(scrollbar) === 0) {
3001
+ overlayStickyScrollbars.add(scrollbar);
3002
+ scrollbar.style.display = "none";
3003
+ scrollable.style.scrollbarWidth = "auto";
3004
+ return () => {};
3005
+ }
3006
+ const onWrapperScroll = () => {
3007
+ if (scrollbar.scrollLeft !== scrollable.scrollLeft) scrollbar.scrollLeft = scrollable.scrollLeft;
3008
+ };
3009
+ const onScrollbarScroll = () => {
3010
+ if (scrollable.scrollLeft !== scrollbar.scrollLeft) scrollable.scrollLeft = scrollbar.scrollLeft;
3011
+ };
3012
+ scrollable.addEventListener("scroll", onWrapperScroll, { passive: true });
3013
+ scrollbar.addEventListener("scroll", onScrollbarScroll, { passive: true });
3014
+ let resizeObserver = null;
3015
+ if (typeof ResizeObserver !== "undefined") {
3016
+ resizeObserver = new ResizeObserver(() => {
3017
+ syncStickyScrollbar(scrollable, scrollbar);
3018
+ });
3019
+ resizeObserver.observe(scrollable);
3020
+ resizeObserver.observe(tableElement);
3021
+ }
3022
+ const cleanup = () => {
3023
+ scrollable.removeEventListener("scroll", onWrapperScroll);
3024
+ scrollbar.removeEventListener("scroll", onScrollbarScroll);
3025
+ if (resizeObserver) resizeObserver.disconnect();
3026
+ };
3027
+ syncStickyScrollbar(scrollable, scrollbar);
3028
+ return cleanup;
3029
+ }
3030
+ function syncStickyScrollbar(scrollable, scrollbar) {
3031
+ if (overlayStickyScrollbars.has(scrollbar)) return;
3032
+ const spacer = scrollbar.firstElementChild;
3033
+ if (!spacer || !isHTMLElement(spacer) || !scrollable.isConnected) return;
3034
+ const view = scrollable.ownerDocument.defaultView;
3035
+ if (!view) {
3036
+ scrollbar.style.display = "none";
3037
+ return;
3038
+ }
3039
+ const overflowX = view.getComputedStyle(scrollable).overflowX;
3040
+ const isScrollable = overflowX === "auto" || overflowX === "scroll";
3041
+ const scrollWidth = scrollable.scrollWidth;
3042
+ const clientWidth = scrollable.clientWidth;
3043
+ const spacerWidth = scrollWidth + "px";
3044
+ if (spacer.style.width !== spacerWidth) spacer.style.width = spacerWidth;
3045
+ const hasOverflow = isScrollable && scrollWidth > clientWidth;
3046
+ scrollbar.style.display = hasOverflow ? "" : "none";
3047
+ }
3048
+ function findStickyScrollbarElements(dom) {
3049
+ if (!dom.hasAttribute("data-lexical-sticky-scrollbar")) return null;
3050
+ const firstChild = dom.firstElementChild;
3051
+ if (!isHTMLDivElement(firstChild)) return null;
3052
+ const tableElement = firstChild.querySelector(":scope > table");
3053
+ if (!isHTMLTableElement(tableElement)) return null;
3054
+ const scrollbar = firstChild.nextElementSibling;
3055
+ if (!isHTMLDivElement(scrollbar)) return null;
3056
+ return {
3057
+ scrollable: firstChild,
3058
+ scrollbar,
3059
+ tableElement
3060
+ };
3061
+ }
2704
3062
  const scrollableEditors = /* @__PURE__ */ new WeakSet();
2705
3063
  function $isScrollableTablesActive(editor = $getEditor()) {
2706
3064
  return scrollableEditors.has(editor);
2707
3065
  }
3066
+ function $isStickyScrollbarActive(editor = $getEditor()) {
3067
+ const dep = getPeerDependencyFromEditor(editor, "@lexical/table/Table");
3068
+ return dep ? dep.output.hasStickyScrollbar.peek() && dep.output.hasHorizontalScroll.peek() : false;
3069
+ }
2708
3070
  function setScrollableTablesActive(editor, active) {
2709
3071
  if (active) {
2710
3072
  if (!editor._config.theme.tableScrollableWrapper) console.warn("TableNode: hasHorizontalScroll is active but theme.tableScrollableWrapper is not defined.");
@@ -2712,14 +3074,20 @@ function setScrollableTablesActive(editor, active) {
2712
3074
  } else scrollableEditors.delete(editor);
2713
3075
  }
2714
3076
  /** @noInheritDoc */
2715
- var TableNode = class TableNode extends ElementNode {
3077
+ var TableNode = class extends ElementNode {
2716
3078
  /** @internal */
2717
- __rowStriping;
2718
- __frozenColumnCount;
2719
- __frozenRowCount;
2720
- __colWidths;
2721
- static getType() {
2722
- return "table";
3079
+ __rowStriping = false;
3080
+ __frozenColumnCount = 0;
3081
+ __frozenRowCount = 0;
3082
+ __colWidths = void 0;
3083
+ $config() {
3084
+ return this.config("table", {
3085
+ extends: ElementNode,
3086
+ importDOM: { table: () => ({
3087
+ conversion: $convertTableElement,
3088
+ priority: 1
3089
+ }) }
3090
+ });
2723
3091
  }
2724
3092
  getColWidths() {
2725
3093
  return this.getLatest().__colWidths;
@@ -2729,9 +3097,6 @@ var TableNode = class TableNode extends ElementNode {
2729
3097
  self.__colWidths = colWidths !== void 0 && __DEV__ ? Object.freeze(colWidths) : colWidths;
2730
3098
  return self;
2731
3099
  }
2732
- static clone(node) {
2733
- return new TableNode(node.__key);
2734
- }
2735
3100
  afterCloneFrom(prevNode) {
2736
3101
  super.afterCloneFrom(prevNode);
2737
3102
  this.__colWidths = prevNode.__colWidths;
@@ -2739,25 +3104,9 @@ var TableNode = class TableNode extends ElementNode {
2739
3104
  this.__frozenColumnCount = prevNode.__frozenColumnCount;
2740
3105
  this.__frozenRowCount = prevNode.__frozenRowCount;
2741
3106
  }
2742
- static importDOM() {
2743
- return { table: (_node) => ({
2744
- conversion: $convertTableElement,
2745
- priority: 1
2746
- }) };
2747
- }
2748
- static importJSON(serializedNode) {
2749
- return $createTableNode().updateFromJSON(serializedNode);
2750
- }
2751
3107
  updateFromJSON(serializedNode) {
2752
3108
  return super.updateFromJSON(serializedNode).setRowStriping(serializedNode.rowStriping || false).setFrozenColumns(serializedNode.frozenColumnCount || 0).setFrozenRows(serializedNode.frozenRowCount || 0).setColWidths(serializedNode.colWidths);
2753
3109
  }
2754
- constructor(key) {
2755
- super(key);
2756
- this.__rowStriping = false;
2757
- this.__frozenColumnCount = 0;
2758
- this.__frozenRowCount = 0;
2759
- this.__colWidths = void 0;
2760
- }
2761
3110
  exportJSON() {
2762
3111
  return {
2763
3112
  ...super.exportJSON(),
@@ -2773,24 +3122,32 @@ var TableNode = class TableNode extends ElementNode {
2773
3122
  getDOMSlot(element) {
2774
3123
  const tableElement = !isHTMLTableElement(element) ? element.querySelector("table") : element;
2775
3124
  if (!isHTMLTableElement(tableElement)) formatDevErrorMessage(`TableNode.getDOMSlot: createDOM() did not return a table`);
2776
- return super.getDOMSlot(element).withElement(tableElement).withAfter(tableElement.querySelector("colgroup"));
3125
+ return super.getDOMSlot(element).withElement(tableElement).withAfter(tableElement.querySelector(":scope > colgroup"));
2777
3126
  }
2778
3127
  createDOM(config, editor) {
2779
- const tableElement = document.createElement("table");
3128
+ const tableElement = $getDocument().createElement("table");
2780
3129
  if (this.__style) setDOMStyleFromCSS(tableElement.style, this.__style);
2781
- const colGroup = document.createElement("colgroup");
2782
- tableElement.appendChild(colGroup);
2783
- setDOMUnmanaged(colGroup);
3130
+ if (this.getColWidths()) {
3131
+ const colGroup = $getDocument().createElement("colgroup");
3132
+ tableElement.appendChild(colGroup);
3133
+ setDOMUnmanaged(colGroup);
3134
+ }
2784
3135
  addClassNamesToElement(tableElement, config.theme.table);
2785
3136
  this.updateTableElement(null, tableElement, config);
2786
3137
  if ($isScrollableTablesActive(editor)) {
2787
- const wrapperElement = document.createElement("div");
2788
- const classes = config.theme.tableScrollableWrapper;
2789
- if (classes) addClassNamesToElement(wrapperElement, classes);
2790
- else wrapperElement.style.overflowX = "auto";
2791
- wrapperElement.appendChild(tableElement);
2792
- this.updateTableWrapper(null, wrapperElement, tableElement, config);
2793
- return wrapperElement;
3138
+ const hasStickyScrollbar = $isStickyScrollbarActive(editor);
3139
+ const scrollableWrapper = $createScrollableWrapper(tableElement, config, hasStickyScrollbar);
3140
+ this.updateTableWrapper(null, scrollableWrapper, tableElement, config);
3141
+ if (hasStickyScrollbar) {
3142
+ const stickyScrollbar = $createStickyScrollbar(config);
3143
+ const outerWrapper = $getDocument().createElement("div");
3144
+ outerWrapper.setAttribute("data-lexical-sticky-scrollbar", "true");
3145
+ outerWrapper.appendChild(scrollableWrapper);
3146
+ outerWrapper.appendChild(stickyScrollbar);
3147
+ setDOMUnmanaged(stickyScrollbar);
3148
+ return outerWrapper;
3149
+ }
3150
+ return scrollableWrapper;
2794
3151
  }
2795
3152
  return tableElement;
2796
3153
  }
@@ -2803,20 +3160,24 @@ var TableNode = class TableNode extends ElementNode {
2803
3160
  if (this.__rowStriping !== (prevNode ? prevNode.__rowStriping : false)) setRowStriping(tableElement, config, this.__rowStriping);
2804
3161
  const prevColCount = prevNode ? prevNode.getColumnCount() : 0;
2805
3162
  const prevColWidths = prevNode ? prevNode.__colWidths : void 0;
2806
- if (this.getColumnCount() !== prevColCount || this.getColWidths() !== prevColWidths) updateColgroup(tableElement, this.getColumnCount(), this.getColWidths());
3163
+ if (this.getColumnCount() !== prevColCount || this.getColWidths() !== prevColWidths) $updateColgroup(tableElement, this.getColumnCount(), this.getColWidths());
2807
3164
  alignTableElement(tableElement, config, this.getFormatType());
2808
3165
  }
2809
3166
  updateDOM(prevNode, dom, config) {
2810
3167
  const tableElement = getTableElement(this, dom);
2811
3168
  if (dom === tableElement === $isScrollableTablesActive()) return true;
2812
- if (isHTMLDivElement(dom)) this.updateTableWrapper(prevNode, dom, tableElement, config);
3169
+ if (isHTMLDivElement(dom)) {
3170
+ if (dom.hasAttribute("data-lexical-sticky-scrollbar") !== $isStickyScrollbarActive()) return true;
3171
+ const scrollable = tableElement.parentElement;
3172
+ if (isHTMLDivElement(scrollable)) this.updateTableWrapper(prevNode, scrollable, tableElement, config);
3173
+ }
2813
3174
  this.updateTableElement(prevNode, tableElement, config);
2814
3175
  return false;
2815
3176
  }
2816
3177
  scaleDOMColWidths(dom, scale) {
2817
3178
  const colWidths = this.getColWidths();
2818
3179
  if (!colWidths) return;
2819
- updateColgroup(getTableElement(this, dom), this.getColumnCount(), colWidths.map((width) => width * scale));
3180
+ $updateColgroup(getTableElement(this, dom), this.getColumnCount(), colWidths.map((width) => width * scale));
2820
3181
  }
2821
3182
  exportDOM(editor) {
2822
3183
  const superExport = super.exportDOM(editor);
@@ -2855,7 +3216,7 @@ var TableNode = class TableNode extends ElementNode {
2855
3216
  }
2856
3217
  const rows = tableElement.querySelectorAll(":scope > tr");
2857
3218
  if (rows.length > 0) {
2858
- const tBody = document.createElement("tbody");
3219
+ const tBody = $getDocument().createElement("tbody");
2859
3220
  for (const row of rows) tBody.appendChild(row);
2860
3221
  tableElement.append(tBody);
2861
3222
  }
@@ -3157,7 +3518,21 @@ function registerTablePlugin(editor, options) {
3157
3518
  }
3158
3519
  function $tableSelectionInsertClipboardNodesCommand(selectionPayload, hasNestedTables) {
3159
3520
  const { nodes, selection } = selectionPayload;
3160
- if (!nodes.some((n) => $isTableNode(n) || $dfsWithSlots(n).some((d) => $isTableNode(d.node)))) return false;
3521
+ if (!nodes.some((n) => $isTableNode(n) || $dfs(n).some((d) => $isTableNode(d.node)))) {
3522
+ if ($isTableSelection(selection)) {
3523
+ let text = "";
3524
+ let lastWasBlock = false;
3525
+ for (const node of nodes) {
3526
+ const isBlock = $isElementNode(node) && !node.isInline();
3527
+ if (text.length > 0 && (isBlock || lastWasBlock)) text += "\n";
3528
+ text += node.getTextContent();
3529
+ lastWasBlock = isBlock;
3530
+ }
3531
+ selection.insertRawText(text);
3532
+ return true;
3533
+ }
3534
+ return false;
3535
+ }
3161
3536
  const isTableSelection = $isTableSelection(selection);
3162
3537
  const isRangeSelection = $isRangeSelection(selection);
3163
3538
  if (!(isRangeSelection && $findMatchingParent(selection.anchor.getNode(), (n) => $isTableCellNode(n)) !== null && $findMatchingParent(selection.focus.getNode(), (n) => $isTableCellNode(n)) !== null || isTableSelection)) return false;
@@ -3165,90 +3540,6 @@ function $tableSelectionInsertClipboardNodesCommand(selectionPayload, hasNestedT
3165
3540
  if (isRangeSelection && hasNestedTables.peek() && !$isMultiCellTableSelection(selection)) return false;
3166
3541
  return true;
3167
3542
  }
3168
- function $insertTableIntoGrid(tableNode, selection) {
3169
- const anchorAndFocus = selection.getStartEndPoints();
3170
- const isTableSelection = $isTableSelection(selection);
3171
- if (anchorAndFocus === null) return false;
3172
- const [anchor, focus] = anchorAndFocus;
3173
- const [anchorCellNode, anchorRowNode, gridNode] = $getNodeTriplet(anchor);
3174
- const focusCellNode = $findMatchingParent(focus.getNode(), (n) => $isTableCellNode(n));
3175
- if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableRowNode(anchorRowNode) || !$isTableNode(gridNode)) return false;
3176
- const [initialGridMap, anchorCellMap, focusCellMap] = $computeTableMap(gridNode, anchorCellNode, focusCellNode);
3177
- const [templateGridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
3178
- const initialRowCount = initialGridMap.length;
3179
- const initialColCount = initialRowCount > 0 ? initialGridMap[0].length : 0;
3180
- let startRow = anchorCellMap.startRow;
3181
- let startCol = anchorCellMap.startColumn;
3182
- let affectedRowCount = templateGridMap.length;
3183
- let affectedColCount = affectedRowCount > 0 ? templateGridMap[0].length : 0;
3184
- if (isTableSelection) {
3185
- const selectionBoundary = $computeTableCellRectBoundary(initialGridMap, anchorCellMap, focusCellMap);
3186
- const selectionRowCount = selectionBoundary.maxRow - selectionBoundary.minRow + 1;
3187
- const selectionColCount = selectionBoundary.maxColumn - selectionBoundary.minColumn + 1;
3188
- startRow = selectionBoundary.minRow;
3189
- startCol = selectionBoundary.minColumn;
3190
- affectedRowCount = Math.min(affectedRowCount, selectionRowCount);
3191
- affectedColCount = Math.min(affectedColCount, selectionColCount);
3192
- }
3193
- let didPerformMergeOperations = false;
3194
- const lastRowForUnmerge = Math.min(initialRowCount, startRow + affectedRowCount) - 1;
3195
- const lastColForUnmerge = Math.min(initialColCount, startCol + affectedColCount) - 1;
3196
- const unmergedKeys = /* @__PURE__ */ new Set();
3197
- for (let row = startRow; row <= lastRowForUnmerge; row++) for (let col = startCol; col <= lastColForUnmerge; col++) {
3198
- const cellMap = initialGridMap[row][col];
3199
- if (unmergedKeys.has(cellMap.cell.getKey())) continue;
3200
- if (cellMap.cell.__rowSpan === 1 && cellMap.cell.__colSpan === 1) continue;
3201
- $unmergeCellNode(cellMap.cell);
3202
- unmergedKeys.add(cellMap.cell.getKey());
3203
- didPerformMergeOperations = true;
3204
- }
3205
- let [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
3206
- const rowsToInsert = affectedRowCount - initialRowCount + startRow;
3207
- for (let i = 0; i < rowsToInsert; i++) {
3208
- const cellMap = interimGridMap[initialRowCount - 1][0];
3209
- $insertTableRowAtNode(cellMap.cell);
3210
- }
3211
- const colsToInsert = affectedColCount - initialColCount + startCol;
3212
- for (let i = 0; i < colsToInsert; i++) {
3213
- const cellMap = interimGridMap[0][initialColCount - 1];
3214
- $insertTableColumnAtNode(cellMap.cell, true, false);
3215
- }
3216
- [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
3217
- for (let row = startRow; row < startRow + affectedRowCount; row++) for (let col = startCol; col < startCol + affectedColCount; col++) {
3218
- const templateRow = row - startRow;
3219
- const templateCol = col - startCol;
3220
- const templateCellMap = templateGridMap[templateRow][templateCol];
3221
- if (templateCellMap.startRow !== templateRow || templateCellMap.startColumn !== templateCol) continue;
3222
- const templateCell = templateCellMap.cell;
3223
- if (templateCell.__rowSpan !== 1 || templateCell.__colSpan !== 1) {
3224
- const cellsToMerge = [];
3225
- const lastRowForMerge = Math.min(row + templateCell.__rowSpan, startRow + affectedRowCount) - 1;
3226
- const lastColForMerge = Math.min(col + templateCell.__colSpan, startCol + affectedColCount) - 1;
3227
- for (let r = row; r <= lastRowForMerge; r++) for (let c = col; c <= lastColForMerge; c++) {
3228
- const cellMap = interimGridMap[r][c];
3229
- cellsToMerge.push(cellMap.cell);
3230
- }
3231
- $mergeCells(cellsToMerge);
3232
- didPerformMergeOperations = true;
3233
- }
3234
- const { cell } = interimGridMap[row][col];
3235
- const backgroundColor = templateCell.getBackgroundColor();
3236
- if (backgroundColor !== null && backgroundColor !== void 0) cell.setBackgroundColor(backgroundColor);
3237
- const originalChildren = cell.getChildren();
3238
- templateCell.getChildren().forEach((child) => {
3239
- if ($isTextNode(child)) {
3240
- $createParagraphNode().append(child);
3241
- cell.append(child);
3242
- } else cell.append(child);
3243
- });
3244
- originalChildren.forEach((n) => n.remove());
3245
- }
3246
- if (isTableSelection && didPerformMergeOperations) {
3247
- const [finalGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
3248
- finalGridMap[anchorCellMap.startRow][anchorCellMap.startColumn].cell.selectEnd();
3249
- }
3250
- return true;
3251
- }
3252
3543
  function $isMultiCellTableSelection(selection) {
3253
3544
  if ($isTableSelection(selection) && !selection.focus.getNode().is(selection.anchor.getNode())) return true;
3254
3545
  if ($isRangeSelection(selection) && $isTableCellNode(selection.anchor.getNode()) && !selection.anchor.getNode().is(selection.focus.getNode())) return true;
@@ -3435,6 +3726,39 @@ const TableImportRules = [
3435
3726
  * LICENSE file in the root directory of this source tree.
3436
3727
  *
3437
3728
  */
3729
+ function registerStickyScrollbar(editor) {
3730
+ const attached = /* @__PURE__ */ new Map();
3731
+ const detachAll = () => {
3732
+ for (const { cleanup } of attached.values()) cleanup();
3733
+ attached.clear();
3734
+ };
3735
+ return mergeRegister(detachAll, editor.registerMutationListener(TableNode, (nodeMutations) => {
3736
+ for (const [nodeKey, mutation] of nodeMutations) {
3737
+ const prev = attached.get(nodeKey);
3738
+ if (mutation === "destroyed") {
3739
+ if (prev) {
3740
+ prev.cleanup();
3741
+ attached.delete(nodeKey);
3742
+ }
3743
+ continue;
3744
+ }
3745
+ const dom = editor.getElementByKey(nodeKey);
3746
+ const parts = dom && isHTMLElement(dom) ? findStickyScrollbarElements(dom) : null;
3747
+ if (prev && parts && prev.parts.scrollable === parts.scrollable && prev.parts.scrollbar === parts.scrollbar && prev.parts.tableElement === parts.tableElement) {
3748
+ syncStickyScrollbar(parts.scrollable, parts.scrollbar);
3749
+ continue;
3750
+ }
3751
+ if (prev) {
3752
+ prev.cleanup();
3753
+ attached.delete(nodeKey);
3754
+ }
3755
+ if (parts) attached.set(nodeKey, {
3756
+ cleanup: attachStickyScrollbarListeners(parts),
3757
+ parts
3758
+ });
3759
+ }
3760
+ }, { skipInitialization: false }));
3761
+ }
3438
3762
  /**
3439
3763
  * Configures {@link TableNode}, {@link TableRowNode}, {@link TableCellNode} and
3440
3764
  * registers table behaviors (see {@link TableConfig})
@@ -3448,6 +3772,7 @@ const TableExtension = /* @__PURE__ */ defineExtension({
3448
3772
  hasCellMerge: true,
3449
3773
  hasHorizontalScroll: true,
3450
3774
  hasNestedTables: false,
3775
+ hasStickyScrollbar: false,
3451
3776
  hasTabHandler: true
3452
3777
  }),
3453
3778
  dependencies: [CoreImportExtension, /* @__PURE__ */ configExtension(DOMImportExtension, { rules: TableImportRules })],
@@ -3459,13 +3784,19 @@ const TableExtension = /* @__PURE__ */ defineExtension({
3459
3784
  ],
3460
3785
  register(editor, config, state) {
3461
3786
  const stores = state.getOutput();
3787
+ let prevStickyScrollbar = false;
3462
3788
  return mergeRegister(effect(() => {
3463
3789
  const hasHorizontalScroll = stores.hasHorizontalScroll.value;
3464
- if ($isScrollableTablesActive(editor) !== hasHorizontalScroll) {
3465
- setScrollableTablesActive(editor, hasHorizontalScroll);
3466
- editor.update($fullReconcile);
3467
- }
3468
- }), registerTablePlugin(editor, stores), effect(() => registerTableSelectionObserver(editor, stores.hasTabHandler.value)), effect(() => stores.hasCellMerge.value ? void 0 : registerTableCellUnmergeTransform(editor)), effect(() => stores.hasCellBackgroundColor.value ? void 0 : editor.registerNodeTransform(TableCellNode, (node) => {
3790
+ const hasStickyScrollbar = stores.hasStickyScrollbar.value && hasHorizontalScroll;
3791
+ const hadHorizontalScroll = $isScrollableTablesActive(editor);
3792
+ if (hadHorizontalScroll !== hasHorizontalScroll) setScrollableTablesActive(editor, hasHorizontalScroll);
3793
+ if (hadHorizontalScroll !== hasHorizontalScroll || prevStickyScrollbar !== hasStickyScrollbar) editor.update($fullReconcile);
3794
+ prevStickyScrollbar = hasStickyScrollbar;
3795
+ }), registerTablePlugin(editor, stores), effect(() => registerTableSelectionObserver(editor, stores.hasTabHandler.value)), effect(() => {
3796
+ if (stores.hasStickyScrollbar.value && stores.hasHorizontalScroll.value) return editor.registerRootListener((rootElement) => {
3797
+ if (rootElement) return registerStickyScrollbar(editor);
3798
+ });
3799
+ }), effect(() => stores.hasCellMerge.value ? void 0 : registerTableCellUnmergeTransform(editor)), effect(() => stores.hasCellBackgroundColor.value ? void 0 : editor.registerNodeTransform(TableCellNode, (node) => {
3469
3800
  if (node.getBackgroundColor() !== null) node.setBackgroundColor(null);
3470
3801
  })));
3471
3802
  }
@@ -3484,4 +3815,4 @@ const TableImportExtension = /* @__PURE__ */ defineExtension({
3484
3815
  name: "@lexical/table/Import"
3485
3816
  });
3486
3817
  //#endregion
3487
- export { $computeTableMap, $computeTableMapSkipCellCheck, $createTableCellNode, $createTableNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $createTableSelectionFrom, $deleteTableColumn, $deleteTableColumnAtSelection, $deleteTableColumn__EXPERIMENTAL, $deleteTableRowAtSelection, $deleteTableRow__EXPERIMENTAL, $findCellNode, $findTableNode, $getElementForTableNode, $getNodeTriplet, $getTableAndElementByKey, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $getTableRowNodeFromTableCellNodeOrThrow, $insertTableColumn, $insertTableColumnAtSelection, $insertTableColumn__EXPERIMENTAL, $insertTableRow, $insertTableRowAtSelection, $insertTableRow__EXPERIMENTAL, $isScrollableTablesActive, $isSimpleTable, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $mergeCells, $moveTableColumn, $removeTableRowAtIndex, $unmergeCell, INSERT_TABLE_COMMAND, TableCellHeaderStates, TableCellNode, TableExtension, TableImportExtension, TableImportRules, TableNode, TableObserver, TableRowNode, TableRowSchema, TableSchema, applyTableHandlers, getDOMCellFromTarget, getTableElement, getTableObserverFromTableElement, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive };
3818
+ export { $computeTableCellRectBoundary, $computeTableMap, $computeTableMapSkipCellCheck, $createTableCellNode, $createTableNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $createTableSelectionFrom, $deleteTableColumn, $deleteTableColumnAtSelection, $deleteTableColumn__EXPERIMENTAL, $deleteTableRowAtSelection, $deleteTableRow__EXPERIMENTAL, $findCellNode, $findTableNode, $getElementForTableNode, $getNodeTriplet, $getTableAndElementByKey, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $getTableRowNodeFromTableCellNodeOrThrow, $insertTableColumn, $insertTableColumnAtNode, $insertTableColumnAtSelection, $insertTableColumn__EXPERIMENTAL, $insertTableRow, $insertTableRowAtNode, $insertTableRowAtSelection, $insertTableRow__EXPERIMENTAL, $isScrollableTablesActive, $isSimpleTable, $isStickyScrollbarActive, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $mergeCells, $moveTableColumn, $moveTableRow, $removeTableRowAtIndex, $setTableColumnIsHeader, $setTableRowIsHeader, $unmergeCell, $unmergeCellNode, INSERT_TABLE_COMMAND, TableCellHeaderStates, TableCellNode, TableExtension, TableImportExtension, TableImportRules, TableNode, TableObserver, TableRowNode, TableRowSchema, TableSchema, applyTableHandlers, getDOMCellFromTarget, getTableElement, getTableObserverFromTableElement, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive };