diff --git a/resources/js/wysiwyg/nodes/custom-table.ts b/resources/js/wysiwyg/nodes/custom-table.ts index c070e06b5..1107f0a90 100644 --- a/resources/js/wysiwyg/nodes/custom-table.ts +++ b/resources/js/wysiwyg/nodes/custom-table.ts @@ -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; } \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts b/resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts index ccf269daa..869de8460 100644 --- a/resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts +++ b/resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts @@ -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; + } } diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss index b5ee69d98..a4b0e632f 100644 --- a/resources/sass/_editor.scss +++ b/resources/sass/_editor.scss @@ -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; } }