mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-30 04:23:11 +03:00
Lexical: Fixed tiny image resizer on image insert
Added specific focus on image insert, and updated resize handler to watch for load events and toggle a resize once loaded.
This commit is contained in:
@ -92,6 +92,7 @@ export const image: EditorButtonDefinition = {
|
|||||||
context.editor.update(() => {
|
context.editor.update(() => {
|
||||||
const link = $createLinkedImageNodeFromImageData(image);
|
const link = $createLinkedImageNodeFromImageData(image);
|
||||||
$insertNodes([link]);
|
$insertNodes([link]);
|
||||||
|
link.select();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -12,17 +12,21 @@ function isNodeWithSize(node: LexicalNode): node is NodeHasSize&LexicalNode {
|
|||||||
|
|
||||||
class NodeResizer {
|
class NodeResizer {
|
||||||
protected context: EditorUiContext;
|
protected context: EditorUiContext;
|
||||||
protected dom: HTMLElement|null = null;
|
protected resizerDOM: HTMLElement|null = null;
|
||||||
|
protected targetDOM: HTMLElement|null = null;
|
||||||
protected scrollContainer: HTMLElement;
|
protected scrollContainer: HTMLElement;
|
||||||
|
|
||||||
protected mouseTracker: MouseDragTracker|null = null;
|
protected mouseTracker: MouseDragTracker|null = null;
|
||||||
protected activeSelection: string = '';
|
protected activeSelection: string = '';
|
||||||
|
protected loadAbortController = new AbortController();
|
||||||
|
|
||||||
constructor(context: EditorUiContext) {
|
constructor(context: EditorUiContext) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.scrollContainer = context.scrollDOM;
|
this.scrollContainer = context.scrollDOM;
|
||||||
|
|
||||||
this.onSelectionChange = this.onSelectionChange.bind(this);
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
||||||
|
this.onTargetDOMLoad = this.onTargetDOMLoad.bind(this);
|
||||||
|
|
||||||
context.manager.onSelectionChange(this.onSelectionChange);
|
context.manager.onSelectionChange(this.onSelectionChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,56 +51,68 @@ class NodeResizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTargetDOMLoad(): void {
|
||||||
|
this.updateResizerPosition();
|
||||||
|
}
|
||||||
|
|
||||||
teardown() {
|
teardown() {
|
||||||
this.context.manager.offSelectionChange(this.onSelectionChange);
|
this.context.manager.offSelectionChange(this.onSelectionChange);
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected showForNode(node: NodeHasSize&LexicalNode, dom: HTMLElement) {
|
protected showForNode(node: NodeHasSize&LexicalNode, targetDOM: HTMLElement) {
|
||||||
this.dom = this.buildDOM();
|
this.resizerDOM = this.buildDOM();
|
||||||
|
this.targetDOM = targetDOM;
|
||||||
|
|
||||||
let ghost = el('span', {class: 'editor-node-resizer-ghost'});
|
let ghost = el('span', {class: 'editor-node-resizer-ghost'});
|
||||||
if ($isImageNode(node)) {
|
if ($isImageNode(node)) {
|
||||||
ghost = el('img', {src: dom.getAttribute('src'), class: 'editor-node-resizer-ghost'});
|
ghost = el('img', {src: targetDOM.getAttribute('src'), class: 'editor-node-resizer-ghost'});
|
||||||
}
|
}
|
||||||
this.dom.append(ghost);
|
this.resizerDOM.append(ghost);
|
||||||
|
|
||||||
this.context.scrollDOM.append(this.dom);
|
this.context.scrollDOM.append(this.resizerDOM);
|
||||||
this.updateDOMPosition(dom);
|
this.updateResizerPosition();
|
||||||
|
|
||||||
this.mouseTracker = this.setupTracker(this.dom, node, dom);
|
this.mouseTracker = this.setupTracker(this.resizerDOM, node, targetDOM);
|
||||||
this.activeSelection = node.getKey();
|
this.activeSelection = node.getKey();
|
||||||
|
|
||||||
|
if (targetDOM.matches('img, embed, iframe, object')) {
|
||||||
|
this.loadAbortController = new AbortController();
|
||||||
|
targetDOM.addEventListener('load', this.onTargetDOMLoad, { signal: this.loadAbortController.signal });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateDOMPosition(nodeDOM: HTMLElement) {
|
protected updateResizerPosition() {
|
||||||
if (!this.dom) {
|
if (!this.resizerDOM || !this.targetDOM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollAreaRect = this.scrollContainer.getBoundingClientRect();
|
const scrollAreaRect = this.scrollContainer.getBoundingClientRect();
|
||||||
const nodeRect = nodeDOM.getBoundingClientRect();
|
const nodeRect = this.targetDOM.getBoundingClientRect();
|
||||||
const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop);
|
const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop);
|
||||||
const left = nodeRect.left - scrollAreaRect.left;
|
const left = nodeRect.left - scrollAreaRect.left;
|
||||||
|
|
||||||
this.dom.style.top = `${top}px`;
|
this.resizerDOM.style.top = `${top}px`;
|
||||||
this.dom.style.left = `${left}px`;
|
this.resizerDOM.style.left = `${left}px`;
|
||||||
this.dom.style.width = nodeRect.width + 'px';
|
this.resizerDOM.style.width = nodeRect.width + 'px';
|
||||||
this.dom.style.height = nodeRect.height + 'px';
|
this.resizerDOM.style.height = nodeRect.height + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateDOMSize(width: number, height: number): void {
|
protected updateDOMSize(width: number, height: number): void {
|
||||||
if (!this.dom) {
|
if (!this.resizerDOM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dom.style.width = width + 'px';
|
this.resizerDOM.style.width = width + 'px';
|
||||||
this.dom.style.height = height + 'px';
|
this.resizerDOM.style.height = height + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected hide() {
|
protected hide() {
|
||||||
this.mouseTracker?.teardown();
|
this.mouseTracker?.teardown();
|
||||||
this.dom?.remove();
|
this.resizerDOM?.remove();
|
||||||
|
this.targetDOM = null;
|
||||||
this.activeSelection = '';
|
this.activeSelection = '';
|
||||||
|
this.loadAbortController.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected buildDOM() {
|
protected buildDOM() {
|
||||||
@ -140,7 +156,7 @@ class NodeResizer {
|
|||||||
|
|
||||||
return new MouseDragTracker(container, '.editor-node-resizer-handle', {
|
return new MouseDragTracker(container, '.editor-node-resizer-handle', {
|
||||||
down(event: MouseEvent, handle: HTMLElement) {
|
down(event: MouseEvent, handle: HTMLElement) {
|
||||||
_this.dom?.classList.add('active');
|
_this.resizerDOM?.classList.add('active');
|
||||||
_this.context.editor.getEditorState().read(() => {
|
_this.context.editor.getEditorState().read(() => {
|
||||||
const domRect = nodeDOM.getBoundingClientRect();
|
const domRect = nodeDOM.getBoundingClientRect();
|
||||||
startingWidth = node.getWidth() || domRect.width;
|
startingWidth = node.getWidth() || domRect.width;
|
||||||
@ -165,10 +181,10 @@ class NodeResizer {
|
|||||||
node.setHeight(hasHeight ? size.height : 0);
|
node.setHeight(hasHeight ? size.height : 0);
|
||||||
_this.context.manager.triggerLayoutUpdate();
|
_this.context.manager.triggerLayoutUpdate();
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
_this.updateDOMPosition(nodeDOM);
|
_this.updateResizerPosition();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
_this.dom?.classList.remove('active');
|
_this.resizerDOM?.classList.remove('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user