TableCellNode
} from "@lexical/table";
import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode";
+import {createStyleMapFromDomStyles, StyleMap} from "../utils/styles";
export type SerializedCustomTableCellNode = Spread<{
styles: Record<string, string>,
}, SerializedTableCellNode>
export class CustomTableCellNode extends TableCellNode {
- __styles: Map<string, string> = new Map;
+ __styles: StyleMap = new Map;
static getType(): string {
return 'custom-table-cell';
return cellNode;
}
- getStyles(): Map<string, string> {
+ getStyles(): StyleMap {
const self = this.getLatest();
return new Map(self.__styles);
}
- setStyles(styles: Map<string, string>): void {
+ setStyles(styles: StyleMap): void {
const self = this.getWritable();
self.__styles = new Map(styles);
}
serializedNode.width,
);
- node.setStyles(new Map<string, string>(Object.entries(serializedNode.styles)));
+ node.setStyles(new Map(Object.entries(serializedNode.styles)));
return node;
}
const output = $convertTableCellNodeElement(domNode);
if (domNode instanceof HTMLElement && output.node instanceof CustomTableCellNode) {
- const styleMap = new Map<string, string>();
- const styleNames = Array.from(domNode.style);
- for (const style of styleNames) {
- styleMap.set(style, domNode.style.getPropertyValue(style));
- }
- output.node.setStyles(styleMap);
+ output.node.setStyles(createStyleMapFromDomStyles(domNode.style));
}
return output;
--- /dev/null
+import {
+ $createParagraphNode,
+ $isElementNode,
+ $isLineBreakNode,
+ $isTextNode,
+ DOMConversionMap,
+ DOMConversionOutput,
+ EditorConfig,
+ LexicalNode,
+ Spread
+} from "lexical";
+
+import {
+ $createTableCellNode,
+ $isTableCellNode,
+ SerializedTableRowNode,
+ TableCellHeaderStates,
+ TableRowNode
+} from "@lexical/table";
+import {createStyleMapFromDomStyles, StyleMap} from "../utils/styles";
+import {NodeKey} from "lexical/LexicalNode";
+
+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(createStyleMapFromDomStyles(domNode.style));
+ }
+
+ 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
import {EditorUiContext} from "../ui/framework/core";
import {MediaNode} from "./media";
import {CustomListItemNode} from "./custom-list-item";
-import {CustomTableCellNode} from "./custom-table-cell-node";
+import {CustomTableCellNode} from "./custom-table-cell";
+import {CustomTableRowNode} from "./custom-table-row";
/**
* Load the nodes for lexical.
ListNode, // Todo - Create custom
CustomListItemNode,
CustomTableNode,
- TableRowNode,
+ CustomTableRowNode,
CustomTableCellNode,
ImageNode,
HorizontalRuleNode,
return new CustomParagraphNode();
}
},
+ {
+ replace: ListItemNode,
+ with: (node: ListItemNode) => {
+ return new CustomListItemNode(node.__value, node.__checked);
+ }
+ },
{
replace: TableNode,
with(node: TableNode) {
}
},
{
- replace: ListItemNode,
- with: (node: ListItemNode) => {
- return new CustomListItemNode(node.__value, node.__checked);
+ replace: TableRowNode,
+ with(node: TableRowNode) {
+ return new CustomTableRowNode();
}
},
{
} from "@lexical/table";
import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
import {$getParentOfType} from "../../../utils/nodes";
-import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell-node";
-import {showCellPropertiesForm} from "../forms/tables";
+import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell";
+import {$showCellPropertiesForm} from "../forms/tables";
import {$mergeTableCellsInSelection} from "../../../utils/tables";
const neverActive = (): boolean => false;
context.editor.getEditorState().read(() => {
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
if ($isCustomTableCellNode(cell)) {
- showCellPropertiesForm(cell, context);
+ $showCellPropertiesForm(cell, context);
}
});
},
EditorSelectFormFieldDefinition
} from "../../framework/forms";
import {EditorUiContext} from "../../framework/core";
-import {CustomTableCellNode} from "../../../nodes/custom-table-cell-node";
+import {CustomTableCellNode} from "../../../nodes/custom-table-cell";
import {EditorFormModal} from "../../framework/modals";
import {$getSelection, ElementFormatType} from "lexical";
-import {$getTableCellsFromSelection, $setTableCellColumnWidth} from "../../../utils/tables";
+import {$getTableCellColumnWidth, $getTableCellsFromSelection, $setTableCellColumnWidth} from "../../../utils/tables";
import {formatSizeValue} from "../../../utils/dom";
const borderStyleInput: EditorSelectFormFieldDefinition = {
}
};
-export function showCellPropertiesForm(cell: CustomTableCellNode, context: EditorUiContext): EditorFormModal {
+export function $showCellPropertiesForm(cell: CustomTableCellNode, context: EditorUiContext): EditorFormModal {
const styles = cell.getStyles();
const modalForm = context.manager.createModal('cell_properties');
modalForm.show({
- width: '', // TODO
+ width: $getTableCellColumnWidth(context.editor, cell),
height: styles.get('height') || '',
type: cell.getTag(),
h_align: cell.getFormatType(),
return true;
},
fields: [
+ // Removed fields:
+ // Removed 'Row Type' as we don't currently support thead/tfoot elements
+ // TinyMCE would move rows up/down into these parents when set
+ // Removed 'Alignment' since this was broken in our editor (applied alignment class to whole parent table)
{
- build() {
- const generalFields: EditorFormFieldDefinition[] = [
- {
- label: 'Row type',
- name: 'type',
- type: 'select',
- valuesByLabel: {
- 'Body': 'body',
- 'Header': 'header',
- 'Footer': 'footer',
- }
- } as EditorSelectFormFieldDefinition,
- alignmentInput,
- {
- label: 'Height',
- name: 'height',
- type: 'text',
- },
- ];
-
- const advancedFields: EditorFormFieldDefinition[] = [
- borderStyleInput,
- borderColorInput,
- backgroundColorInput,
- ];
-
- return new EditorFormTabs([
- {
- label: 'General',
- contents: generalFields,
- },
- {
- label: 'Advanced',
- contents: advancedFields,
- }
- ])
- }
+ label: 'Height', // style on tr: height
+ name: 'height',
+ type: 'text',
},
+ borderStyleInput, // style on tr: height
+ borderColorInput, // style on tr: height
+ backgroundColorInput, // style on tr: height
],
};
export const tableProperties: EditorFormDefinition = {
--- /dev/null
+
+export type StyleMap = Map<string, string>;
+
+export function createStyleMapFromDomStyles(domStyles: CSSStyleDeclaration): StyleMap {
+ const styleMap: StyleMap = new Map();
+ const styleNames: string[] = Array.from(domStyles);
+ for (const style of styleNames) {
+ styleMap.set(style, domStyles.getPropertyValue(style));
+ }
+ return styleMap;
+}
\ No newline at end of file
import {CustomTableNode} from "../nodes/custom-table";
-import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell-node";
+import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
import {$isTableRowNode} from "@lexical/table";
export class TableMap {
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-node";
+import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
import {$getParentOfType} from "./nodes";
import {$getNodeFromSelection} from "./selection";
import {formatSizeValue} from "./dom";
}
}
+export function $getTableCellColumnWidth(editor: LexicalEditor, cell: CustomTableCellNode): string {
+ const table = $getTableFromCell(cell)
+ const index = $getCellColumnIndex(cell);
+ if (!table) {
+ return '';
+ }
+
+ const widths = table.getColWidths();
+ return (widths.length > index) ? widths[index] : '';
+}
+
export function $getTableCellsFromSelection(selection: BaseSelection|null): CustomTableCellNode[] {
if ($isTableSelection(selection)) {
const nodes = selection.getNodes();