mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	refactor(components): split editor toolbar for mobile
This commit is contained in:
		| @@ -22,7 +22,6 @@ import LauncherContainer from "../widgets/containers/launcher_container.js"; | |||||||
| import RootContainer from "../widgets/containers/root_container.js"; | import RootContainer from "../widgets/containers/root_container.js"; | ||||||
| import SharedInfoWidget from "../widgets/shared_info.js"; | import SharedInfoWidget from "../widgets/shared_info.js"; | ||||||
| import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.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 SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js"; | ||||||
| import AboutDialog from "../widgets/dialogs/about.js"; | import AboutDialog from "../widgets/dialogs/about.js"; | ||||||
| import HelpDialog from "../widgets/dialogs/help.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 RecentChangesDialog from "../widgets/dialogs/recent_changes.js"; | ||||||
| import PromptDialog from "../widgets/dialogs/prompt.js"; | import PromptDialog from "../widgets/dialogs/prompt.js"; | ||||||
| import RefreshButton from "../widgets/floating_buttons/refresh_button.js"; | import RefreshButton from "../widgets/floating_buttons/refresh_button.js"; | ||||||
|  | import MobileEditorToolbar from "../widgets/ribbon_widgets/mobile_editor_toolbar.js"; | ||||||
|  |  | ||||||
| const MOBILE_CSS = ` | const MOBILE_CSS = ` | ||||||
| <style> | <style> | ||||||
| @@ -182,7 +182,7 @@ export default class MobileLayout { | |||||||
|                     .child(new TabRowWidget().css("height", "40px")) |                     .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 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 AboutDialog()) | ||||||
|             .child(new HelpDialog()) |             .child(new HelpDialog()) | ||||||
|             .child(new RecentChangesDialog()) |             .child(new RecentChangesDialog()) | ||||||
|   | |||||||
| @@ -21,53 +21,6 @@ const TPL = /*html*/`\ | |||||||
|     .classic-toolbar-widget .ck.ck-button.ck-disabled { |     .classic-toolbar-widget .ck.ck-button.ck-disabled { | ||||||
|         opacity: 0.3; |         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> | </style> | ||||||
| `; | `; | ||||||
|  |  | ||||||
| @@ -82,13 +35,6 @@ const TPL = /*html*/`\ | |||||||
|  */ |  */ | ||||||
| export default class ClassicEditorToolbar extends NoteContextAwareWidget { | export default class ClassicEditorToolbar extends NoteContextAwareWidget { | ||||||
|  |  | ||||||
|     private observer: MutationObserver; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         this.observer = new MutationObserver((e) => this.#onDropdownStateChanged(e)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     get name() { |     get name() { | ||||||
|         return "classicEditor"; |         return "classicEditor"; | ||||||
|     } |     } | ||||||
| @@ -100,33 +46,6 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget { | |||||||
|     doRender() { |     doRender() { | ||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.contentSized(); |         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() { |     async getTitle() { | ||||||
| @@ -139,7 +58,7 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async #shouldDisplay() { |     async #shouldDisplay() { | ||||||
|         if (utils.isDesktop() && options.get("textNoteEditorType") !== "ckeditor-classic") { |         if (options.get("textNoteEditorType") !== "ckeditor-classic") { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -154,10 +73,4 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget { | |||||||
|         return true; |         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()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user