--- /dev/null
+import {SerializedTableNode, TableNode, TableRowNode} from "@lexical/table";
+import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
+import {EditorConfig} from "lexical/LexicalEditor";
+import {el} from "../helpers";
+
+export type SerializedCustomTableNode = Spread<{
+ id: string;
+ colWidths: string[];
+}, SerializedTableNode>
+
+export class CustomTableNode extends TableNode {
+ __id: string = '';
+ __colWidths: string[] = [];
+
+ static getType() {
+ return 'custom-table';
+ }
+
+ setId(id: string) {
+ const self = this.getWritable();
+ self.__id = id;
+ }
+
+ getId(): string {
+ const self = this.getLatest();
+ return self.__id;
+ }
+
+ setColWidths(widths: string[]) {
+ const self = this.getWritable();
+ self.__colWidths = widths;
+ }
+
+ getColWidths(): string[] {
+ const self = this.getLatest();
+ return self.__colWidths;
+ }
+
+ static clone(node: CustomTableNode) {
+ const newNode = new CustomTableNode(node.__key);
+ newNode.__id = node.__id;
+ newNode.__colWidths = node.__colWidths;
+ return newNode;
+ }
+
+ createDOM(config: EditorConfig): HTMLElement {
+ const dom = super.createDOM(config);
+ const id = this.getId();
+ if (id) {
+ dom.setAttribute('id', id);
+ }
+
+ 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);
+ }
+
+ return dom;
+ }
+
+ updateDOM(): boolean {
+ return true;
+ }
+
+ exportJSON(): SerializedCustomTableNode {
+ return {
+ ...super.exportJSON(),
+ type: 'custom-table',
+ version: 1,
+ id: this.__id,
+ colWidths: this.__colWidths,
+ };
+ }
+
+ static importJSON(serializedNode: SerializedCustomTableNode): CustomTableNode {
+ const node = $createCustomTableNode();
+ node.setId(serializedNode.id);
+ node.setColWidths(serializedNode.colWidths);
+ return node;
+ }
+
+ static importDOM(): DOMConversionMap|null {
+ return {
+ table(node: HTMLElement): DOMConversion|null {
+ return {
+ conversion: (element: HTMLElement): DOMConversionOutput|null => {
+ const node = $createCustomTableNode();
+
+ if (element.id) {
+ node.setId(element.id);
+ }
+
+ const colWidths = getTableColumnWidths(element as HTMLTableElement);
+ node.setColWidths(colWidths);
+
+ return {node};
+ },
+ priority: 1,
+ };
+ },
+ };
+ }
+}
+
+function getTableColumnWidths(table: HTMLTableElement): string[] {
+ const rows = table.querySelectorAll('tr');
+ let maxColCount: number = 0;
+ let maxColRow: HTMLTableRowElement|null = null;
+
+ for (const row of rows) {
+ if (row.childElementCount > maxColCount) {
+ maxColRow = row;
+ maxColCount = row.childElementCount;
+ }
+ }
+
+ const colGroup = table.querySelector('colgroup');
+ let widths: string[] = [];
+ if (colGroup && colGroup.childElementCount === maxColCount) {
+ widths = extractWidthsFromRow(colGroup);
+ }
+ if (widths.filter(Boolean).length === 0 && maxColRow) {
+ widths = extractWidthsFromRow(maxColRow);
+ }
+
+ return widths;
+}
+
+function extractWidthsFromRow(row: HTMLTableRowElement|HTMLTableColElement) {
+ return [...row.children].map(child => extractWidthFromElement(child as HTMLElement))
+}
+
+function extractWidthFromElement(element: HTMLElement): string {
+ let width = element.style.width || element.getAttribute('width');
+ if (!Number.isNaN(Number(width))) {
+ width = width + 'px';
+ }
+
+ return width || '';
+}
+
+export function $createCustomTableNode(): CustomTableNode {
+ return new CustomTableNode();
+}
+
+export function $isCustomTableNode(node: LexicalNode | null | undefined): boolean {
+ return node instanceof CustomTableNode;
+}
+
+export function $setTableColumnWidth(node: CustomTableNode, columnIndex: number, width: number): void {
+ const rows = node.getChildren() as TableRowNode[];
+ let maxCols = 0;
+ for (const row of rows) {
+ const cellCount = row.getChildren().length;
+ if (cellCount > maxCols) {
+ maxCols = cellCount;
+ }
+ }
+
+ let colWidths = node.getColWidths();
+ if (colWidths.length === 0 || colWidths.length < maxCols) {
+ colWidths = Array(maxCols).fill('');
+ }
+
+ if (columnIndex + 1 > colWidths.length) {
+ console.error(`Attempted to set table column width for column [${columnIndex}] but only ${colWidths.length} columns found`);
+ }
+
+ colWidths[columnIndex] = width + 'px';
+ node.setColWidths(colWidths);
+ console.log('setting col widths', node, colWidths);
+}
\ No newline at end of file
import {DetailsNode, SummaryNode} from "./details";
import {ListItemNode, ListNode} from "@lexical/list";
import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
+import {CustomTableNode} from "./custom-table";
/**
* Load the nodes for lexical.
QuoteNode, // Todo - Create custom
ListNode, // Todo - Create custom
ListItemNode,
- TableNode, // Todo - Create custom,
+ CustomTableNode,
TableRowNode,
TableCellNode,
ImageNode,
DetailsNode, SummaryNode,
CustomParagraphNode,
+ LinkNode,
{
replace: ParagraphNode,
with: (node: ParagraphNode) => {
return new CustomParagraphNode();
}
},
- LinkNode,
+ {
+ replace: TableNode,
+ with(node: TableNode) {
+ return new CustomTableNode();
+ }
+ },
];
}
undo,
warningCallout
} from "./defaults/button-definitions";
-import {EditorContainerUiElement, EditorSimpleClassContainer} from "./framework/core";
+import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext} from "./framework/core";
import {el} from "../helpers";
import {EditorFormatMenu} from "./framework/blocks/format-menu";
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
import {EditorColorPicker} from "./framework/blocks/color-picker";
import {EditorTableCreator} from "./framework/blocks/table-creator";
import {EditorColorButton} from "./framework/blocks/color-button";
+import {$isCustomTableNode, $setTableColumnWidth, CustomTableNode} from "../nodes/custom-table";
+import {$getRoot} from "lexical";
export function getMainEditorFullToolbar(): EditorContainerUiElement {
return new EditorSimpleClassContainer('editor-toolbar-main', [
// Meta elements
new EditorButton(source),
+
+ // Test
+ new EditorButton({
+ label: 'Expand table col 2',
+ action(context: EditorUiContext) {
+ context.editor.update(() => {
+ const root = $getRoot();
+ let table: CustomTableNode|null = null;
+ for (const child of root.getChildren()) {
+ if ($isCustomTableNode(child)) {
+ table = child as CustomTableNode;
+ break;
+ }
+ }
+
+ if (table) {
+ $setTableColumnWidth(table, 1, 500);
+ }
+ });
+ },
+ isActive() {
+ return false;
+ }
+ })
]);
}
\ No newline at end of file