Lexical: Further improvements to table selection and captions
- Fixed errors with selection and range handling due to captions existing. - Updated TableNode change handling to update existing DOM instead of re-creating, which avoids breaking an attached selection helper. - To support, Added function to handle node change detection and apply relevant dom updates for common properties.
This commit is contained in:
parent
d9ea52522e
commit
b862f12a50
|
@ -1,5 +1,6 @@
|
|||
import {sizeToPixels} from "../../../utils/dom";
|
||||
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
|
||||
import {elem} from "../../../../services/dom";
|
||||
|
||||
export type CommonBlockAlignment = 'left' | 'right' | 'center' | 'justify' | '';
|
||||
const validAlignments: CommonBlockAlignment[] = ['left', 'right', 'center', 'justify'];
|
||||
|
@ -82,6 +83,38 @@ export function commonPropertiesDifferent(nodeA: CommonBlockInterface, nodeB: Co
|
|||
nodeA.__dir !== nodeB.__dir;
|
||||
}
|
||||
|
||||
export function applyCommonPropertyChanges(prevNode: CommonBlockInterface, currentNode: CommonBlockInterface, element: HTMLElement): void {
|
||||
if (prevNode.__id !== currentNode.__id) {
|
||||
element.setAttribute('id', currentNode.__id);
|
||||
}
|
||||
|
||||
if (prevNode.__alignment !== currentNode.__alignment) {
|
||||
for (const alignment of validAlignments) {
|
||||
element.classList.remove('align-' + alignment);
|
||||
}
|
||||
|
||||
if (currentNode.__alignment) {
|
||||
element.classList.add('align-' + currentNode.__alignment);
|
||||
}
|
||||
}
|
||||
|
||||
if (prevNode.__inset !== currentNode.__inset) {
|
||||
if (currentNode.__inset) {
|
||||
element.style.paddingLeft = `${currentNode.__inset}px`;
|
||||
} else {
|
||||
element.style.removeProperty('paddingLeft');
|
||||
}
|
||||
}
|
||||
|
||||
if (prevNode.__dir !== currentNode.__dir) {
|
||||
if (currentNode.__dir) {
|
||||
element.dir = currentNode.__dir;
|
||||
} else {
|
||||
element.removeAttribute('dir');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function updateElementWithCommonBlockProps(element: HTMLElement, node: CommonBlockInterface): void {
|
||||
if (node.__id) {
|
||||
element.setAttribute('id', node.__id);
|
||||
|
|
|
@ -30,12 +30,13 @@ import {TableDOMCell, TableDOMTable} from './LexicalTableObserver';
|
|||
import {getTable} from './LexicalTableSelectionHelpers';
|
||||
import {CommonBlockNode, copyCommonBlockProperties, SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
|
||||
import {
|
||||
applyCommonPropertyChanges,
|
||||
commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||
setCommonBlockPropsFromElement,
|
||||
updateElementWithCommonBlockProps
|
||||
} from "lexical/nodes/common";
|
||||
import {el, extractStyleMapFromElement, StyleMap} from "../../utils/dom";
|
||||
import {getTableColumnWidths} from "../../utils/tables";
|
||||
import {buildColgroupFromTableWidths, getTableColumnWidths} from "../../utils/tables";
|
||||
|
||||
export type SerializedTableNode = Spread<{
|
||||
colWidths: string[];
|
||||
|
@ -98,15 +99,8 @@ export class TableNode extends CommonBlockNode {
|
|||
updateElementWithCommonBlockProps(tableElement, this);
|
||||
|
||||
const colWidths = this.getColWidths();
|
||||
if (colWidths.length > 0) {
|
||||
const colgroup = el('colgroup');
|
||||
for (const width of colWidths) {
|
||||
const col = el('col');
|
||||
if (width) {
|
||||
col.style.width = width;
|
||||
}
|
||||
colgroup.append(col);
|
||||
}
|
||||
const colgroup = buildColgroupFromTableWidths(colWidths);
|
||||
if (colgroup) {
|
||||
tableElement.append(colgroup);
|
||||
}
|
||||
|
||||
|
@ -117,11 +111,29 @@ export class TableNode extends CommonBlockNode {
|
|||
return tableElement;
|
||||
}
|
||||
|
||||
updateDOM(_prevNode: TableNode): boolean {
|
||||
return commonPropertiesDifferent(_prevNode, this)
|
||||
|| this.__colWidths.join(':') !== _prevNode.__colWidths.join(':')
|
||||
|| this.__styles.size !== _prevNode.__styles.size
|
||||
|| (Array.from(this.__styles.values()).join(':') !== (Array.from(_prevNode.__styles.values()).join(':')));
|
||||
updateDOM(_prevNode: TableNode, dom: HTMLElement): boolean {
|
||||
applyCommonPropertyChanges(_prevNode, this, dom);
|
||||
|
||||
if (this.__colWidths.join(':') !== _prevNode.__colWidths.join(':')) {
|
||||
const existingColGroup = Array.from(dom.children).find(child => child.nodeName === 'COLGROUP');
|
||||
const newColGroup = buildColgroupFromTableWidths(this.__colWidths);
|
||||
if (existingColGroup) {
|
||||
existingColGroup.remove();
|
||||
}
|
||||
|
||||
if (newColGroup) {
|
||||
dom.prepend(newColGroup);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.from(this.__styles.values()).join(':') !== Array.from(_prevNode.__styles.values()).join(':')) {
|
||||
dom.style.cssText = '';
|
||||
for (const [name, value] of this.__styles.entries()) {
|
||||
dom.style.setProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exportDOM(editor: LexicalEditor): DOMExportOutput {
|
||||
|
|
|
@ -916,14 +916,14 @@ export function getTable(tableElement: HTMLElement): TableDOMTable {
|
|||
domRows.length = 0;
|
||||
|
||||
while (currentNode != null) {
|
||||
const nodeMame = currentNode.nodeName;
|
||||
const nodeName = currentNode.nodeName;
|
||||
|
||||
if (nodeMame === 'COLGROUP') {
|
||||
if (nodeName === 'COLGROUP' || nodeName === 'CAPTION') {
|
||||
currentNode = currentNode.nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeMame === 'TD' || nodeMame === 'TH') {
|
||||
if (nodeName === 'TD' || nodeName === 'TH') {
|
||||
const elem = currentNode as HTMLElement;
|
||||
const cell = {
|
||||
elem,
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
TableRowNode,
|
||||
} from './LexicalTableRowNode';
|
||||
import {$isTableSelection} from './LexicalTableSelection';
|
||||
import {$isCaptionNode} from "@lexical/table/LexicalCaptionNode";
|
||||
|
||||
export function $createTableNodeWithDimensions(
|
||||
rowCount: number,
|
||||
|
@ -779,7 +780,7 @@ export function $computeTableMapSkipCellCheck(
|
|||
return tableMap[row] === undefined || tableMap[row][column] === undefined;
|
||||
}
|
||||
|
||||
const gridChildren = grid.getChildren();
|
||||
const gridChildren = grid.getChildren().filter(node => !$isCaptionNode(node));
|
||||
for (let i = 0; i < gridChildren.length; i++) {
|
||||
const row = gridChildren[i];
|
||||
invariant(
|
||||
|
|
|
@ -56,7 +56,7 @@ class TableSelectionHandler {
|
|||
tableNode,
|
||||
tableElement,
|
||||
this.editor,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
this.tableSelections.set(nodeKey, tableSelection);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from "@lexical/table";
|
||||
import {$getParentOfType} from "./nodes";
|
||||
import {$getNodeFromSelection} from "./selection";
|
||||
import {formatSizeValue} from "./dom";
|
||||
import {el, formatSizeValue} from "./dom";
|
||||
import {TableMap} from "./table-map";
|
||||
|
||||
function $getTableFromCell(cell: TableCellNode): TableNode|null {
|
||||
|
@ -140,6 +140,23 @@ export function $getTableCellColumnWidth(editor: LexicalEditor, cell: TableCellN
|
|||
return (widths.length > index) ? widths[index] : '';
|
||||
}
|
||||
|
||||
export function buildColgroupFromTableWidths(colWidths: string[]): HTMLElement|null {
|
||||
if (colWidths.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const colgroup = el('colgroup');
|
||||
for (const width of colWidths) {
|
||||
const col = el('col');
|
||||
if (width) {
|
||||
col.style.width = width;
|
||||
}
|
||||
colgroup.append(col);
|
||||
}
|
||||
|
||||
return colgroup;
|
||||
}
|
||||
|
||||
export function $getTableCellsFromSelection(selection: BaseSelection|null): TableCellNode[] {
|
||||
if ($isTableSelection(selection)) {
|
||||
const nodes = selection.getNodes();
|
||||
|
|
Loading…
Reference in New Issue