Usage
Use anything you like in the default slot of the ContextMenu, and right-click on it to display the menu.
<script setup lang="ts">
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<B24ContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) bg-default text-(length:--ui-font-size-sm) aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<B24ContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) bg-default text-(length:--ui-font-size-sm) aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
Items
Use the items prop as an array of objects with the following properties:
label?: stringicon?: IconComponentavatar?: AvatarPropskbds?: string[] | KbdProps[]type?: "link" | "label" | "separator" | "checkbox"color?: ContextMenu['color']"checked?: booleandisabled?: booleanslot?: stringonSelect?: (e: Event) => voidonUpdateChecked?: (checked: boolean) => voidchildren?: ContextMenuItem[] | ContextMenuItem[][]class?: anyb24ui?: { item?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelExternalIcon?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue }
You can pass any property from the Link component such as to, target, etc.
<script setup lang="ts">
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<B24ContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<B24ContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
items prop to create separated groups of items.children array of objects with the same properties as the items prop to create a nested menu which can be controlled using the open, defaultOpen and content properties.Modal
Use the modal prop to control whether the ContextMenu blocks interaction with outside content. Defaults to true.
<script setup lang="ts">
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[]>([
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
])
</script>
<template>
<B24ContextMenu
:modal="false"
:items="items"
:b24ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[]>([
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
])
</script>
<template>
<B24ContextMenu
:modal="false"
:items="items"
:b24ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
Disabled
Use the disabled prop to disable the ContextMenu.
<script setup lang="ts">
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[]>([
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
])
</script>
<template>
<B24ContextMenu disabled :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from '@bitrix24/b24ui-nuxt'
const items = ref<ContextMenuItem[]>([
{
label: 'System'
},
{
label: 'Light'
},
{
label: 'Dark'
}
])
</script>
<template>
<B24ContextMenu disabled :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-design-outline-na-stroke) text-(length:--ui-font-size-sm) bg-default aspect-video w-72"
>
Right click here
</div>
</B24ContextMenu>
</template>
Examples
With checkbox items
You can use the type property with checkbox and use the checked / onUpdateChecked properties to control the checked state of the item.
checked state of items, it's recommended to wrap your items array inside a computed.With color items
You can use the color property to highlight certain items with a color.
Control open state 2.15+
You can control the open state by using the v-model:open directive. When opened programmatically, the menu is anchored to the trigger instead of the cursor.
defineShortcuts, you can toggle the ContextMenu by pressing O.With custom slot
Use the slot property to customize a specific item.
You will have access to the following slots:
#{{ item.slot }}#{{ item.slot }}-leading#{{ item.slot }}-label#{{ item.slot }}-trailing
#item, #item-leading, #item-label and #item-trailing slots to customize all items.Extract shortcuts
Use the extractShortcuts utility to automatically define shortcuts from menu items with a kbds property. It recursively extracts shortcuts and returns an object compatible with defineShortcuts.
<script setup lang="ts">
const items = [
[{
label: 'Show Sidebar',
kbds: ['meta', 'S'],
onSelect() {
console.log('Show Sidebar clicked')
}
}, {
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'D'],
onSelect() {
console.log('Show Toolbar clicked')
}
}, {
label: 'Collapse Pinned Tabs',
disabled: true
}], [{
label: 'Refresh the Page'
}, {
label: 'Clear Cookies and Refresh'
}, {
label: 'Clear Cache and Refresh'
}, {
type: 'separator' as const
}, {
label: 'Developer',
children: [[{
label: 'View Source',
kbds: ['option', 'meta', 'U'],
onSelect() {
console.log('View Source clicked')
}
}, {
label: 'Developer Tools',
kbds: ['option', 'meta', 'I'],
onSelect() {
console.log('Developer Tools clicked')
}
}], [{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'C'],
onSelect() {
console.log('Inspect Elements clicked')
}
}], [{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'J'],
onSelect() {
console.log('JavaScript Console clicked')
}
}]]
}]
]
defineShortcuts(extractShortcuts(items))
</script>
select function of the corresponding item.API
Props
Slots
Emits
Theme
export default {
slots: {
content: 'base-mode flex flex-col max-h-[min(var(--max-height-popup-menu),var(--reka-context-menu-content-available-height,var(--max-height-popup-menu)))] bg-(--ui-color-bg-content-primary) shadow-(--popup-window-box-shadow) rounded-(--ui-border-radius-xl) will-change-[opacity] motion-safe:data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] motion-safe:data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)] origin-(--reka-context-menu-content-transform-origin) font-[family-name:var(--ui-font-family-primary)] relative isolate px-0 py-(--menu-popup-padding) bitrix-mobile:py-0! pointer-events-auto',
viewport: 'relative w-full min-w-48 flex-1 scroll-py-1 overflow-x-hidden overflow-y-auto scrollbar-thin',
group: 'grid',
label: 'w-full h-(--popup-window-delimiter-section-height) bitrix-mobile:h-[27px] px-4.5 mt-(--menu-item-block-stack-space) bitrix-mobile:pe-0 bitrix-mobile:mt-0 bitrix-mobile:-mb-[12px] flex flex-row rtl:flex-row-reverse items-center select-none outline-none whitespace-nowrap text-start text-(length:--ui-size-sm) text-(--ui-color-base-3) bitrix-mobile:[&_[data-slot=itemLabel]]:text-(--ui-color-base-5) font-(--ui-font-weight-normal) after:ms-2 after:block after:flex-1 after:min-w-3 after:h-px after:bg-(--ui-color-divider-vibrant-default)',
separator: 'bg-(--ui-color-divider-vibrant-default) my-2 mx-4.5 h-px bitrix-mobile:my-0 bitrix-mobile:mx-0 bitrix-mobile:h-1.5',
item: 'group w-full h-9 bitrix-mobile:h-10.5 px-4.5 mt-(--menu-item-block-stack-space) bitrix-mobile:mt-0 relative flex flex-row rtl:flex-row-reverse items-center gap-2 select-none outline-none whitespace-nowrap cursor-pointer data-disabled:cursor-not-allowed data-disabled:opacity-30 text-start text-(length:--ui-font-size-md) text-(--ui-color-base-1) hover:text-(--ui-color-base-1) data-highlighted:text-(--ui-color-base-1) data-[state=open]:text-(--ui-color-base-1) hover:bg-(--ui-color-base-black-fixed)/3 dark:hover:bg-(--ui-color-base-black-fixed) active:bg-(--ui-color-base-black-fixed)/6 dark:active:bg-(--ui-color-base-black-fixed) data-highlighted:not-active:bg-(--ui-color-base-black-fixed)/3 dark:data-highlighted:not-active:bg-(--ui-color-base-black-fixed) data-[state=open]:bg-(--ui-color-base-black-fixed)/6 dark:data-[state=open]:bg-(--ui-color-base-black-fixed) bitrix-mobile:data-[state=open]:bg-(--ui-color-bg-state-hover-default) transition-colors bitrix-mobile:[[data-slot=item]+&]:border-t bitrix-mobile:border-t-(--ui-color-divider-vibrant-default) bitrix-mobile:data-highlighted:bg-(--ui-color-bg-state-hover-default)',
itemLeadingIcon: 'size-[25px] shrink-0 text-(--ui-color-base-5)',
itemLeadingAvatar: 'shrink-0 size-4 me-1',
itemLeadingAvatarSize: '2xs',
itemTrailing: 'ml-auto rtl:ml-0 rtl:mr-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0 size-[25px] text-(--ui-color-base-5)',
itemTrailingKbds: 'shrink-0 hidden lg:inline-flex items-center gap-0.5',
itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
itemTrailingKbdsSize: 'md',
itemLabel: 'max-w-60 truncate -mt-px',
itemDescription: 'max-w-60 truncate -mt-[6px] text-description text-(length:--ui-font-size-sm)',
itemLabelExternalIcon: 'inline-block size-[25px] text-(--ui-color-base-5)'
},
variants: {
color: {
'air-primary': {
item: 'style-filled'
},
'air-primary-success': {
item: 'style-filled-success'
},
'air-primary-alert': {
item: 'style-filled-alert'
},
'air-primary-copilot': {
item: 'style-filled-copilot'
},
'air-primary-warning': {
item: 'style-filled-warning'
}
},
active: {
true: {
item: '',
itemLeadingIcon: 'text-(--ui-color-accent-main-primary) hover:text-(--ui-color-accent-main-primary) group-data-[state=open]:text-(--ui-color-accent-main-primary)'
},
false: {}
},
loading: {
true: {
itemLeadingIcon: 'animate-spin'
}
}
},
compoundVariants: [],
defaultVariants: {}
}