mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-22 19:02:31 +01: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 {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);
|
||||
}
|
||||
|
||||
}
|
@ -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 {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
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user