Lexical: Made image resize handles functional

This commit is contained in:
Dan Brown 2024-06-05 17:18:58 +01:00
parent ba871ec46a
commit e959c468f6
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 80 additions and 31 deletions

View File

@ -80,7 +80,6 @@ export class ImageNode extends DecoratorNode<EditorDecoratorAdapter> {
setWidth(width: number): void {
const self = this.getWritable();
self.__width = width;
console.log('widrg', width)
}
getWidth(): number {

View File

@ -7,48 +7,65 @@ import {ImageNode} from "../../nodes/image";
export class ImageDecorator extends EditorDecorator {
protected dom: HTMLElement|null = null;
protected dragLastMouseUp: number = 0;
buildDOM(context: EditorUiContext) {
const handleClasses = ['nw', 'ne', 'se', 'sw'];
const handleEls = handleClasses.map(c => {
return el('div', {class: `editor-image-decorator-handle ${c}`});
});
let handleElems: HTMLElement[] = [];
const decorateEl = el('div', {
class: 'editor-image-decorator',
}, handleEls);
}, []);
let selected = false;
const windowClick = (event: MouseEvent) => {
if (!decorateEl.contains(event.target as Node)) {
if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
unselect();
}
};
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;
}
selected = true;
decorateEl.classList.add('selected');
window.addEventListener('click', windowClick);
};
const unselect = () => {
decorateEl.classList.remove('selected');
window.removeEventListener('click', windowClick);
};
const handleClasses = ['nw', 'ne', 'se', 'sw'];
handleElems = handleClasses.map(c => {
return el('div', {class: `editor-image-decorator-handle ${c}`});
});
decorateEl.append(...handleElems);
decorateEl.addEventListener('mousedown', mouseDown);
decorateEl.addEventListener('click', (event) => {
context.editor.update(() => {
const nodeSelection = $createNodeSelection();
nodeSelection.add(this.getNode().getKey());
$setSelection(nodeSelection);
});
};
select();
});
decorateEl.addEventListener('mousedown', (event: MouseEvent) => {
const handle = (event.target as Element).closest('.editor-image-decorator-handle');
if (handle) {
this.startHandlingResize(handle, event, context);
const unselect = () => {
selected = false;
// handlingResize = false;
decorateEl.classList.remove('selected');
window.removeEventListener('click', windowClick);
decorateEl.removeEventListener('mousedown', mouseDown);
for (const el of handleElems) {
el.remove();
}
};
decorateEl.addEventListener('click', (event) => {
select();
});
return decorateEl;
@ -63,26 +80,56 @@ export class ImageDecorator extends EditorDecorator {
return this.dom;
}
startHandlingResize(element: Node, event: MouseEvent, context: EditorUiContext) {
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;
let hasHeight = false;
context.editor.getEditorState().read(() => {
startingWidth = node.getWidth() || startingWidth;
startingHeight = node.getHeight() || startingHeight;
if (node.getHeight()) {
hasHeight = true;
}
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) => {
const xChange = event.screenX - startingX;
const yChange = event.screenY - startingY;
console.log({ xChange, yChange });
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(xChange, 2) + Math.pow(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;
}
context.editor.update(() => {
const node = this.getNode() as ImageNode;
node.setWidth(node.getWidth() + xChange);
node.setHeight(node.getHeight() + yChange);
node.setWidth(newWidth);
node.setHeight(newHeight);
});
};
const mouseUpListener = (event: MouseEvent) => {
window.removeEventListener('mousemove', mouseMoveListener);
window.removeEventListener('mouseup', mouseUpListener);
}
this.dragLastMouseUp = Date.now();
};
window.addEventListener('mousemove', mouseMoveListener);
window.addEventListener('mouseup', mouseUpListener);

View File

@ -85,20 +85,23 @@
display: inline-flex;
}
.editor-image-decorator {
display: inline-block;
position: absolute;
border: 1px solid var(--editor-color-primary);
left: 0;
right: 0;
width: 100%;
height: 100%;
display: inline-block;
&.selected {
border: 1px dashed var(--editor-color-primary);
}
}
.editor-image-decorator-handle {
position: absolute;
display: block;
width: 10px;
height: 10px;
background-color: var(--editor-color-primary);
border: 2px solid var(--editor-color-primary);
background-color: #FFF;
user-select: none;
&.nw {
inset-inline-start: -5px;