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 {
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)) {
}
};
- 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;
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();
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();
}
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
--- /dev/null
+
+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
import {LexicalEditor} from "lexical";
import {el} from "../../../helpers";
+import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
protected editor: LexicalEditor;
protected editArea: HTMLElement;
protected markerDom: MarkerDomRecord|null = null;
+ protected mouseTracker: MouseDragTracker|null = null;
constructor(editor: LexicalEditor, editArea: HTMLElement) {
this.editor = editor;
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
+ }
+ });
+ }
}