mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-06-02 23:56:56 +08:00
Lexical: Added toolbar scroll/resize handling
Also added smarter above/below positioning to respond if toolbar would be off the bottom of the editor, and added hide/show when they'd go outside editor scroll bounds.
This commit is contained in:
@ -11,6 +11,7 @@ export type EditorUiContext = {
|
||||
editor: LexicalEditor; // Lexical editor instance
|
||||
editorDOM: HTMLElement; // DOM element the editor is bound to
|
||||
containerDOM: HTMLElement; // DOM element which contains all editor elements
|
||||
scrollDOM: HTMLElement; // DOM element which is the main content scroll container
|
||||
translate: (text: string) => string; // Translate function
|
||||
manager: EditorUIManager; // UI Manager instance for this editor
|
||||
lastSelection: BaseSelection|null; // The last tracked selection made by the user
|
||||
|
@ -21,6 +21,7 @@ export class EditorUIManager {
|
||||
|
||||
setContext(context: EditorUiContext) {
|
||||
this.context = context;
|
||||
this.setupEventListeners(context);
|
||||
this.setupEditor(context.editor);
|
||||
}
|
||||
|
||||
@ -130,9 +131,10 @@ export class EditorUIManager {
|
||||
}
|
||||
|
||||
protected updateContextToolbars(update: EditorUiStateUpdate): void {
|
||||
for (const toolbar of this.activeContextToolbars) {
|
||||
toolbar.empty();
|
||||
toolbar.getDOMElement().remove();
|
||||
for (let i = this.activeContextToolbars.length - 1; i >= 0; i--) {
|
||||
const toolbar = this.activeContextToolbars[i];
|
||||
toolbar.destroy();
|
||||
this.activeContextToolbars.splice(i, 1);
|
||||
}
|
||||
|
||||
const node = (update.selection?.getNodes() || [])[0] || null;
|
||||
@ -161,12 +163,12 @@ export class EditorUIManager {
|
||||
}
|
||||
|
||||
for (const [target, contents] of contentByTarget) {
|
||||
const toolbar = new EditorContextToolbar(contents);
|
||||
const toolbar = new EditorContextToolbar(target, contents);
|
||||
toolbar.setContext(this.getContext());
|
||||
this.activeContextToolbars.push(toolbar);
|
||||
|
||||
this.getContext().containerDOM.append(toolbar.getDOMElement());
|
||||
toolbar.attachTo(target);
|
||||
toolbar.updatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,4 +204,15 @@ export class EditorUIManager {
|
||||
}
|
||||
editor.registerDecoratorListener(domDecorateListener);
|
||||
}
|
||||
|
||||
protected setupEventListeners(context: EditorUiContext) {
|
||||
const updateToolbars = (event: Event) => {
|
||||
for (const toolbar of this.activeContextToolbars) {
|
||||
toolbar.updatePosition();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', updateToolbars, {capture: true, passive: true});
|
||||
window.addEventListener('resize', updateToolbars, {passive: true});
|
||||
}
|
||||
}
|
@ -9,20 +9,44 @@ export type EditorContextToolbarDefinition = {
|
||||
|
||||
export class EditorContextToolbar extends EditorContainerUiElement {
|
||||
|
||||
protected target: HTMLElement;
|
||||
|
||||
constructor(target: HTMLElement, children: EditorUiElement[]) {
|
||||
super(children);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
protected buildDOM(): HTMLElement {
|
||||
return el('div', {
|
||||
class: 'editor-context-toolbar',
|
||||
}, this.getChildren().map(child => child.getDOMElement()));
|
||||
}
|
||||
|
||||
attachTo(target: HTMLElement) {
|
||||
const targetBounds = target.getBoundingClientRect();
|
||||
updatePosition() {
|
||||
const editorBounds = this.getContext().scrollDOM.getBoundingClientRect();
|
||||
const targetBounds = this.target.getBoundingClientRect();
|
||||
const dom = this.getDOMElement();
|
||||
const domBounds = dom.getBoundingClientRect();
|
||||
|
||||
const showing = targetBounds.bottom > editorBounds.top
|
||||
&& targetBounds.top < editorBounds.bottom;
|
||||
|
||||
dom.hidden = !showing;
|
||||
|
||||
if (!showing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const showAbove: boolean = targetBounds.bottom + 6 + domBounds.height > editorBounds.bottom;
|
||||
dom.classList.toggle('is-above', showAbove);
|
||||
|
||||
const targetMid = targetBounds.left + (targetBounds.width / 2);
|
||||
const targetLeft = targetMid - (domBounds.width / 2);
|
||||
dom.style.top = (targetBounds.bottom + 6) + 'px';
|
||||
if (showAbove) {
|
||||
dom.style.top = (targetBounds.top - 6 - domBounds.height) + 'px';
|
||||
} else {
|
||||
dom.style.top = (targetBounds.bottom + 6) + 'px';
|
||||
}
|
||||
dom.style.left = targetLeft + 'px';
|
||||
}
|
||||
|
||||
@ -32,11 +56,16 @@ export class EditorContextToolbar extends EditorContainerUiElement {
|
||||
dom.append(...children.map(child => child.getDOMElement()));
|
||||
}
|
||||
|
||||
empty() {
|
||||
protected empty() {
|
||||
const children = this.getChildren();
|
||||
for (const child of children) {
|
||||
child.getDOMElement().remove();
|
||||
}
|
||||
this.removeChildren(...children);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.empty();
|
||||
this.getDOMElement().remove();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user