@lexical/table 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LexicalTable.dev.js +224 -31
- package/{LexicalTable.dev.esm.js → LexicalTable.dev.mjs} +225 -32
- package/{LexicalTable.esm.js → LexicalTable.mjs} +2 -2
- package/LexicalTable.node.mjs +43 -0
- package/LexicalTable.prod.js +78 -72
- package/LexicalTable.prod.mjs +7 -0
- package/LexicalTableObserver.d.ts +1 -0
- package/LexicalTableSelectionHelpers.d.ts +2 -0
- package/index.d.ts +0 -1
- package/package.json +23 -7
- package/LexicalTable.prod.esm.js +0 -7
package/LexicalTable.dev.js
CHANGED
@@ -221,6 +221,11 @@ function convertTableCellNodeElement(domNode) {
|
|
221
221
|
if (backgroundColor !== '') {
|
222
222
|
tableCellNode.__backgroundColor = backgroundColor;
|
223
223
|
}
|
224
|
+
const style = domNode_.style;
|
225
|
+
const hasBoldFontWeight = style.fontWeight === '700' || style.fontWeight === 'bold';
|
226
|
+
const hasLinethroughTextDecoration = style.textDecoration === 'line-through';
|
227
|
+
const hasItalicFontStyle = style.fontStyle === 'italic';
|
228
|
+
const hasUnderlineTextDecoration = style.textDecoration === 'underline';
|
224
229
|
return {
|
225
230
|
after: childLexicalNodes => {
|
226
231
|
if (childLexicalNodes.length === 0) {
|
@@ -234,6 +239,20 @@ function convertTableCellNodeElement(domNode) {
|
|
234
239
|
if (lexical.$isLineBreakNode(lexicalNode) && lexicalNode.getTextContent() === '\n') {
|
235
240
|
return null;
|
236
241
|
}
|
242
|
+
if (lexical.$isTextNode(lexicalNode)) {
|
243
|
+
if (hasBoldFontWeight) {
|
244
|
+
lexicalNode.toggleFormat('bold');
|
245
|
+
}
|
246
|
+
if (hasLinethroughTextDecoration) {
|
247
|
+
lexicalNode.toggleFormat('strikethrough');
|
248
|
+
}
|
249
|
+
if (hasItalicFontStyle) {
|
250
|
+
lexicalNode.toggleFormat('italic');
|
251
|
+
}
|
252
|
+
if (hasUnderlineTextDecoration) {
|
253
|
+
lexicalNode.toggleFormat('underline');
|
254
|
+
}
|
255
|
+
}
|
237
256
|
paragraphNode.append(lexicalNode);
|
238
257
|
return paragraphNode;
|
239
258
|
}
|
@@ -1296,7 +1315,6 @@ function $getChildrenRecursively(node) {
|
|
1296
1315
|
* LICENSE file in the root directory of this source tree.
|
1297
1316
|
*
|
1298
1317
|
*/
|
1299
|
-
const getDOMSelection = targetWindow => CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
|
1300
1318
|
class TableObserver {
|
1301
1319
|
constructor(editor, tableNodeKey) {
|
1302
1320
|
this.isHighlightingCells = false;
|
@@ -1319,6 +1337,7 @@ class TableObserver {
|
|
1319
1337
|
this.focusCell = null;
|
1320
1338
|
this.hasHijackedSelectionStyles = false;
|
1321
1339
|
this.trackTable();
|
1340
|
+
this.isSelecting = false;
|
1322
1341
|
}
|
1323
1342
|
getTable() {
|
1324
1343
|
return this.table;
|
@@ -1458,7 +1477,7 @@ class TableObserver {
|
|
1458
1477
|
this.focusY = cellY;
|
1459
1478
|
if (this.isHighlightingCells) {
|
1460
1479
|
const focusTableCellNode = lexical.$getNearestNodeFromDOMNode(cell.elem);
|
1461
|
-
if (this.tableSelection != null && this.anchorCellNodeKey != null && $isTableCellNode(focusTableCellNode)) {
|
1480
|
+
if (this.tableSelection != null && this.anchorCellNodeKey != null && $isTableCellNode(focusTableCellNode) && tableNode.is($findTableNode(focusTableCellNode))) {
|
1462
1481
|
const focusNodeKey = focusTableCellNode.getKey();
|
1463
1482
|
this.tableSelection = this.tableSelection.clone() || $createTableSelection();
|
1464
1483
|
this.focusCellNodeKey = focusNodeKey;
|
@@ -1556,6 +1575,7 @@ class TableObserver {
|
|
1556
1575
|
*
|
1557
1576
|
*/
|
1558
1577
|
const LEXICAL_ELEMENT_KEY = '__lexicalTableSelection';
|
1578
|
+
const getDOMSelection = targetWindow => CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
|
1559
1579
|
function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
1560
1580
|
const rootElement = editor.getRootElement();
|
1561
1581
|
if (rootElement === null) {
|
@@ -1564,6 +1584,24 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1564
1584
|
const tableObserver = new TableObserver(editor, tableNode.getKey());
|
1565
1585
|
const editorWindow = editor._window || window;
|
1566
1586
|
attachTableObserverToTableElement(tableElement, tableObserver);
|
1587
|
+
const createMouseHandlers = () => {
|
1588
|
+
const onMouseUp = () => {
|
1589
|
+
tableObserver.isSelecting = false;
|
1590
|
+
editorWindow.removeEventListener('mouseup', onMouseUp);
|
1591
|
+
editorWindow.removeEventListener('mousemove', onMouseMove);
|
1592
|
+
};
|
1593
|
+
const onMouseMove = moveEvent => {
|
1594
|
+
const focusCell = getDOMCellFromTarget(moveEvent.target);
|
1595
|
+
if (focusCell !== null && (tableObserver.anchorX !== focusCell.x || tableObserver.anchorY !== focusCell.y)) {
|
1596
|
+
moveEvent.preventDefault();
|
1597
|
+
tableObserver.setFocusCellForSelection(focusCell);
|
1598
|
+
}
|
1599
|
+
};
|
1600
|
+
return {
|
1601
|
+
onMouseMove: onMouseMove,
|
1602
|
+
onMouseUp: onMouseUp
|
1603
|
+
};
|
1604
|
+
};
|
1567
1605
|
tableElement.addEventListener('mousedown', event => {
|
1568
1606
|
setTimeout(() => {
|
1569
1607
|
if (event.button !== 0) {
|
@@ -1577,17 +1615,11 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1577
1615
|
stopEvent(event);
|
1578
1616
|
tableObserver.setAnchorCellForSelection(anchorCell);
|
1579
1617
|
}
|
1580
|
-
const
|
1581
|
-
|
1582
|
-
|
1583
|
-
};
|
1584
|
-
|
1585
|
-
const focusCell = getDOMCellFromTarget(moveEvent.target);
|
1586
|
-
if (focusCell !== null && (tableObserver.anchorX !== focusCell.x || tableObserver.anchorY !== focusCell.y)) {
|
1587
|
-
moveEvent.preventDefault();
|
1588
|
-
tableObserver.setFocusCellForSelection(focusCell);
|
1589
|
-
}
|
1590
|
-
};
|
1618
|
+
const {
|
1619
|
+
onMouseUp,
|
1620
|
+
onMouseMove
|
1621
|
+
} = createMouseHandlers();
|
1622
|
+
tableObserver.isSelecting = true;
|
1591
1623
|
editorWindow.addEventListener('mouseup', onMouseUp);
|
1592
1624
|
editorWindow.addEventListener('mousemove', onMouseMove);
|
1593
1625
|
}, 0);
|
@@ -1655,18 +1687,6 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1655
1687
|
// TODO: Fix Delete Line in Table Cells.
|
1656
1688
|
return true;
|
1657
1689
|
}
|
1658
|
-
if (command === lexical.DELETE_CHARACTER_COMMAND || command === lexical.DELETE_WORD_COMMAND) {
|
1659
|
-
if (selection.isCollapsed() && selection.anchor.offset === 0) {
|
1660
|
-
if (nearestElementNode !== topLevelCellElementNode) {
|
1661
|
-
const children = nearestElementNode.getChildren();
|
1662
|
-
const newParagraphNode = lexical.$createParagraphNode();
|
1663
|
-
children.forEach(child => newParagraphNode.append(child));
|
1664
|
-
nearestElementNode.replace(newParagraphNode);
|
1665
|
-
nearestElementNode.getWritable().__parent = tableCellNode.getKey();
|
1666
|
-
return true;
|
1667
|
-
}
|
1668
|
-
}
|
1669
|
-
}
|
1670
1690
|
}
|
1671
1691
|
return false;
|
1672
1692
|
};
|
@@ -1752,6 +1772,13 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1752
1772
|
if (!$isTableCellNode(tableCellNode)) {
|
1753
1773
|
return false;
|
1754
1774
|
}
|
1775
|
+
if (typeof payload === 'string') {
|
1776
|
+
const edgePosition = $getTableEdgeCursorPosition(editor, selection, tableNode);
|
1777
|
+
if (edgePosition) {
|
1778
|
+
$insertParagraphAtTableEdge(edgePosition, tableNode, [lexical.$createTextNode(payload)]);
|
1779
|
+
return true;
|
1780
|
+
}
|
1781
|
+
}
|
1755
1782
|
}
|
1756
1783
|
return false;
|
1757
1784
|
}, lexical.COMMAND_PRIORITY_CRITICAL));
|
@@ -1882,7 +1909,11 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1882
1909
|
const isBackward = selection.isBackward();
|
1883
1910
|
if (isPartialyWithinTable) {
|
1884
1911
|
const newSelection = selection.clone();
|
1885
|
-
|
1912
|
+
if (isFocusInside) {
|
1913
|
+
newSelection.focus.set(tableNode.getParentOrThrow().getKey(), isBackward ? tableNode.getIndexWithinParent() : tableNode.getIndexWithinParent() + 1, 'element');
|
1914
|
+
} else {
|
1915
|
+
newSelection.anchor.set(tableNode.getParentOrThrow().getKey(), isBackward ? tableNode.getIndexWithinParent() + 1 : tableNode.getIndexWithinParent(), 'element');
|
1916
|
+
}
|
1886
1917
|
lexical.$setSelection(newSelection);
|
1887
1918
|
$addHighlightStyleToTable(editor, tableObserver);
|
1888
1919
|
} else if (isWithinTable) {
|
@@ -1891,6 +1922,34 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1891
1922
|
if (!anchorCellNode.is(focusCellNode)) {
|
1892
1923
|
tableObserver.setAnchorCellForSelection(getObserverCellFromCellNode(anchorCellNode));
|
1893
1924
|
tableObserver.setFocusCellForSelection(getObserverCellFromCellNode(focusCellNode), true);
|
1925
|
+
if (!tableObserver.isSelecting) {
|
1926
|
+
setTimeout(() => {
|
1927
|
+
const {
|
1928
|
+
onMouseUp,
|
1929
|
+
onMouseMove
|
1930
|
+
} = createMouseHandlers();
|
1931
|
+
tableObserver.isSelecting = true;
|
1932
|
+
editorWindow.addEventListener('mouseup', onMouseUp);
|
1933
|
+
editorWindow.addEventListener('mousemove', onMouseMove);
|
1934
|
+
}, 0);
|
1935
|
+
}
|
1936
|
+
}
|
1937
|
+
}
|
1938
|
+
} else if (selection && $isTableSelection(selection) && selection.is(prevSelection) && selection.tableKey === tableNode.getKey()) {
|
1939
|
+
// if selection goes outside of the table we need to change it to Range selection
|
1940
|
+
const domSelection = getDOMSelection(editor._window);
|
1941
|
+
if (domSelection && domSelection.anchorNode && domSelection.focusNode) {
|
1942
|
+
const focusNode = lexical.$getNearestNodeFromDOMNode(domSelection.focusNode);
|
1943
|
+
const isFocusOutside = focusNode && !tableNode.is($findTableNode(focusNode));
|
1944
|
+
const anchorNode = lexical.$getNearestNodeFromDOMNode(domSelection.anchorNode);
|
1945
|
+
const isAnchorInside = anchorNode && tableNode.is($findTableNode(anchorNode));
|
1946
|
+
if (isFocusOutside && isAnchorInside && domSelection.rangeCount > 0) {
|
1947
|
+
const newSelection = lexical.$createRangeSelectionFromDom(domSelection, editor);
|
1948
|
+
if (newSelection) {
|
1949
|
+
newSelection.anchor.set(tableNode.getKey(), selection.isBackward() ? tableNode.getChildrenSize() : 0, 'element');
|
1950
|
+
domSelection.removeAllRanges();
|
1951
|
+
lexical.$setSelection(newSelection);
|
1952
|
+
}
|
1894
1953
|
}
|
1895
1954
|
}
|
1896
1955
|
}
|
@@ -1909,6 +1968,18 @@ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
|
|
1909
1968
|
}
|
1910
1969
|
return false;
|
1911
1970
|
}, lexical.COMMAND_PRIORITY_CRITICAL));
|
1971
|
+
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.INSERT_PARAGRAPH_COMMAND, () => {
|
1972
|
+
const selection = lexical.$getSelection();
|
1973
|
+
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed() || !$isSelectionInTable(selection, tableNode)) {
|
1974
|
+
return false;
|
1975
|
+
}
|
1976
|
+
const edgePosition = $getTableEdgeCursorPosition(editor, selection, tableNode);
|
1977
|
+
if (edgePosition) {
|
1978
|
+
$insertParagraphAtTableEdge(edgePosition, tableNode);
|
1979
|
+
return true;
|
1980
|
+
}
|
1981
|
+
return false;
|
1982
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL));
|
1912
1983
|
return tableObserver;
|
1913
1984
|
}
|
1914
1985
|
function attachTableObserverToTableElement(tableElement, tableObserver) {
|
@@ -2165,14 +2236,31 @@ function $findTableNode(node) {
|
|
2165
2236
|
function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
2166
2237
|
const selection = lexical.$getSelection();
|
2167
2238
|
if (!$isSelectionInTable(selection, tableNode)) {
|
2239
|
+
if (direction === 'backward' && lexical.$isRangeSelection(selection) && selection.isCollapsed()) {
|
2240
|
+
const anchorType = selection.anchor.type;
|
2241
|
+
const anchorOffset = selection.anchor.offset;
|
2242
|
+
if (anchorType !== 'element' && !(anchorType === 'text' && anchorOffset === 0)) {
|
2243
|
+
return false;
|
2244
|
+
}
|
2245
|
+
const anchorNode = selection.anchor.getNode();
|
2246
|
+
if (!anchorNode) {
|
2247
|
+
return false;
|
2248
|
+
}
|
2249
|
+
const parentNode = utils.$findMatchingParent(anchorNode, n => lexical.$isElementNode(n) && !n.isInline());
|
2250
|
+
if (!parentNode) {
|
2251
|
+
return false;
|
2252
|
+
}
|
2253
|
+
const siblingNode = parentNode.getPreviousSibling();
|
2254
|
+
if (!siblingNode || !$isTableNode(siblingNode)) {
|
2255
|
+
return false;
|
2256
|
+
}
|
2257
|
+
stopEvent(event);
|
2258
|
+
siblingNode.selectEnd();
|
2259
|
+
return true;
|
2260
|
+
}
|
2168
2261
|
return false;
|
2169
2262
|
}
|
2170
2263
|
if (lexical.$isRangeSelection(selection) && selection.isCollapsed()) {
|
2171
|
-
// Horizontal move between cels seem to work well without interruption
|
2172
|
-
// so just exit early, and handle vertical moves
|
2173
|
-
if (direction === 'backward' || direction === 'forward') {
|
2174
|
-
return false;
|
2175
|
-
}
|
2176
2264
|
const {
|
2177
2265
|
anchor,
|
2178
2266
|
focus
|
@@ -2190,6 +2278,18 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2190
2278
|
return $handleArrowKey(editor, event, direction, anchorCellTable, tableObserver);
|
2191
2279
|
}
|
2192
2280
|
}
|
2281
|
+
if (direction === 'backward' || direction === 'forward') {
|
2282
|
+
const anchorType = anchor.type;
|
2283
|
+
const anchorOffset = anchor.offset;
|
2284
|
+
const anchorNode = anchor.getNode();
|
2285
|
+
if (!anchorNode) {
|
2286
|
+
return false;
|
2287
|
+
}
|
2288
|
+
if (isExitingTableAnchor(anchorType, anchorOffset, anchorNode, direction)) {
|
2289
|
+
return $handleTableExit(event, anchorNode, tableNode, direction);
|
2290
|
+
}
|
2291
|
+
return false;
|
2292
|
+
}
|
2193
2293
|
const anchorCellDom = editor.getElementByKey(anchorCellNode.__key);
|
2194
2294
|
const anchorDOM = editor.getElementByKey(anchor.key);
|
2195
2295
|
if (anchorDOM == null || anchorCellDom == null) {
|
@@ -2261,6 +2361,99 @@ function stopEvent(event) {
|
|
2261
2361
|
event.stopImmediatePropagation();
|
2262
2362
|
event.stopPropagation();
|
2263
2363
|
}
|
2364
|
+
function isExitingTableAnchor(type, offset, anchorNode, direction) {
|
2365
|
+
return isExitingTableElementAnchor(type, anchorNode, direction) || isExitingTableTextAnchor(type, offset, anchorNode, direction);
|
2366
|
+
}
|
2367
|
+
function isExitingTableElementAnchor(type, anchorNode, direction) {
|
2368
|
+
return type === 'element' && (direction === 'backward' ? anchorNode.getPreviousSibling() === null : anchorNode.getNextSibling() === null);
|
2369
|
+
}
|
2370
|
+
function isExitingTableTextAnchor(type, offset, anchorNode, direction) {
|
2371
|
+
const parentNode = utils.$findMatchingParent(anchorNode, n => lexical.$isElementNode(n) && !n.isInline());
|
2372
|
+
if (!parentNode) {
|
2373
|
+
return false;
|
2374
|
+
}
|
2375
|
+
const hasValidOffset = direction === 'backward' ? offset === 0 : offset === anchorNode.getTextContentSize();
|
2376
|
+
return type === 'text' && hasValidOffset && (direction === 'backward' ? parentNode.getPreviousSibling() === null : parentNode.getNextSibling() === null);
|
2377
|
+
}
|
2378
|
+
function $handleTableExit(event, anchorNode, tableNode, direction) {
|
2379
|
+
const anchorCellNode = utils.$findMatchingParent(anchorNode, $isTableCellNode);
|
2380
|
+
if (!$isTableCellNode(anchorCellNode)) {
|
2381
|
+
return false;
|
2382
|
+
}
|
2383
|
+
const [tableMap, cellValue] = $computeTableMap(tableNode, anchorCellNode, anchorCellNode);
|
2384
|
+
if (!isExitingCell(tableMap, cellValue, direction)) {
|
2385
|
+
return false;
|
2386
|
+
}
|
2387
|
+
const toNode = getExitingToNode(anchorNode, direction, tableNode);
|
2388
|
+
if (!toNode || $isTableNode(toNode)) {
|
2389
|
+
return false;
|
2390
|
+
}
|
2391
|
+
stopEvent(event);
|
2392
|
+
if (direction === 'backward') {
|
2393
|
+
toNode.selectEnd();
|
2394
|
+
} else {
|
2395
|
+
toNode.selectStart();
|
2396
|
+
}
|
2397
|
+
return true;
|
2398
|
+
}
|
2399
|
+
function isExitingCell(tableMap, cellValue, direction) {
|
2400
|
+
const firstCell = tableMap[0][0];
|
2401
|
+
const lastCell = tableMap[tableMap.length - 1][tableMap[0].length - 1];
|
2402
|
+
const {
|
2403
|
+
startColumn,
|
2404
|
+
startRow
|
2405
|
+
} = cellValue;
|
2406
|
+
return direction === 'backward' ? startColumn === firstCell.startColumn && startRow === firstCell.startRow : startColumn === lastCell.startColumn && startRow === lastCell.startRow;
|
2407
|
+
}
|
2408
|
+
function getExitingToNode(anchorNode, direction, tableNode) {
|
2409
|
+
const parentNode = utils.$findMatchingParent(anchorNode, n => lexical.$isElementNode(n) && !n.isInline());
|
2410
|
+
if (!parentNode) {
|
2411
|
+
return undefined;
|
2412
|
+
}
|
2413
|
+
const anchorSibling = direction === 'backward' ? parentNode.getPreviousSibling() : parentNode.getNextSibling();
|
2414
|
+
return anchorSibling && $isTableNode(anchorSibling) ? anchorSibling : direction === 'backward' ? tableNode.getPreviousSibling() : tableNode.getNextSibling();
|
2415
|
+
}
|
2416
|
+
function $insertParagraphAtTableEdge(edgePosition, tableNode, children) {
|
2417
|
+
const paragraphNode = lexical.$createParagraphNode();
|
2418
|
+
if (edgePosition === 'first') {
|
2419
|
+
tableNode.insertBefore(paragraphNode);
|
2420
|
+
} else {
|
2421
|
+
tableNode.insertAfter(paragraphNode);
|
2422
|
+
}
|
2423
|
+
paragraphNode.append(...(children || []));
|
2424
|
+
paragraphNode.selectEnd();
|
2425
|
+
}
|
2426
|
+
function $getTableEdgeCursorPosition(editor, selection, tableNode) {
|
2427
|
+
// TODO: Add support for nested tables
|
2428
|
+
const domSelection = window.getSelection();
|
2429
|
+
if (!domSelection || domSelection.anchorNode !== editor.getRootElement()) {
|
2430
|
+
return undefined;
|
2431
|
+
}
|
2432
|
+
const anchorCellNode = utils.$findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
|
2433
|
+
if (!anchorCellNode) {
|
2434
|
+
return undefined;
|
2435
|
+
}
|
2436
|
+
const parentTable = utils.$findMatchingParent(anchorCellNode, n => $isTableNode(n));
|
2437
|
+
if (!$isTableNode(parentTable) || !parentTable.is(tableNode)) {
|
2438
|
+
return undefined;
|
2439
|
+
}
|
2440
|
+
const [tableMap, cellValue] = $computeTableMap(tableNode, anchorCellNode, anchorCellNode);
|
2441
|
+
const firstCell = tableMap[0][0];
|
2442
|
+
const lastCell = tableMap[tableMap.length - 1][tableMap[0].length - 1];
|
2443
|
+
const {
|
2444
|
+
startRow,
|
2445
|
+
startColumn
|
2446
|
+
} = cellValue;
|
2447
|
+
const isAtFirstCell = startRow === firstCell.startRow && startColumn === firstCell.startColumn;
|
2448
|
+
const isAtLastCell = startRow === lastCell.startRow && startColumn === lastCell.startColumn;
|
2449
|
+
if (isAtFirstCell) {
|
2450
|
+
return 'first';
|
2451
|
+
} else if (isAtLastCell) {
|
2452
|
+
return 'last';
|
2453
|
+
} else {
|
2454
|
+
return undefined;
|
2455
|
+
}
|
2456
|
+
}
|
2264
2457
|
|
2265
2458
|
/**
|
2266
2459
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|