Context Menu
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ref } from 'vue'
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@oku-ui/primitives'
const checkboxOne = ref(false)
const checkboxTwo = ref(false)
const person = ref('pedro')
function handleClick() {
// eslint-disable-next-line no-alert
alert('hello!')
}
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger
as-child
class="block border-2 border-white border-dashed text-white rounded text-[15px] select-none py-[45px] w-[300px] text-center"
>
<span> Right click here. </span>
</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent
class="min-w-[220px] z-30 bg-white outline-none rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="5"
>
<ContextMenuItem
value="New Tab"
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
@click="handleClick"
>
New Tab <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+T
</div>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-indigo4 data-[state=open]:text-mauve12 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1 data-[highlighted]:data-[state=open]:bg-indigo9 data-[highlighted]:data-[state=open]:text-indigo1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2"
:align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuItem
value="New Window"
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
New Window <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+N
</div>
</ContextMenuItem>
<ContextMenuItem
value="New Private Window"
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
disabled
>
New Private Window <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⇧+⌘+N
</div>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more tools"
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none w-full outline-none data-[state=open]:bg-indigo4 data-[state=open]:text-mauve12 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1 data-[highlighted]:data-[state=open]:bg-indigo9 data-[highlighted]:data-[state=open]:text-indigo1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2"
:align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Developer Tools
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-indigo4 data-[state=open]:text-mauve12 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1 data-[highlighted]:data-[state=open]:bg-indigo9 data-[highlighted]:data-[state=open]:text-indigo1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2"
:align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Developer Tools
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-indigo4 data-[state=open]:text-mauve12 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1 data-[highlighted]:data-[state=open]:bg-indigo9 data-[highlighted]:data-[state=open]:text-indigo1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2"
:align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuCheckboxItem
v-model="checkboxOne"
class="group text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator> Show Bookmarks <div
class="ml-auto pl-[20px] text-mauve12 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+B
</div>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem
v-model="checkboxTwo"
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator> Show Full URLs
</ContextMenuCheckboxItem>
<ContextMenuSeparator class="h-[1px] bg-indigo6 m-[5px]" />
<ContextMenuLabel class="pl-[25px] text-xs leading-[25px] text-mauve12">
People
</ContextMenuLabel>
<ContextMenuRadioGroup v-model="person">
<ContextMenuRadioItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
value="pedro"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</ContextMenuItemIndicator> Pedro Duarte
</ContextMenuRadioItem>
<ContextMenuRadioItem
class="text-[13px] leading-none text-mauve12 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-indigo9 data-[highlighted]:text-indigo1"
value="colm"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</ContextMenuItemIndicator> Colm Tuite
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
Features
- Supports submenus with configurable reading direction.
- Supports items, labels, groups of items.
- Supports checkable items (single or multiple) with optional indeterminate state.
- Supports modal and non-modal modes.
- Customize side, alignment, offsets, collision handling.
- Focus is fully managed.
- Full keyboard navigation.
- Typeahead support.
- Dismissing and layering behavior is highly customizable.
- Triggers with a long-press on touch devices
Installation
Install the component from your command line.
$ npm add @oku-ui/primitives
Anatomy
Import all parts and piece them together.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger />
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuLabel />
<ContextMenuItem />
<ContextMenuGroup>
<ContextMenuItem />
</ContextMenuGroup>
<ContextMenuCheckboxItem>
<ContextMenuItemIndicator />
</ContextMenuCheckboxItem>
<ContextMenuRadioGroup>
<ContextMenuRadioItem>
<ContextMenuItemIndicator />
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
<ContextMenuSub>
<ContextMenuSubTrigger />
<ContextMenuPortal>
<ContextMenuSubContent />
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator />
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
API Reference
Adheres to the Menu WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Root
Contains all the parts of a context menu.
Prop | Default | Type |
---|---|---|
dir | 'ltr' | 'rtl' | |
modal | boolean |
Emit | Payload |
---|---|
update:open | [open: boolean] |
Trigger
The area that opens the context menu. Wrap it around the target you want the context menu to open from when right-clicking (or using the relevant keyboard shortcuts).
Prop | Default | Type |
---|---|---|
as | 'div' | object | AsTag |
disabled | boolean |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
Portal
When used, portals the content part into the body
.
Content
The component that pops out in an open context menu.
Prop | Default | Type |
---|---|---|
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-context-menu-content-available-width |
The remaining width between the trigger and the boundary edge
|
--radix-context-menu-content-available-height |
The remaining height between the trigger and the boundary edge
|
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
Arrow
An optional arrow element to render alongside a submenu. This can be used to help visually link the trigger item with the ContextMenu.Content
. Must be rendered inside ContextMenu.Content
.
Item
The component that contains the context menu items.
Data Attribute | Value |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Group
Used to group multiple ContextMenu.Item
s.
Label
Used to render a label. It won't be focusable using arrow keys.
CheckboxItem
An item that can be controlled and rendered like a checkbox.
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
RadioGroup
Used to group multiple ContextMenu.RadioItem
s.
RadioItem
An item that can be controlled and rendered like a radio.
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
ItemIndicator
Renders when the parent ContextMenu.CheckboxItem
or ContextMenu.RadioItem
is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
Separator
Used to visually separate items in the context menu.
Sub
Contains all the parts of a submenu.
Prop | Default | Type |
---|---|---|
defaultOpen | boolean | |
open | boolean |
Emit | Payload |
---|---|
update:open | [isOpen: boolean] |
SubTrigger
An item that opens a submenu. Must be rendered inside ContextMenu.Sub
.
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
SubContent
The component that pops out when a submenu is open. Must be rendered inside ContextMenu.Sub
.
Prop | Default | Type |
---|---|---|
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-context-menu-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-context-menu-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
Examples
With submenus
You can create submenus by using ContextMenuSub
in combination with its parts.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger>Sub menu →</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent>
<ContextMenuItem>Sub menu item</ContextMenuItem>
<ContextMenuItem>Sub menu item</ContextMenuItem>
<ContextMenuArrow />
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With disabled items
You can add special styles to disabled items via the data-disabled
attribute.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem
class="ContextMenuItem"
disabled
>
…
</ContextMenuItem>
<ContextMenuItem class="ContextMenuItem">
…
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuItem[data-disabled] {
color: gainsboro;
}
With separators
Use the Separator
part to add a separator between items.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With labels
Use the Label
part to help label a section.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuTrigger,
} from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuLabel>Label</ContextMenuLabel>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With checkbox items
Use the CheckboxItem
part to add an item that can be checked.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@oku-ui/primitives'
import { Icon } from '@iconify/vue'
const checked = ref(true)
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuCheckboxItem v-model="checked">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Checkbox item
</ContextMenuCheckboxItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With radio items
Use the RadioGroup
and RadioItem
parts to add an item that can be checked amongst others.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@oku-ui/primitives'
import { Icon } from '@iconify/vue'
const color = ref('blue')
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuRadioGroup v-model="color">
<ContextMenuRadioItem value="red">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Red
</ContextMenuRadioItem>
<ContextMenuRadioItem value="blue">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Blue
</ContextMenuRadioItem>
<ContextMenuRadioItem value="green">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Green
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With complex items
You can add extra decorative elements in the Item
parts, such as images.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>
<img src="…">
Adolfo Hess
</ContextMenuItem>
<ContextMenuItem>
<img src="…">
Miyah Myles
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
Constrain the content/sub-content size
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-context-menu-trigger-width
and --radix-context-menu-content-available-height
to support this. Use them to constrain the content dimensions.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
width: var(--radix-context-menu-trigger-width);
max-height: var(--radix-context-menu-content-available-height);
}
Origin-aware animations
We expose a CSS custom property --radix-context-menu-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
transform-origin: var(--radix-context-menu-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
Collision-aware animations
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from '@oku-ui/primitives'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.ContextMenuContent[data-side="top"] {
animation-name: slideUp;
}
.ContextMenuContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Accessibility
Uses roving tabindex to manage focus movement among menu items.
Keyboard Interactions
Key | Description |
---|---|
Space | Activates the focused item. |
Enter | Activates the focused item. |
ArrowDown | Moves focus to the next item. |
ArrowUp | Moves focus to the previous item. |
ArrowRightArrowLeft |
When focus is on ContextMenu.SubTrigger , opens or closes
the submenu depending on reading direction.
|
Esc | Closes the context menu |