1 import {EditorDecorator} from "../framework/decorator";
2 import {el} from "../../helpers";
3 import {$createNodeSelection, $setSelection} from "lexical";
4 import {EditorUiContext} from "../framework/core";
5 import {ImageNode} from "../../nodes/image";
8 export class ImageDecorator extends EditorDecorator {
9 protected dom: HTMLElement|null = null;
10 protected dragLastMouseUp: number = 0;
12 buildDOM(context: EditorUiContext) {
13 let handleElems: HTMLElement[] = [];
14 const decorateEl = el('div', {
15 class: 'editor-image-decorator',
19 const windowClick = (event: MouseEvent) => {
20 if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
25 const mouseDown = (event: MouseEvent) => {
26 const handle = (event.target as HTMLElement).closest('.editor-image-decorator-handle') as HTMLElement|null;
28 // handlingResize = true;
29 this.startHandlingResize(handle, event, context);
33 const select = () => {
39 decorateEl.classList.add('selected');
40 window.addEventListener('click', windowClick);
42 const handleClasses = ['nw', 'ne', 'se', 'sw'];
43 handleElems = handleClasses.map(c => {
44 return el('div', {class: `editor-image-decorator-handle ${c}`});
46 decorateEl.append(...handleElems);
47 decorateEl.addEventListener('mousedown', mouseDown);
49 context.editor.update(() => {
50 const nodeSelection = $createNodeSelection();
51 nodeSelection.add(this.getNode().getKey());
52 $setSelection(nodeSelection);
56 const unselect = () => {
58 // handlingResize = false;
59 decorateEl.classList.remove('selected');
60 window.removeEventListener('click', windowClick);
61 decorateEl.removeEventListener('mousedown', mouseDown);
62 for (const el of handleElems) {
67 decorateEl.addEventListener('click', (event) => {
74 render(context: EditorUiContext): HTMLElement {
79 this.dom = this.buildDOM(context);
83 startHandlingResize(element: HTMLElement, event: MouseEvent, context: EditorUiContext) {
84 const startingX = event.screenX;
85 const startingY = event.screenY;
86 const node = this.getNode() as ImageNode;
87 let startingWidth = element.clientWidth;
88 let startingHeight = element.clientHeight;
89 let startingRatio = startingWidth / startingHeight;
90 let hasHeight = false;
91 context.editor.getEditorState().read(() => {
92 startingWidth = node.getWidth() || startingWidth;
93 startingHeight = node.getHeight() || startingHeight;
94 if (node.getHeight()) {
97 startingRatio = startingWidth / startingHeight;
100 const flipXChange = element.classList.contains('nw') || element.classList.contains('sw');
101 const flipYChange = element.classList.contains('nw') || element.classList.contains('ne');
103 const mouseMoveListener = (event: MouseEvent) => {
104 let xChange = event.screenX - startingX;
106 xChange = 0 - xChange;
108 let yChange = event.screenY - startingY;
110 yChange = 0 - yChange;
112 const balancedChange = Math.sqrt(Math.pow(xChange, 2) + Math.pow(yChange, 2));
113 const increase = xChange + yChange > 0;
114 const directedChange = increase ? balancedChange : 0-balancedChange;
115 const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
118 newHeight = newWidth * startingRatio;
121 context.editor.update(() => {
122 const node = this.getNode() as ImageNode;
123 node.setWidth(newWidth);
124 node.setHeight(newHeight);
128 const mouseUpListener = (event: MouseEvent) => {
129 window.removeEventListener('mousemove', mouseMoveListener);
130 window.removeEventListener('mouseup', mouseUpListener);
131 this.dragLastMouseUp = Date.now();
134 window.addEventListener('mousemove', mouseMoveListener);
135 window.addEventListener('mouseup', mouseUpListener);