1 import {EditorDecorator} from "../framework/decorator";
2 import {el, $selectSingleNode} from "../../helpers";
3 import {$createNodeSelection, $setSelection} from "lexical";
4 import {EditorUiContext} from "../framework/core";
5 import {ImageNode} from "../../nodes/image";
6 import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
9 export class ImageDecorator extends EditorDecorator {
10 protected dom: HTMLElement|null = null;
11 protected dragLastMouseUp: number = 0;
13 buildDOM(context: EditorUiContext) {
14 let handleElems: HTMLElement[] = [];
15 const decorateEl = el('div', {
16 class: 'editor-image-decorator',
19 let tracker: MouseDragTracker|null = null;
21 const windowClick = (event: MouseEvent) => {
22 if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
27 const select = () => {
33 decorateEl.classList.add('selected');
34 window.addEventListener('click', windowClick);
36 const handleClasses = ['nw', 'ne', 'se', 'sw'];
37 handleElems = handleClasses.map(c => {
38 return el('div', {class: `editor-image-decorator-handle ${c}`});
40 decorateEl.append(...handleElems);
41 tracker = this.setupTracker(decorateEl, context);
43 context.editor.update(() => {
44 $selectSingleNode(this.getNode());
48 const unselect = () => {
50 decorateEl.classList.remove('selected');
51 window.removeEventListener('click', windowClick);
53 for (const el of handleElems) {
58 decorateEl.addEventListener('click', (event) => {
65 render(context: EditorUiContext): HTMLElement {
70 this.dom = this.buildDOM(context);
74 setupTracker(container: HTMLElement, context: EditorUiContext): MouseDragTracker {
75 let startingWidth: number = 0;
76 let startingHeight: number = 0;
77 let startingRatio: number = 0;
78 let hasHeight = false;
79 let firstChange = true;
80 let node: ImageNode = this.getNode() as ImageNode;
82 let flipXChange: boolean = false;
83 let flipYChange: boolean = false;
85 return new MouseDragTracker(container, '.editor-image-decorator-handle', {
86 down(event: MouseEvent, handle: HTMLElement) {
87 context.editor.getEditorState().read(() => {
88 startingWidth = node.getWidth() || startingWidth;
89 startingHeight = node.getHeight() || startingHeight;
90 if (node.getHeight()) {
93 startingRatio = startingWidth / startingHeight;
96 flipXChange = handle.classList.contains('nw') || handle.classList.contains('sw');
97 flipYChange = handle.classList.contains('nw') || handle.classList.contains('ne');
99 move(event: MouseEvent, handle: HTMLElement, distance: MouseDragTrackerDistance) {
100 let xChange = distance.x;
102 xChange = 0 - xChange;
104 let yChange = distance.y;
106 yChange = 0 - yChange;
108 const balancedChange = Math.sqrt(Math.pow(Math.abs(xChange), 2) + Math.pow(Math.abs(yChange), 2));
109 const increase = xChange + yChange > 0;
110 const directedChange = increase ? balancedChange : 0-balancedChange;
111 const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
114 newHeight = newWidth * startingRatio;
117 const updateOptions = firstChange ? {} : {tag: 'history-merge'};
118 context.editor.update(() => {
119 const node = _this.getNode() as ImageNode;
120 node.setWidth(newWidth);
121 node.setHeight(newHeight);
126 _this.dragLastMouseUp = Date.now();