@context-menu-kit/vue
v0.1.1
Published
Vue components for context-menu-kit.
Downloads
324
Maintainers
Readme
context-menu-kit
A lightweight UI kit for customizable context menus.
context-menu-kit provides a framework-agnostic core package, plus ready-to-use React and Vue integrations for building flexible right-click context menus.
Features
- Headless architecture
- Framework-agnostic core
- React support
- Vue support
- Multiple context menu regions
- Different menus for different regions
- Different UI for different regions
- Built-in default renderer
- Fully customizable item rendering
- Fully customizable menu body rendering
- Custom styling via CSS classes
Packages
@context-menu-kit/core
Framework-agnostic state and event helpers. Use this package when you want to build your own renderer.
@context-menu-kit/react
React components and render props built on top of the core package.
@context-menu-kit/vue
Vue components and slots built on top of the core package.
Installation
Install the core package only if you want to build your own renderer:
pnpm add @context-menu-kit/coreInstall React support:
pnpm add @context-menu-kit/core @context-menu-kit/reactInstall Vue support:
pnpm add @context-menu-kit/core @context-menu-kit/vueQuick Start
React
import { ContextMenu, ContextMenuProvider } from "@context-menu-kit/react"
import "@context-menu-kit/react/styles.css"
const items = [
{
id: "copy",
label: "Copy",
onClick: () => {
console.log("Copy clicked")
}
},
{
id: "paste",
label: "Paste",
onClick: () => {
console.log("Paste clicked")
}
}
]
export function App() {
return (
<ContextMenuProvider>
<ContextMenu items={items}>
<div>Right click here</div>
</ContextMenu>
</ContextMenuProvider>
)
}Vue
<script setup lang="ts">
import { ContextMenu, ContextMenuProvider } from "@context-menu-kit/vue"
import "@context-menu-kit/vue/styles.css"
const items = [
{
id: "copy",
label: "Copy",
onClick: () => {
console.log("Copy clicked")
}
},
{
id: "paste",
label: "Paste",
onClick: () => {
console.log("Paste clicked")
}
}
]
</script>
<template>
<ContextMenuProvider>
<ContextMenu :items="items">
<div>Right click here</div>
</ContextMenu>
</ContextMenuProvider>
</template>Core API
The core package stores the active context menu state and notifies subscribers whenever the menu opens or closes.
ContextMenuItem
type ContextMenuItem<T = unknown> = {
id: string
label?: string
disabled?: boolean
children?: ContextMenuItem<T>[]
onClick?: (item: ContextMenuItem) => void
data?: T
}Properties
| Property | Description |
| ---------- | -------------------------------------------------------------- |
| id | Required unique item id. |
| label | Optional text used by the default renderers. |
| disabled | Prevents the default renderer from triggering the item. |
| children | Reserved for nested menu data. |
| onClick | Called when the user selects an enabled menu item. |
| data | Custom user data for renderers, slots, and app-specific logic. |
openContextMenu(options)
import { openContextMenu } from "@context-menu-kit/core"
openContextMenu({
id: "file-menu",
x: event.clientX,
y: event.clientY,
items,
event,
meta: {
source: "file-area"
}
})ContextMenuOpenOptions
type ContextMenuOpenOptions = {
id: string
x: number
y: number
items: ContextMenuItem[]
meta?: Record<string, unknown>
event?: MouseEvent
}closeContextMenu()
import { closeContextMenu } from "@context-menu-kit/core"
closeContextMenu()getContextMenuState()
import { getContextMenuState } from "@context-menu-kit/core"
const state = getContextMenuState()subscribeContextMenu(listener)
import { subscribeContextMenu } from "@context-menu-kit/core"
const unsubscribe = subscribeContextMenu((state) => {
console.log(state)
})
unsubscribe()ContextMenuState
type ContextMenuState = {
isOpen: boolean
id: string | null
x: number
y: number
items: ContextMenuItem[]
event?: MouseEvent
meta?: Record<string, unknown>
}React API
The React package provides components and render props for common usage.
Imports
import {
ContextMenu,
ContextMenuProvider,
closeContextMenu,
type ContextMenuItem,
type ContextMenuProps,
type ContextMenuRenderOptions,
type RenderItemProps,
type RenderBodyProps
} from "@context-menu-kit/react"
import "@context-menu-kit/react/styles.css"<ContextMenuProvider>
Mount this once around the area that should support context menus. It automatically renders the global menu renderer.
<ContextMenuProvider>
<App />
</ContextMenuProvider><ContextMenu>
type ContextMenuProps<T = unknown> = {
items: ContextMenuItem<T>[]
children: ReactNode
bodyClass?: string
itemClass?: string
renderItem?: (props: RenderItemProps<T>) => ReactNode
renderBody?: (props: RenderBodyProps<T>) => ReactNode
}Custom Item Renderer
<ContextMenu
items={items}
renderItem={({ item, close }) => (
<button
disabled={item.disabled}
onClick={() => {
if (item.disabled) return
item.onClick?.(item)
close()
}}
>
{item.label}
</button>
)}
>
<div>Right click here</div>
</ContextMenu>Custom Menu Renderer
<ContextMenu
items={items}
renderBody={({ items, close }) => (
<div className="my-menu">
{items.map((item) => (
<button
key={item.id}
disabled={item.disabled}
onClick={() => {
if (item.disabled) return
item.onClick?.(item)
close()
}}
>
{item.label}
</button>
))}
</div>
)}
>
<div>Right click here</div>
</ContextMenu>Vue API
The Vue package provides components and slots for common usage.
Imports
import {
ContextMenu,
ContextMenuProvider,
closeContextMenu,
type ContextMenuItem,
type ContextMenuProps,
type ContextMenuSlotProps,
type ContextMenuBodySlotProps
} from "@context-menu-kit/vue"
import "@context-menu-kit/vue/styles.css"<ContextMenuProvider>
Mount this once around the area that should support context menus. It automatically renders the global menu renderer.
<ContextMenuProvider>
<App />
</ContextMenuProvider><ContextMenu>
interface ContextMenuProps<T = unknown> {
items: ContextMenuItem<T>[]
bodyClass?: string
itemClass?: string
}Custom Item Slot
<ContextMenu :items="items">
<template #item="{ item, close }">
<button
:disabled="item.disabled"
type="button"
@click="
() => {
if (item.disabled) return
item.onClick?.(item)
close()
}
"
>
{{ item.label }}
</button>
</template>
<div>Right click here</div>
</ContextMenu>Custom Body Slot
<ContextMenu :items="items">
<template #body="{ items, close }">
<div class="my-menu">
<button
v-for="item in items"
:key="item.id"
:disabled="item.disabled"
type="button"
@click="
() => {
if (item.disabled) return
item.onClick?.(item)
close()
}
"
>
{{ item.label }}
</button>
</div>
</template>
<div>Right click here</div>
</ContextMenu>Styles
React and Vue packages both ship a default stylesheet. Import the stylesheet once in your app, or pass custom class names to replace the default body and item classes.
React Styles
import "@context-menu-kit/react/styles.css"Vue Styles
import "@context-menu-kit/vue/styles.css"Default Classes
.cmk-body
.cmk-item
.cmk-item:hover
.cmk-item-disabledCustom Classes
React
<ContextMenu items={items} bodyClass="my-menu" itemClass="my-menu-item">
<div>Right click here</div>
</ContextMenu>Vue
<ContextMenu :items="items" body-class="my-menu" item-class="my-menu-item">
<div>Right click here</div>
</ContextMenu>Monorepo Structure
packages/
├─ core
├─ react
└─ vueRoadmap
- [x] Core
- [x] React
- [x] Vue
- [ ] Nested menus
- [ ] Keyboard navigation
- [ ] Accessibility improvements
License
MIT
