mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
refactorted shortcut handling into a service
This commit is contained in:
parent
0985314fb7
commit
720fb0f73e
@ -13,6 +13,7 @@ import appContext from "./app_context.js";
|
||||
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
|
||||
import BasicWidget from "../widgets/basic_widget.js";
|
||||
import SpacedUpdate from "./spaced_update.js";
|
||||
import shortcutService from "./shortcuts.js";
|
||||
|
||||
/**
|
||||
* This is the main frontend API interface for scripts. It's published in the local "api" object.
|
||||
@ -509,7 +510,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
* @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
|
||||
* @param {function} handler
|
||||
*/
|
||||
this.bindGlobalShortcut = utils.bindGlobalShortcut;
|
||||
this.bindGlobalShortcut = shortcutService.bindGlobalShortcut;
|
||||
|
||||
/**
|
||||
* Trilium runs in backend and frontend process, when something is changed on the backend from script,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import server from "./server.js";
|
||||
import utils from "./utils.js";
|
||||
import appContext from "./app_context.js";
|
||||
import shortcutService from "./shortcuts.js";
|
||||
|
||||
const keyboardActionRepo = {};
|
||||
|
||||
@ -31,7 +31,7 @@ async function setupActionsForElement(scope, $el, component) {
|
||||
|
||||
for (const action of actions) {
|
||||
for (const shortcut of action.effectiveShortcuts) {
|
||||
utils.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
|
||||
shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,14 +39,14 @@ async function setupActionsForElement(scope, $el, component) {
|
||||
getActionsForScope("window").then(actions => {
|
||||
for (const action of actions) {
|
||||
for (const shortcut of action.effectiveShortcuts) {
|
||||
utils.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
|
||||
shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.get('keyboard-shortcuts-for-notes').then(shortcutForNotes => {
|
||||
for (const shortcut in shortcutForNotes) {
|
||||
utils.bindGlobalShortcut(shortcut, async () => {
|
||||
shortcutService.bindGlobalShortcut(shortcut, async () => {
|
||||
appContext.tabManager.getActiveContext().setNote(shortcutForNotes[shortcut]);
|
||||
});
|
||||
}
|
||||
@ -64,7 +64,7 @@ function setElementActionHandler($el, actionName, handler) {
|
||||
|
||||
for (const shortcut of action.effectiveShortcuts) {
|
||||
if (shortcut) {
|
||||
utils.bindElShortcut($el, shortcut, handler);
|
||||
shortcutService.bindElShortcut($el, shortcut, handler);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2,15 +2,16 @@
|
||||
* Mac specific initialization
|
||||
*/
|
||||
import utils from "./utils.js";
|
||||
import shortcutService from "./shortcuts.js";
|
||||
|
||||
function init() {
|
||||
if (utils.isElectron() && utils.isMac()) {
|
||||
utils.bindGlobalShortcut('meta+c', () => exec("copy"));
|
||||
utils.bindGlobalShortcut('meta+v', () => exec('paste'));
|
||||
utils.bindGlobalShortcut('meta+x', () => exec('cut'));
|
||||
utils.bindGlobalShortcut('meta+a', () => exec('selectAll'));
|
||||
utils.bindGlobalShortcut('meta+z', () => exec('undo'));
|
||||
utils.bindGlobalShortcut('meta+y', () => exec('redo'));
|
||||
shortcutService.bindGlobalShortcut('meta+c', () => exec("copy"));
|
||||
shortcutService.bindGlobalShortcut('meta+v', () => exec('paste'));
|
||||
shortcutService.bindGlobalShortcut('meta+x', () => exec('cut'));
|
||||
shortcutService.bindGlobalShortcut('meta+a', () => exec('selectAll'));
|
||||
shortcutService.bindGlobalShortcut('meta+z', () => exec('undo'));
|
||||
shortcutService.bindGlobalShortcut('meta+y', () => exec('redo'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,4 +23,4 @@ function exec(cmd) {
|
||||
|
||||
export default {
|
||||
init
|
||||
}
|
||||
}
|
||||
|
36
src/public/app/services/shortcuts.js
Normal file
36
src/public/app/services/shortcuts.js
Normal file
@ -0,0 +1,36 @@
|
||||
import utils from "./utils.js";
|
||||
|
||||
function bindGlobalShortcut(keyboardShortcut, handler) {
|
||||
bindElShortcut($(document), keyboardShortcut, handler);
|
||||
}
|
||||
|
||||
function bindElShortcut($el, keyboardShortcut, handler) {
|
||||
if (utils.isDesktop()) {
|
||||
keyboardShortcut = normalizeShortcut(keyboardShortcut);
|
||||
|
||||
$el.bind('keydown', keyboardShortcut, e => {
|
||||
handler(e);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize to the form expected by the jquery.hotkeys.js
|
||||
*/
|
||||
function normalizeShortcut(shortcut) {
|
||||
return shortcut
|
||||
.toLowerCase()
|
||||
.replace("enter", "return")
|
||||
.replace("delete", "del")
|
||||
.replace("ctrl+alt", "alt+ctrl")
|
||||
.replace("meta+alt", "alt+meta"); // alt needs to be first;
|
||||
}
|
||||
|
||||
export default {
|
||||
bindGlobalShortcut,
|
||||
bindElShortcut,
|
||||
normalizeShortcut
|
||||
}
|
@ -132,35 +132,6 @@ function randomString(len) {
|
||||
return text;
|
||||
}
|
||||
|
||||
function bindGlobalShortcut(keyboardShortcut, handler) {
|
||||
bindElShortcut($(document), keyboardShortcut, handler);
|
||||
}
|
||||
|
||||
function bindElShortcut($el, keyboardShortcut, handler) {
|
||||
if (isDesktop()) {
|
||||
keyboardShortcut = normalizeShortcut(keyboardShortcut);
|
||||
|
||||
$el.bind('keydown', keyboardShortcut, e => {
|
||||
handler(e);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize to the form expected by the jquery.hotkeys.js
|
||||
*/
|
||||
function normalizeShortcut(shortcut) {
|
||||
return shortcut
|
||||
.toLowerCase()
|
||||
.replace("enter", "return")
|
||||
.replace("delete", "del")
|
||||
.replace("ctrl+alt", "alt+ctrl")
|
||||
.replace("meta+alt", "alt+meta"); // alt needs to be first;
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
return window.device === "mobile"
|
||||
// window.device is not available in setup
|
||||
@ -387,8 +358,6 @@ export default {
|
||||
formatLabel,
|
||||
toObject,
|
||||
randomString,
|
||||
bindGlobalShortcut,
|
||||
bindElShortcut,
|
||||
isMobile,
|
||||
isDesktop,
|
||||
setCookie,
|
||||
@ -402,7 +371,6 @@ export default {
|
||||
focusSavedElement,
|
||||
isHtmlEmpty,
|
||||
clearBrowserCache,
|
||||
normalizeShortcut,
|
||||
copySelectionToClipboard,
|
||||
dynamicRequire,
|
||||
timeLimit,
|
||||
|
@ -8,6 +8,7 @@ import promotedAttributeDefinitionParser from '../../services/promoted_attribute
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import SpacedUpdate from "../../services/spaced_update.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="attr-detail">
|
||||
@ -271,8 +272,8 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
|
||||
this.$widget = $(TPL);
|
||||
|
||||
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose());
|
||||
utils.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose());
|
||||
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose());
|
||||
shortcutService.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose());
|
||||
|
||||
|
||||
this.$title = this.$widget.find('.attr-detail-title');
|
||||
|
@ -2,6 +2,7 @@ import noteAutocompleteService from '../../services/note_autocomplete.js';
|
||||
import utils from "../../services/utils.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
|
||||
const TPL = `<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
@ -42,7 +43,7 @@ export default class JumpToNoteDialog extends BasicWidget {
|
||||
this.$showInFullTextButton = this.$widget.find(".show-in-full-text-button");
|
||||
this.$showInFullTextButton.on('click', e => this.showInFullText(e));
|
||||
|
||||
utils.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e));
|
||||
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e));
|
||||
}
|
||||
|
||||
async jumpToNoteEvent() {
|
||||
|
@ -3,6 +3,7 @@ import toastService from "../../services/toast.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||
@ -42,7 +43,7 @@ export default class MarkdownImportDialog extends BasicWidget {
|
||||
|
||||
this.$widget.on('shown.bs.modal', () => this.$importTextarea.trigger('focus'));
|
||||
|
||||
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm());
|
||||
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm());
|
||||
}
|
||||
|
||||
async convertMarkdownToHtml(text) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
import utils from "../services/utils.js";
|
||||
import protectedSessionHolder from "../services/protected_session_holder.js";
|
||||
import server from "../services/server.js";
|
||||
import SpacedUpdate from "../services/spaced_update.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
import branchService from "../services/branches.js";
|
||||
import shortcutService from "../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-title-widget">
|
||||
@ -58,13 +58,13 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
|
||||
this.deleteNoteOnEscape = false;
|
||||
});
|
||||
|
||||
utils.bindElShortcut(this.$noteTitle, 'esc', () => {
|
||||
shortcutService.bindElShortcut(this.$noteTitle, 'esc', () => {
|
||||
if (this.deleteNoteOnEscape && this.noteContext.isActive()) {
|
||||
branchService.deleteNotes(Object.values(this.noteContext.note.parentToBranch));
|
||||
}
|
||||
});
|
||||
|
||||
utils.bindElShortcut(this.$noteTitle, 'return', () => {
|
||||
shortcutService.bindElShortcut(this.$noteTitle, 'return', () => {
|
||||
this.triggerCommand('focusOnDetail', {ntxId: this.noteContext.ntxId});
|
||||
});
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import syncService from "../services/sync.js";
|
||||
import options from "../services/options.js";
|
||||
import protectedSessionHolder from "../services/protected_session_holder.js";
|
||||
import dialogService from "../services/dialog.js";
|
||||
import shortcutService from "../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="tree-wrapper">
|
||||
@ -1331,7 +1332,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
|
||||
for (const action of actions) {
|
||||
for (const shortcut of action.effectiveShortcuts) {
|
||||
hotKeyMap[utils.normalizeShortcut(shortcut)] = node => {
|
||||
hotKeyMap[shortcutService.normalizeShortcut(shortcut)] = node => {
|
||||
const notePath = treeService.getNotePath(node);
|
||||
|
||||
this.triggerCommand(action.actionName, {node, notePath});
|
||||
|
@ -1,10 +1,10 @@
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import server from "../services/server.js";
|
||||
import linkService from "../services/link.js";
|
||||
import dateNotesService from "../services/date_notes.js";
|
||||
import froca from "../services/froca.js";
|
||||
import utils from "../services/utils.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
import shortcutService from "../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="quick-search input-group input-group-sm">
|
||||
@ -66,7 +66,7 @@ export default class QuickSearchWidget extends BasicWidget {
|
||||
})
|
||||
}
|
||||
|
||||
utils.bindElShortcut(this.$searchString, 'return', () => {
|
||||
shortcutService.bindElShortcut(this.$searchString, 'return', () => {
|
||||
if (this.$dropdownMenu.is(":visible")) {
|
||||
this.search(); // just update already visible dropdown
|
||||
} else {
|
||||
@ -76,11 +76,11 @@ export default class QuickSearchWidget extends BasicWidget {
|
||||
this.$searchString.focus();
|
||||
});
|
||||
|
||||
utils.bindElShortcut(this.$searchString, 'down', () => {
|
||||
shortcutService.bindElShortcut(this.$searchString, 'down', () => {
|
||||
this.$dropdownMenu.find('.dropdown-item:first').focus();
|
||||
});
|
||||
|
||||
utils.bindElShortcut(this.$searchString, 'esc', () => {
|
||||
shortcutService.bindElShortcut(this.$searchString, 'esc', () => {
|
||||
this.$dropdownToggle.dropdown('hide');
|
||||
});
|
||||
|
||||
@ -120,7 +120,7 @@ export default class QuickSearchWidget extends BasicWidget {
|
||||
appContext.tabManager.getActiveContext().setNote(note.noteId);
|
||||
}
|
||||
});
|
||||
utils.bindElShortcut($link, 'return', () => {
|
||||
shortcutService.bindElShortcut($link, 'return', () => {
|
||||
this.$dropdownToggle.dropdown("hide");
|
||||
|
||||
appContext.tabManager.getActiveContext().setNote(note.noteId);
|
||||
@ -140,9 +140,9 @@ export default class QuickSearchWidget extends BasicWidget {
|
||||
|
||||
$showInFullButton.on('click', () => this.showInFullSearch());
|
||||
|
||||
utils.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch());
|
||||
shortcutService.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch());
|
||||
|
||||
utils.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus());
|
||||
shortcutService.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus());
|
||||
|
||||
this.$dropdownToggle.dropdown('update');
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import AbstractSearchOption from "./abstract_search_option.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import SpacedUpdate from "../../services/spaced_update.js";
|
||||
import server from "../../services/server.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
|
||||
const TPL = `
|
||||
<tr>
|
||||
@ -45,7 +45,7 @@ export default class SearchString extends AbstractSearchOption {
|
||||
this.$searchString = $option.find('.search-string');
|
||||
this.$searchString.on('input', () => this.spacedUpdate.scheduleUpdate());
|
||||
|
||||
utils.bindElShortcut(this.$searchString, 'return', async () => {
|
||||
shortcutService.bindElShortcut(this.$searchString, 'return', async () => {
|
||||
// this also in effect disallows new lines in query string.
|
||||
// on one hand this makes sense since search string is a label
|
||||
// on the other hand it could be nice for structuring long search string. It's probably a niche case though.
|
||||
|
Loading…
x
Reference in New Issue
Block a user