diff --git a/apps/client/src/components/note_context.ts b/apps/client/src/components/note_context.ts index 11d32cf0d..3a8a54310 100644 --- a/apps/client/src/components/note_context.ts +++ b/apps/client/src/components/note_context.ts @@ -12,6 +12,7 @@ import type FNote from "../entities/fnote.js"; import type TypeWidget from "../widgets/type_widgets/type_widget.js"; import type { CKTextEditor } from "@triliumnext/ckeditor5"; import type CodeMirror from "@triliumnext/codemirror"; +import { closeActiveDialog } from "../services/dialog.js"; export interface SetNoteOpts { triggerSwitchEvent?: unknown; @@ -83,7 +84,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded"> await this.triggerEvent("beforeNoteSwitch", { noteContext: this }); - utils.closeActiveDialog(); + closeActiveDialog(); this.notePath = resolvedNotePath; this.viewScope = opts.viewScope; diff --git a/apps/client/src/services/dialog.ts b/apps/client/src/services/dialog.ts index e2d93250f..240172f49 100644 --- a/apps/client/src/services/dialog.ts +++ b/apps/client/src/services/dialog.ts @@ -1,6 +1,41 @@ +import { Modal } from "bootstrap"; import appContext from "../components/app_context.js"; import type { ConfirmDialogOptions, ConfirmDialogResult, ConfirmWithMessageOptions } from "../widgets/dialogs/confirm.js"; import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js"; +import { focusSavedElement, saveFocusedElement } from "./focus.js"; + +export async function openDialog($dialog: JQuery, closeActDialog = true) { + if (closeActDialog) { + closeActiveDialog(); + glob.activeDialog = $dialog; + } + + saveFocusedElement(); + Modal.getOrCreateInstance($dialog[0]).show(); + + $dialog.on("hidden.bs.modal", () => { + const $autocompleteEl = $(".aa-input"); + if ("autocomplete" in $autocompleteEl) { + $autocompleteEl.autocomplete("close"); + } + + if (!glob.activeDialog || glob.activeDialog === $dialog) { + focusSavedElement(); + } + }); + + const keyboardActionsService = (await import("./keyboard_actions.js")).default; + keyboardActionsService.updateDisplayedShortcuts($dialog); + + return $dialog; +} + +export function closeActiveDialog() { + if (glob.activeDialog) { + Modal.getOrCreateInstance(glob.activeDialog[0]).hide(); + glob.activeDialog = null; + } +} async function info(message: string) { return new Promise((res) => appContext.triggerCommand("showInfoDialog", { message, callback: res })); diff --git a/apps/client/src/services/focus.ts b/apps/client/src/services/focus.ts new file mode 100644 index 000000000..066c74558 --- /dev/null +++ b/apps/client/src/services/focus.ts @@ -0,0 +1,29 @@ +let $lastFocusedElement: JQuery | null; + +// perhaps there should be saved focused element per tab? +export function saveFocusedElement() { + $lastFocusedElement = $(":focus"); +} + +export function focusSavedElement() { + if (!$lastFocusedElement) { + return; + } + + if ($lastFocusedElement.hasClass("ck")) { + // must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607 + // the bug manifests itself in resetting the cursor position to the first character - jumping above + + const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance"); + + if (editor) { + editor.editing.view.focus(); + } else { + console.log("Could not find CKEditor instance to focus last element"); + } + } else { + $lastFocusedElement.focus(); + } + + $lastFocusedElement = null; +} diff --git a/apps/client/src/services/utils.ts b/apps/client/src/services/utils.ts index 412650605..c7d37e4d9 100644 --- a/apps/client/src/services/utils.ts +++ b/apps/client/src/services/utils.ts @@ -1,5 +1,4 @@ import dayjs from "dayjs"; -import { Modal } from "bootstrap"; import type { ViewScope } from "./link.js"; const SVG_MIME = "image/svg+xml"; @@ -275,69 +274,6 @@ function getMimeTypeClass(mime: string) { return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`; } -function closeActiveDialog() { - if (glob.activeDialog) { - Modal.getOrCreateInstance(glob.activeDialog[0]).hide(); - glob.activeDialog = null; - } -} - -let $lastFocusedElement: JQuery | null; - -// perhaps there should be saved focused element per tab? -function saveFocusedElement() { - $lastFocusedElement = $(":focus"); -} - -function focusSavedElement() { - if (!$lastFocusedElement) { - return; - } - - if ($lastFocusedElement.hasClass("ck")) { - // must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607 - // the bug manifests itself in resetting the cursor position to the first character - jumping above - - const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance"); - - if (editor) { - editor.editing.view.focus(); - } else { - console.log("Could not find CKEditor instance to focus last element"); - } - } else { - $lastFocusedElement.focus(); - } - - $lastFocusedElement = null; -} - -async function openDialog($dialog: JQuery, closeActDialog = true) { - if (closeActDialog) { - closeActiveDialog(); - glob.activeDialog = $dialog; - } - - saveFocusedElement(); - Modal.getOrCreateInstance($dialog[0]).show(); - - $dialog.on("hidden.bs.modal", () => { - const $autocompleteEl = $(".aa-input"); - if ("autocomplete" in $autocompleteEl) { - $autocompleteEl.autocomplete("close"); - } - - if (!glob.activeDialog || glob.activeDialog === $dialog) { - focusSavedElement(); - } - }); - - const keyboardActionsService = (await import("./keyboard_actions.js")).default; - keyboardActionsService.updateDisplayedShortcuts($dialog); - - return $dialog; -} - function isHtmlEmpty(html: string) { if (!html) { return true; @@ -823,10 +759,6 @@ export default { setCookie, getNoteTypeClass, getMimeTypeClass, - closeActiveDialog, - openDialog, - saveFocusedElement, - focusSavedElement, isHtmlEmpty, clearBrowserCache, copySelectionToClipboard, diff --git a/apps/client/src/widgets/attribute_widgets/attribute_detail.ts b/apps/client/src/widgets/attribute_widgets/attribute_detail.ts index 08bb764fc..9017673f3 100644 --- a/apps/client/src/widgets/attribute_widgets/attribute_detail.ts +++ b/apps/client/src/widgets/attribute_widgets/attribute_detail.ts @@ -11,6 +11,7 @@ import utils from "../../services/utils.js"; import shortcutService from "../../services/shortcuts.js"; import appContext from "../../components/app_context.js"; import type { Attribute } from "../../services/attribute_parser.js"; +import { focusSavedElement, saveFocusedElement } from "../../services/focus.js"; const TPL = /*html*/`
@@ -483,7 +484,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { return; } - utils.saveFocusedElement(); + saveFocusedElement(); this.attrType = this.getAttrType(attribute); @@ -605,7 +606,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { this.hide(); - utils.focusSavedElement(); + focusSavedElement(); } async cancelAndClose() { @@ -613,7 +614,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { this.hide(); - utils.focusSavedElement(); + focusSavedElement(); } userEditedAttribute() { diff --git a/apps/client/src/widgets/dialogs/about.ts b/apps/client/src/widgets/dialogs/about.ts index 2c364d756..06cf118ec 100644 --- a/apps/client/src/widgets/dialogs/about.ts +++ b/apps/client/src/widgets/dialogs/about.ts @@ -4,6 +4,7 @@ import BasicWidget from "../basic_widget.js"; import openService from "../../services/open.js"; import server from "../../services/server.js"; import utils from "../../services/utils.js"; +import { openDialog } from "../../services/dialog.js"; interface AppInfo { appVersion: string; @@ -111,6 +112,6 @@ export default class AboutDialog extends BasicWidget { async openAboutDialogEvent() { await this.refresh(); - utils.openDialog(this.$widget); + openDialog(this.$widget); } } diff --git a/apps/client/src/widgets/dialogs/add_link.ts b/apps/client/src/widgets/dialogs/add_link.ts index fe2295442..d7758c92d 100644 --- a/apps/client/src/widgets/dialogs/add_link.ts +++ b/apps/client/src/widgets/dialogs/add_link.ts @@ -1,11 +1,11 @@ import { t } from "../../services/i18n.js"; import treeService from "../../services/tree.js"; import noteAutocompleteService from "../../services/note_autocomplete.js"; -import utils from "../../services/utils.js"; import BasicWidget from "../basic_widget.js"; import type { Suggestion } from "../../services/note_autocomplete.js"; import type { default as TextTypeWidget } from "../type_widgets/editable_text.js"; import type { EventData } from "../../components/app_context.js"; +import { openDialog } from "../../services/dialog.js"; const TPL = /*html*/`