ElementNode,
} from 'lexical';
-import {COLUMN_WIDTH, PIXEL_VALUE_REG_EXP} from './constants';
+import {extractStyleMapFromElement, StyleMap} from "../../utils/dom";
+import {CommonBlockAlignment, extractAlignmentFromElement} from "../../nodes/_common";
export const TableCellHeaderStates = {
BOTH: 3,
headerState: TableCellHeaderState;
width?: number;
backgroundColor?: null | string;
+ styles: Record<string, string>;
+ alignment: CommonBlockAlignment;
},
SerializedElementNode
>;
__width?: number;
/** @internal */
__backgroundColor: null | string;
+ /** @internal */
+ __styles: StyleMap = new Map;
+ /** @internal */
+ __alignment: CommonBlockAlignment = '';
static getType(): string {
return 'tablecell';
);
cellNode.__rowSpan = node.__rowSpan;
cellNode.__backgroundColor = node.__backgroundColor;
+ cellNode.__styles = new Map(node.__styles);
+ cellNode.__alignment = node.__alignment;
return cellNode;
}
}
static importJSON(serializedNode: SerializedTableCellNode): TableCellNode {
- const colSpan = serializedNode.colSpan || 1;
- const rowSpan = serializedNode.rowSpan || 1;
- const cellNode = $createTableCellNode(
- serializedNode.headerState,
- colSpan,
- serializedNode.width || undefined,
+ const node = $createTableCellNode(
+ serializedNode.headerState,
+ serializedNode.colSpan,
+ serializedNode.width,
);
- cellNode.__rowSpan = rowSpan;
- cellNode.__backgroundColor = serializedNode.backgroundColor || null;
- return cellNode;
+
+ if (serializedNode.rowSpan) {
+ node.setRowSpan(serializedNode.rowSpan);
+ }
+
+ node.setStyles(new Map(Object.entries(serializedNode.styles)));
+ node.setAlignment(serializedNode.alignment);
+
+ return node;
}
constructor(
this.hasHeader() && config.theme.tableCellHeader,
);
+ for (const [name, value] of this.__styles.entries()) {
+ element.style.setProperty(name, value);
+ }
+
+ if (this.__alignment) {
+ element.classList.add('align-' + this.__alignment);
+ }
+
return element;
}
exportDOM(editor: LexicalEditor): DOMExportOutput {
const {element} = super.exportDOM(editor);
-
- if (element) {
- const element_ = element as HTMLTableCellElement;
- element_.style.border = '1px solid black';
- if (this.__colSpan > 1) {
- element_.colSpan = this.__colSpan;
- }
- if (this.__rowSpan > 1) {
- element_.rowSpan = this.__rowSpan;
- }
- element_.style.width = `${this.getWidth() || COLUMN_WIDTH}px`;
-
- element_.style.verticalAlign = 'top';
- element_.style.textAlign = 'start';
-
- const backgroundColor = this.getBackgroundColor();
- if (backgroundColor !== null) {
- element_.style.backgroundColor = backgroundColor;
- } else if (this.hasHeader()) {
- element_.style.backgroundColor = '#f2f3f5';
- }
- }
-
return {
element,
};
rowSpan: this.__rowSpan,
type: 'tablecell',
width: this.getWidth(),
+ styles: Object.fromEntries(this.__styles),
+ alignment: this.__alignment,
};
}
return this.getLatest().__width;
}
+ clearWidth(): void {
+ const self = this.getWritable();
+ self.__width = undefined;
+ }
+
+ getStyles(): StyleMap {
+ const self = this.getLatest();
+ return new Map(self.__styles);
+ }
+
+ setStyles(styles: StyleMap): void {
+ const self = this.getWritable();
+ self.__styles = new Map(styles);
+ }
+
+ setAlignment(alignment: CommonBlockAlignment) {
+ const self = this.getWritable();
+ self.__alignment = alignment;
+ }
+
+ getAlignment(): CommonBlockAlignment {
+ const self = this.getLatest();
+ return self.__alignment;
+ }
+
+ updateTag(tag: string): void {
+ const isHeader = tag.toLowerCase() === 'th';
+ const state = isHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
+ const self = this.getWritable();
+ self.__headerState = state;
+ }
+
getBackgroundColor(): null | string {
return this.getLatest().__backgroundColor;
}
prevNode.__width !== this.__width ||
prevNode.__colSpan !== this.__colSpan ||
prevNode.__rowSpan !== this.__rowSpan ||
- prevNode.__backgroundColor !== this.__backgroundColor
+ prevNode.__backgroundColor !== this.__backgroundColor ||
+ prevNode.__styles !== this.__styles ||
+ prevNode.__alignment !== this.__alignment
);
}
}
export function $convertTableCellNodeElement(
- domNode: Node,
+ domNode: Node,
): DOMConversionOutput {
const domNode_ = domNode as HTMLTableCellElement;
const nodeName = domNode.nodeName.toLowerCase();
let width: number | undefined = undefined;
+
+ const PIXEL_VALUE_REG_EXP = /^(\d+(?:\.\d+)?)px$/;
if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
width = parseFloat(domNode_.style.width);
}
const tableCellNode = $createTableCellNode(
- nodeName === 'th'
- ? TableCellHeaderStates.ROW
- : TableCellHeaderStates.NO_STATUS,
- domNode_.colSpan,
- width,
+ nodeName === 'th'
+ ? TableCellHeaderStates.ROW
+ : TableCellHeaderStates.NO_STATUS,
+ domNode_.colSpan,
+ width,
);
tableCellNode.__rowSpan = domNode_.rowSpan;
- const backgroundColor = domNode_.style.backgroundColor;
- if (backgroundColor !== '') {
- tableCellNode.__backgroundColor = backgroundColor;
- }
const style = domNode_.style;
const textDecoration = style.textDecoration.split(' ');
const hasBoldFontWeight =
- style.fontWeight === '700' || style.fontWeight === 'bold';
+ style.fontWeight === '700' || style.fontWeight === 'bold';
const hasLinethroughTextDecoration = textDecoration.includes('line-through');
const hasItalicFontStyle = style.fontStyle === 'italic';
const hasUnderlineTextDecoration = textDecoration.includes('underline');
+
+ if (domNode instanceof HTMLElement) {
+ tableCellNode.setStyles(extractStyleMapFromElement(domNode));
+ tableCellNode.setAlignment(extractAlignmentFromElement(domNode));
+ }
+
return {
after: (childLexicalNodes) => {
if (childLexicalNodes.length === 0) {
if ($isTableCellNode(parentLexicalNode) && !$isElementNode(lexicalNode)) {
const paragraphNode = $createParagraphNode();
if (
- $isLineBreakNode(lexicalNode) &&
- lexicalNode.getTextContent() === '\n'
+ $isLineBreakNode(lexicalNode) &&
+ lexicalNode.getTextContent() === '\n'
) {
return null;
}
}
export function $createTableCellNode(
- headerState: TableCellHeaderState,
+ headerState: TableCellHeaderState = TableCellHeaderStates.NO_STATUS,
colSpan = 1,
width?: number,
): TableCellNode {
*/
import type {TableCellNode} from './LexicalTableCellNode';
-import type {
+import {
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
LexicalEditor,
LexicalNode,
NodeKey,
- SerializedElementNode,
+ SerializedElementNode, Spread,
} from 'lexical';
import {addClassNamesToElement, isHTMLElement} from '@lexical/utils';
import {$isTableCellNode} from './LexicalTableCellNode';
import {TableDOMCell, TableDOMTable} from './LexicalTableObserver';
-import {$isTableRowNode, TableRowNode} from './LexicalTableRowNode';
import {getTable} from './LexicalTableSelectionHelpers';
-
-export type SerializedTableNode = SerializedElementNode;
+import {CommonBlockNode, copyCommonBlockProperties} from "lexical/nodes/CommonBlockNode";
+import {
+ commonPropertiesDifferent, deserializeCommonBlockNode,
+ SerializedCommonBlockNode, setCommonBlockPropsFromElement,
+ updateElementWithCommonBlockProps
+} from "../../nodes/_common";
+import {el, extractStyleMapFromElement, StyleMap} from "../../utils/dom";
+import {getTableColumnWidths} from "../../utils/tables";
+
+export type SerializedTableNode = Spread<{
+ colWidths: string[];
+ styles: Record<string, string>,
+}, SerializedCommonBlockNode>
/** @noInheritDoc */
-export class TableNode extends ElementNode {
+export class TableNode extends CommonBlockNode {
+ __colWidths: string[] = [];
+ __styles: StyleMap = new Map;
+
static getType(): string {
return 'table';
}
static clone(node: TableNode): TableNode {
- return new TableNode(node.__key);
+ const newNode = new TableNode(node.__key);
+ copyCommonBlockProperties(node, newNode);
+ newNode.__colWidths = node.__colWidths;
+ newNode.__styles = new Map(node.__styles);
+ return newNode;
}
static importDOM(): DOMConversionMap | null {
}
static importJSON(_serializedNode: SerializedTableNode): TableNode {
- return $createTableNode();
+ const node = $createTableNode();
+ deserializeCommonBlockNode(_serializedNode, node);
+ node.setColWidths(_serializedNode.colWidths);
+ node.setStyles(new Map(Object.entries(_serializedNode.styles)));
+ return node;
}
constructor(key?: NodeKey) {
super(key);
}
- exportJSON(): SerializedElementNode {
+ exportJSON(): SerializedTableNode {
return {
...super.exportJSON(),
type: 'table',
version: 1,
+ colWidths: this.__colWidths,
+ styles: Object.fromEntries(this.__styles),
};
}
addClassNamesToElement(tableElement, config.theme.table);
+ 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);
+ }
+ tableElement.append(colgroup);
+ }
+
+ for (const [name, value] of this.__styles.entries()) {
+ tableElement.style.setProperty(name, value);
+ }
+
return tableElement;
}
- updateDOM(): boolean {
- return false;
+ 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(':')));
}
exportDOM(editor: LexicalEditor): DOMExportOutput {
return true;
}
+ setColWidths(widths: string[]) {
+ const self = this.getWritable();
+ self.__colWidths = widths;
+ }
+
+ getColWidths(): string[] {
+ const self = this.getLatest();
+ return self.__colWidths;
+ }
+
+ getStyles(): StyleMap {
+ const self = this.getLatest();
+ return new Map(self.__styles);
+ }
+
+ setStyles(styles: StyleMap): void {
+ const self = this.getWritable();
+ self.__styles = new Map(styles);
+ }
+
getCordsFromCellNode(
tableCellNode: TableCellNode,
table: TableDOMTable,
return getTable(tableElement);
}
-export function $convertTableElement(_domNode: Node): DOMConversionOutput {
- return {node: $createTableNode()};
+export function $convertTableElement(element: HTMLElement): DOMConversionOutput {
+ const node = $createTableNode();
+ setCommonBlockPropsFromElement(element, node);
+
+ const colWidths = getTableColumnWidths(element as HTMLTableElement);
+ node.setColWidths(colWidths);
+ node.setStyles(extractStyleMapFromElement(element));
+
+ return {node};
}
export function $createTableNode(): TableNode {
SerializedElementNode,
} from 'lexical';
-import {PIXEL_VALUE_REG_EXP} from './constants';
+import {extractStyleMapFromElement, sizeToPixels, StyleMap} from "../../utils/dom";
export type SerializedTableRowNode = Spread<
{
- height?: number;
+ styles: Record<string, string>,
+ height?: number,
},
SerializedElementNode
>;
export class TableRowNode extends ElementNode {
/** @internal */
__height?: number;
+ /** @internal */
+ __styles: StyleMap = new Map();
static getType(): string {
return 'tablerow';
}
static clone(node: TableRowNode): TableRowNode {
- return new TableRowNode(node.__height, node.__key);
+ const newNode = new TableRowNode(node.__key);
+ newNode.__styles = new Map(node.__styles);
+ return newNode;
}
static importDOM(): DOMConversionMap | null {
}
static importJSON(serializedNode: SerializedTableRowNode): TableRowNode {
- return $createTableRowNode(serializedNode.height);
+ const node = $createTableRowNode();
+
+ node.setStyles(new Map(Object.entries(serializedNode.styles)));
+
+ return node;
}
- constructor(height?: number, key?: NodeKey) {
+ constructor(key?: NodeKey) {
super(key);
- this.__height = height;
}
exportJSON(): SerializedTableRowNode {
return {
...super.exportJSON(),
- ...(this.getHeight() && {height: this.getHeight()}),
type: 'tablerow',
version: 1,
+ styles: Object.fromEntries(this.__styles),
+ height: this.__height || 0,
};
}
element.style.height = `${this.__height}px`;
}
+ for (const [name, value] of this.__styles.entries()) {
+ element.style.setProperty(name, value);
+ }
+
addClassNamesToElement(element, config.theme.tableRow);
return element;
return true;
}
+ getStyles(): StyleMap {
+ const self = this.getLatest();
+ return new Map(self.__styles);
+ }
+
+ setStyles(styles: StyleMap): void {
+ const self = this.getWritable();
+ self.__styles = new Map(styles);
+ }
+
setHeight(height: number): number | null | undefined {
const self = this.getWritable();
self.__height = height;
}
updateDOM(prevNode: TableRowNode): boolean {
- return prevNode.__height !== this.__height;
+ return prevNode.__height !== this.__height
+ || prevNode.__styles !== this.__styles;
}
canBeEmpty(): false {
}
export function $convertTableRowElement(domNode: Node): DOMConversionOutput {
- const domNode_ = domNode as HTMLTableCellElement;
- let height: number | undefined = undefined;
+ const rowNode = $createTableRowNode();
+ const domNode_ = domNode as HTMLElement;
+
+ const height = sizeToPixels(domNode_.style.height);
+ rowNode.setHeight(height);
- if (PIXEL_VALUE_REG_EXP.test(domNode_.style.height)) {
- height = parseFloat(domNode_.style.height);
+ if (domNode instanceof HTMLElement) {
+ rowNode.setStyles(extractStyleMapFromElement(domNode));
}
- return {node: $createTableRowNode(height)};
+ return {node: rowNode};
}
-export function $createTableRowNode(height?: number): TableRowNode {
- return $applyNodeReplacement(new TableRowNode(height));
+export function $createTableRowNode(): TableRowNode {
+ return $applyNodeReplacement(new TableRowNode());
}
export function $isTableRowNode(
),
);
- tableObserver.listenersToRemove.add(
- editor.registerCommand<ElementFormatType>(
- FORMAT_ELEMENT_COMMAND,
- (formatType) => {
- const selection = $getSelection();
- if (
- !$isTableSelection(selection) ||
- !$isSelectionInTable(selection, tableNode)
- ) {
- return false;
- }
-
- const anchorNode = selection.anchor.getNode();
- const focusNode = selection.focus.getNode();
- if (!$isTableCellNode(anchorNode) || !$isTableCellNode(focusNode)) {
- return false;
- }
-
- const [tableMap, anchorCell, focusCell] = $computeTableMap(
- tableNode,
- anchorNode,
- focusNode,
- );
- const maxRow = Math.max(anchorCell.startRow, focusCell.startRow);
- const maxColumn = Math.max(
- anchorCell.startColumn,
- focusCell.startColumn,
- );
- const minRow = Math.min(anchorCell.startRow, focusCell.startRow);
- const minColumn = Math.min(
- anchorCell.startColumn,
- focusCell.startColumn,
- );
- for (let i = minRow; i <= maxRow; i++) {
- for (let j = minColumn; j <= maxColumn; j++) {
- const cell = tableMap[i][j].cell;
- cell.setFormat(formatType);
-
- const cellChildren = cell.getChildren();
- for (let k = 0; k < cellChildren.length; k++) {
- const child = cellChildren[k];
- if ($isElementNode(child) && !child.isInline()) {
- child.setFormat(formatType);
- }
- }
- }
- }
- return true;
- },
- COMMAND_PRIORITY_CRITICAL,
- ),
- );
-
tableObserver.listenersToRemove.add(
editor.registerCommand(
CONTROLLED_TEXT_INSERTION_COMMAND,
`<tr class="${editorConfig.theme.tableRow}"></tr>`,
);
- const rowHeight = 36;
- const rowWithCustomHeightNode = $createTableRowNode(36);
+ const rowWithCustomHeightNode = $createTableRowNode();
expect(rowWithCustomHeightNode.createDOM(editorConfig).outerHTML).toBe(
- `<tr style="height: ${rowHeight}px;" class="${editorConfig.theme.tableRow}"></tr>`,
+ `<tr class="${editorConfig.theme.tableRow}"></tr>`,
);
});
});
+++ /dev/null
-import {
- $createParagraphNode,
- $isElementNode,
- $isLineBreakNode,
- $isTextNode,
- DOMConversionMap,
- DOMConversionOutput,
- DOMExportOutput,
- EditorConfig,
- LexicalEditor,
- LexicalNode,
- Spread
-} from "lexical";
-
-import {
- $createTableCellNode,
- $isTableCellNode,
- SerializedTableCellNode,
- TableCellHeaderStates,
- TableCellNode
-} from "@lexical/table";
-import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode";
-import {extractStyleMapFromElement, StyleMap} from "../utils/dom";
-import {CommonBlockAlignment, extractAlignmentFromElement} from "./_common";
-
-export type SerializedCustomTableCellNode = Spread<{
- styles: Record<string, string>;
- alignment: CommonBlockAlignment;
-}, SerializedTableCellNode>
-
-export class CustomTableCellNode extends TableCellNode {
- __styles: StyleMap = new Map;
- __alignment: CommonBlockAlignment = '';
-
- static getType(): string {
- return 'custom-table-cell';
- }
-
- static clone(node: CustomTableCellNode): CustomTableCellNode {
- const cellNode = new CustomTableCellNode(
- node.__headerState,
- node.__colSpan,
- node.__width,
- node.__key,
- );
- cellNode.__rowSpan = node.__rowSpan;
- cellNode.__styles = new Map(node.__styles);
- cellNode.__alignment = node.__alignment;
- return cellNode;
- }
-
- clearWidth(): void {
- const self = this.getWritable();
- self.__width = undefined;
- }
-
- getStyles(): StyleMap {
- const self = this.getLatest();
- return new Map(self.__styles);
- }
-
- setStyles(styles: StyleMap): void {
- const self = this.getWritable();
- self.__styles = new Map(styles);
- }
-
- setAlignment(alignment: CommonBlockAlignment) {
- const self = this.getWritable();
- self.__alignment = alignment;
- }
-
- getAlignment(): CommonBlockAlignment {
- const self = this.getLatest();
- return self.__alignment;
- }
-
- updateTag(tag: string): void {
- const isHeader = tag.toLowerCase() === 'th';
- const state = isHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
- const self = this.getWritable();
- self.__headerState = state;
- }
-
- createDOM(config: EditorConfig): HTMLElement {
- const element = super.createDOM(config);
-
- for (const [name, value] of this.__styles.entries()) {
- element.style.setProperty(name, value);
- }
-
- if (this.__alignment) {
- element.classList.add('align-' + this.__alignment);
- }
-
- return element;
- }
-
- updateDOM(prevNode: CustomTableCellNode): boolean {
- return super.updateDOM(prevNode)
- || this.__styles !== prevNode.__styles
- || this.__alignment !== prevNode.__alignment;
- }
-
- static importDOM(): DOMConversionMap | null {
- return {
- td: (node: Node) => ({
- conversion: $convertCustomTableCellNodeElement,
- priority: 0,
- }),
- th: (node: Node) => ({
- conversion: $convertCustomTableCellNodeElement,
- priority: 0,
- }),
- };
- }
-
- exportDOM(editor: LexicalEditor): DOMExportOutput {
- const element = this.createDOM(editor._config);
- return {
- element
- };
- }
-
- static importJSON(serializedNode: SerializedCustomTableCellNode): CustomTableCellNode {
- const node = $createCustomTableCellNode(
- serializedNode.headerState,
- serializedNode.colSpan,
- serializedNode.width,
- );
-
- node.setStyles(new Map(Object.entries(serializedNode.styles)));
- node.setAlignment(serializedNode.alignment);
-
- return node;
- }
-
- exportJSON(): SerializedCustomTableCellNode {
- return {
- ...super.exportJSON(),
- type: 'custom-table-cell',
- styles: Object.fromEntries(this.__styles),
- alignment: this.__alignment,
- };
- }
-}
-
-function $convertCustomTableCellNodeElement(domNode: Node): DOMConversionOutput {
- const output = $convertTableCellNodeElement(domNode);
-
- if (domNode instanceof HTMLElement && output.node instanceof CustomTableCellNode) {
- output.node.setStyles(extractStyleMapFromElement(domNode));
- output.node.setAlignment(extractAlignmentFromElement(domNode));
- }
-
- return output;
-}
-
-/**
- * Function taken from:
- * https://github.com/facebook/lexical/blob/e1881a6e409e1541c10dd0b5378f3a38c9dc8c9e/packages/lexical-table/src/LexicalTableCellNode.ts#L289
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- * MIT LICENSE
- * Modified since copy.
- */
-export function $convertTableCellNodeElement(
- domNode: Node,
-): DOMConversionOutput {
- const domNode_ = domNode as HTMLTableCellElement;
- const nodeName = domNode.nodeName.toLowerCase();
-
- let width: number | undefined = undefined;
-
-
- const PIXEL_VALUE_REG_EXP = /^(\d+(?:\.\d+)?)px$/;
- if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
- width = parseFloat(domNode_.style.width);
- }
-
- const tableCellNode = $createTableCellNode(
- nodeName === 'th'
- ? TableCellHeaderStates.ROW
- : TableCellHeaderStates.NO_STATUS,
- domNode_.colSpan,
- width,
- );
-
- tableCellNode.__rowSpan = domNode_.rowSpan;
-
- const style = domNode_.style;
- const textDecoration = style.textDecoration.split(' ');
- const hasBoldFontWeight =
- style.fontWeight === '700' || style.fontWeight === 'bold';
- const hasLinethroughTextDecoration = textDecoration.includes('line-through');
- const hasItalicFontStyle = style.fontStyle === 'italic';
- const hasUnderlineTextDecoration = textDecoration.includes('underline');
- return {
- after: (childLexicalNodes) => {
- if (childLexicalNodes.length === 0) {
- childLexicalNodes.push($createParagraphNode());
- }
- return childLexicalNodes;
- },
- forChild: (lexicalNode, parentLexicalNode) => {
- if ($isTableCellNode(parentLexicalNode) && !$isElementNode(lexicalNode)) {
- const paragraphNode = $createParagraphNode();
- if (
- $isLineBreakNode(lexicalNode) &&
- lexicalNode.getTextContent() === '\n'
- ) {
- return null;
- }
- if ($isTextNode(lexicalNode)) {
- if (hasBoldFontWeight) {
- lexicalNode.toggleFormat('bold');
- }
- if (hasLinethroughTextDecoration) {
- lexicalNode.toggleFormat('strikethrough');
- }
- if (hasItalicFontStyle) {
- lexicalNode.toggleFormat('italic');
- }
- if (hasUnderlineTextDecoration) {
- lexicalNode.toggleFormat('underline');
- }
- }
- paragraphNode.append(lexicalNode);
- return paragraphNode;
- }
-
- return lexicalNode;
- },
- node: tableCellNode,
- };
-}
-
-
-export function $createCustomTableCellNode(
- headerState: TableCellHeaderState = TableCellHeaderStates.NO_STATUS,
- colSpan = 1,
- width?: number,
-): CustomTableCellNode {
- return new CustomTableCellNode(headerState, colSpan, width);
-}
-
-export function $isCustomTableCellNode(node: LexicalNode | null | undefined): node is CustomTableCellNode {
- return node instanceof CustomTableCellNode;
-}
\ No newline at end of file
+++ /dev/null
-import {
- DOMConversionMap,
- DOMConversionOutput,
- EditorConfig,
- LexicalNode,
- Spread
-} from "lexical";
-
-import {
- SerializedTableRowNode,
- TableRowNode
-} from "@lexical/table";
-import {NodeKey} from "lexical/LexicalNode";
-import {extractStyleMapFromElement, StyleMap} from "../utils/dom";
-
-export type SerializedCustomTableRowNode = Spread<{
- styles: Record<string, string>,
-}, SerializedTableRowNode>
-
-export class CustomTableRowNode extends TableRowNode {
- __styles: StyleMap = new Map();
-
- constructor(key?: NodeKey) {
- super(0, key);
- }
-
- static getType(): string {
- return 'custom-table-row';
- }
-
- static clone(node: CustomTableRowNode): CustomTableRowNode {
- const cellNode = new CustomTableRowNode(node.__key);
-
- cellNode.__styles = new Map(node.__styles);
- return cellNode;
- }
-
- getStyles(): StyleMap {
- const self = this.getLatest();
- return new Map(self.__styles);
- }
-
- setStyles(styles: StyleMap): void {
- const self = this.getWritable();
- self.__styles = new Map(styles);
- }
-
- createDOM(config: EditorConfig): HTMLElement {
- const element = super.createDOM(config);
-
- for (const [name, value] of this.__styles.entries()) {
- element.style.setProperty(name, value);
- }
-
- return element;
- }
-
- updateDOM(prevNode: CustomTableRowNode): boolean {
- return super.updateDOM(prevNode)
- || this.__styles !== prevNode.__styles;
- }
-
- static importDOM(): DOMConversionMap | null {
- return {
- tr: (node: Node) => ({
- conversion: $convertTableRowElement,
- priority: 0,
- }),
- };
- }
-
- static importJSON(serializedNode: SerializedCustomTableRowNode): CustomTableRowNode {
- const node = $createCustomTableRowNode();
-
- node.setStyles(new Map(Object.entries(serializedNode.styles)));
-
- return node;
- }
-
- exportJSON(): SerializedCustomTableRowNode {
- return {
- ...super.exportJSON(),
- height: 0,
- type: 'custom-table-row',
- styles: Object.fromEntries(this.__styles),
- };
- }
-}
-
-export function $convertTableRowElement(domNode: Node): DOMConversionOutput {
- const rowNode = $createCustomTableRowNode();
-
- if (domNode instanceof HTMLElement) {
- rowNode.setStyles(extractStyleMapFromElement(domNode));
- }
-
- return {node: rowNode};
-}
-
-export function $createCustomTableRowNode(): CustomTableRowNode {
- return new CustomTableRowNode();
-}
-
-export function $isCustomTableRowNode(node: LexicalNode | null | undefined): node is CustomTableRowNode {
- return node instanceof CustomTableRowNode;
-}
\ No newline at end of file
+++ /dev/null
-import {SerializedTableNode, TableNode} from "@lexical/table";
-import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
-import {EditorConfig} from "lexical/LexicalEditor";
-
-import {el, extractStyleMapFromElement, StyleMap} from "../utils/dom";
-import {getTableColumnWidths} from "../utils/tables";
-import {
- CommonBlockAlignment, deserializeCommonBlockNode,
- SerializedCommonBlockNode,
- setCommonBlockPropsFromElement,
- updateElementWithCommonBlockProps
-} from "./_common";
-
-export type SerializedCustomTableNode = Spread<Spread<{
- colWidths: string[];
- styles: Record<string, string>,
-}, SerializedTableNode>, SerializedCommonBlockNode>
-
-export class CustomTableNode extends TableNode {
- __id: string = '';
- __colWidths: string[] = [];
- __styles: StyleMap = new Map;
- __alignment: CommonBlockAlignment = '';
- __inset: number = 0;
-
- static getType() {
- return 'custom-table';
- }
-
- setId(id: string) {
- const self = this.getWritable();
- self.__id = id;
- }
-
- getId(): string {
- const self = this.getLatest();
- return self.__id;
- }
-
- setAlignment(alignment: CommonBlockAlignment) {
- const self = this.getWritable();
- self.__alignment = alignment;
- }
-
- getAlignment(): CommonBlockAlignment {
- const self = this.getLatest();
- return self.__alignment;
- }
-
- setInset(size: number) {
- const self = this.getWritable();
- self.__inset = size;
- }
-
- getInset(): number {
- const self = this.getLatest();
- return self.__inset;
- }
-
- setColWidths(widths: string[]) {
- const self = this.getWritable();
- self.__colWidths = widths;
- }
-
- getColWidths(): string[] {
- const self = this.getLatest();
- return self.__colWidths;
- }
-
- getStyles(): StyleMap {
- const self = this.getLatest();
- return new Map(self.__styles);
- }
-
- setStyles(styles: StyleMap): void {
- const self = this.getWritable();
- self.__styles = new Map(styles);
- }
-
- static clone(node: CustomTableNode) {
- const newNode = new CustomTableNode(node.__key);
- newNode.__id = node.__id;
- newNode.__colWidths = node.__colWidths;
- newNode.__styles = new Map(node.__styles);
- newNode.__alignment = node.__alignment;
- newNode.__inset = node.__inset;
- return newNode;
- }
-
- createDOM(config: EditorConfig): HTMLElement {
- const dom = super.createDOM(config);
- updateElementWithCommonBlockProps(dom, 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);
- }
- dom.append(colgroup);
- }
-
- for (const [name, value] of this.__styles.entries()) {
- dom.style.setProperty(name, value);
- }
-
- return dom;
- }
-
- updateDOM(): boolean {
- return true;
- }
-
- exportJSON(): SerializedCustomTableNode {
- return {
- ...super.exportJSON(),
- type: 'custom-table',
- version: 1,
- id: this.__id,
- colWidths: this.__colWidths,
- styles: Object.fromEntries(this.__styles),
- alignment: this.__alignment,
- inset: this.__inset,
- };
- }
-
- static importJSON(serializedNode: SerializedCustomTableNode): CustomTableNode {
- const node = $createCustomTableNode();
- deserializeCommonBlockNode(serializedNode, node);
- node.setColWidths(serializedNode.colWidths);
- node.setStyles(new Map(Object.entries(serializedNode.styles)));
- return node;
- }
-
- static importDOM(): DOMConversionMap|null {
- return {
- table(node: HTMLElement): DOMConversion|null {
- return {
- conversion: (element: HTMLElement): DOMConversionOutput|null => {
- const node = $createCustomTableNode();
- setCommonBlockPropsFromElement(element, node);
-
- const colWidths = getTableColumnWidths(element as HTMLTableElement);
- node.setColWidths(colWidths);
- node.setStyles(extractStyleMapFromElement(element));
-
- return {node};
- },
- priority: 1,
- };
- },
- };
- }
-}
-
-export function $createCustomTableNode(): CustomTableNode {
- return new CustomTableNode();
-}
-
-export function $isCustomTableNode(node: LexicalNode | null | undefined): node is CustomTableNode {
- return node instanceof CustomTableNode;
-}
import {DetailsNode, SummaryNode} from "./details";
import {ListItemNode, ListNode} from "@lexical/list";
import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
-import {CustomTableNode} from "./custom-table";
import {HorizontalRuleNode} from "./horizontal-rule";
import {CodeBlockNode} from "./code-block";
import {DiagramNode} from "./diagram";
import {EditorUiContext} from "../ui/framework/core";
import {MediaNode} from "./media";
-import {CustomTableCellNode} from "./custom-table-cell";
-import {CustomTableRowNode} from "./custom-table-row";
import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
QuoteNode,
ListNode,
ListItemNode,
- CustomTableNode,
- CustomTableRowNode,
- CustomTableCellNode,
+ TableNode,
+ TableRowNode,
+ TableCellNode,
ImageNode, // TODO - Alignment
HorizontalRuleNode,
DetailsNode, SummaryNode,
MediaNode, // TODO - Alignment
ParagraphNode,
LinkNode,
- {
- replace: TableNode,
- with(node: TableNode) {
- return new CustomTableNode();
- }
- },
- {
- replace: TableRowNode,
- with(node: TableRowNode) {
- return new CustomTableRowNode();
- }
- },
- {
- replace: TableCellNode,
- with: (node: TableCellNode) => {
- const cell = new CustomTableCellNode(
- node.__headerState,
- node.__colSpan,
- node.__width,
- );
- cell.__rowSpan = node.__rowSpan;
- return cell;
- }
- },
];
}
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
import {EditorUiContext} from "../../framework/core";
import {$getSelection, BaseSelection} from "lexical";
-import {$isCustomTableNode} from "../../../nodes/custom-table";
import {
$deleteTableColumn__EXPERIMENTAL,
$deleteTableRow__EXPERIMENTAL,
$insertTableColumn__EXPERIMENTAL,
- $insertTableRow__EXPERIMENTAL,
- $isTableNode, $isTableSelection, $unmergeCell, TableCellNode,
+ $insertTableRow__EXPERIMENTAL, $isTableCellNode,
+ $isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode,
} from "@lexical/table";
import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
import {$getParentOfType} from "../../../utils/nodes";
-import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell";
import {$showCellPropertiesForm, $showRowPropertiesForm, $showTablePropertiesForm} from "../forms/tables";
import {
$clearTableFormatting,
$getTableRowsFromSelection,
$mergeTableCellsInSelection
} from "../../../utils/tables";
-import {$isCustomTableRowNode} from "../../../nodes/custom-table-row";
import {
$copySelectedColumnsToClipboard,
$copySelectedRowsToClipboard,
} from "../../../utils/table-copy-paste";
const neverActive = (): boolean => false;
-const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
+const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
export const table: EditorBasicButtonDefinition = {
label: 'Table',
action(context: EditorUiContext) {
context.editor.getEditorState().read(() => {
const table = $getTableFromSelection($getSelection());
- if ($isCustomTableNode(table)) {
+ if ($isTableNode(table)) {
$showTablePropertiesForm(table, context);
}
});
format: 'long',
action(context: EditorUiContext) {
context.editor.update(() => {
- const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
- if (!$isCustomTableCellNode(cell)) {
+ const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
+ if (!$isTableCellNode(cell)) {
return;
}
const table = $getParentOfType(cell, $isTableNode);
- if ($isCustomTableNode(table)) {
+ if ($isTableNode(table)) {
$clearTableFormatting(table);
}
});
format: 'long',
action(context: EditorUiContext) {
context.editor.update(() => {
- const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
- if (!$isCustomTableCellNode(cell)) {
+ const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
+ if (!$isTableCellNode(cell)) {
return;
}
- const table = $getParentOfType(cell, $isCustomTableNode);
- if ($isCustomTableNode(table)) {
+ const table = $getParentOfType(cell, $isTableNode);
+ if ($isTableNode(table)) {
$clearTableSizes(table);
}
});
icon: deleteIcon,
action(context: EditorUiContext) {
context.editor.update(() => {
- const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
+ const table = $getNodeFromSelection($getSelection(), $isTableNode);
if (table) {
table.remove();
}
action(context: EditorUiContext) {
context.editor.getEditorState().read(() => {
const rows = $getTableRowsFromSelection($getSelection());
- if ($isCustomTableRowNode(rows[0])) {
+ if ($isTableRowNode(rows[0])) {
$showRowPropertiesForm(rows[0], context);
}
});
format: 'long',
action(context: EditorUiContext) {
context.editor.getEditorState().read(() => {
- const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
- if ($isCustomTableCellNode(cell)) {
+ const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
+ if ($isTableCellNode(cell)) {
$showCellPropertiesForm(cell, context);
}
});
},
isActive: neverActive,
isDisabled(selection) {
- const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as TableCellNode|null;
+ const cell = $getNodeFromSelection(selection, $isTableCellNode) as TableCellNode|null;
if (cell) {
const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;
return !merged;
EditorSelectFormFieldDefinition
} from "../../framework/forms";
import {EditorUiContext} from "../../framework/core";
-import {CustomTableCellNode} from "../../../nodes/custom-table-cell";
import {EditorFormModal} from "../../framework/modals";
import {$getSelection, ElementFormatType} from "lexical";
import {
$setTableCellColumnWidth
} from "../../../utils/tables";
import {formatSizeValue} from "../../../utils/dom";
-import {CustomTableRowNode} from "../../../nodes/custom-table-row";
-import {CustomTableNode} from "../../../nodes/custom-table";
+import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
+import {CommonBlockAlignment} from "../../../nodes/_common";
const borderStyleInput: EditorSelectFormFieldDefinition = {
label: 'Border style',
}
};
-export function $showCellPropertiesForm(cell: CustomTableCellNode, context: EditorUiContext): EditorFormModal {
+export function $showCellPropertiesForm(cell: TableCellNode, context: EditorUiContext): EditorFormModal {
const styles = cell.getStyles();
const modalForm = context.manager.createModal('cell_properties');
modalForm.show({
width: $getTableCellColumnWidth(context.editor, cell),
height: styles.get('height') || '',
type: cell.getTag(),
- h_align: cell.getFormatType(),
+ h_align: cell.getAlignment(),
v_align: styles.get('vertical-align') || '',
border_width: styles.get('border-width') || '',
border_style: styles.get('border-style') || '',
$setTableCellColumnWidth(cell, width);
cell.updateTag(formData.get('type')?.toString() || '');
- cell.setFormat((formData.get('h_align')?.toString() || '') as ElementFormatType);
+ cell.setAlignment((formData.get('h_align')?.toString() || '') as CommonBlockAlignment);
const styles = cell.getStyles();
styles.set('height', formatSizeValue(formData.get('height')?.toString() || ''));
],
};
-export function $showRowPropertiesForm(row: CustomTableRowNode, context: EditorUiContext): EditorFormModal {
+export function $showRowPropertiesForm(row: TableRowNode, context: EditorUiContext): EditorFormModal {
const styles = row.getStyles();
const modalForm = context.manager.createModal('row_properties');
modalForm.show({
],
};
-export function $showTablePropertiesForm(table: CustomTableNode, context: EditorUiContext): EditorFormModal {
+export function $showTablePropertiesForm(table: TableNode, context: EditorUiContext): EditorFormModal {
const styles = table.getStyles();
const modalForm = context.manager.createModal('table_properties');
modalForm.show({
border_color: styles.get('border-color') || '',
background_color: styles.get('background-color') || '',
// caption: '', TODO
- align: table.getFormatType(),
+ align: table.getAlignment(),
});
return modalForm;
}
styles.set('background-color', formData.get('background_color')?.toString() || '');
table.setStyles(styles);
- table.setFormat(formData.get('align') as ElementFormatType);
+ table.setAlignment(formData.get('align') as CommonBlockAlignment);
const cellPadding = (formData.get('cell_padding')?.toString() || '');
if (cellPadding) {
const cellPaddingFormatted = formatSizeValue(cellPadding);
- $forEachTableCell(table, (cell: CustomTableCellNode) => {
+ $forEachTableCell(table, (cell: TableCellNode) => {
const styles = cell.getStyles();
styles.set('padding', cellPaddingFormatted);
cell.setStyles(styles);
import {EditorUiElement} from "../core";
import {$createTableNodeWithDimensions} from "@lexical/table";
-import {CustomTableNode} from "../../../nodes/custom-table";
import {$insertNewBlockNodeAtSelection} from "../../../utils/selection";
import {el} from "../../../utils/dom";
const colWidths = Array(columns).fill(targetColWidth + 'px');
this.getContext().editor.update(() => {
- const table = $createTableNodeWithDimensions(rows, columns, false) as CustomTableNode;
+ const table = $createTableNodeWithDimensions(rows, columns, false);
table.setColWidths(colWidths);
$insertNewBlockNodeAtSelection(table);
});
import {$getNearestNodeFromDOMNode, LexicalEditor} from "lexical";
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
-import {CustomTableNode} from "../../../nodes/custom-table";
-import {TableRowNode} from "@lexical/table";
+import {TableNode, TableRowNode} from "@lexical/table";
import {el} from "../../../utils/dom";
import {$getTableColumnWidth, $setTableColumnWidth} from "../../../utils/tables";
_this.editor.update(() => {
const table = $getNearestNodeFromDOMNode(parentTable);
- if (table instanceof CustomTableNode) {
+ if (table instanceof TableNode) {
const originalWidth = $getTableColumnWidth(_this.editor, table, cellIndex);
const newWidth = Math.max(originalWidth + change, 10);
$setTableColumnWidth(table, cellIndex, newWidth);
import {$getNodeByKey, LexicalEditor} from "lexical";
import {NodeKey} from "lexical/LexicalNode";
import {
+ $isTableNode,
applyTableHandlers,
HTMLTableElementWithWithTableSelectionState,
TableNode,
TableObserver
} from "@lexical/table";
-import {$isCustomTableNode, CustomTableNode} from "../../../nodes/custom-table";
// File adapted from logic in:
// https://github.com/facebook/lexical/blob/f373759a7849f473d34960a6bf4e34b2a011e762/packages/lexical-react/src/LexicalTablePlugin.ts#L49
}
protected init() {
- this.unregisterMutationListener = this.editor.registerMutationListener(CustomTableNode, (mutations) => {
+ this.unregisterMutationListener = this.editor.registerMutationListener(TableNode, (mutations) => {
for (const [nodeKey, mutation] of mutations) {
if (mutation === 'created') {
this.editor.getEditorState().read(() => {
- const tableNode = $getNodeByKey<CustomTableNode>(nodeKey);
- if ($isCustomTableNode(tableNode)) {
+ const tableNode = $getNodeByKey<TableNode>(nodeKey);
+ if ($isTableNode(tableNode)) {
this.initializeTableNode(tableNode);
}
});
import {NodeClipboard} from "./node-clipboard";
-import {CustomTableRowNode} from "../nodes/custom-table-row";
import {$getTableCellsFromSelection, $getTableFromSelection, $getTableRowsFromSelection} from "./tables";
import {$getSelection, BaseSelection, LexicalEditor} from "lexical";
-import {$createCustomTableCellNode, $isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
-import {CustomTableNode} from "../nodes/custom-table";
import {TableMap} from "./table-map";
-import {$isTableSelection} from "@lexical/table";
+import {
+ $createTableCellNode,
+ $isTableCellNode,
+ $isTableSelection,
+ TableCellNode,
+ TableNode,
+ TableRowNode
+} from "@lexical/table";
import {$getNodeFromSelection} from "./selection";
-const rowClipboard: NodeClipboard<CustomTableRowNode> = new NodeClipboard<CustomTableRowNode>();
+const rowClipboard: NodeClipboard<TableRowNode> = new NodeClipboard<TableRowNode>();
export function isRowClipboardEmpty(): boolean {
return rowClipboard.size() === 0;
}
-export function validateRowsToCopy(rows: CustomTableRowNode[]): void {
+export function validateRowsToCopy(rows: TableRowNode[]): void {
let commonRowSize: number|null = null;
for (const row of rows) {
- const cells = row.getChildren().filter(n => $isCustomTableCellNode(n));
+ const cells = row.getChildren().filter(n => $isTableCellNode(n));
let rowSize = 0;
for (const cell of cells) {
rowSize += cell.getColSpan() || 1;
}
}
-export function validateRowsToPaste(rows: CustomTableRowNode[], targetTable: CustomTableNode): void {
+export function validateRowsToPaste(rows: TableRowNode[], targetTable: TableNode): void {
const tableColCount = (new TableMap(targetTable)).columnCount;
for (const row of rows) {
- const cells = row.getChildren().filter(n => $isCustomTableCellNode(n));
+ const cells = row.getChildren().filter(n => $isTableCellNode(n));
let rowSize = 0;
for (const cell of cells) {
rowSize += cell.getColSpan() || 1;
}
while (rowSize < tableColCount) {
- row.append($createCustomTableCellNode());
+ row.append($createTableCellNode());
rowSize++;
}
}
}
}
-const columnClipboard: NodeClipboard<CustomTableCellNode>[] = [];
+const columnClipboard: NodeClipboard<TableCellNode>[] = [];
-function setColumnClipboard(columns: CustomTableCellNode[][]): void {
+function setColumnClipboard(columns: TableCellNode[][]): void {
const newClipboards = columns.map(cells => {
- const clipboard = new NodeClipboard<CustomTableCellNode>();
+ const clipboard = new NodeClipboard<TableCellNode>();
clipboard.set(...cells);
return clipboard;
});
return {from: shape.fromX, to: shape.toX};
}
- const cell = $getNodeFromSelection(selection, $isCustomTableCellNode);
+ const cell = $getNodeFromSelection(selection, $isTableCellNode);
const table = $getTableFromSelection(selection);
- if (!$isCustomTableCellNode(cell) || !table) {
+ if (!$isTableCellNode(cell) || !table) {
return null;
}
return {from: range.fromX, to: range.toX};
}
-function $getTableColumnCellsFromSelection(range: TableRange, table: CustomTableNode): CustomTableCellNode[][] {
+function $getTableColumnCellsFromSelection(range: TableRange, table: TableNode): TableCellNode[][] {
const map = new TableMap(table);
const columns = [];
for (let x = range.from; x <= range.to; x++) {
return columns;
}
-function validateColumnsToCopy(columns: CustomTableCellNode[][]): void {
+function validateColumnsToCopy(columns: TableCellNode[][]): void {
let commonColSize: number|null = null;
for (const cells of columns) {
setColumnClipboard(columns);
}
-function validateColumnsToPaste(columns: CustomTableCellNode[][], targetTable: CustomTableNode) {
+function validateColumnsToPaste(columns: TableCellNode[][], targetTable: TableNode) {
const tableRowCount = (new TableMap(targetTable)).rowCount;
for (const cells of columns) {
let colSize = 0;
}
while (colSize < tableRowCount) {
- cells.push($createCustomTableCellNode());
+ cells.push($createTableCellNode());
colSize++;
}
}
-import {CustomTableNode} from "../nodes/custom-table";
-import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
-import {$isTableRowNode} from "@lexical/table";
+import {$isTableCellNode, $isTableRowNode, TableCellNode, TableNode} from "@lexical/table";
export type CellRange = {
fromX: number;
// Represents an array (rows*columns in length) of cell nodes from top-left to
// bottom right. Cells may repeat where merged and covering multiple spaces.
- cells: CustomTableCellNode[] = [];
+ cells: TableCellNode[] = [];
- constructor(table: CustomTableNode) {
+ constructor(table: TableNode) {
this.buildCellMap(table);
}
- protected buildCellMap(table: CustomTableNode) {
- const rowsAndCells: CustomTableCellNode[][] = [];
- const setCell = (x: number, y: number, cell: CustomTableCellNode) => {
+ protected buildCellMap(table: TableNode) {
+ const rowsAndCells: TableCellNode[][] = [];
+ const setCell = (x: number, y: number, cell: TableCellNode) => {
if (typeof rowsAndCells[y] === 'undefined') {
rowsAndCells[y] = [];
}
const rowNodes = table.getChildren().filter(r => $isTableRowNode(r));
for (let rowIndex = 0; rowIndex < rowNodes.length; rowIndex++) {
const rowNode = rowNodes[rowIndex];
- const cellNodes = rowNode.getChildren().filter(c => $isCustomTableCellNode(c));
+ const cellNodes = rowNode.getChildren().filter(c => $isTableCellNode(c));
let targetColIndex: number = 0;
for (let cellIndex = 0; cellIndex < cellNodes.length; cellIndex++) {
const cellNode = cellNodes[cellIndex];
this.columnCount = Math.max(...rowsAndCells.map(r => r.length));
const cells = [];
- let lastCell: CustomTableCellNode = rowsAndCells[0][0];
+ let lastCell: TableCellNode = rowsAndCells[0][0];
for (let y = 0; y < this.rowCount; y++) {
for (let x = 0; x < this.columnCount; x++) {
if (!rowsAndCells[y] || !rowsAndCells[y][x]) {
this.cells = cells;
}
- public getCellAtPosition(x: number, y: number): CustomTableCellNode {
+ public getCellAtPosition(x: number, y: number): TableCellNode {
const position = (y * this.columnCount) + x;
if (position >= this.cells.length) {
throw new Error(`TableMap Error: Attempted to get cell ${position+1} of ${this.cells.length}`);
return this.cells[position];
}
- public getCellsInRange(range: CellRange): CustomTableCellNode[] {
+ public getCellsInRange(range: CellRange): TableCellNode[] {
const minX = Math.max(Math.min(range.fromX, range.toX), 0);
const maxX = Math.min(Math.max(range.fromX, range.toX), this.columnCount - 1);
const minY = Math.max(Math.min(range.fromY, range.toY), 0);
const maxY = Math.min(Math.max(range.fromY, range.toY), this.rowCount - 1);
- const cells = new Set<CustomTableCellNode>();
+ const cells = new Set<TableCellNode>();
for (let y = minY; y <= maxY; y++) {
for (let x = minX; x <= maxX; x++) {
return [...cells.values()];
}
- public getCellsInColumn(columnIndex: number): CustomTableCellNode[] {
+ public getCellsInColumn(columnIndex: number): TableCellNode[] {
return this.getCellsInRange({
fromX: columnIndex,
toX: columnIndex,
});
}
- public getRangeForCell(cell: CustomTableCellNode): CellRange|null {
+ public getRangeForCell(cell: TableCellNode): CellRange|null {
let range: CellRange|null = null;
const cellKey = cell.getKey();
import {BaseSelection, LexicalEditor} from "lexical";
-import {$isTableRowNode, $isTableSelection, TableRowNode, TableSelection, TableSelectionShape} from "@lexical/table";
-import {$isCustomTableNode, CustomTableNode} from "../nodes/custom-table";
-import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
+import {
+ $isTableCellNode,
+ $isTableNode,
+ $isTableRowNode,
+ $isTableSelection, TableCellNode, TableNode,
+ TableRowNode,
+ TableSelection,
+} from "@lexical/table";
import {$getParentOfType} from "./nodes";
import {$getNodeFromSelection} from "./selection";
import {formatSizeValue} from "./dom";
import {TableMap} from "./table-map";
-import {$isCustomTableRowNode, CustomTableRowNode} from "../nodes/custom-table-row";
-function $getTableFromCell(cell: CustomTableCellNode): CustomTableNode|null {
- return $getParentOfType(cell, $isCustomTableNode) as CustomTableNode|null;
+function $getTableFromCell(cell: TableCellNode): TableNode|null {
+ return $getParentOfType(cell, $isTableNode) as TableNode|null;
}
export function getTableColumnWidths(table: HTMLTableElement): string[] {
return width || '';
}
-export function $setTableColumnWidth(node: CustomTableNode, columnIndex: number, width: number|string): void {
+export function $setTableColumnWidth(node: TableNode, columnIndex: number, width: number|string): void {
const rows = node.getChildren() as TableRowNode[];
let maxCols = 0;
for (const row of rows) {
node.setColWidths(colWidths);
}
-export function $getTableColumnWidth(editor: LexicalEditor, node: CustomTableNode, columnIndex: number): number {
+export function $getTableColumnWidth(editor: LexicalEditor, node: TableNode, columnIndex: number): number {
const colWidths = node.getColWidths();
if (colWidths.length > columnIndex && colWidths[columnIndex].endsWith('px')) {
return Number(colWidths[columnIndex].replace('px', ''));
return 0;
}
-function $getCellColumnIndex(node: CustomTableCellNode): number {
+function $getCellColumnIndex(node: TableCellNode): number {
const row = node.getParent();
if (!$isTableRowNode(row)) {
return -1;
}
let index = 0;
- const cells = row.getChildren<CustomTableCellNode>();
+ const cells = row.getChildren<TableCellNode>();
for (const cell of cells) {
let colSpan = cell.getColSpan() || 1;
index += colSpan;
return index - 1;
}
-export function $setTableCellColumnWidth(cell: CustomTableCellNode, width: string): void {
+export function $setTableCellColumnWidth(cell: TableCellNode, width: string): void {
const table = $getTableFromCell(cell)
const index = $getCellColumnIndex(cell);
}
}
-export function $getTableCellColumnWidth(editor: LexicalEditor, cell: CustomTableCellNode): string {
+export function $getTableCellColumnWidth(editor: LexicalEditor, cell: TableCellNode): string {
const table = $getTableFromCell(cell)
const index = $getCellColumnIndex(cell);
if (!table) {
return (widths.length > index) ? widths[index] : '';
}
-export function $getTableCellsFromSelection(selection: BaseSelection|null): CustomTableCellNode[] {
+export function $getTableCellsFromSelection(selection: BaseSelection|null): TableCellNode[] {
if ($isTableSelection(selection)) {
const nodes = selection.getNodes();
- return nodes.filter(n => $isCustomTableCellNode(n));
+ return nodes.filter(n => $isTableCellNode(n));
}
- const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as CustomTableCellNode;
+ const cell = $getNodeFromSelection(selection, $isTableCellNode) as TableCellNode;
return cell ? [cell] : [];
}
firstCell.setRowSpan(newHeight);
}
-export function $getTableRowsFromSelection(selection: BaseSelection|null): CustomTableRowNode[] {
+export function $getTableRowsFromSelection(selection: BaseSelection|null): TableRowNode[] {
const cells = $getTableCellsFromSelection(selection);
- const rowsByKey: Record<string, CustomTableRowNode> = {};
+ const rowsByKey: Record<string, TableRowNode> = {};
for (const cell of cells) {
const row = cell.getParent();
- if ($isCustomTableRowNode(row)) {
+ if ($isTableRowNode(row)) {
rowsByKey[row.getKey()] = row;
}
}
return Object.values(rowsByKey);
}
-export function $getTableFromSelection(selection: BaseSelection|null): CustomTableNode|null {
+export function $getTableFromSelection(selection: BaseSelection|null): TableNode|null {
const cells = $getTableCellsFromSelection(selection);
if (cells.length === 0) {
return null;
}
- const table = $getParentOfType(cells[0], $isCustomTableNode);
- if ($isCustomTableNode(table)) {
+ const table = $getParentOfType(cells[0], $isTableNode);
+ if ($isTableNode(table)) {
return table;
}
return null;
}
-export function $clearTableSizes(table: CustomTableNode): void {
+export function $clearTableSizes(table: TableNode): void {
table.setColWidths([]);
// TODO - Extra form things once table properties and extra things
// are supported
for (const row of table.getChildren()) {
- if (!$isCustomTableRowNode(row)) {
+ if (!$isTableRowNode(row)) {
continue;
}
rowStyles.delete('width');
row.setStyles(rowStyles);
- const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
+ const cells = row.getChildren().filter(c => $isTableCellNode(c));
for (const cell of cells) {
const cellStyles = cell.getStyles();
cellStyles.delete('height');
}
}
-export function $clearTableFormatting(table: CustomTableNode): void {
+export function $clearTableFormatting(table: TableNode): void {
table.setColWidths([]);
table.setStyles(new Map);
for (const row of table.getChildren()) {
- if (!$isCustomTableRowNode(row)) {
+ if (!$isTableRowNode(row)) {
continue;
}
row.setStyles(new Map);
- row.setFormat('');
- const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
+ const cells = row.getChildren().filter(c => $isTableCellNode(c));
for (const cell of cells) {
cell.setStyles(new Map);
cell.clearWidth();
- cell.setFormat('');
}
}
}
* Perform the given callback for each cell in the given table.
* Returning false from the callback stops the function early.
*/
-export function $forEachTableCell(table: CustomTableNode, callback: (c: CustomTableCellNode) => void|false): void {
+export function $forEachTableCell(table: TableNode, callback: (c: TableCellNode) => void|false): void {
outer: for (const row of table.getChildren()) {
- if (!$isCustomTableRowNode(row)) {
+ if (!$isTableRowNode(row)) {
continue;
}
const cells = row.getChildren();
for (const cell of cells) {
- if (!$isCustomTableCellNode(cell)) {
+ if (!$isTableCellNode(cell)) {
return;
}
const result = callback(cell);
}
}
-export function $getCellPaddingForTable(table: CustomTableNode): string {
+export function $getCellPaddingForTable(table: TableNode): string {
let padding: string|null = null;
- $forEachTableCell(table, (cell: CustomTableCellNode) => {
+ $forEachTableCell(table, (cell: TableCellNode) => {
const cellPadding = cell.getStyles().get('padding') || ''
if (padding === null) {
padding = cellPadding;