return focus to the previously focused element after closing the dialog, fixes #861

This commit is contained in:
zadam 2020-02-09 10:00:13 +01:00
parent 990a84c202
commit 402718d293
26 changed files with 74 additions and 112 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.40.1", "version": "0.40.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -10,8 +10,6 @@ const $buildRevision = $("#build-revision");
const $dataDirectory = $("#data-directory"); const $dataDirectory = $("#data-directory");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
const appInfo = await server.get('app-info'); const appInfo = await server.get('app-info');
$appVersion.text(appInfo.appVersion); $appVersion.text(appInfo.appVersion);
@ -22,7 +20,5 @@ export async function showDialog() {
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision); $buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
$dataDirectory.text(appInfo.dataDirectory); $dataDirectory.text(appInfo.dataDirectory);
glob.activeDialog = $dialog; utils.openDialog($dialog);
$dialog.modal();
} }

View File

@ -11,13 +11,9 @@ const $linkTitle = $("#link-title");
const $addLinkTitleFormGroup = $("#add-link-title-form-group"); const $addLinkTitleFormGroup = $("#add-link-title-form-group");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
$addLinkTitleFormGroup.toggle(!hasSelection()); $addLinkTitleFormGroup.toggle(!hasSelection());
glob.activeDialog = $dialog; utils.openDialog($dialog);
$dialog.modal();
$autoComplete.val('').trigger('focus'); $autoComplete.val('').trigger('focus');
$linkTitle.val(''); $linkTitle.val('');

View File

@ -287,8 +287,6 @@ function initKoPlugins() {
} }
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
await libraryLoader.requireLibrary(libraryLoader.KNOCKOUT); await libraryLoader.requireLibrary(libraryLoader.KNOCKOUT);
// lazily apply bindings on first use // lazily apply bindings on first use
@ -300,11 +298,9 @@ export async function showDialog() {
ko.applyBindings(attributesModel, $dialog[0]); ko.applyBindings(attributesModel, $dialog[0]);
} }
glob.activeDialog = $dialog;
await attributesModel.loadAttributes(); await attributesModel.loadAttributes();
$dialog.modal(); utils.openDialog($dialog);
} }
$dialog.on('focus', '.attribute-name', function (e) { $dialog.on('focus', '.attribute-name', function (e) {

View File

@ -6,11 +6,7 @@ const $backendLogTextArea = $("#backend-log-textarea");
const $refreshBackendLog = $("#refresh-backend-log-button"); const $refreshBackendLog = $("#refresh-backend-log-button");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
load(); load();
} }

View File

@ -13,10 +13,6 @@ const $noteTitle = $('#branch-prefix-note-title');
let branchId; let branchId;
export async function showDialog(node) { export async function showDialog(node) {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
branchId = node.data.branchId; branchId = node.data.branchId;
const branch = treeCache.getBranch(branchId); const branch = treeCache.getBranch(branchId);
@ -30,7 +26,7 @@ export async function showDialog(node) {
return; return;
} }
$dialog.modal(); utils.openDialog($dialog);
$treePrefixInput.val(branch.prefix); $treePrefixInput.val(branch.prefix);

View File

@ -22,11 +22,7 @@ export async function showDialog(noteIds) {
} }
} }
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
$noteAutoComplete.val('').trigger('focus'); $noteAutoComplete.val('').trigger('focus');

View File

@ -17,8 +17,6 @@ let taskId = '';
let branchId = null; let branchId = null;
export async function showDialog(node, defaultType) { export async function showDialog(node, defaultType) {
utils.closeActiveDialog();
// each opening of the dialog resets the taskId so we don't associate it with previous exports anymore // each opening of the dialog resets the taskId so we don't associate it with previous exports anymore
taskId = ''; taskId = '';
$exportButton.removeAttr("disabled"); $exportButton.removeAttr("disabled");
@ -38,9 +36,7 @@ export async function showDialog(node, defaultType) {
$("#opml-v2").prop("checked", true); // setting default $("#opml-v2").prop("checked", true); // setting default
glob.activeDialog = $dialog; utils.openDialog($dialog);
$dialog.modal();
branchId = node.data.branchId; branchId = node.data.branchId;

View File

@ -3,9 +3,5 @@ import utils from "../services/utils.js";
const $dialog = $("#help-dialog"); const $dialog = $("#help-dialog");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
} }

View File

@ -16,8 +16,6 @@ const $explodeArchivesCheckbox = $("#explode-archives-checkbox");
let parentNoteId = null; let parentNoteId = null;
export async function showDialog(node) { export async function showDialog(node) {
utils.closeActiveDialog();
$fileUploadInput.val('').trigger('change'); // to trigger Import button disabling listener below $fileUploadInput.val('').trigger('change'); // to trigger Import button disabling listener below
$safeImportCheckbox.prop("checked", true); $safeImportCheckbox.prop("checked", true);
@ -26,13 +24,11 @@ export async function showDialog(node) {
$codeImportedAsCodeCheckbox.prop("checked", true); $codeImportedAsCodeCheckbox.prop("checked", true);
$explodeArchivesCheckbox.prop("checked", true); $explodeArchivesCheckbox.prop("checked", true);
glob.activeDialog = $dialog;
parentNoteId = node.data.noteId; parentNoteId = node.data.noteId;
$noteTitle.text(await treeUtils.getNoteTitle(parentNoteId)); $noteTitle.text(await treeUtils.getNoteTitle(parentNoteId));
$dialog.modal(); utils.openDialog($dialog);
} }
$form.on('submit', () => { $form.on('submit', () => {

View File

@ -10,13 +10,9 @@ let callback = null;
export async function showDialog(cb) { export async function showDialog(cb) {
callback = cb; callback = cb;
utils.closeActiveDialog();
glob.activeDialog = $dialog;
$autoComplete.val(''); $autoComplete.val('');
$dialog.modal(); utils.openDialog($dialog);
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true }); noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true });
noteAutocompleteService.showRecentNotes($autoComplete); noteAutocompleteService.showRecentNotes($autoComplete);

View File

@ -10,13 +10,9 @@ let $originallyFocused; // element focused before the dialog was opened so we ca
export function info(message) { export function info(message) {
$originallyFocused = $(':focus'); $originallyFocused = $(':focus');
utils.closeActiveDialog();
glob.activeDialog = $dialog;
$infoContent.text(message); $infoContent.text(message);
$dialog.modal(); utils.openDialog($dialog);
return new Promise((res, rej) => { resolve = res; }); return new Promise((res, rej) => { resolve = res; });
} }

View File

@ -8,13 +8,9 @@ const $autoComplete = $("#jump-to-note-autocomplete");
const $showInFullTextButton = $("#show-in-full-text-button"); const $showInFullTextButton = $("#show-in-full-text-button");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
$autoComplete.val(''); $autoComplete.val('');
$dialog.modal(); utils.openDialog($dialog);
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true }) noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true })
.on('autocomplete:selected', function(event, suggestion, dataset) { .on('autocomplete:selected', function(event, suggestion, dataset) {

View File

@ -16,10 +16,6 @@ function getOptions() {
} }
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
// set default settings // set default settings
$maxNotesInput.val(20); $maxNotesInput.val(20);
@ -27,7 +23,7 @@ export async function showDialog() {
$linkMapContainer.empty(); $linkMapContainer.empty();
$dialog.modal(); utils.openDialog($dialog);
} }
$dialog.on('shown.bs.modal', () => { $dialog.on('shown.bs.modal', () => {

View File

@ -37,9 +37,7 @@ export async function importMarkdownInline() {
convertMarkdownToHtml(text); convertMarkdownToHtml(text);
} }
else { else {
glob.activeDialog = $dialog; utils.openDialog($dialog);
$dialog.modal();
} }
} }

View File

@ -1,7 +1,5 @@
import noteAutocompleteService from "../services/note_autocomplete.js"; import noteAutocompleteService from "../services/note_autocomplete.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import cloningService from "../services/cloning.js";
import treeUtils from "../services/tree_utils.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import treeCache from "../services/tree_cache.js"; import treeCache from "../services/tree_cache.js";
import treeChangesService from "../services/branches.js"; import treeChangesService from "../services/branches.js";
@ -18,11 +16,7 @@ let movedNodes;
export async function showDialog(nodes) { export async function showDialog(nodes) {
movedNodes = nodes; movedNodes = nodes;
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
$noteAutoComplete.val('').trigger('focus'); $noteAutoComplete.val('').trigger('focus');

View File

@ -10,11 +10,7 @@ const $mime = $("#note-info-mime");
const $okButton = $("#note-info-ok-button"); const $okButton = $("#note-info-ok-button");
export function showDialog() { export function showDialog() {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
const activeNote = noteDetailService.getActiveTabNote(); const activeNote = noteDetailService.getActiveTabNote();

View File

@ -29,11 +29,7 @@ export async function showCurrentNoteRevisions() {
} }
export async function showNoteRevisionsDialog(noteId, noteRevisionId) { export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
await loadNoteRevisions(noteId, noteRevisionId); await loadNoteRevisions(noteId, noteRevisionId);
} }

View File

@ -5,11 +5,7 @@ const $dialog = $("#note-source-dialog");
const $noteSource = $("#note-source"); const $noteSource = $("#note-source");
export function showDialog() { export function showDialog() {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
const noteText = noteDetailService.getActiveTabNote().content; const noteText = noteDetailService.getActiveTabNote().content;

View File

@ -6,13 +6,9 @@ import utils from "../services/utils.js";
const $dialog = $("#options-dialog"); const $dialog = $("#options-dialog");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
const options = await server.get('options'); const options = await server.get('options');
$dialog.modal(); utils.openDialog($dialog);
(await Promise.all([ (await Promise.all([
import('./options/advanced.js'), import('./options/advanced.js'),

View File

@ -12,10 +12,6 @@ let resolve;
let shownCb; let shownCb;
export function ask({ message, defaultValue, shown }) { export function ask({ message, defaultValue, shown }) {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
shownCb = shown; shownCb = shown;
$question = $("<label>") $question = $("<label>")
@ -34,7 +30,7 @@ export function ask({ message, defaultValue, shown }) {
.append($question) .append($question)
.append($answer)); .append($answer));
$dialog.modal(); utils.openDialog($dialog);
return new Promise((res, rej) => { resolve = res; }); return new Promise((res, rej) => { resolve = res; });
} }

View File

@ -1,11 +1,12 @@
import protectedSessionService from "../services/protected_session.js"; import protectedSessionService from "../services/protected_session.js";
import utils from "../services/utils.js";
const $dialog = $("#protected-session-password-dialog"); const $dialog = $("#protected-session-password-dialog");
const $passwordForm = $dialog.find(".protected-session-password-form"); const $passwordForm = $dialog.find(".protected-session-password-form");
const $passwordInput = $dialog.find(".protected-session-password"); const $passwordInput = $dialog.find(".protected-session-password");
export function show() { export function show() {
$dialog.modal(); utils.openDialog($dialog);
$passwordInput.trigger('focus'); $passwordInput.trigger('focus');
} }

View File

@ -8,11 +8,7 @@ const $dialog = $("#recent-changes-dialog");
const $content = $("#recent-changes-content"); const $content = $("#recent-changes-content");
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog(); utils.openDialog($dialog);
glob.activeDialog = $dialog;
$dialog.modal();
const result = await server.get('recent-changes'); const result = await server.get('recent-changes');

View File

@ -14,13 +14,9 @@ let codeEditor;
$dialog.on("shown.bs.modal", e => initEditor()); $dialog.on("shown.bs.modal", e => initEditor());
export async function showDialog() { export async function showDialog() {
utils.closeActiveDialog();
glob.activeDialog = $dialog;
await showTableSchemas(); await showTableSchemas();
$dialog.modal(); utils.openDialog($dialog);
} }
async function initEditor() { async function initEditor() {

View File

@ -2,6 +2,7 @@ import treeService from './tree.js';
import treeCache from "./tree_cache.js"; import treeCache from "./tree_cache.js";
import server from './server.js'; import server from './server.js';
import toastService from "./toast.js"; import toastService from "./toast.js";
import utils from "./utils.js";
const $searchInput = $("input[name='search-text']"); const $searchInput = $("input[name='search-text']");
const $resetSearchButton = $("#reset-search-button"); const $resetSearchButton = $("#reset-search-button");
@ -28,6 +29,8 @@ const helpText = `
</p>`; </p>`;
function showSearch() { function showSearch() {
utils.saveFocusedElement();
$searchBox.slideDown(); $searchBox.slideDown();
$searchBox.tooltip({ $searchBox.tooltip({
@ -49,6 +52,8 @@ function hideSearch() {
$searchResults.hide(); $searchResults.hide();
$searchBox.slideUp(); $searchBox.slideUp();
utils.focusSavedElement();
} }
function toggleSearch() { function toggleSearch() {

View File

@ -209,9 +209,50 @@ function getMimeTypeClass(mime) {
function closeActiveDialog() { function closeActiveDialog() {
if (glob.activeDialog) { if (glob.activeDialog) {
glob.activeDialog.modal('hide'); glob.activeDialog.modal('hide');
glob.activeDialog = null;
} }
} }
let $lastFocusedElement = null;
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
import("./note_detail.js").then(noteDetail => {
noteDetail.default.getActiveEditor().editing.view.focus();
});
} else {
$lastFocusedElement.focus();
}
$lastFocusedElement = null;
}
function openDialog($dialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
saveFocusedElement();
$dialog.modal();
$dialog.on('hidden.bs.modal', () => {
if (!glob.activeDialog || glob.activeDialog === $dialog) {
focusSavedElement();
}
});
}
function isHtmlEmpty(html) { function isHtmlEmpty(html) {
html = html.toLowerCase(); html = html.toLowerCase();
@ -281,6 +322,9 @@ export default {
getNoteTypeClass, getNoteTypeClass,
getMimeTypeClass, getMimeTypeClass,
closeActiveDialog, closeActiveDialog,
openDialog,
saveFocusedElement,
focusSavedElement,
isHtmlEmpty, isHtmlEmpty,
clearBrowserCache, clearBrowserCache,
getUrlForDownload, getUrlForDownload,