@chocbite/ts-lib-context-menu
v1.3.3
Published
UI context menu library for TypeScript
Readme
@chocbite/ts-lib-context-menu
Framework-agnostic TypeScript library for right-click / touch-and-hold context menus built on native Web Components.
Installation
npm install @chocbite/ts-lib-context-menuBasic Usage
Import the library and attach a context menu to an element. The CSS is auto-injected on import.
import ctm from "@chocbite/ts-lib-context-menu";
ctm.attach(
document.getElementById("editor")!,
ctm.menu([
ctm.line("Cut", () => document.execCommand("cut")),
ctm.line("Copy", () => document.execCommand("copy")),
ctm.line("Paste", () => document.execCommand("paste")),
]),
);Right-click (or long-press) the element to open the menu.
API
ctm.menu(lines)
Creates a menu panel. lines can be a static array, a lazy factory, or a Promise/async factory (shows a spinner while loading).
// Static array
const menu = ctm.menu([
ctm.line("Option A", () => console.log("A")),
ctm.line("Option B", () => console.log("B")),
]);
// Factory — evaluated on every open
const menu = ctm.menu(() => [
ctm.line("Timestamp", () => console.log(Date.now())),
]);
// Async factory — shows a spinner until the Promise resolves
const menu = ctm.menu(async () => {
const items = await fetchMenuItems();
return items.map((item) => ctm.line(item.label, item.action));
});ctm.line(text, func, icon?, shortcut?, checkmark?)
Creates a clickable menu item.
import { material_delete_rounded } from "@chocbite/ts-lib-icons";
ctm.line("Delete", () => deleteFile());
ctm.line("Delete", () => deleteFile(), material_delete_rounded());
ctm.line("Save", () => save(), undefined, "Ctrl+S");
ctm.line("Mute", () => toggleMute(), undefined, undefined, isMuted);ctm.sub(text, menu, icon?)
Creates a submenu trigger. Hover (300 ms) or keyboard (→ / Enter / Space) opens the nested menu.
const colorMenu = ctm.menu([
ctm.line("Red", () => setColor("red")),
ctm.line("Green", () => setColor("green")),
ctm.line("Blue", () => setColor("blue")),
]);
const menu = ctm.menu([
ctm.line("Copy", () => copy()),
ctm.sub("Color", colorMenu),
ctm.line("Delete", () => remove()),
]);ctm.devider()
Inserts a horizontal separator.
const menu = ctm.menu([
ctm.line("Cut", () => cut()),
ctm.line("Copy", () => copy()),
ctm.devider(),
ctm.line("Delete", () => remove()),
]);ctm.attach(element, lines, block?)
Binds a context menu to a DOM element.
// Static menu
ctm.attach(el, menu);
// Dynamic menu — factory called on each open
import { some, none } from "@chocbite/ts-lib-result";
ctm.attach(el, () =>
some(
ctm.menu([
ctm.line("Edit", () => edit(el)),
ctm.line("Delete", () => remove(el)),
]),
),
);
// Conditionally suppress the menu
ctm.attach(el, menu, () => el.dataset.readonly === "true");
// Return None to suppress for a specific open
ctm.attach(el, () => (isLocked(el) ? none() : some(menu)));ctm.detach(element)
Removes a previously attached context menu.
ctm.detach(el);ctm.summon(menu, element?, x?, y?, dont_cover?)
Opens a menu programmatically — no right-click needed.
// At specific coordinates, in main document
ctm.summon(menu, undefined, event.clientX, event.clientY);
// Centered on an element, in same document as buttonEl
ctm.summon(menu, buttonEl);
// Positioned beside an element, in same document as buttonEl, without covering it
ctm.summon(menu, buttonEl, undefined, undefined, true);ctm.default(lines | false)
Sets a document-wide fallback context menu shown on any right-click with no element-specific menu.
// Apply a global fallback
ctm.default(
ctm.menu([
ctm.line("View source", () => window.open("view-source:" + location.href)),
ctm.line("Inspect", () => console.log("inspect")),
]),
);
// Suppress the browser's native context menu everywhere
ctm.default(false);ctm.has(element)
Returns true if a menu is attached to the element.
if (!ctm.has(el)) {
ctm.attach(el, menu);
}Conditional Items
undefined values in a lines array are silently skipped, making conditional items clean:
ctm.menu(() => [
ctm.line("Edit", () => edit()),
canDelete ? ctm.line("Delete", () => remove()) : undefined,
isAdmin ? ctm.line("Admin Panel", () => openAdmin()) : undefined,
]);Theming
Theming via @chocbite/ts-lib-theme.
License
MIT
