mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	refactor(components): split editor toolbar for mobile
This commit is contained in:
		
							parent
							
								
									c422c3e5b9
								
							
						
					
					
						commit
						fbba76bbb3
					
				@ -22,7 +22,6 @@ import LauncherContainer from "../widgets/containers/launcher_container.js";
 | 
			
		||||
import RootContainer from "../widgets/containers/root_container.js";
 | 
			
		||||
import SharedInfoWidget from "../widgets/shared_info.js";
 | 
			
		||||
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
 | 
			
		||||
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
 | 
			
		||||
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
 | 
			
		||||
import AboutDialog from "../widgets/dialogs/about.js";
 | 
			
		||||
import HelpDialog from "../widgets/dialogs/help.js";
 | 
			
		||||
@ -32,6 +31,7 @@ import JumpToNoteDialog from "../widgets/dialogs/jump_to_note.js";
 | 
			
		||||
import RecentChangesDialog from "../widgets/dialogs/recent_changes.js";
 | 
			
		||||
import PromptDialog from "../widgets/dialogs/prompt.js";
 | 
			
		||||
import RefreshButton from "../widgets/floating_buttons/refresh_button.js";
 | 
			
		||||
import MobileEditorToolbar from "../widgets/ribbon_widgets/mobile_editor_toolbar.js";
 | 
			
		||||
 | 
			
		||||
const MOBILE_CSS = `
 | 
			
		||||
<style>
 | 
			
		||||
@ -182,7 +182,7 @@ export default class MobileLayout {
 | 
			
		||||
                    .child(new TabRowWidget().css("height", "40px"))
 | 
			
		||||
                    .child(new FlexContainer("row").class("horizontal").css("height", "53px").child(new LauncherContainer(true)).child(new GlobalMenuWidget(true)).id("launcher-pane"))
 | 
			
		||||
            )
 | 
			
		||||
            .child(new ClassicEditorToolbar())
 | 
			
		||||
            .child(new MobileEditorToolbar())
 | 
			
		||||
            .child(new AboutDialog())
 | 
			
		||||
            .child(new HelpDialog())
 | 
			
		||||
            .child(new RecentChangesDialog())
 | 
			
		||||
 | 
			
		||||
@ -21,53 +21,6 @@ const TPL = /*html*/`\
 | 
			
		||||
    .classic-toolbar-widget .ck.ck-button.ck-disabled {
 | 
			
		||||
        opacity: 0.3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: flex-end;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
        overscroll-behavior: none;
 | 
			
		||||
        z-index: 500;
 | 
			
		||||
        user-select: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.visible::-webkit-scrollbar {
 | 
			
		||||
        height: 3px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media (max-width: 991px) {
 | 
			
		||||
        body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
            bottom: calc(var(--tab-bar-height) + var(--launcher-pane-height) + var(--mobile-bottom-offset));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media (min-width: 991px) {
 | 
			
		||||
        body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
            bottom: 0;
 | 
			
		||||
            left: 25%;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.dropdown-active {
 | 
			
		||||
        height: 50vh;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget .ck.ck-toolbar {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        background-color: var(--main-background-color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget .ck.ck-dropdown__panel {
 | 
			
		||||
        bottom: 100% !important;
 | 
			
		||||
        top: unset !important;
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
@ -82,13 +35,6 @@ const TPL = /*html*/`\
 | 
			
		||||
 */
 | 
			
		||||
export default class ClassicEditorToolbar extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
    private observer: MutationObserver;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        this.observer = new MutationObserver((e) => this.#onDropdownStateChanged(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get name() {
 | 
			
		||||
        return "classicEditor";
 | 
			
		||||
    }
 | 
			
		||||
@ -100,33 +46,6 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget {
 | 
			
		||||
    doRender() {
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
        this.contentSized();
 | 
			
		||||
 | 
			
		||||
        if (utils.isMobile()) {
 | 
			
		||||
            // The virtual keyboard obscures the editing toolbar so we have to reposition by calculating the height of the keyboard.
 | 
			
		||||
            window.visualViewport?.addEventListener("resize", () => this.#adjustPosition());
 | 
			
		||||
            window.addEventListener("scroll", () => this.#adjustPosition());
 | 
			
		||||
 | 
			
		||||
            // Observe when a dropdown is expanded to apply a style that allows the dropdown to be visible, since we can't have the element both visible and the toolbar scrollable.
 | 
			
		||||
            this.observer.disconnect();
 | 
			
		||||
            this.observer.observe(this.$widget[0], {
 | 
			
		||||
                attributeFilter: ["aria-expanded"],
 | 
			
		||||
                subtree: true
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #onDropdownStateChanged(e: MutationRecord[]) {
 | 
			
		||||
        const dropdownActive = e.map((e) => (e.target as any).ariaExpanded === "true").reduce((acc, e) => acc && e);
 | 
			
		||||
        this.$widget[0].classList.toggle("dropdown-active", dropdownActive);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #adjustPosition() {
 | 
			
		||||
        let bottom = window.innerHeight - (window.visualViewport?.height || 0);
 | 
			
		||||
 | 
			
		||||
        // When the keyboard is not visible, align it to the launcher bar instead.
 | 
			
		||||
        bottom = Math.max(bottom, document.getElementById("mobile-bottom-bar")?.offsetHeight || 0);
 | 
			
		||||
 | 
			
		||||
        this.$widget.css("bottom", `${bottom}px`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getTitle() {
 | 
			
		||||
@ -139,7 +58,7 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async #shouldDisplay() {
 | 
			
		||||
        if (utils.isDesktop() && options.get("textNoteEditorType") !== "ckeditor-classic") {
 | 
			
		||||
        if (options.get("textNoteEditorType") !== "ckeditor-classic") {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -154,10 +73,4 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshWithNote() {
 | 
			
		||||
        if (utils.isMobile()) {
 | 
			
		||||
            this.toggleExt(await this.#shouldDisplay());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										139
									
								
								src/public/app/widgets/ribbon_widgets/mobile_editor_toolbar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/public/app/widgets/ribbon_widgets/mobile_editor_toolbar.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,139 @@
 | 
			
		||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
 | 
			
		||||
 | 
			
		||||
const TPL = /*html*/`\
 | 
			
		||||
<div class="classic-toolbar-widget"></div>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
    .classic-toolbar-widget {
 | 
			
		||||
        --ck-color-toolbar-background: transparent;
 | 
			
		||||
        --ck-color-button-default-background: transparent;
 | 
			
		||||
        --ck-color-button-default-disabled-background: transparent;
 | 
			
		||||
        min-height: 39px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .classic-toolbar-widget .ck.ck-toolbar {
 | 
			
		||||
        border: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .classic-toolbar-widget .ck.ck-button.ck-disabled {
 | 
			
		||||
        opacity: 0.3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: flex-end;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
        overscroll-behavior: none;
 | 
			
		||||
        z-index: 500;
 | 
			
		||||
        user-select: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.visible::-webkit-scrollbar {
 | 
			
		||||
        height: 3px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media (max-width: 991px) {
 | 
			
		||||
        body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
            bottom: calc(var(--tab-bar-height) + var(--launcher-pane-height) + var(--mobile-bottom-offset));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media (min-width: 991px) {
 | 
			
		||||
        body.mobile .classic-toolbar-widget.visible {
 | 
			
		||||
            bottom: 0;
 | 
			
		||||
            left: 25%;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget.dropdown-active {
 | 
			
		||||
        height: 50vh;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget .ck.ck-toolbar {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        background-color: var(--main-background-color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.mobile .classic-toolbar-widget .ck.ck-dropdown__panel {
 | 
			
		||||
        bottom: 100% !important;
 | 
			
		||||
        top: unset !important;
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handles the editing toolbar when the CKEditor is in decoupled mode.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This toolbar is only enabled if the user has selected the classic CKEditor.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The ribbon item is active by default for text notes, as long as they are not in read-only mode.
 | 
			
		||||
 */
 | 
			
		||||
export default class MobileEditorToolbar extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
    private observer: MutationObserver;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        this.observer = new MutationObserver((e) => this.#onDropdownStateChanged(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get name() {
 | 
			
		||||
        return "classicEditor";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    doRender() {
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
        this.contentSized();
 | 
			
		||||
 | 
			
		||||
        // The virtual keyboard obscures the editing toolbar so we have to reposition by calculating the height of the keyboard.
 | 
			
		||||
        window.visualViewport?.addEventListener("resize", () => this.#adjustPosition());
 | 
			
		||||
        window.addEventListener("scroll", () => this.#adjustPosition());
 | 
			
		||||
 | 
			
		||||
        // Observe when a dropdown is expanded to apply a style that allows the dropdown to be visible, since we can't have the element both visible and the toolbar scrollable.
 | 
			
		||||
        this.observer.disconnect();
 | 
			
		||||
        this.observer.observe(this.$widget[0], {
 | 
			
		||||
            attributeFilter: ["aria-expanded"],
 | 
			
		||||
            subtree: true
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #onDropdownStateChanged(e: MutationRecord[]) {
 | 
			
		||||
        const dropdownActive = e.map((e) => (e.target as any).ariaExpanded === "true").reduce((acc, e) => acc && e);
 | 
			
		||||
        this.$widget[0].classList.toggle("dropdown-active", dropdownActive);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #adjustPosition() {
 | 
			
		||||
        let bottom = window.innerHeight - (window.visualViewport?.height || 0);
 | 
			
		||||
 | 
			
		||||
        // When the keyboard is not visible, align it to the launcher bar instead.
 | 
			
		||||
        bottom = Math.max(bottom, document.getElementById("mobile-bottom-bar")?.offsetHeight || 0);
 | 
			
		||||
 | 
			
		||||
        this.$widget.css("bottom", `${bottom}px`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async #shouldDisplay() {
 | 
			
		||||
        if (!this.note || this.note.type !== "text") {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await this.noteContext?.isReadOnly()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshWithNote() {
 | 
			
		||||
        this.toggleExt(await this.#shouldDisplay());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user