]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Extracted mouse drag tracking to new helper
authorDan Brown <redacted>
Tue, 25 Jun 2024 17:33:29 +0000 (18:33 +0100)
committerDan Brown <redacted>
Tue, 25 Jun 2024 17:33:29 +0000 (18:33 +0100)
resources/js/wysiwyg/ui/decorators/image.ts
resources/js/wysiwyg/ui/framework/helpers/mouse-drag-tracker.ts [new file with mode: 0644]
resources/js/wysiwyg/ui/framework/helpers/table-resizer.ts

index 1692d078d0c0c39913d6ba1714470735eacabe40..1e8bfd165a9bd96b9f26ac3024b40d3a4e5edbaa 100644 (file)
@@ -3,6 +3,7 @@ import {el} from "../../helpers";
 import {$createNodeSelection, $setSelection} from "lexical";
 import {EditorUiContext} from "../framework/core";
 import {ImageNode} from "../../nodes/image";
+import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
 
 
 export class ImageDecorator extends EditorDecorator {
@@ -15,6 +16,7 @@ export class ImageDecorator extends EditorDecorator {
             class: 'editor-image-decorator',
         }, []);
         let selected = false;
+        let tracker: MouseDragTracker|null = null;
 
         const windowClick = (event: MouseEvent) => {
             if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
@@ -22,14 +24,6 @@ export class ImageDecorator extends EditorDecorator {
             }
         };
 
-        const mouseDown = (event: MouseEvent) => {
-            const handle = (event.target as HTMLElement).closest('.editor-image-decorator-handle') as HTMLElement|null;
-            if (handle) {
-                // handlingResize = true;
-                this.startHandlingResize(handle, event, context);
-            }
-        };
-
         const select = () => {
             if (selected) {
                 return;
@@ -44,7 +38,7 @@ export class ImageDecorator extends EditorDecorator {
                 return el('div', {class: `editor-image-decorator-handle ${c}`});
             });
             decorateEl.append(...handleElems);
-            decorateEl.addEventListener('mousedown', mouseDown);
+            tracker = this.setupTracker(decorateEl, context);
 
             context.editor.update(() => {
                 const nodeSelection = $createNodeSelection();
@@ -55,10 +49,9 @@ export class ImageDecorator extends EditorDecorator {
 
         const unselect = () => {
             selected = false;
-            // handlingResize = false;
             decorateEl.classList.remove('selected');
             window.removeEventListener('click', windowClick);
-            decorateEl.removeEventListener('mousedown', mouseDown);
+            tracker?.teardown();
             for (const el of handleElems) {
                 el.remove();
             }
@@ -80,62 +73,61 @@ export class ImageDecorator extends EditorDecorator {
         return this.dom;
     }
 
-    startHandlingResize(element: HTMLElement, event: MouseEvent, context: EditorUiContext) {
-        const startingX = event.screenX;
-        const startingY = event.screenY;
-        const node = this.getNode() as ImageNode;
-        let startingWidth = element.clientWidth;
-        let startingHeight = element.clientHeight;
-        let startingRatio = startingWidth / startingHeight;
+    setupTracker(container: HTMLElement, context: EditorUiContext): MouseDragTracker {
+        let startingWidth: number = 0;
+        let startingHeight: number = 0;
+        let startingRatio: number = 0;
         let hasHeight = false;
         let firstChange = true;
-        context.editor.getEditorState().read(() => {
-            startingWidth = node.getWidth() || startingWidth;
-            startingHeight = node.getHeight() || startingHeight;
-            if (node.getHeight()) {
-                hasHeight = true;
+        let node: ImageNode = this.getNode() as ImageNode;
+        let _this = this;
+        let flipXChange: boolean = false;
+        let flipYChange: boolean = false;
+
+        return new MouseDragTracker(container, '.editor-image-decorator-handle', {
+            down(event: MouseEvent, handle: HTMLElement) {
+                context.editor.getEditorState().read(() => {
+                    startingWidth = node.getWidth() || startingWidth;
+                    startingHeight = node.getHeight() || startingHeight;
+                    if (node.getHeight()) {
+                        hasHeight = true;
+                    }
+                    startingRatio = startingWidth / startingHeight;
+                });
+
+                flipXChange = handle.classList.contains('nw') || handle.classList.contains('sw');
+                flipYChange = handle.classList.contains('nw') || handle.classList.contains('ne');
+            },
+            move(event: MouseEvent, handle: HTMLElement, distance: MouseDragTrackerDistance) {
+                let xChange = distance.x;
+                if (flipXChange) {
+                    xChange = 0 - xChange;
+                }
+                let yChange = distance.y;
+                if (flipYChange) {
+                    yChange = 0 - yChange;
+                }
+                const balancedChange = Math.sqrt(Math.pow(Math.abs(xChange), 2) + Math.pow(Math.abs(yChange), 2));
+                const increase = xChange + yChange > 0;
+                const directedChange = increase ? balancedChange : 0-balancedChange;
+                const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
+                let newHeight = 0;
+                if (hasHeight) {
+                    newHeight = newWidth * startingRatio;
+                }
+
+                const updateOptions = firstChange ? {} : {tag: 'history-merge'};
+                context.editor.update(() => {
+                    const node = _this.getNode() as ImageNode;
+                    node.setWidth(newWidth);
+                    node.setHeight(newHeight);
+                }, updateOptions);
+                firstChange = false;
+            },
+            up() {
+                _this.dragLastMouseUp = Date.now();
             }
-            startingRatio = startingWidth / startingHeight;
         });
-
-        const flipXChange = element.classList.contains('nw') || element.classList.contains('sw');
-        const flipYChange = element.classList.contains('nw') || element.classList.contains('ne');
-
-        const mouseMoveListener = (event: MouseEvent) => {
-            let xChange = event.screenX - startingX;
-            if (flipXChange) {
-                xChange = 0 - xChange;
-            }
-            let yChange = event.screenY - startingY;
-            if (flipYChange) {
-                yChange = 0 - yChange;
-            }
-            const balancedChange = Math.sqrt(Math.pow(Math.abs(xChange), 2) + Math.pow(Math.abs(yChange), 2));
-            const increase = xChange + yChange > 0;
-            const directedChange = increase ? balancedChange : 0-balancedChange;
-            const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
-            let newHeight = 0;
-            if (hasHeight) {
-                newHeight = newWidth * startingRatio;
-            }
-
-            const updateOptions = firstChange ? {} : {tag: 'history-merge'};
-            context.editor.update(() => {
-                const node = this.getNode() as ImageNode;
-                node.setWidth(newWidth);
-                node.setHeight(newHeight);
-            }, updateOptions);
-            firstChange = false;
-        };
-
-        const mouseUpListener = (event: MouseEvent) => {
-            window.removeEventListener('mousemove', mouseMoveListener);
-            window.removeEventListener('mouseup', mouseUpListener);
-            this.dragLastMouseUp = Date.now();
-        };
-
-        window.addEventListener('mousemove', mouseMoveListener);
-        window.addEventListener('mouseup', mouseUpListener);
     }
 
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/framework/helpers/mouse-drag-tracker.ts b/resources/js/wysiwyg/ui/framework/helpers/mouse-drag-tracker.ts
new file mode 100644 (file)
index 0000000..141f9b2
--- /dev/null
@@ -0,0 +1,76 @@
+
+export type MouseDragTrackerDistance = {
+    x: number;
+    y: number;
+}
+
+export type MouseDragTrackerOptions = {
+    down?: (event: MouseEvent, element: HTMLElement) => any;
+    move?: (event: MouseEvent, element: HTMLElement, distance: MouseDragTrackerDistance) => any;
+    up?: (event: MouseEvent, element: HTMLElement, distance: MouseDragTrackerDistance) => any;
+}
+
+export class MouseDragTracker {
+    protected container: HTMLElement;
+    protected dragTargetSelector: string;
+    protected options: MouseDragTrackerOptions;
+
+    protected startX: number = 0;
+    protected startY: number = 0;
+    protected target: HTMLElement|null = null;
+
+    constructor(container: HTMLElement, dragTargetSelector: string, options: MouseDragTrackerOptions) {
+        this.container = container;
+        this.dragTargetSelector = dragTargetSelector;
+        this.options = options;
+
+        this.onMouseDown = this.onMouseDown.bind(this);
+        this.onMouseMove = this.onMouseMove.bind(this);
+        this.onMouseUp = this.onMouseUp.bind(this);
+        this.container.addEventListener('mousedown', this.onMouseDown);
+    }
+
+    teardown() {
+        this.container.removeEventListener('mousedown', this.onMouseDown);
+        this.container.removeEventListener('mouseup', this.onMouseUp);
+        this.container.removeEventListener('mousemove', this.onMouseMove);
+    }
+
+    protected onMouseDown(event: MouseEvent) {
+        this.target = (event.target as HTMLElement).closest(this.dragTargetSelector);
+        if (!this.target) {
+            return;
+        }
+
+        this.startX = event.screenX;
+        this.startY = event.screenY;
+
+        window.addEventListener('mousemove', this.onMouseMove);
+        window.addEventListener('mouseup', this.onMouseUp);
+        if (this.options.down) {
+            this.options.down(event, this.target);
+        }
+    }
+
+    protected onMouseMove(event: MouseEvent) {
+        if (this.options.move && this.target) {
+            this.options.move(event, this.target, {
+                x: event.screenX - this.startX,
+                y: event.screenY - this.startY,
+            });
+        }
+    }
+
+    protected onMouseUp(event: MouseEvent) {
+        window.removeEventListener('mousemove', this.onMouseMove);
+        window.removeEventListener('mouseup', this.onMouseUp);
+
+        if (this.options.up && this.target) {
+            this.options.up(event, this.target, {
+                x: event.screenX - this.startX,
+                y: event.screenY - this.startY,
+            });
+        }
+    }
+
+}
\ No newline at end of file
index 53017e93b939e00cbdd443e1ea1939af738d4aac..ccf269daa0fe0d6c0765d0fb4f99fc85bf4e2d95 100644 (file)
@@ -1,5 +1,6 @@
 import {LexicalEditor} from "lexical";
 import {el} from "../../../helpers";
+import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
 
 type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
 
@@ -7,6 +8,7 @@ class TableResizer {
     protected editor: LexicalEditor;
     protected editArea: HTMLElement;
     protected markerDom: MarkerDomRecord|null = null;
+    protected mouseTracker: MouseDragTracker|null = null;
 
     constructor(editor: LexicalEditor, editArea: HTMLElement) {
         this.editor = editor;
@@ -49,14 +51,27 @@ class TableResizer {
     getMarkers(): MarkerDomRecord {
         if (!this.markerDom) {
             this.markerDom = {
-                x: el('div', {class: 'editor-table-marker-column'}),
-                y: el('div', {class: 'editor-table-marker-row'}),
+                x: el('div', {class: 'editor-table-marker editor-table-marker-column'}),
+                y: el('div', {class: 'editor-table-marker editor-table-marker-row'}),
             }
-            this.editArea.after(this.markerDom.x, this.markerDom.y);
+            const wrapper = el('div', {
+                class: 'editor-table-marker-wrap',
+            }, [this.markerDom.x, this.markerDom.y]);
+            this.editArea.after(wrapper);
+            this.watchMarkerMouseDrags(wrapper);
         }
 
         return this.markerDom;
     }
+
+    watchMarkerMouseDrags(wrapper: HTMLElement) {
+        this.mouseTracker = new MouseDragTracker(wrapper, '.editor-table-marker', {
+            up(event: MouseEvent, marker: HTMLElement, distance: MouseDragTrackerDistance) {
+                console.log('up', distance, marker);
+                // TODO - Update row/column for distance
+            }
+        });
+    }
 }