port js to ts

This commit is contained in:
Jin 2025-02-25 02:02:20 +01:00
parent 34762236d1
commit 16b58a58a3
7 changed files with 252 additions and 118 deletions

View File

@ -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();
}
}
}

View 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();
}
}
}

View File

@ -1,6 +1,7 @@
import RightDropdownButtonWidget from "./right_dropdown_button.js"; import RightDropdownButtonWidget from "./right_dropdown_button.js";
import linkService from "../../services/link.js"; import linkService from "../../services/link.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import type FNote from "../../entities/fnote.js";
const DROPDOWN_TPL = ` const DROPDOWN_TPL = `
<div class="bookmark-folder-widget"> <div class="bookmark-folder-widget">
@ -43,25 +44,35 @@ const DROPDOWN_TPL = `
<ul class="children-notes"></ul> <ul class="children-notes"></ul>
</div>`; </div>`;
interface LinkOptions {
showTooltip: boolean;
showNoteIcon: boolean;
}
export default class BookmarkFolderWidget extends RightDropdownButtonWidget { 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); super(utils.escapeHtml(note.title), note.getIcon(), DROPDOWN_TPL);
this.note = note; this.note = note;
} }
doRender() { doRender(): void {
super.doRender(); super.doRender();
this.$parentNote = this.$dropdownContent.find(".parent-note"); this.$parentNote = this.$dropdownContent.find(".parent-note");
this.$childrenNotes = this.$dropdownContent.find(".children-notes"); this.$childrenNotes = this.$dropdownContent.find(".children-notes");
} }
async dropdownShown() { async dropdownShown(): Promise<void> {
this.$parentNote.empty(); this.$parentNote.empty();
this.$childrenNotes.empty(); this.$childrenNotes.empty();
const linkOptions = { const linkOptions: LinkOptions = {
showTooltip: false, showTooltip: false,
showNoteIcon: true showNoteIcon: true
}; };
@ -73,5 +84,5 @@ export default class BookmarkFolderWidget extends RightDropdownButtonWidget {
} }
} }
refreshIcon() {} refreshIcon(): void {}
} }

View File

@ -0,0 +1,76 @@
import OnClickButtonWidget from "./onclick_button.js";
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(): boolean {
return Boolean(super.isEnabled() && this.note && this.noteContext?.viewScope?.viewMode === "default");
}
constructor() {
super();
this.icon("bx-edit-alt")
.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: FNote): Promise<void> {
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
this.toggleInt(false);
} else {
// prevent flickering by assuming hidden before async operation
this.toggleInt(false);
const wasVisible = this.isVisible();
// can't do this in isEnabled() since isReadOnly is async
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 && this.$widget) {
this.$widget.addClass("bx-tada bx-lg");
setTimeout(() => {
this.$widget?.removeClass("bx-tada bx-lg");
}, 1700);
}
}
await super.refreshWithNote(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 }: { noteId: string }): Promise<void> {
if (this.isNote(noteId)) {
await this.refresh();
}
}
}

View File

@ -1,5 +1,6 @@
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import { Tooltip, Dropdown } from "bootstrap"; import { Tooltip, Dropdown } from "bootstrap";
type PopoverPlacement = Tooltip.PopoverPlacement;
const TPL = ` const TPL = `
<div class="dropdown right-dropdown-widget dropend"> <div class="dropdown right-dropdown-widget dropend">
@ -14,7 +15,17 @@ const TPL = `
`; `;
export default class RightDropdownButtonWidget extends BasicWidget { 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(); super();
this.iconClass = iconClass; this.iconClass = iconClass;
@ -26,13 +37,13 @@ export default class RightDropdownButtonWidget extends BasicWidget {
}; };
} }
doRender() { doRender(): void {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$dropdownMenu = this.$widget.find(".dropdown-menu"); 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 = 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, placement: this.settings.titlePlacement,
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [this.settings.titlePlacement]
}); });
@ -48,7 +59,8 @@ export default class RightDropdownButtonWidget extends BasicWidget {
await this.dropdownShown(); await this.dropdownShown();
const rect = this.$dropdownMenu[0].getBoundingClientRect(); 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) { if (pixelsToBottom < 0) {
this.$dropdownMenu.css("top", pixelsToBottom); this.$dropdownMenu.css("top", pixelsToBottom);
@ -60,5 +72,5 @@ export default class RightDropdownButtonWidget extends BasicWidget {
} }
// to be overridden // to be overridden
async dropdownShow() {} async dropdownShown(): Promise<void> {}
} }

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}