mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-06 09:10:06 +00:00
Lexical: Made image resize handles functional
This commit is contained in:
parent
ba871ec46a
commit
e959c468f6
3 changed files with 80 additions and 31 deletions
resources
|
@ -80,7 +80,6 @@ export class ImageNode extends DecoratorNode<EditorDecoratorAdapter> {
|
||||||
setWidth(width: number): void {
|
setWidth(width: number): void {
|
||||||
const self = this.getWritable();
|
const self = this.getWritable();
|
||||||
self.__width = width;
|
self.__width = width;
|
||||||
console.log('widrg', width)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWidth(): number {
|
getWidth(): number {
|
||||||
|
|
|
@ -7,48 +7,65 @@ import {ImageNode} from "../../nodes/image";
|
||||||
|
|
||||||
export class ImageDecorator extends EditorDecorator {
|
export class ImageDecorator extends EditorDecorator {
|
||||||
protected dom: HTMLElement|null = null;
|
protected dom: HTMLElement|null = null;
|
||||||
|
protected dragLastMouseUp: number = 0;
|
||||||
|
|
||||||
buildDOM(context: EditorUiContext) {
|
buildDOM(context: EditorUiContext) {
|
||||||
const handleClasses = ['nw', 'ne', 'se', 'sw'];
|
let handleElems: HTMLElement[] = [];
|
||||||
const handleEls = handleClasses.map(c => {
|
|
||||||
return el('div', {class: `editor-image-decorator-handle ${c}`});
|
|
||||||
});
|
|
||||||
|
|
||||||
const decorateEl = el('div', {
|
const decorateEl = el('div', {
|
||||||
class: 'editor-image-decorator',
|
class: 'editor-image-decorator',
|
||||||
}, handleEls);
|
}, []);
|
||||||
|
let selected = false;
|
||||||
|
|
||||||
const windowClick = (event: MouseEvent) => {
|
const windowClick = (event: MouseEvent) => {
|
||||||
if (!decorateEl.contains(event.target as Node)) {
|
if (!decorateEl.contains(event.target as Node) && (Date.now() - this.dragLastMouseUp > 100)) {
|
||||||
unselect();
|
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 = () => {
|
const select = () => {
|
||||||
|
if (selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = true;
|
||||||
decorateEl.classList.add('selected');
|
decorateEl.classList.add('selected');
|
||||||
window.addEventListener('click', windowClick);
|
window.addEventListener('click', windowClick);
|
||||||
};
|
|
||||||
|
|
||||||
const unselect = () => {
|
const handleClasses = ['nw', 'ne', 'se', 'sw'];
|
||||||
decorateEl.classList.remove('selected');
|
handleElems = handleClasses.map(c => {
|
||||||
window.removeEventListener('click', windowClick);
|
return el('div', {class: `editor-image-decorator-handle ${c}`});
|
||||||
};
|
});
|
||||||
|
decorateEl.append(...handleElems);
|
||||||
|
decorateEl.addEventListener('mousedown', mouseDown);
|
||||||
|
|
||||||
decorateEl.addEventListener('click', (event) => {
|
|
||||||
context.editor.update(() => {
|
context.editor.update(() => {
|
||||||
const nodeSelection = $createNodeSelection();
|
const nodeSelection = $createNodeSelection();
|
||||||
nodeSelection.add(this.getNode().getKey());
|
nodeSelection.add(this.getNode().getKey());
|
||||||
$setSelection(nodeSelection);
|
$setSelection(nodeSelection);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
select();
|
const unselect = () => {
|
||||||
});
|
selected = false;
|
||||||
|
// handlingResize = false;
|
||||||
decorateEl.addEventListener('mousedown', (event: MouseEvent) => {
|
decorateEl.classList.remove('selected');
|
||||||
const handle = (event.target as Element).closest('.editor-image-decorator-handle');
|
window.removeEventListener('click', windowClick);
|
||||||
if (handle) {
|
decorateEl.removeEventListener('mousedown', mouseDown);
|
||||||
this.startHandlingResize(handle, event, context);
|
for (const el of handleElems) {
|
||||||
|
el.remove();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
decorateEl.addEventListener('click', (event) => {
|
||||||
|
select();
|
||||||
});
|
});
|
||||||
|
|
||||||
return decorateEl;
|
return decorateEl;
|
||||||
|
@ -63,26 +80,56 @@ export class ImageDecorator extends EditorDecorator {
|
||||||
return this.dom;
|
return this.dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
startHandlingResize(element: Node, event: MouseEvent, context: EditorUiContext) {
|
startHandlingResize(element: HTMLElement, event: MouseEvent, context: EditorUiContext) {
|
||||||
const startingX = event.screenX;
|
const startingX = event.screenX;
|
||||||
const startingY = event.screenY;
|
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 mouseMoveListener = (event: MouseEvent) => {
|
||||||
const xChange = event.screenX - startingX;
|
let xChange = event.screenX - startingX;
|
||||||
const yChange = event.screenY - startingY;
|
if (flipXChange) {
|
||||||
console.log({ xChange, yChange });
|
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(() => {
|
context.editor.update(() => {
|
||||||
const node = this.getNode() as ImageNode;
|
const node = this.getNode() as ImageNode;
|
||||||
node.setWidth(node.getWidth() + xChange);
|
node.setWidth(newWidth);
|
||||||
node.setHeight(node.getHeight() + yChange);
|
node.setHeight(newHeight);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const mouseUpListener = (event: MouseEvent) => {
|
const mouseUpListener = (event: MouseEvent) => {
|
||||||
window.removeEventListener('mousemove', mouseMoveListener);
|
window.removeEventListener('mousemove', mouseMoveListener);
|
||||||
window.removeEventListener('mouseup', mouseUpListener);
|
window.removeEventListener('mouseup', mouseUpListener);
|
||||||
}
|
this.dragLastMouseUp = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener('mousemove', mouseMoveListener);
|
window.addEventListener('mousemove', mouseMoveListener);
|
||||||
window.addEventListener('mouseup', mouseUpListener);
|
window.addEventListener('mouseup', mouseUpListener);
|
||||||
|
|
|
@ -85,20 +85,23 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
.editor-image-decorator {
|
.editor-image-decorator {
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 1px solid var(--editor-color-primary);
|
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
&.selected {
|
||||||
|
border: 1px dashed var(--editor-color-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.editor-image-decorator-handle {
|
.editor-image-decorator-handle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: block;
|
display: block;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background-color: var(--editor-color-primary);
|
border: 2px solid var(--editor-color-primary);
|
||||||
|
background-color: #FFF;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
&.nw {
|
&.nw {
|
||||||
inset-inline-start: -5px;
|
inset-inline-start: -5px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue