mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Extracted mouse drag tracking to new helper
This commit is contained in:
parent
3af22ce754
commit
59936631ec
@ -3,6 +3,7 @@ import {el} from "../../helpers";
|
|||||||
import {$createNodeSelection, $setSelection} from "lexical";
|
import {$createNodeSelection, $setSelection} from "lexical";
|
||||||
import {EditorUiContext} from "../framework/core";
|
import {EditorUiContext} from "../framework/core";
|
||||||
import {ImageNode} from "../../nodes/image";
|
import {ImageNode} from "../../nodes/image";
|
||||||
|
import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
|
||||||
|
|
||||||
|
|
||||||
export class ImageDecorator extends EditorDecorator {
|
export class ImageDecorator extends EditorDecorator {
|
||||||
@ -15,6 +16,7 @@ export class ImageDecorator extends EditorDecorator {
|
|||||||
class: 'editor-image-decorator',
|
class: 'editor-image-decorator',
|
||||||
}, []);
|
}, []);
|
||||||
let selected = false;
|
let selected = false;
|
||||||
|
let tracker: MouseDragTracker|null = null;
|
||||||
|
|
||||||
const windowClick = (event: MouseEvent) => {
|
const windowClick = (event: MouseEvent) => {
|
||||||
if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
|
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 = () => {
|
const select = () => {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
return;
|
return;
|
||||||
@ -44,7 +38,7 @@ export class ImageDecorator extends EditorDecorator {
|
|||||||
return el('div', {class: `editor-image-decorator-handle ${c}`});
|
return el('div', {class: `editor-image-decorator-handle ${c}`});
|
||||||
});
|
});
|
||||||
decorateEl.append(...handleElems);
|
decorateEl.append(...handleElems);
|
||||||
decorateEl.addEventListener('mousedown', mouseDown);
|
tracker = this.setupTracker(decorateEl, context);
|
||||||
|
|
||||||
context.editor.update(() => {
|
context.editor.update(() => {
|
||||||
const nodeSelection = $createNodeSelection();
|
const nodeSelection = $createNodeSelection();
|
||||||
@ -55,10 +49,9 @@ export class ImageDecorator extends EditorDecorator {
|
|||||||
|
|
||||||
const unselect = () => {
|
const unselect = () => {
|
||||||
selected = false;
|
selected = false;
|
||||||
// handlingResize = false;
|
|
||||||
decorateEl.classList.remove('selected');
|
decorateEl.classList.remove('selected');
|
||||||
window.removeEventListener('click', windowClick);
|
window.removeEventListener('click', windowClick);
|
||||||
decorateEl.removeEventListener('mousedown', mouseDown);
|
tracker?.teardown();
|
||||||
for (const el of handleElems) {
|
for (const el of handleElems) {
|
||||||
el.remove();
|
el.remove();
|
||||||
}
|
}
|
||||||
@ -80,62 +73,61 @@ export class ImageDecorator extends EditorDecorator {
|
|||||||
return this.dom;
|
return this.dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
startHandlingResize(element: HTMLElement, event: MouseEvent, context: EditorUiContext) {
|
setupTracker(container: HTMLElement, context: EditorUiContext): MouseDragTracker {
|
||||||
const startingX = event.screenX;
|
let startingWidth: number = 0;
|
||||||
const startingY = event.screenY;
|
let startingHeight: number = 0;
|
||||||
const node = this.getNode() as ImageNode;
|
let startingRatio: number = 0;
|
||||||
let startingWidth = element.clientWidth;
|
|
||||||
let startingHeight = element.clientHeight;
|
|
||||||
let startingRatio = startingWidth / startingHeight;
|
|
||||||
let hasHeight = false;
|
let hasHeight = false;
|
||||||
let firstChange = true;
|
let firstChange = true;
|
||||||
context.editor.getEditorState().read(() => {
|
let node: ImageNode = this.getNode() as ImageNode;
|
||||||
startingWidth = node.getWidth() || startingWidth;
|
let _this = this;
|
||||||
startingHeight = node.getHeight() || startingHeight;
|
let flipXChange: boolean = false;
|
||||||
if (node.getHeight()) {
|
let flipYChange: boolean = false;
|
||||||
hasHeight = true;
|
|
||||||
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import {LexicalEditor} from "lexical";
|
import {LexicalEditor} from "lexical";
|
||||||
import {el} from "../../../helpers";
|
import {el} from "../../../helpers";
|
||||||
|
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
|
||||||
|
|
||||||
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
|
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
|
||||||
|
|
||||||
@ -7,6 +8,7 @@ class TableResizer {
|
|||||||
protected editor: LexicalEditor;
|
protected editor: LexicalEditor;
|
||||||
protected editArea: HTMLElement;
|
protected editArea: HTMLElement;
|
||||||
protected markerDom: MarkerDomRecord|null = null;
|
protected markerDom: MarkerDomRecord|null = null;
|
||||||
|
protected mouseTracker: MouseDragTracker|null = null;
|
||||||
|
|
||||||
constructor(editor: LexicalEditor, editArea: HTMLElement) {
|
constructor(editor: LexicalEditor, editArea: HTMLElement) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
@ -49,14 +51,27 @@ class TableResizer {
|
|||||||
getMarkers(): MarkerDomRecord {
|
getMarkers(): MarkerDomRecord {
|
||||||
if (!this.markerDom) {
|
if (!this.markerDom) {
|
||||||
this.markerDom = {
|
this.markerDom = {
|
||||||
x: el('div', {class: 'editor-table-marker-column'}),
|
x: el('div', {class: 'editor-table-marker editor-table-marker-column'}),
|
||||||
y: el('div', {class: 'editor-table-marker-row'}),
|
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;
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user