mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-08-19 00:42:29 +08:00
refactor(client/ts): use filtered generics for context menu commands
This commit is contained in:
parent
19652fbbce
commit
b01725101d
@ -18,6 +18,8 @@ import NoteDetailWidget from "../widgets/note_detail.js";
|
|||||||
import { ResolveOptions } from "../widgets/dialogs/delete_notes.js";
|
import { ResolveOptions } from "../widgets/dialogs/delete_notes.js";
|
||||||
import { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
|
import { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
|
||||||
import { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js";
|
import { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js";
|
||||||
|
import { Node } from "../services/tree.js";
|
||||||
|
import FAttribute from "../entities/fattribute.js";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||||
@ -31,11 +33,28 @@ interface BeforeUploadListener extends Component {
|
|||||||
beforeUnloadEvent(): boolean;
|
beforeUnloadEvent(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for the data/arguments for a given command (see {@link CommandMappings}).
|
||||||
|
*/
|
||||||
interface CommandData {
|
interface CommandData {
|
||||||
ntxId?: string;
|
ntxId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandMappings = {
|
/**
|
||||||
|
* Represents a set of commands that are triggered from the context menu, providing information such as the selected note.
|
||||||
|
*/
|
||||||
|
export interface ContextMenuCommandData extends CommandData {
|
||||||
|
node: Node;
|
||||||
|
notePath: string;
|
||||||
|
noteId?: string;
|
||||||
|
selectedOrActiveBranchIds: any; // TODO: Remove any once type is defined
|
||||||
|
selectedOrActiveNoteIds: any; // TODO: Remove any once type is defined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keys represent the different commands that can be triggered via {@link AppContext#triggerCommand} (first argument), and the values represent the data or arguments definition of the given command. All data for commands must extend {@link CommandData}.
|
||||||
|
*/
|
||||||
|
export type CommandMappings = {
|
||||||
"api-log-messages": CommandData;
|
"api-log-messages": CommandData;
|
||||||
focusOnDetail: Required<CommandData>;
|
focusOnDetail: Required<CommandData>;
|
||||||
searchNotes: CommandData & {
|
searchNotes: CommandData & {
|
||||||
@ -73,12 +92,42 @@ type CommandMappings = {
|
|||||||
openNoteInNewTab: CommandData;
|
openNoteInNewTab: CommandData;
|
||||||
openNoteInNewSplit: CommandData;
|
openNoteInNewSplit: CommandData;
|
||||||
openNoteInNewWindow: CommandData;
|
openNoteInNewWindow: CommandData;
|
||||||
openNoteInSplit: CommandData;
|
|
||||||
openInTab: CommandData;
|
openInTab: ContextMenuCommandData;
|
||||||
insertNoteAfter: CommandData;
|
openNoteInSplit: ContextMenuCommandData;
|
||||||
insertChildNote: CommandData;
|
toggleNoteHoisting: ContextMenuCommandData;
|
||||||
convertNoteToAttachment: CommandData;
|
insertNoteAfter: ContextMenuCommandData;
|
||||||
copyNotePathToClipboard: CommandData;
|
insertChildNote: ContextMenuCommandData;
|
||||||
|
protectSubtree: ContextMenuCommandData;
|
||||||
|
unprotectSubtree: ContextMenuCommandData;
|
||||||
|
openBulkActionsDialog: ContextMenuCommandData;
|
||||||
|
editBranchPrefix: ContextMenuCommandData;
|
||||||
|
convertNoteToAttachment: ContextMenuCommandData;
|
||||||
|
duplicateSubtree: ContextMenuCommandData;
|
||||||
|
expandSubtree: ContextMenuCommandData;
|
||||||
|
collapseSubtree: ContextMenuCommandData;
|
||||||
|
sortChildNotes: ContextMenuCommandData;
|
||||||
|
copyNotePathToClipboard: ContextMenuCommandData;
|
||||||
|
recentChangesInSubtree: ContextMenuCommandData;
|
||||||
|
cutNotesToClipboard: ContextMenuCommandData;
|
||||||
|
copyNotesToClipboard: ContextMenuCommandData;
|
||||||
|
pasteNotesFromClipboard: ContextMenuCommandData;
|
||||||
|
pasteNotesAfterFromClipboard: ContextMenuCommandData;
|
||||||
|
moveNotesTo: ContextMenuCommandData;
|
||||||
|
cloneNotesTo: ContextMenuCommandData;
|
||||||
|
deleteNotes: ContextMenuCommandData;
|
||||||
|
importIntoNote: ContextMenuCommandData;
|
||||||
|
exportNote: ContextMenuCommandData;
|
||||||
|
searchInSubtree: ContextMenuCommandData;
|
||||||
|
|
||||||
|
addNoteLauncher: ContextMenuCommandData;
|
||||||
|
addScriptLauncher: ContextMenuCommandData;
|
||||||
|
addWidgetLauncher: ContextMenuCommandData;
|
||||||
|
addSpacerLauncher: ContextMenuCommandData;
|
||||||
|
moveLauncherToVisible: ContextMenuCommandData;
|
||||||
|
moveLauncherToAvailable: ContextMenuCommandData;
|
||||||
|
resetLauncher: ContextMenuCommandData;
|
||||||
|
|
||||||
executeInActiveNoteDetailWidget: CommandData & {
|
executeInActiveNoteDetailWidget: CommandData & {
|
||||||
callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void
|
callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void
|
||||||
};
|
};
|
||||||
@ -92,17 +141,11 @@ type CommandMappings = {
|
|||||||
showPasswordNotSet: CommandData;
|
showPasswordNotSet: CommandData;
|
||||||
showProtectedSessionPasswordDialog: CommandData;
|
showProtectedSessionPasswordDialog: CommandData;
|
||||||
closeProtectedSessionPasswordDialog: CommandData;
|
closeProtectedSessionPasswordDialog: CommandData;
|
||||||
resetLauncher: CommandData;
|
|
||||||
addNoteLauncher: CommandData;
|
|
||||||
addScriptLauncher: CommandData;
|
|
||||||
addWidgetLauncher: CommandData;
|
|
||||||
addSpacerLauncher: CommandData;
|
|
||||||
moveLauncherToVisible: CommandData;
|
|
||||||
moveLauncherToAvailable: CommandData;
|
|
||||||
duplicateSubtree: CommandData;
|
|
||||||
deleteNotes: CommandData;
|
|
||||||
copyImageReferenceToClipboard: CommandData;
|
copyImageReferenceToClipboard: CommandData;
|
||||||
copyImageToClipboard: CommandData;
|
copyImageToClipboard: CommandData;
|
||||||
|
updateAttributesList: {
|
||||||
|
attributes: FAttribute[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventMappings = {
|
type EventMappings = {
|
||||||
@ -123,9 +166,19 @@ type EventMappings = {
|
|||||||
|
|
||||||
type CommandAndEventMappings = (CommandMappings & EventMappings);
|
type CommandAndEventMappings = (CommandMappings & EventMappings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}.
|
||||||
|
*/
|
||||||
export type CommandNames = keyof CommandMappings;
|
export type CommandNames = keyof CommandMappings;
|
||||||
type EventNames = keyof EventMappings;
|
type EventNames = keyof EventMappings;
|
||||||
|
|
||||||
|
type FilterByValueType<T, ValueType> = { [K in keyof T]: T[K] extends ValueType ? K : never; }[keyof T];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic which filters {@link CommandNames} to provide only those commands that take in as data the desired implementation of {@link CommandData}. Mostly useful for contextual menu, to enforce consistency in the commands.
|
||||||
|
*/
|
||||||
|
export type FilteredCommandNames<T extends CommandData> = keyof Pick<CommandMappings, FilterByValueType<CommandMappings, T>>;
|
||||||
|
|
||||||
class AppContext extends Component {
|
class AppContext extends Component {
|
||||||
|
|
||||||
isMainWindow: boolean;
|
isMainWindow: boolean;
|
||||||
@ -225,9 +278,8 @@ class AppContext extends Component {
|
|||||||
return this.handleEvent(name, data);
|
return this.handleEvent(name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove ignore once all commands are mapped out.
|
triggerCommand<K extends CommandNames>(name: K, _data?: CommandMappings[K]) {
|
||||||
//@ts-ignore
|
const data = _data || {};
|
||||||
triggerCommand<K extends CommandNames>(name: K, data: CommandMappings[K] = {}) {
|
|
||||||
for (const executor of this.components) {
|
for (const executor of this.components) {
|
||||||
const fun = (executor as any)[`${name}Command`];
|
const fun = (executor as any)[`${name}Command`];
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import utils from '../services/utils.js';
|
import utils from '../services/utils.js';
|
||||||
|
import { CommandMappings, CommandNames } from './app_context.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for all components in the Trilium's frontend.
|
* Abstract class for all components in the Trilium's frontend.
|
||||||
@ -84,7 +85,8 @@ export default class Component {
|
|||||||
return promises.length > 0 ? Promise.all(promises) : null;
|
return promises.length > 0 ? Promise.all(promises) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerCommand(name: string, data = {}): Promise<unknown> | undefined | null {
|
triggerCommand<K extends CommandNames>(name: string, _data?: CommandMappings[K]): Promise<unknown> | undefined | null {
|
||||||
|
const data = _data || {};
|
||||||
const fun = (this as any)[`${name}Command`];
|
const fun = (this as any)[`${name}Command`];
|
||||||
|
|
||||||
if (fun) {
|
if (fun) {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { MenuCommandItem } from "../menus/context_menu.js";
|
import { MenuCommandItem } from "../menus/context_menu.js";
|
||||||
|
import { CommandNames } from "./app_context.js";
|
||||||
|
|
||||||
type ListenerReturnType = void | Promise<void>;
|
type ListenerReturnType = void | Promise<void>;
|
||||||
|
|
||||||
export interface SelectMenuItemEventListener {
|
export interface SelectMenuItemEventListener<T extends CommandNames> {
|
||||||
selectMenuItemHandler(item: MenuCommandItem): ListenerReturnType;
|
selectMenuItemHandler(item: MenuCommandItem<T>): ListenerReturnType;
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
import { CommandNames } from '../components/app_context.js';
|
import { CommandNames } from '../components/app_context.js';
|
||||||
import keyboardActionService from '../services/keyboard_actions.js';
|
import keyboardActionService from '../services/keyboard_actions.js';
|
||||||
|
|
||||||
interface ContextMenuOptions {
|
interface ContextMenuOptions<T extends CommandNames> {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
orientation?: "left";
|
orientation?: "left";
|
||||||
selectMenuItemHandler: MenuHandler;
|
selectMenuItemHandler: MenuHandler<T>;
|
||||||
items: MenuItem[];
|
items: MenuItem<T>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MenuSeparatorItem {
|
interface MenuSeparatorItem {
|
||||||
title: "----"
|
title: "----"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuCommandItem {
|
export interface MenuCommandItem<T extends CommandNames> {
|
||||||
title: string;
|
title: string;
|
||||||
command?: CommandNames;
|
command?: T;
|
||||||
type?: string;
|
type?: string;
|
||||||
uiIcon?: string;
|
uiIcon?: string;
|
||||||
templateNoteId?: string;
|
templateNoteId?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
handler?: MenuHandler;
|
handler?: MenuHandler<T>;
|
||||||
items?: MenuItem[];
|
items?: MenuItem<T>[] | null;
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
spellingSuggestion?: string;
|
spellingSuggestion?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MenuItem = MenuCommandItem | MenuSeparatorItem;
|
export type MenuItem<T extends CommandNames> = MenuCommandItem<T> | MenuSeparatorItem;
|
||||||
export type MenuHandler = (item: MenuCommandItem, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
|
export type MenuHandler<T extends CommandNames> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
|
||||||
|
|
||||||
class ContextMenu {
|
class ContextMenu {
|
||||||
|
|
||||||
private $widget!: JQuery<HTMLElement>;
|
private $widget!: JQuery<HTMLElement>;
|
||||||
private dateContextMenuOpenedMs: number;
|
private dateContextMenuOpenedMs: number;
|
||||||
private options?: ContextMenuOptions;
|
private options?: ContextMenuOptions<any>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.$widget = $("#context-menu-container");
|
this.$widget = $("#context-menu-container");
|
||||||
@ -43,7 +43,7 @@ class ContextMenu {
|
|||||||
$(document).on('click', () => this.hide());
|
$(document).on('click', () => this.hide());
|
||||||
}
|
}
|
||||||
|
|
||||||
async show(options: ContextMenuOptions) {
|
async show<T extends CommandNames>(options: ContextMenuOptions<T>) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
if (this.$widget.hasClass("show")) {
|
if (this.$widget.hasClass("show")) {
|
||||||
@ -119,7 +119,7 @@ class ContextMenu {
|
|||||||
}).addClass("show");
|
}).addClass("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
addItems($parent: JQuery<HTMLElement>, items: MenuItem[]) {
|
addItems($parent: JQuery<HTMLElement>, items: MenuItem<any>[]) {
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -4,6 +4,7 @@ import zoomService from "../components/zoom.js";
|
|||||||
import contextMenu, { MenuItem } from "./context_menu.js";
|
import contextMenu, { MenuItem } from "./context_menu.js";
|
||||||
import { t } from "../services/i18n.js";
|
import { t } from "../services/i18n.js";
|
||||||
import type { BrowserWindow } from "electron";
|
import type { BrowserWindow } from "electron";
|
||||||
|
import { CommandNames } from "../components/app_context.js";
|
||||||
|
|
||||||
function setupContextMenu() {
|
function setupContextMenu() {
|
||||||
const electron = utils.dynamicRequire('electron');
|
const electron = utils.dynamicRequire('electron');
|
||||||
@ -18,7 +19,7 @@ function setupContextMenu() {
|
|||||||
const isMac = process.platform === "darwin";
|
const isMac = process.platform === "darwin";
|
||||||
const platformModifier = isMac ? 'Meta' : 'Ctrl';
|
const platformModifier = isMac ? 'Meta' : 'Ctrl';
|
||||||
|
|
||||||
const items: MenuItem[] = [];
|
const items: MenuItem<CommandNames>[] = [];
|
||||||
|
|
||||||
if (params.misspelledWord) {
|
if (params.misspelledWord) {
|
||||||
for (const suggestion of params.dictionarySuggestions) {
|
for (const suggestion of params.dictionarySuggestions) {
|
||||||
|
@ -6,8 +6,11 @@ import server from "../services/server.js";
|
|||||||
import { t } from '../services/i18n.js';
|
import { t } from '../services/i18n.js';
|
||||||
import type { SelectMenuItemEventListener } from '../components/events.js';
|
import type { SelectMenuItemEventListener } from '../components/events.js';
|
||||||
import NoteTreeWidget from '../widgets/note_tree.js';
|
import NoteTreeWidget from '../widgets/note_tree.js';
|
||||||
|
import { FilteredCommandNames, ContextMenuCommandData } from '../components/app_context.js';
|
||||||
|
|
||||||
export default class LauncherContextMenu implements SelectMenuItemEventListener {
|
type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>;
|
||||||
|
|
||||||
|
export default class LauncherContextMenu implements SelectMenuItemEventListener<LauncherCommandNames> {
|
||||||
|
|
||||||
private treeWidget: NoteTreeWidget;
|
private treeWidget: NoteTreeWidget;
|
||||||
private node: Node;
|
private node: Node;
|
||||||
@ -26,7 +29,7 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMenuItems(): Promise<MenuItem[]> {
|
async getMenuItems(): Promise<MenuItem<LauncherCommandNames>[]> {
|
||||||
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
||||||
const parentNoteId = this.node.getParent().data.noteId;
|
const parentNoteId = this.node.getParent().data.noteId;
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener
|
|||||||
const canBeDeleted = !note?.noteId.startsWith("_"); // fixed notes can't be deleted
|
const canBeDeleted = !note?.noteId.startsWith("_"); // fixed notes can't be deleted
|
||||||
const canBeReset = !canBeDeleted && note?.isLaunchBarConfig();
|
const canBeReset = !canBeDeleted && note?.isLaunchBarConfig();
|
||||||
|
|
||||||
const items: (MenuItem | null)[] = [
|
const items: (MenuItem<LauncherCommandNames> | null)[] = [
|
||||||
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-note-launcher"), command: 'addNoteLauncher', uiIcon: "bx bx-note" } : null,
|
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-note-launcher"), command: 'addNoteLauncher', uiIcon: "bx bx-note" } : null,
|
||||||
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-script-launcher"), command: 'addScriptLauncher', uiIcon: "bx bx-code-curly" } : null,
|
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-script-launcher"), command: 'addScriptLauncher', uiIcon: "bx bx-code-curly" } : null,
|
||||||
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-custom-widget"), command: 'addWidgetLauncher', uiIcon: "bx bx-customize" } : null,
|
(isVisibleRoot || isAvailableRoot) ? { title: t("launcher_context_menu.add-custom-widget"), command: 'addWidgetLauncher', uiIcon: "bx bx-customize" } : null,
|
||||||
@ -59,7 +62,7 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener
|
|||||||
return items.filter(row => row !== null);
|
return items.filter(row => row !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectMenuItemHandler({command}: MenuCommandItem) {
|
async selectMenuItemHandler({command}: MenuCommandItem<LauncherCommandNames>) {
|
||||||
if (!command) {
|
if (!command) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import froca from "../services/froca.js";
|
|||||||
import clipboard from '../services/clipboard.js';
|
import clipboard from '../services/clipboard.js';
|
||||||
import noteCreateService from "../services/note_create.js";
|
import noteCreateService from "../services/note_create.js";
|
||||||
import contextMenu, { MenuCommandItem, MenuItem } from "./context_menu.js";
|
import contextMenu, { MenuCommandItem, MenuItem } from "./context_menu.js";
|
||||||
import appContext from "../components/app_context.js";
|
import appContext, { ContextMenuCommandData, FilteredCommandNames } from "../components/app_context.js";
|
||||||
import noteTypesService from "../services/note_types.js";
|
import noteTypesService from "../services/note_types.js";
|
||||||
import server from "../services/server.js";
|
import server from "../services/server.js";
|
||||||
import toastService from "../services/toast.js";
|
import toastService from "../services/toast.js";
|
||||||
@ -18,7 +18,9 @@ interface ConvertToAttachmentResponse {
|
|||||||
attachment?: FAttachment;
|
attachment?: FAttachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TreeContextMenu implements SelectMenuItemEventListener {
|
type TreeCommandNames = FilteredCommandNames<ContextMenuCommandData>;
|
||||||
|
|
||||||
|
export default class TreeContextMenu implements SelectMenuItemEventListener<TreeCommandNames> {
|
||||||
|
|
||||||
private treeWidget: NoteTreeWidget;
|
private treeWidget: NoteTreeWidget;
|
||||||
private node: Node;
|
private node: Node;
|
||||||
@ -37,7 +39,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMenuItems(): Promise<MenuItem[]> {
|
async getMenuItems(): Promise<MenuItem<TreeCommandNames>[]> {
|
||||||
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
||||||
const branch = froca.getBranch(this.node.data.branchId);
|
const branch = froca.getBranch(this.node.data.branchId);
|
||||||
const isNotRoot = note?.noteId !== 'root';
|
const isNotRoot = note?.noteId !== 'root';
|
||||||
@ -56,7 +58,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener {
|
|||||||
const parentNotSearch = !parentNote || parentNote.type !== 'search';
|
const parentNotSearch = !parentNote || parentNote.type !== 'search';
|
||||||
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
||||||
|
|
||||||
return [
|
const items: (MenuItem<TreeCommandNames> | null)[] = [
|
||||||
{ title: `${t("tree-context-menu.open-in-a-new-tab")} <kbd>Ctrl+Click</kbd>`, command: "openInTab", uiIcon: "bx bx-link-external", enabled: noSelectedNotes },
|
{ title: `${t("tree-context-menu.open-in-a-new-tab")} <kbd>Ctrl+Click</kbd>`, command: "openInTab", uiIcon: "bx bx-link-external", enabled: noSelectedNotes },
|
||||||
|
|
||||||
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
|
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
|
||||||
@ -149,10 +151,11 @@ export default class TreeContextMenu implements SelectMenuItemEventListener {
|
|||||||
{ title: `${t("tree-context-menu.search-in-subtree")} <kbd data-command="searchInSubtree"></kbd>`, command: "searchInSubtree", uiIcon: "bx bx-search",
|
{ title: `${t("tree-context-menu.search-in-subtree")} <kbd data-command="searchInSubtree"></kbd>`, command: "searchInSubtree", uiIcon: "bx bx-search",
|
||||||
enabled: notSearch && noSelectedNotes },
|
enabled: notSearch && noSelectedNotes },
|
||||||
|
|
||||||
].filter(row => row !== null) as MenuItem[];
|
];
|
||||||
|
return items.filter(row => row !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectMenuItemHandler({command, type, templateNoteId}: MenuCommandItem) {
|
async selectMenuItemHandler({command, type, templateNoteId}: MenuCommandItem<TreeCommandNames>) {
|
||||||
const notePath = treeService.getNotePath(this.node);
|
const notePath = treeService.getNotePath(this.node);
|
||||||
|
|
||||||
if (command === 'openInTab') {
|
if (command === 'openInTab') {
|
||||||
@ -210,7 +213,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener {
|
|||||||
navigator.clipboard.writeText('#' + notePath);
|
navigator.clipboard.writeText('#' + notePath);
|
||||||
}
|
}
|
||||||
else if (command) {
|
else if (command) {
|
||||||
this.treeWidget.triggerCommand(command, {
|
this.treeWidget.triggerCommand<TreeCommandNames>(command, {
|
||||||
node: this.node,
|
node: this.node,
|
||||||
notePath: notePath,
|
notePath: notePath,
|
||||||
noteId: this.node.data.noteId,
|
noteId: this.node.data.noteId,
|
||||||
|
@ -2,10 +2,12 @@ import server from "./server.js";
|
|||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import { MenuItem } from "../menus/context_menu.js";
|
import { MenuItem } from "../menus/context_menu.js";
|
||||||
import { CommandNames } from "../components/app_context.js";
|
import { ContextMenuCommandData, FilteredCommandNames } from "../components/app_context.js";
|
||||||
|
|
||||||
async function getNoteTypeItems(command?: CommandNames) {
|
type NoteTypeCommandNames = FilteredCommandNames<ContextMenuCommandData>;
|
||||||
const items: MenuItem[] = [
|
|
||||||
|
async function getNoteTypeItems(command?: NoteTypeCommandNames) {
|
||||||
|
const items: MenuItem<NoteTypeCommandNames>[] = [
|
||||||
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
||||||
{ title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" },
|
{ title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" },
|
||||||
{ title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" },
|
{ title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" },
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CommandNames } from "../../components/app_context.js";
|
||||||
import { MenuCommandItem } from "../../menus/context_menu.js";
|
import { MenuCommandItem } from "../../menus/context_menu.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import noteTypesService from "../../services/note_types.js";
|
import noteTypesService from "../../services/note_types.js";
|
||||||
@ -128,7 +129,7 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
|||||||
if (noteType.title === '----') {
|
if (noteType.title === '----') {
|
||||||
this.$noteTypeDropdown.append($('<h6 class="dropdown-header">').append(t("note_type_chooser.templates")));
|
this.$noteTypeDropdown.append($('<h6 class="dropdown-header">').append(t("note_type_chooser.templates")));
|
||||||
} else {
|
} else {
|
||||||
const commandItem = (noteType as MenuCommandItem)
|
const commandItem = (noteType as MenuCommandItem<CommandNames>)
|
||||||
this.$noteTypeDropdown.append(
|
this.$noteTypeDropdown.append(
|
||||||
$('<a class="dropdown-item" tabindex="0">')
|
$('<a class="dropdown-item" tabindex="0">')
|
||||||
.attr("data-note-type", commandItem.type || "")
|
.attr("data-note-type", commandItem.type || "")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user