client: add support for custom menu items

This commit is contained in:
Adorian Doran 2025-11-17 18:58:40 +02:00
parent fc8042aa25
commit e011f99161

View File

@ -2,7 +2,7 @@ import { KeyboardActionNames } from "@triliumnext/commons";
import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js"; import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js";
import note_tooltip from "../services/note_tooltip.js"; import note_tooltip from "../services/note_tooltip.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import { should } from "vitest"; import { h, JSX, render } from "preact";
export interface ContextMenuOptions<T> { export interface ContextMenuOptions<T> {
x: number; x: number;
@ -15,6 +15,11 @@ export interface ContextMenuOptions<T> {
onHide?: () => void; onHide?: () => void;
} }
export interface CustomMenuItem {
kind: "custom",
componentFn: () => JSX.Element;
}
export interface MenuSeparatorItem { export interface MenuSeparatorItem {
kind: "separator"; kind: "separator";
} }
@ -51,7 +56,7 @@ export interface MenuCommandItem<T> {
columns?: number; columns?: number;
} }
export type MenuItem<T> = MenuCommandItem<T> | MenuSeparatorItem | MenuHeader; export type MenuItem<T> = MenuCommandItem<T> | CustomMenuItem | MenuSeparatorItem | MenuHeader;
export type MenuHandler<T> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void; export type MenuHandler<T> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent; export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent;
@ -202,6 +207,32 @@ class ContextMenu {
$group.append($("<h6>").addClass("dropdown-header").text(item.title)); $group.append($("<h6>").addClass("dropdown-header").text(item.title));
shouldResetGroup = true; shouldResetGroup = true;
} else { } else {
if ("kind" in item && item.kind === "custom") {
// Custom menu item
$group.append(this.createCustomMenuItem(item));
} else {
// Standard menu item
$group.append(this.createMenuItem(item));
}
// After adding a menu item, if the previous item was a separator or header,
// reset the group so that the next item will be appended directly to the parent.
if (shouldResetGroup) {
$group = $parent;
shouldResetGroup = false;
};
}
}
}
private createCustomMenuItem(item: CustomMenuItem) {
const element = document.createElement("li");
element.classList.add("dropdown-custom-item");
render(h(item.componentFn, {}), element);
return element;
}
private createMenuItem(item: MenuCommandItem<any>) {
const $icon = $("<span>"); const $icon = $("<span>");
if ("uiIcon" in item || "checked" in item) { if ("uiIcon" in item || "checked" in item) {
@ -311,17 +342,7 @@ class ContextMenu {
$item.append($subMenu); $item.append($subMenu);
} }
return $item;
$group.append($item);
// After adding a menu item, if the previous item was a separator or header,
// reset the group so that the next item will be appended directly to the parent.
if (shouldResetGroup) {
$group = $parent;
shouldResetGroup = false;
};
}
}
} }
async hide() { async hide() {