]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Made table resize handles more efficent & less buggy
authorDan Brown <redacted>
Fri, 13 Jun 2025 15:38:53 +0000 (16:38 +0100)
committerDan Brown <redacted>
Fri, 13 Jun 2025 15:38:53 +0000 (16:38 +0100)
Fine mouse movement and handles will now only be active when actually
within a table, otherwise less frequent mouseovers are used to track if
in/out a table.
Hides handles when out of a table, preventing a range of issues with
stray handles floating about.

resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts

index 4256fdafc65a98fecac926d48ef061c51fece98c..ef1d3cb5f2370f442fac9839428b3bcd5dcc84af 100644 (file)
@@ -15,6 +15,7 @@ class TableResizer {
     protected targetCell: HTMLElement|null = null;
     protected xMarkerAtStart : boolean = false;
     protected yMarkerAtStart : boolean = false;
     protected targetCell: HTMLElement|null = null;
     protected xMarkerAtStart : boolean = false;
     protected yMarkerAtStart : boolean = false;
+    protected activeInTable: boolean = false;
 
     constructor(editor: LexicalEditor, editScrollContainer: HTMLElement) {
         this.editor = editor;
 
     constructor(editor: LexicalEditor, editScrollContainer: HTMLElement) {
         this.editor = editor;
@@ -33,9 +34,10 @@ class TableResizer {
     }
 
     protected setupListeners() {
     }
 
     protected setupListeners() {
+        this.onTableMouseOver = this.onTableMouseOver.bind(this);
         this.onCellMouseMove = this.onCellMouseMove.bind(this);
         this.onScrollOrResize = this.onScrollOrResize.bind(this);
         this.onCellMouseMove = this.onCellMouseMove.bind(this);
         this.onScrollOrResize = this.onScrollOrResize.bind(this);
-        this.editScrollContainer.addEventListener('mousemove', this.onCellMouseMove);
+        this.editScrollContainer.addEventListener('mouseover', this.onTableMouseOver, { passive: true });
         window.addEventListener('scroll', this.onScrollOrResize, {capture: true, passive: true});
         window.addEventListener('resize', this.onScrollOrResize, {passive: true});
     }
         window.addEventListener('scroll', this.onScrollOrResize, {capture: true, passive: true});
         window.addEventListener('resize', this.onScrollOrResize, {passive: true});
     }
@@ -44,8 +46,26 @@ class TableResizer {
         this.updateCurrentMarkerTargetPosition();
     }
 
         this.updateCurrentMarkerTargetPosition();
     }
 
+    protected onTableMouseOver(event: MouseEvent): void {
+        if (this.dragging) {
+            return;
+        }
+
+        const table = (event.target as HTMLElement).closest('table') as HTMLElement|null;
+
+        if (table && !this.activeInTable) {
+            this.editScrollContainer.addEventListener('mousemove', this.onCellMouseMove, { passive: true });
+            this.onCellMouseMove(event);
+            this.activeInTable = true;
+        } else if (!table && this.activeInTable) {
+            this.editScrollContainer.removeEventListener('mousemove', this.onCellMouseMove);
+            this.hideMarkers();
+            this.activeInTable = false;
+        }
+    }
+
     protected onCellMouseMove(event: MouseEvent) {
     protected onCellMouseMove(event: MouseEvent) {
-        const cell = (event.target as HTMLElement).closest('td,th') as HTMLElement;
+        const cell = (event.target as HTMLElement).closest('td,th') as HTMLElement|null;
         if (!cell || this.dragging) {
             return;
         }
         if (!cell || this.dragging) {
             return;
         }
@@ -66,10 +86,16 @@ class TableResizer {
     protected updateMarkersTo(cell: HTMLElement, xPos: number, yPos: number) {
         const markers: MarkerDomRecord = this.getMarkers();
         const table = cell.closest('table') as HTMLElement;
     protected updateMarkersTo(cell: HTMLElement, xPos: number, yPos: number) {
         const markers: MarkerDomRecord = this.getMarkers();
         const table = cell.closest('table') as HTMLElement;
+        const caption: HTMLTableCaptionElement|null = table.querySelector('caption');
         const tableRect = table.getBoundingClientRect();
         const editBounds = this.editScrollContainer.getBoundingClientRect();
 
         const tableRect = table.getBoundingClientRect();
         const editBounds = this.editScrollContainer.getBoundingClientRect();
 
-        const maxTop = Math.max(tableRect.top, editBounds.top);
+        let tableTop = tableRect.top;
+        if (caption) {
+            tableTop = caption.getBoundingClientRect().bottom;
+        }
+
+        const maxTop = Math.max(tableTop, editBounds.top);
         const maxBottom = Math.min(tableRect.bottom, editBounds.bottom);
         const maxHeight = maxBottom - maxTop;
         markers.x.style.left = xPos + 'px';
         const maxBottom = Math.min(tableRect.bottom, editBounds.bottom);
         const maxHeight = maxBottom - maxTop;
         markers.x.style.left = xPos + 'px';
@@ -85,6 +111,13 @@ class TableResizer {
         markers.x.hidden = tableRect.top > editBounds.bottom || tableRect.bottom < editBounds.top;
     }
 
         markers.x.hidden = tableRect.top > editBounds.bottom || tableRect.bottom < editBounds.top;
     }
 
+    protected hideMarkers(): void {
+        if (this.markerDom) {
+            this.markerDom.x.hidden = true;
+            this.markerDom.y.hidden = true;
+        }
+    }
+
     protected updateCurrentMarkerTargetPosition(): void {
         if (!this.targetCell) {
             return;
     protected updateCurrentMarkerTargetPosition(): void {
         if (!this.targetCell) {
             return;