Lexical: Linked up table resize handler (unfinished)

This commit is contained in:
Dan Brown 2024-06-26 13:52:00 +01:00
parent 59936631ec
commit b1130cb1c3
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 110 additions and 22 deletions

View File

@ -1,5 +1,5 @@
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";
@ -111,6 +111,21 @@ export class CustomTableNode extends TableNode {
}
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;
@ -122,16 +137,7 @@ function getTableColumnWidths(table: HTMLTableElement): string[] {
}
}
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) {
@ -140,7 +146,7 @@ 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';
}
@ -176,5 +182,23 @@ export function $setTableColumnWidth(node: CustomTableNode, columnIndex: number,
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;
}

View File

@ -1,6 +1,7 @@
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};
@ -9,6 +10,10 @@ class TableResizer {
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;
@ -19,7 +24,7 @@ class TableResizer {
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);
}
});
@ -29,8 +34,13 @@ class TableResizer {
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);
}
@ -65,13 +75,68 @@ class TableResizer {
}
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;
}
}

View File

@ -169,14 +169,13 @@
}
}
.editor-table-marker-row,
.editor-table-marker-column {
.editor-table-marker {
position: fixed;
background-color: var(--editor-color-primary);
z-index: 99;
user-select: none;
opacity: 0;
&:hover {
&:hover, &.active {
opacity: 0.4;
}
}