import {SerializedTableNode, TableNode, TableRowNode} from "@lexical/table";
-import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
+import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalEditor, LexicalNode, Spread} from "lexical";
import {EditorConfig} from "lexical/LexicalEditor";
import {el} from "../helpers";
}
function getTableColumnWidths(table: HTMLTableElement): string[] {
+ const maxColRow = getMaxColRowFromTable(table);
+
+ const colGroup = table.querySelector('colgroup');
+ let widths: string[] = [];
+ if (colGroup && (colGroup.childElementCount === maxColRow?.childElementCount || !maxColRow)) {
+ widths = extractWidthsFromRow(colGroup);
+ }
+ if (widths.filter(Boolean).length === 0 && maxColRow) {
+ widths = extractWidthsFromRow(maxColRow);
+ }
+
+ return widths;
+}
+
+function getMaxColRowFromTable(table: HTMLTableElement): HTMLTableRowElement|null {
const rows = table.querySelectorAll('tr');
let maxColCount: number = 0;
let maxColRow: HTMLTableRowElement|null = null;
}
}
- 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;
+ return maxColRow;
}
function extractWidthsFromRow(row: HTMLTableRowElement|HTMLTableColElement) {
function extractWidthFromElement(element: HTMLElement): string {
let width = element.style.width || element.getAttribute('width');
- if (!Number.isNaN(Number(width))) {
+ if (width && !Number.isNaN(Number(width))) {
width = width + 'px';
}
colWidths[columnIndex] = width + 'px';
node.setColWidths(colWidths);
- console.log('setting col widths', node, colWidths);
+}
+
+export function $getTableColumnWidth(editor: LexicalEditor, node: CustomTableNode, columnIndex: number): number {
+ const colWidths = node.getColWidths();
+ if (colWidths.length > columnIndex && colWidths[columnIndex].endsWith('px')) {
+ return Number(colWidths[columnIndex].replace('px', ''));
+ }
+
+ // Otherwise, get from table element
+ const table = editor.getElementByKey(node.__key) as HTMLTableElement|null;
+ if (table) {
+ const maxColRow = getMaxColRowFromTable(table);
+ if (maxColRow && maxColRow.children.length > columnIndex) {
+ const cell = maxColRow.children[columnIndex];
+ return cell.clientWidth;
+ }
+ }
+
+ return 0;
}
\ No newline at end of file
-import {LexicalEditor} from "lexical";
+import {$getNearestNodeFromDOMNode, LexicalEditor} from "lexical";
import {el} from "../../../helpers";
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
+import {$getTableColumnWidth, $setTableColumnWidth, CustomTableNode} from "../../../nodes/custom-table";
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
protected editArea: HTMLElement;
protected markerDom: MarkerDomRecord|null = null;
protected mouseTracker: MouseDragTracker|null = null;
+ protected dragging: boolean = false;
+ protected targetCell: HTMLElement|null = null;
+ protected xMarkerAtStart : boolean = false;
+ protected yMarkerAtStart : boolean = false;
constructor(editor: LexicalEditor, editArea: HTMLElement) {
this.editor = editor;
setupListeners() {
this.editArea.addEventListener('mousemove', event => {
const cell = (event.target as HTMLElement).closest('td,th');
- if (cell) {
+ if (cell && !this.dragging) {
this.onCellMouseMove(cell as HTMLElement, event);
}
});
const rect = cell.getBoundingClientRect();
const midX = rect.left + (rect.width / 2);
const midY = rect.top + (rect.height / 2);
- const xMarkerPos = event.clientX <= midX ? rect.left : rect.right;
- const yMarkerPos = event.clientY <= midY ? rect.top : rect.bottom;
+
+ this.targetCell = cell;
+ this.xMarkerAtStart = event.clientX <= midX;
+ this.yMarkerAtStart = event.clientY <= midY;
+
+ const xMarkerPos = this.xMarkerAtStart ? rect.left : rect.right;
+ const yMarkerPos = this.yMarkerAtStart ? rect.top : rect.bottom;
this.updateMarkersTo(cell, xMarkerPos, yMarkerPos);
}
}
watchMarkerMouseDrags(wrapper: HTMLElement) {
+ const _this = this;
+ let markerStart: number = 0;
+ let markerProp: 'left' | 'top' = 'left';
+
this.mouseTracker = new MouseDragTracker(wrapper, '.editor-table-marker', {
+ down(event: MouseEvent, marker: HTMLElement) {
+ marker.classList.add('active');
+ _this.dragging = true;
+
+ markerProp = marker.classList.contains('editor-table-marker-column') ? 'left' : 'top';
+ markerStart = Number(marker.style[markerProp].replace('px', ''));
+ },
+ move(event: MouseEvent, marker: HTMLElement, distance: MouseDragTrackerDistance) {
+ marker.style[markerProp] = (markerStart + distance[markerProp === 'left' ? 'x' : 'y']) + 'px';
+ },
up(event: MouseEvent, marker: HTMLElement, distance: MouseDragTrackerDistance) {
- console.log('up', distance, marker);
- // TODO - Update row/column for distance
+ marker.classList.remove('active');
+ marker.style.left = '0';
+ marker.style.top = '0';
+
+ _this.dragging = false;
+ console.log('up', distance, marker, markerProp, _this.targetCell);
+ const parentTable = _this.targetCell?.closest('table');
+
+ if (markerProp === 'left' && _this.targetCell && parentTable) {
+ const cellIndex = _this.getTargetCellColumnIndex();
+ _this.editor.update(() => {
+ const table = $getNearestNodeFromDOMNode(parentTable);
+ if (table instanceof CustomTableNode) {
+ const originalWidth = $getTableColumnWidth(_this.editor, table, cellIndex);
+ const newWidth = Math.max(originalWidth + distance.x, 10);
+ $setTableColumnWidth(table, cellIndex, newWidth);
+ }
+ });
+ }
}
});
}
+
+ getTargetCellColumnIndex(): number {
+ const cell = this.targetCell;
+ if (cell === null) {
+ return -1;
+ }
+
+ let index = 0;
+ const row = cell.parentElement;
+ for (const rowCell of row?.children || []) {
+ let size = Number(rowCell.getAttribute('colspan'));
+ if (Number.isNaN(size) || size < 1) {
+ size = 1;
+ }
+
+ index += size;
+
+ if (rowCell === cell) {
+ return index - 1;
+ }
+ }
+
+ return -1;
+ }
}