mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	
						commit
						cd78955080
					
				| @ -1,56 +0,0 @@ | ||||
| import FlexContainer from "./containers/flex_container.js"; | ||||
| import OpenNoteButtonWidget from "./buttons/open_note_button_widget.js"; | ||||
| import BookmarkFolderWidget from "./buttons/bookmark_folder.js"; | ||||
| import froca from "../services/froca.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| 
 | ||||
| export default class BookmarkButtons extends FlexContainer { | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(isHorizontalLayout ? "row" : "column"); | ||||
| 
 | ||||
|         this.contentSized(); | ||||
|         this.settings = {}; | ||||
|     } | ||||
| 
 | ||||
|     async refresh() { | ||||
|         this.$widget.empty(); | ||||
|         this.children = []; | ||||
|         this.noteIds = []; | ||||
| 
 | ||||
|         const bookmarkParentNote = await froca.getNote("_lbBookmarks"); | ||||
| 
 | ||||
|         for (const note of await bookmarkParentNote.getChildNotes()) { | ||||
|             this.noteIds.push(note.noteId); | ||||
| 
 | ||||
|             const buttonWidget = note.isLabelTruthy("bookmarkFolder") ? new BookmarkFolderWidget(note) : new OpenNoteButtonWidget(note).class("launcher-button"); | ||||
|             if (this.settings.titlePlacement) { | ||||
|                 if (!buttonWidget.settings) { | ||||
|                     buttonWidget = {}; | ||||
|                 } | ||||
|                 buttonWidget.settings.titlePlacement = this.settings.titlePlacement; | ||||
|             } | ||||
| 
 | ||||
|             this.child(buttonWidget); | ||||
| 
 | ||||
|             this.$widget.append(buttonWidget.render()); | ||||
| 
 | ||||
|             buttonWidget.refreshIcon(); | ||||
|         } | ||||
| 
 | ||||
|         utils.reloadTray(); | ||||
|     } | ||||
| 
 | ||||
|     initialRenderCompleteEvent() { | ||||
|         this.refresh(); | ||||
|     } | ||||
| 
 | ||||
|     entitiesReloadedEvent({ loadResults }) { | ||||
|         if (loadResults.getBranchRows().find((branch) => branch.parentNoteId === "_lbBookmarks")) { | ||||
|             this.refresh(); | ||||
|         } | ||||
| 
 | ||||
|         if (loadResults.getAttributeRows().find((attr) => attr.type === "label" && ["iconClass", "workspaceIconClass", "bookmarkFolder"].includes(attr.name) && this.noteIds.includes(attr.noteId))) { | ||||
|             this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										78
									
								
								src/public/app/widgets/bookmark_buttons.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/public/app/widgets/bookmark_buttons.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| import FlexContainer from "./containers/flex_container.js"; | ||||
| import OpenNoteButtonWidget from "./buttons/open_note_button_widget.js"; | ||||
| import BookmarkFolderWidget from "./buttons/bookmark_folder.js"; | ||||
| import froca from "../services/froca.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import type { EventData } from "../components/app_context.js"; | ||||
| import type Component from "../components/component.js"; | ||||
| 
 | ||||
| interface BookmarkButtonsSettings { | ||||
|     titlePlacement?: string; | ||||
| } | ||||
| 
 | ||||
| export default class BookmarkButtons extends FlexContainer<Component> { | ||||
|     private settings: BookmarkButtonsSettings; | ||||
|     private noteIds: string[]; | ||||
| 
 | ||||
|     constructor(isHorizontalLayout: boolean) { | ||||
|         super(isHorizontalLayout ? "row" : "column"); | ||||
| 
 | ||||
|         this.contentSized(); | ||||
|         this.settings = {}; | ||||
|         this.noteIds = []; | ||||
|     } | ||||
| 
 | ||||
|     async refresh(): Promise<void> { | ||||
|         this.$widget.empty(); | ||||
|         this.children = []; | ||||
|         this.noteIds = []; | ||||
| 
 | ||||
|         const bookmarkParentNote = await froca.getNote("_lbBookmarks"); | ||||
| 
 | ||||
|         if (!bookmarkParentNote) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         for (const note of await bookmarkParentNote.getChildNotes()) { | ||||
|             this.noteIds.push(note.noteId); | ||||
| 
 | ||||
|             let buttonWidget: OpenNoteButtonWidget | BookmarkFolderWidget = note.isLabelTruthy("bookmarkFolder") | ||||
|                 ? new BookmarkFolderWidget(note) | ||||
|                 : new OpenNoteButtonWidget(note).class("launcher-button"); | ||||
| 
 | ||||
|             if (this.settings.titlePlacement) { | ||||
|                 if (!('settings' in buttonWidget)) { | ||||
|                     (buttonWidget as any).settings = {}; | ||||
|                 } | ||||
| 
 | ||||
|                 (buttonWidget as any).settings.titlePlacement = this.settings.titlePlacement; | ||||
|             } | ||||
| 
 | ||||
|             this.child(buttonWidget); | ||||
| 
 | ||||
|             this.$widget.append(buttonWidget.render()); | ||||
| 
 | ||||
|             buttonWidget.refreshIcon(); | ||||
|         } | ||||
| 
 | ||||
|         utils.reloadTray(); | ||||
|     } | ||||
| 
 | ||||
|     initialRenderCompleteEvent(): void { | ||||
|         this.refresh(); | ||||
|     } | ||||
| 
 | ||||
|     entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">): void { | ||||
|         if (loadResults.getBranchRows().find((branch) => branch.parentNoteId === "_lbBookmarks")) { | ||||
|             this.refresh(); | ||||
|         } | ||||
| 
 | ||||
|         if (loadResults.getAttributeRows().find((attr) => | ||||
|             attr.type === "label" && | ||||
|             attr.name && ["iconClass", "workspaceIconClass", "bookmarkFolder"].includes(attr.name) && | ||||
|             attr.noteId && this.noteIds.includes(attr.noteId) | ||||
|         )) { | ||||
|             this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,7 @@ | ||||
| import RightDropdownButtonWidget from "./right_dropdown_button.js"; | ||||
| import linkService from "../../services/link.js"; | ||||
| import utils from "../../services/utils.js"; | ||||
| import type FNote from "../../entities/fnote.js"; | ||||
| 
 | ||||
| const DROPDOWN_TPL = ` | ||||
| <div class="bookmark-folder-widget"> | ||||
| @ -43,25 +44,35 @@ const DROPDOWN_TPL = ` | ||||
|     <ul class="children-notes"></ul> | ||||
| </div>`;
 | ||||
| 
 | ||||
| interface LinkOptions { | ||||
|     showTooltip: boolean; | ||||
|     showNoteIcon: boolean; | ||||
| } | ||||
| 
 | ||||
| export default class BookmarkFolderWidget extends RightDropdownButtonWidget { | ||||
|     constructor(note) { | ||||
|     private note: FNote; | ||||
|     private $parentNote!: JQuery<HTMLElement>; | ||||
|     private $childrenNotes!: JQuery<HTMLElement>; | ||||
|     declare $dropdownContent: JQuery<HTMLElement>; | ||||
| 
 | ||||
|     constructor(note: FNote) { | ||||
|         super(utils.escapeHtml(note.title), note.getIcon(), DROPDOWN_TPL); | ||||
| 
 | ||||
|         this.note = note; | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
|     doRender(): void { | ||||
|         super.doRender(); | ||||
| 
 | ||||
|         this.$parentNote = this.$dropdownContent.find(".parent-note"); | ||||
|         this.$childrenNotes = this.$dropdownContent.find(".children-notes"); | ||||
|     } | ||||
| 
 | ||||
|     async dropdownShown() { | ||||
|     async dropdownShown(): Promise<void> { | ||||
|         this.$parentNote.empty(); | ||||
|         this.$childrenNotes.empty(); | ||||
| 
 | ||||
|         const linkOptions = { | ||||
|         const linkOptions: LinkOptions = { | ||||
|             showTooltip: false, | ||||
|             showNoteIcon: true | ||||
|         }; | ||||
| @ -73,5 +84,5 @@ export default class BookmarkFolderWidget extends RightDropdownButtonWidget { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     refreshIcon() {} | ||||
|     refreshIcon(): void {} | ||||
| } | ||||
| @ -3,10 +3,13 @@ import appContext from "../../components/app_context.js"; | ||||
| import attributeService from "../../services/attributes.js"; | ||||
| import protectedSessionHolder from "../../services/protected_session_holder.js"; | ||||
| import { t } from "../../services/i18n.js"; | ||||
| import LoadResults from "../../services/load_results.js"; | ||||
| import type { AttributeRow } from "../../services/load_results.js"; | ||||
| import FNote from "../../entities/fnote.js"; | ||||
| 
 | ||||
| export default class EditButton extends OnClickButtonWidget { | ||||
|     isEnabled() { | ||||
|         return super.isEnabled() && this.note && this.noteContext.viewScope.viewMode === "default"; | ||||
|     isEnabled(): boolean { | ||||
|         return Boolean(super.isEnabled() && this.note && this.noteContext?.viewScope?.viewMode === "default"); | ||||
|     } | ||||
| 
 | ||||
|     constructor() { | ||||
| @ -16,15 +19,15 @@ export default class EditButton extends OnClickButtonWidget { | ||||
|             .title(t("edit_button.edit_this_note")) | ||||
|             .titlePlacement("bottom") | ||||
|             .onClick((widget) => { | ||||
|                 if (this.noteContext?.viewScope) { | ||||
|                     this.noteContext.viewScope.readOnlyTemporarilyDisabled = true; | ||||
| 
 | ||||
|                     appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext }); | ||||
| 
 | ||||
|                 } | ||||
|                 this.refresh(); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     async refreshWithNote(note) { | ||||
|     async refreshWithNote(note: FNote): Promise<void> { | ||||
|         if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|             this.toggleInt(false); | ||||
|         } else { | ||||
| @ -34,15 +37,16 @@ export default class EditButton extends OnClickButtonWidget { | ||||
|             const wasVisible = this.isVisible(); | ||||
| 
 | ||||
|             // can't do this in isEnabled() since isReadOnly is async
 | ||||
|             this.toggleInt(await this.noteContext.isReadOnly()); | ||||
|             const isReadOnly = await this.noteContext?.isReadOnly(); | ||||
|             this.toggleInt(Boolean(isReadOnly)); | ||||
| 
 | ||||
|             // make the edit button stand out on the first display, otherwise
 | ||||
|             // it's difficult to notice that the note is readonly
 | ||||
|             if (this.isVisible() && !wasVisible) { | ||||
|             if (this.isVisible() && !wasVisible && this.$widget) { | ||||
|                 this.$widget.addClass("bx-tada bx-lg"); | ||||
| 
 | ||||
|                 setTimeout(() => { | ||||
|                     this.$widget.removeClass("bx-tada bx-lg"); | ||||
|                     this.$widget?.removeClass("bx-tada bx-lg"); | ||||
|                 }, 1700); | ||||
|             } | ||||
|         } | ||||
| @ -50,15 +54,21 @@ export default class EditButton extends OnClickButtonWidget { | ||||
|         await super.refreshWithNote(note); | ||||
|     } | ||||
| 
 | ||||
|     entitiesReloadedEvent({ loadResults }) { | ||||
|         if (loadResults.getAttributeRows().find((attr) => attr.type === "label" && attr.name.toLowerCase().includes("readonly") && attributeService.isAffecting(attr, this.note))) { | ||||
|     entitiesReloadedEvent({ loadResults }: { loadResults: LoadResults }): void { | ||||
|         if (loadResults.getAttributeRows().find((attr: AttributeRow) => | ||||
|             attr.type === "label" && | ||||
|             attr.name?.toLowerCase().includes("readonly") && | ||||
|             this.note && | ||||
|             attributeService.isAffecting(attr, this.note) | ||||
|         )) { | ||||
|             if (this.noteContext?.viewScope) { | ||||
|                 this.noteContext.viewScope.readOnlyTemporarilyDisabled = false; | ||||
| 
 | ||||
|             } | ||||
|             this.refresh(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async noteTypeMimeChangedEvent({ noteId }) { | ||||
|     async noteTypeMimeChangedEvent({ noteId }: { noteId: string }): Promise<void> { | ||||
|         if (this.isNote(noteId)) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
| @ -1,5 +1,6 @@ | ||||
| import BasicWidget from "../basic_widget.js"; | ||||
| import { Tooltip, Dropdown } from "bootstrap"; | ||||
| type PopoverPlacement = Tooltip.PopoverPlacement; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="dropdown right-dropdown-widget dropend"> | ||||
| @ -14,7 +15,17 @@ const TPL = ` | ||||
| `;
 | ||||
| 
 | ||||
| export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|     constructor(title, iconClass, dropdownTpl) { | ||||
|     protected iconClass: string; | ||||
|     protected title: string; | ||||
|     protected dropdownTpl: string; | ||||
|     protected settings: { titlePlacement: PopoverPlacement }; | ||||
|     protected $dropdownMenu!: JQuery<HTMLElement>; | ||||
|     protected dropdown!: Dropdown; | ||||
|     protected $tooltip!: JQuery<HTMLElement>; | ||||
|     protected tooltip!: Tooltip; | ||||
|     public $dropdownContent!: JQuery<HTMLElement>; | ||||
| 
 | ||||
|     constructor(title: string, iconClass: string, dropdownTpl: string) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.iconClass = iconClass; | ||||
| @ -29,10 +40,10 @@ export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$dropdownMenu = this.$widget.find(".dropdown-menu"); | ||||
|         this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")); | ||||
|         this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0]); | ||||
| 
 | ||||
|         this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title); | ||||
|         this.tooltip = new Tooltip(this.$tooltip, { | ||||
|         this.tooltip = new Tooltip(this.$tooltip[0], { | ||||
|             placement: this.settings.titlePlacement, | ||||
|             fallbackPlacements: [this.settings.titlePlacement] | ||||
|         }); | ||||
| @ -48,7 +59,8 @@ export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|             await this.dropdownShown(); | ||||
| 
 | ||||
|             const rect = this.$dropdownMenu[0].getBoundingClientRect(); | ||||
|             const pixelsToBottom = $(window).height() - rect.bottom; | ||||
|             const windowHeight = $(window).height() || 0; | ||||
|             const pixelsToBottom = windowHeight - rect.bottom; | ||||
| 
 | ||||
|             if (pixelsToBottom < 0) { | ||||
|                 this.$dropdownMenu.css("top", pixelsToBottom); | ||||
| @ -60,5 +72,5 @@ export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|     } | ||||
| 
 | ||||
|     // to be overridden
 | ||||
|     async dropdownShow() {} | ||||
|     async dropdownShown(): Promise<void> {} | ||||
| } | ||||
| @ -1,49 +0,0 @@ | ||||
| import OnClickButtonWidget from "./onclick_button.js"; | ||||
| import appContext from "../../components/app_context.js"; | ||||
| import attributeService from "../../services/attributes.js"; | ||||
| import { t } from "../../services/i18n.js"; | ||||
| 
 | ||||
| export default class ShowHighlightsListWidgetButton extends OnClickButtonWidget { | ||||
|     isEnabled() { | ||||
|         return super.isEnabled() && this.note && this.note.type === "text" && this.noteContext.viewScope.viewMode === "default"; | ||||
|     } | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         this.icon("bx-highlight") | ||||
|             .title(t("show_highlights_list_widget_button.show_highlights_list")) | ||||
|             .titlePlacement("bottom") | ||||
|             .onClick((widget) => { | ||||
|                 this.noteContext.viewScope.highlightsListTemporarilyHidden = false; | ||||
|                 appContext.triggerEvent("showHighlightsListWidget", { noteId: this.noteId }); | ||||
|                 this.toggleInt(false); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     async refreshWithNote(note) { | ||||
|         this.toggleInt(this.noteContext.viewScope.highlightsListTemporarilyHidden); | ||||
|     } | ||||
|     async reEvaluateHighlightsListWidgetVisibilityEvent({ noteId }) { | ||||
|         if (noteId === this.noteId) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
|     async entitiesReloadedEvent({ loadResults }) { | ||||
|         if (loadResults.isNoteContentReloaded(this.noteId)) { | ||||
|             await this.refresh(); | ||||
|         } else if ( | ||||
|             loadResults | ||||
|                 .getAttributeRows() | ||||
|                 .find((attr) => attr.type === "label" && (attr.name.toLowerCase().includes("readonly") || attr.name === "hideHighlightWidget") && attributeService.isAffecting(attr, this.note)) | ||||
|         ) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async noteTypeMimeChangedEvent({ noteId }) { | ||||
|         if (this.isNote(noteId)) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| import OnClickButtonWidget from "./onclick_button.js"; | ||||
| import appContext from "../../components/app_context.js"; | ||||
| import attributeService from "../../services/attributes.js"; | ||||
| import { t } from "../../services/i18n.js"; | ||||
| import LoadResults from "../../services/load_results.js"; | ||||
| import type { AttributeRow } from "../../services/load_results.js"; | ||||
| 
 | ||||
| export default class ShowHighlightsListWidgetButton extends OnClickButtonWidget { | ||||
|     isEnabled(): boolean { | ||||
|         return Boolean(super.isEnabled() && this.note && this.note.type === "text" && this.noteContext?.viewScope?.viewMode === "default"); | ||||
|     } | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         this.icon("bx-highlight") | ||||
|             .title(t("show_highlights_list_widget_button.show_highlights_list")) | ||||
|             .titlePlacement("bottom") | ||||
|             .onClick((widget) => { | ||||
|                 if (this.noteContext?.viewScope && this.noteId) { | ||||
|                     this.noteContext.viewScope.highlightsListTemporarilyHidden = false; | ||||
|                     appContext.triggerEvent("showHighlightsListWidget", { noteId: this.noteId }); | ||||
|                 } | ||||
|                 this.toggleInt(false); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     async refreshWithNote(): Promise<void> { | ||||
|         if (this.noteContext?.viewScope) { | ||||
|             this.toggleInt(this.noteContext.viewScope.highlightsListTemporarilyHidden); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async reEvaluateHighlightsListWidgetVisibilityEvent({ noteId }: { noteId: string }): Promise<void> { | ||||
|         if (noteId === this.noteId) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async entitiesReloadedEvent({ loadResults }: { loadResults: LoadResults }): Promise<void> { | ||||
|         if (this.noteId && loadResults.isNoteContentReloaded(this.noteId)) { | ||||
|             await this.refresh(); | ||||
|         } else if ( | ||||
|             loadResults | ||||
|                 .getAttributeRows() | ||||
|                 .find((attr: AttributeRow) => | ||||
|                     attr.type === "label" && | ||||
|                     (attr.name?.toLowerCase().includes("readonly") || attr.name === "hideHighlightWidget") && | ||||
|                     this.note && | ||||
|                     attributeService.isAffecting(attr, this.note) | ||||
|                 ) | ||||
|         ) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async noteTypeMimeChangedEvent({ noteId }: { noteId: string }): Promise<void> { | ||||
|         if (this.isNote(noteId)) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran