1 import {EditorDecorator} from "../framework/decorator";
2 import {$createNodeSelection, $setSelection} from "lexical";
3 import {EditorUiContext} from "../framework/core";
4 import {ImageNode} from "../../nodes/image";
5 import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
6 import {$selectSingleNode} from "../../utils/selection";
7 import {el} from "../../utils/dom";
10 export class ImageDecorator extends EditorDecorator {
11 protected dom: HTMLElement|null = null;
12 protected dragLastMouseUp: number = 0;
14 buildDOM(context: EditorUiContext) {
15 let handleElems: HTMLElement[] = [];
16 const decorateEl = el('div', {
17 class: 'editor-image-decorator',
20 let tracker: MouseDragTracker|null = null;
22 const windowClick = (event: MouseEvent) => {
23 if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
28 const select = () => {
34 decorateEl.classList.add('selected');
35 window.addEventListener('click', windowClick);
37 const handleClasses = ['nw', 'ne', 'se', 'sw'];
38 handleElems = handleClasses.map(c => {
39 return el('div', {class: `editor-image-decorator-handle ${c}`});
41 decorateEl.append(...handleElems);
42 tracker = this.setupTracker(decorateEl, context);
44 context.editor.update(() => {
45 $selectSingleNode(this.getNode());
49 const unselect = () => {
51 decorateEl.classList.remove('selected');
52 window.removeEventListener('click', windowClick);
54 for (const el of handleElems) {
59 decorateEl.addEventListener('click', (event) => {
66 render(context: EditorUiContext): HTMLElement {
71 this.dom = this.buildDOM(context);
75 setupTracker(container: HTMLElement, context: EditorUiContext): MouseDragTracker {
76 let startingWidth: number = 0;
77 let startingHeight: number = 0;
78 let startingRatio: number = 0;
79 let hasHeight = false;
80 let firstChange = true;
81 let node: ImageNode = this.getNode() as ImageNode;
83 let flipXChange: boolean = false;
84 let flipYChange: boolean = false;
86 return new MouseDragTracker(container, '.editor-image-decorator-handle', {
87 down(event: MouseEvent, handle: HTMLElement) {
88 context.editor.getEditorState().read(() => {
89 startingWidth = node.getWidth() || startingWidth;
90 startingHeight = node.getHeight() || startingHeight;
91 if (node.getHeight()) {
94 startingRatio = startingWidth / startingHeight;
97 flipXChange = handle.classList.contains('nw') || handle.classList.contains('sw');
98 flipYChange = handle.classList.contains('nw') || handle.classList.contains('ne');
100 move(event: MouseEvent, handle: HTMLElement, distance: MouseDragTrackerDistance) {
101 let xChange = distance.x;
103 xChange = 0 - xChange;
105 let yChange = distance.y;
107 yChange = 0 - yChange;
109 const balancedChange = Math.sqrt(Math.pow(Math.abs(xChange), 2) + Math.pow(Math.abs(yChange), 2));
110 const increase = xChange + yChange > 0;
111 const directedChange = increase ? balancedChange : 0-balancedChange;
112 const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
115 newHeight = newWidth * startingRatio;
118 const updateOptions = firstChange ? {} : {tag: 'history-merge'};
119 context.editor.update(() => {
120 const node = _this.getNode() as ImageNode;
121 node.setWidth(newWidth);
122 node.setHeight(newHeight);
127 _this.dragLastMouseUp = Date.now();