@nicobarbieri/quickctx
Version:
Fast & easy custom context menus for your web projects.
397 lines (302 loc) • 22.7 kB
Markdown
# QuickCTX ✨
**A simple, powerful, and fast library for creating custom context menus on any HTML element.**
QuickCTX allows you to build beautiful, fully functional, and deeply nested context menus with just a few lines of code. It's lightweight, has zero dependencies, and is designed for an excellent developer experience, giving you full control over behavior and appearance.
---
[](https://www.npmjs.com/package/@nicobarbieri/quickctx)
[](https://opensource.org/licenses/MIT)
[](https://standardjs.com)
## Index
1. [Key Features](#-key-features)
2. [Installation](#-installation)
3. [Quick Start](#-quick-start)
4. [Core Concepts](#-core-concepts)
5. [Styling & Theming](#-styling--theming)
6. [Advanced Configuration & API Reference](#️-advanced-configuration--api-reference)
7. [Support the project <3](#️-support-the-project)
7. [License](#-license)
## 🚀 Key Features
* **Zero Dependencies:** Pure, vanilla JavaScript for maximum performance and minimum footprint.
* **Highly Configurable:** Control everything from triggers and animations to class names and contextual filtering.
* **Intuitive API:** Get up and running in minutes with the `createAndBindMenu` helper method.
* **Nested Submenus:** Create complex, multi-level submenus with ease.
* **Flexible Action Handling:** Use direct function binding or a clean, event-driven approach.
* **Smart & Efficient:** A central manager handles all menus, optimizing performance with a single set of global event listeners.
* **Fully Themable:** Comes with clean default styles that are a breeze to override with CSS variables or custom classes.
## 📦 Installation
Choose your preferred method to integrate QuickCTX into your project.
### 1. NPM / Yarn
For modern JavaScript projects (like React, Vue, Svelte, etc.), installing from NPM is the recommended approach.
```bash
npm install @nicobarbieri/quickctx
```
or
```bash
yarn add @nicobarbieri/quickctx
```
Then, you can import `QuickCTX` and `MenuCommand` directly into your project:
```javascript
import { QuickCTX, MenuCommand } from '@nicobarbieri/quickctx';
import '@nicobarbieri/quickctx/dist/quickctx.css'; // Import the default style
```
Importing the default stylesheet provides a solid visual foundation. You can use it as a base and easily customize the appearance by overriding its CSS variables, or replace it entirely with your own stylesheet. QuickCTX only handles the necessary internal state (like positioning and `display`) and the dynamic assignment of classes, giving you complete freedom over the final look and feel. See the [Styling & Theming](#-styling--theming) section for more details.
### 2. Browser / CDN
For direct use in HTML files or for quick prototyping, you can use the UMD bundle via a CDN like jsDelivr. Just add this to your HTML file:
```html
<!-- Add the Stylesheet in your <head> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@nicobarbieri/quickctx/dist/quickctx.css">
<!-- Add the Script at the end of your <body> -->
<script src="https://cdn.jsdelivr.net/npm/@nicobarbieri/quickctx/dist/quickctx.umd.min.js"></script>
```
This will make the `QuickCTX` and `MenuCommand` classes available globally under the `window.QuickCTX` object.
## ⚡ Quick Start
Creating your first context menu takes just a moment.
**HTML:**
```html
<div id="my-element">Right-click me!</div>
```
**JavaScript:**
```javascript
// 1. Initialize the manager
const ctxManager = new QuickCTX();
// 2. Define and bind your menu
ctxManager.createAndBindMenu({
menuId: 'myFirstMenu',
selector: '#my-element', // Bind to our div
structure: [
{ label: "Copy", iconClass: 'fa fa-copy', action: () => console.log("Copy action!") },
{ label: "Paste", iconClass: 'fa fa-paste', action: () => console.log("Paste action!") },
MenuCommand.Separator(), // Adds a dividing line
{ label: "Delete", iconClass: 'fa fa-trash', action: () => console.log("Delete action!"), disabled: true }
]
});
```
## 🧠 Core Concepts
### 1. The `QuickCTX` Manager
First, you create a single instance of the `QuickCTX` class. This "manager" will handle all your menus and their events. You can pass a global configuration object during initialization to set the default behavior for all menus.
```javascript
const ctxManager = new QuickCTX({
defaultTrigger: 'contextmenu',
globalFilterStrategy: 'disable'
});
```
All available configuration options are detailed in the [Advanced Configuration & API Reference](#️-advanced-configuration--api-reference) section.
### 2. Creating & Configuring Menus
The `createAndBindMenu` method is your primary tool. It takes a single configuration object to define a menu's content and behavior, and automatically binds it to the specified HTML elements.
```javascript
ctxManager.createAndBindMenu({
menuId: 'imageMenu',
selector: '[data-type="image"]',
triggerEvent: 'click', // <- Override the global default for this menu only
headerText: 'Image Actions',
structure: [ /* ... command objects ... */ ]
});
```
#### Menu Configuration Options
| Property | Type | Description |
| --------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `menuId` | `string` | **Required.** A unique ID for this menu configuration. |
| `structure` | `Array<object>` | **Required.** The array of command objects that defines the menu's items. See table below. |
| `selector` | `string \| HTMLElement \| Array` | A CSS selector or HTML element(s) to automatically bind this menu to. |
| `defaultTargetType` | `string` | A default "type" to apply to all commands in this menu, useful for contextual filtering. |
| `headerText` | `string` | Optional text for the menu's header. Can use `{type}` for dynamic replacement (e.g., "Actions for {type}"). |
| `triggerEvent` | `string` | Overrides the global `defaultTrigger` for this specific menu. |
| `filterStrategy` | `string` | Overrides the global `globalFilterStrategy` for this specific menu. |
#### Command Object Properties (`structure` array items)
| Property | Type | Description |
| ------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `label` | `string` | **Required.** The visible text of the menu item. |
| `action` | `Function \| string` | The function to execute, or the name of a pre-registered action. |
| `iconClass` | `string` | Optional. CSS classes for an icon (e.g., `'fa solid fa-star'`). |
| `disabled` | `boolean` | Optional. If `true`, the item is visible but not clickable. Defaults to `false`. |
| `type` | `'action'\|'sublist'\|'separator'` | Optional. Defaults to `'action'`. Use `'sublist'` for nested menus. |
| `subCommands` | `Array<object>` | If `type` is `'sublist'`, this holds the structure for the nested submenu. |
| `targetTypes` | `Array<string>` | Optional. An array of strings to specify for which element types this command should be active. Defaults to `['*']` (all). |
For separators, you can simply use the static helper `MenuCommand.Separator("Optional Subheader")`. Providing content turns the separator into a sub-header.
### 3. Powerful Contextual Logic with `targetTypes`
This is where QuickCTX shines. You can use a single menu configuration for multiple types of elements and show, hide, or disable commands dynamically.
First, assign types to your HTML elements:
```html
<div class="item" data-custom-ctxmenu="fileMenu" data-custom-ctxmenu-type="folder">Folder</div>
<div class="item" data-custom-ctxmenu="fileMenu" data-custom-ctxmenu-type="image">Image</div>
<div class="item" data-custom-ctxmenu="fileMenu" data-custom-ctxmenu-type="text">Text File</div>
```
Now, define your menu structure using `targetTypes`.
```javascript
ctxManager.createAndBindMenu({
menuId: 'fileMenu',
selector: '.item',
filterStrategy: 'disable', // 'disable' will grey out irrelevant items
structure: [
// This command is active for ALL types
{ label: "Properties", action: showProperties, targetTypes: ['*'] },
// This command is ONLY active for 'folder' type elements
{ label: "Open Folder", action: openFolder, targetTypes: ['folder'] },
// This command is active for 'image' and 'text' types
{ label: "Edit", action: editFile, targetTypes: ['image', 'text'] },
MenuCommand.Separator(),
{ label: "Delete", action: deleteItem, targetTypes: ['*'] }
]
});
```
When a user interacts with a "folder", the "Open Folder" command will be enabled, while "Edit" will be disabled (or hidden, if `filterStrategy` is set to `'hide'`). This allows for incredibly powerful and clean contextual logic.
### 4. Handling Actions
#### Direct Binding
Pass a function directly to the `action` property. The function receives an object containing the DOM `target` and the `command` configuration.
```javascript
{
label: "Inspect",
action: (event) => {
console.log(`Action executed on element:`, event.target);
console.log(`Command details:`, event.command.label);
}
}
```
#### Event-Driven Approach
For more decoupled code, listen for the `QuickCTXActionSelected` custom event, which is dispatched on the target element whenever an action is executed.
```javascript
document.addEventListener('QuickCTXActionSelected', (e) => {
const { commandId, commandLabel, targetElement, targetType } = e.detail;
console.log(`User selected '${commandLabel}' on an element of type '${targetType}'`);
});
```
## 🎨 Styling & Theming
You have full control over the appearance of your menus.
#### Method 1: Overriding CSS Variables (Easy & Recommended)
This is the simplest way to quickly theme your menus. The default stylesheet is built with CSS custom properties (variables) that you can easily override.
**Example:** Let's replicate the theme from the project's example page.
In your main CSS file (like `index.css`), you can define your site's color palette, and then create a ruleset for `.quickctx-container` that maps your theme to QuickCTX's variables.
```css
/* 1. Your site's main theme variables */
body {
--primary-color: #1B9AAA;
--secondary-color-lighter: hsl(43, 55%, 95%);
--element-background-hover: #DDDBCB;
--text-color: #101010;
--border-color: rgba(0, 0, 0, 0.125);
--basic-shadow: 0px 2px 8px rgba(0,0,0,0.1);
}
/* 2. Map your theme to QuickCTX's variables */
.quickctx-container {
--quickctx-background: var(--secondary-color-lighter);
--quickctx-element-background-hover: var(--element-background-hover);
--quickctx-text-color: var(--text-color);
--quickctx-border-color: var(--border-color);
--quickctx-header-text-color: var(--primary-color);
--quickctx-shadow: var(--basic-shadow);
--quickctx-font-family: 'Inter', sans-serif;
--quickctx-border-radius: 8px;
}
```
#### Method 2: Overriding CSS Classes (Advanced)
For deeper integration or to use a different styling methodology (like Tailwind CSS), you can provide your own class names via the `classes` option in the constructor.
**Hybrid Approach (Recommended for overrides):**
A great strategy is to keep the default class and add your own custom class for overrides. This way, you inherit the base functionality and only style what you need.
**JavaScript:**
```javascript
// In your QuickCTX initialization
const ctxManager = new QuickCTX({
classes: {
// Keep the default class and add our custom one
container: 'quickctx-container ctxstyle-override'
}
});
```
**CSS:**
Now in your CSS, you can target `.ctxstyle-override` to add or change styles with high specificity.
```css
/* Your custom override class from the example page */
.dark-theme .quickctx-container.ctxstyle-override {
--quickctx-background: var(--element-background);
--quickctx-header-background: transparent;
/* Add a modern backdrop filter for dark mode */
backdrop-filter: blur(10px) brightness(0.8);
}
```
**Total Replacement Approach:**
You can also completely replace the default classes if you plan to write all the styles from scratch.
```javascript
const ctxManager = new QuickCTX({
classes: {
container: 'shadow-lg bg-white rounded-md border',
item: 'px-4 py-2 hover:bg-blue-500',
disabled: 'opacity-50 cursor-not-allowed'
}
});
```
*Note: If you replace the classes entirely, you will be responsible for recreating all base styles, including, for example, animations handled by classes like `.quickctx--open` and `.quickctx--closing`.*
The full list of overridable classes and CSS variables are available in the [Advanced Configuration & API Reference](#️-advanced-configuration--api-reference) section.
## ⚙️ Advanced Configuration & API Reference
You can pass a configuration object to the `new QuickCTX(options)` constructor to set global defaults. Many of these can be overridden on a per-menu basis in `createAndBindMenu`.
#### Main `options` Object
| Property | Type | Default | Description |
| ------------------------ | -------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
| `defaultTrigger` | `string` | `'contextmenu'` | Default trigger event. Options: `'contextmenu'`, `'click'`, `'dblclick'`, `'hover'`. |
| `globalFilterStrategy` | `string` | `'hide'` | What to do with commands that don't match the element type. Options: `'hide'`, `'disable'`. |
| `overlapStrategy` | `string` | `'closest'` | For nested elements, which menu to show. Options: `'closest'`, `'deepest'`. |
| `animations` | `object` | `{...}` | An object to control all animation timings. See table below. |
| `classes` | `object` | `{...}` | An object to override default CSS class names. See table below. |
#### `animations` Object
| Property | Type | Default | Description |
| ----------------------- | -------- | ------- | ------------------------------------------------------------------ |
| `menuOpenDuration` | `number` | `200` | Duration in ms of the main menu opening animation. |
| `menuCloseDuration` | `number` | `200` | Duration in ms of the main menu closing animation. |
| `hoverMenuOpenDelay` | `number` | `450` | Delay in ms before a hover-triggered menu opens. |
| `hoverMenuCloseDelay` | `number` | `300` | Delay in ms before a hover-triggered menu closes after mouseout. |
| `submenuOpenDelay` | `number` | `150` | Delay in ms for opening submenus on hover. |
| `submenuCloseDelay` | `number` | `200` | Delay in ms before closing submenus after mouse leave. |
#### `classes` Object
| Property | Default | Description |
| ------------------ | ------------------------------ | ----------------------------------------------- |
| `container` | `'quickctx-container'` | The main menu container `<div>`. |
| `header` | `'quickctx-header'` | The header `<div>`. |
| `list` | `'quickctx-list'` | The `<ul>` element holding the commands. |
| `item` | `'quickctx-item'` | The `<li>` element for a single command. |
| `separator` | `'quickctx-separator'` | A `<li>` used as a separator line. |
| `sublist` | `'quickctx-sublist'` | A class added to submenu containers (`<div>`). |
| `sublistCommand` | `'quickctx-sublist-command'` | A class added to `<li>` items that open submenus. |
| `disabled` | `'quickctx-item--disabled'` | Added to disabled `<li>` items. |
| `hidden` | `'quickctx-item--hidden'` | (Not used by default, available for custom CSS) |
| `icon` | `'quickctx-icon'` | The `<span>` wrapper for an icon. |
| `opening` | `'quickctx--opening'` | State class during the opening animation. |
| `open` | `'quickctx--open'` | State class when the menu is fully open. |
| `closing` | `'quickctx--closing'` | State class during the closing animation. |
#### All CSS Variables
| Variable | Default | Description |
| ------------------------------------- | ---------------- | ----------------------------------------- |
| `--quickctx-background` | `#ffffff` | Main background color of the menu. |
| `--quickctx-element-background-hover` | `#f0f0f0` | Background of items on hover. |
| `--quickctx-header-background` | `#f7f7f7` | Background color of the header. |
| `--quickctx-header-text-color` | `#555555` | Text color of the header. |
| `--quickctx-text-color` | `#333333` | Default text color for items. |
| `--quickctx-text-color-hover` | `#000000` | Text color for items on hover. |
| `--quickctx-text-color-disabled` | `#aaaaaa` | Text color for disabled items. |
| `--quickctx-border-size` | `1px` | Border width for menu and separators. |
| `--quickctx-border-color` | `#cccccc` | Border color for the menu. |
| `--quickctx-container-padding` | `4px 0` | Padding for the main container. |
| `--quickctx-element-padding` | `8px 12px 8px 24px` | Padding for individual menu items. |
| `--quickctx-header-padding` | `6px 12px` | Padding for the header. |
| `--quickctx-border-radius` | `4px` | Corner radius for the menu. |
| `--quickctx-min-width` | `180px` | Minimum width of the menu. |
| `--quickctx-font-family` | `Arial, sans-serif` | Font family used in the menu. |
| `--quickctx-font-size` | `14px` | Base font size. |
| `--quickctx-shadow` | `2px 2px...` | Box shadow for the menu. |
#### Public Methods
| Method | Description |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `createAndBindMenu(menuOptions)` | The primary method to create, configure, and bind a menu in a single call. |
| `addMenuConfiguration(config)` | Adds a menu configuration to the manager without binding it to any elements. |
| `updateMenuConfiguration(menuId, newOptions)` | Updates an existing menu configuration at runtime (e.g., to change its trigger). |
| `bindMenuToElements(selector, menuId, type)` | Binds an existing menu configuration to one or more elements. |
| `unbindMenuFromElements(selector)` | Removes menu bindings from one or more elements. |
| `registerAction(name, callback)` | Registers a named action that can be referenced by string in the `structure` array. |
| `setLogger(loggerFunction)` | Sets a custom function to handle library logs (e.g., to display them in a custom UI element). |
| `setLoggerIsEnabled(boolean)` | Enables or disables logging to the console or the custom logger. |
| `updateOptions(newOptions)` | Updates the manager's configuration at runtime. |
## ❤️ Support the Project
If you find QuickCTX useful and want to support its development, you could offer me a [ko-fi](https://ko-fi.com/V7V11GPBTJ)! Any support is greatly appreciated. ;)
<br>
<div align="center">
[](https://ko-fi.com/V7V11GPBTJ)
</div>
## 📄 License
QuickCTX is open-source software licensed under the [MIT License](LICENSE).