note launchers by default open in the active note context but follow the same logic with ctrl/middle click as links

This commit is contained in:
zadam 2022-12-09 16:48:00 +01:00
parent dfb462cf35
commit 5ac332960e
6 changed files with 80 additions and 19 deletions

View File

@ -90,27 +90,27 @@ function getNotePathFromLink($link) {
return url ? getNotePathFromUrl(url) : null; return url ? getNotePathFromUrl(url) : null;
} }
function goToLink(e) { function goToLink(evt) {
const $link = $(e.target).closest("a,.block-link"); const $link = $(evt.target).closest("a,.block-link");
const address = $link.attr('href'); const address = $link.attr('href');
if (address?.startsWith("data:")) { if (address?.startsWith("data:")) {
return true; return true;
} }
e.preventDefault(); evt.preventDefault();
e.stopPropagation(); evt.stopPropagation();
const notePath = getNotePathFromLink($link); const notePath = getNotePathFromLink($link);
const ctrlKey = (!utils.isMac() && e.ctrlKey) || (utils.isMac() && e.metaKey); const ctrlKey = utils.isCtrlKey(evt);
if (notePath) { if (notePath) {
if ((e.which === 1 && ctrlKey) || e.which === 2) { if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
appContext.tabManager.openTabWithNoteWithHoisting(notePath); appContext.tabManager.openTabWithNoteWithHoisting(notePath);
} }
else if (e.which === 1) { else if (evt.which === 1) {
const ntxId = $(e.target).closest("[data-ntx-id]").attr("data-ntx-id"); const ntxId = $(evt.target).closest("[data-ntx-id]").attr("data-ntx-id");
const noteContext = ntxId const noteContext = ntxId
? appContext.tabManager.getNoteContextById(ntxId) ? appContext.tabManager.getNoteContextById(ntxId)
@ -124,7 +124,7 @@ function goToLink(e) {
} }
} }
else { else {
if ((e.which === 1 && ctrlKey) || e.which === 2 if ((evt.which === 1 && ctrlKey) || evt.which === 2
|| $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices
|| $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices || $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices
) { ) {

View File

@ -60,6 +60,11 @@ function isMac() {
return navigator.platform.indexOf('Mac') > -1; return navigator.platform.indexOf('Mac') > -1;
} }
function isCtrlKey(evt) {
return (!isMac() && evt.ctrlKey)
|| (isMac() && evt.metaKey);
}
function assertArguments() { function assertArguments() {
for (const i in arguments) { for (const i in arguments) {
if (!arguments[i]) { if (!arguments[i]) {
@ -362,6 +367,7 @@ export default {
now, now,
isElectron, isElectron,
isMac, isMac,
isCtrlKey,
assertArguments, assertArguments,
escapeHtml, escapeHtml,
stopWatch, stopWatch,

View File

@ -1,23 +1,55 @@
import AbstractLauncher from "./abstract_launcher.js"; import AbstractLauncher from "./abstract_launcher.js";
import dialogService from "../../../services/dialog.js"; import dialogService from "../../../services/dialog.js";
import appContext from "../../../components/app_context.js"; import appContext from "../../../components/app_context.js";
import utils from "../../../services/utils.js";
import linkContextMenuService from "../../../menus/link_context_menu.js";
// we're intentionally displaying the launcher title and icon instead of the target
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only downside is more work in setting up the typical case
// where you actually want to have both title and icon in sync, but for those cases there are bookmarks
export default class NoteLauncher extends AbstractLauncher { export default class NoteLauncher extends AbstractLauncher {
constructor(launcherNote) { constructor(launcherNote) {
super(launcherNote); super(launcherNote);
this.title(this.launcherNote.title) this.title(this.launcherNote.title)
.icon(this.launcherNote.getIcon()) .icon(this.launcherNote.getIcon())
.onClick(() => this.launch()); .onClick((widget, evt) => this.launch(evt))
.onAuxClick((widget, evt) => this.launch(evt))
.onContextMenu(evt => {
const targetNoteId = this.getTargetNoteId();
if (!targetNoteId) {
return;
}
linkContextMenuService.openContextMenu(targetNoteId, evt);
});
} }
launch() { launch(evt) {
// we're intentionally displaying the launcher title and icon instead of the target const targetNoteId = this.getTargetNoteId();
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok), if (!targetNoteId) {
// but on the launchpad you want them distinguishable. return;
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad }
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only (but major) downside is more work in setting up the typical case where you actually want to have both title and icon in sync. if (!evt) {
// keyboard shortcut
appContext.tabManager.getActiveContext().setNote(targetNoteId)
return;
}
const ctrlKey = utils.isCtrlKey(evt);
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId);
} else {
appContext.tabManager.getActiveContext().setNote(targetNoteId);
}
}
getTargetNoteId() {
const targetNoteId = this.launcherNote.getRelationValue('targetNote'); const targetNoteId = this.launcherNote.getRelationValue('targetNote');
if (!targetNoteId) { if (!targetNoteId) {
@ -25,7 +57,7 @@ export default class NoteLauncher extends AbstractLauncher {
return; return;
} }
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true); return targetNoteId;
} }
getTitle() { getTitle() {

View File

@ -13,10 +13,23 @@ export default class OnClickButtonWidget extends AbstractButtonWidget {
} else { } else {
console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings); console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings);
} }
if (this.settings.onAuxClick) {
this.$widget.on("auxclick", e => {
this.$widget.tooltip("hide");
this.settings.onAuxClick(this, e);
});
}
} }
onClick(handler) { onClick(handler) {
this.settings.onClick = handler; this.settings.onClick = handler;
return this; return this;
} }
onAuxClick(handler) {
this.settings.onAuxClick = handler;
return this;
}
} }

View File

@ -56,6 +56,12 @@ const HIDDEN_SUBTREE_DEFINITION = {
title: 'Bulk action', title: 'Bulk action',
type: 'doc', type: 'doc',
}, },
{
// place for user scripts hidden stuff (scripts should not create notes directly under hidden root)
id: 'userHidden',
title: 'User Hidden',
type: 'text',
},
{ {
id: LBTPL_ROOT, id: LBTPL_ROOT,
title: 'Launch Bar Templates', title: 'Launch Bar Templates',

View File

@ -111,7 +111,11 @@ function getAndValidateParent(params) {
throw new ValidationError(`Parent note "${params.parentNoteId}" not found.`); throw new ValidationError(`Parent note "${params.parentNoteId}" not found.`);
} }
if (!params.ignoreForbiddenParents && (parentNote.isLaunchBarConfig() || parentNote.isOptions())) { if (parentNote.type === 'launcher' && parentNote.noteId !== 'lbBookmarks') {
throw new ValidationError(`Creating child notes into launcher notes is not allowed.`);
}
if (!params.ignoreForbiddenParents && (['lbRoot', 'hidden'].includes(parentNote.noteId) || parentNote.isOptions())) {
throw new ValidationError(`Creating child notes into '${parentNote.noteId}' is not allowed.`); throw new ValidationError(`Creating child notes into '${parentNote.noteId}' is not allowed.`);
} }