Context Menu
A menu of actions that a right click or long press on an area opens, at the pointer.
- v1.0.0
- WAI-ARIA: Menu
- SSR
- Zoneless
- 3.0 kB gzip
- +20.3 kB Angular CDK, shared
import { Component, signal } from '@angular/core';
import { NdIcon } from '@ndop/core';
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@/ui/context-menu/context-menu';
/**
* Browser-style actions on an area: shortcuts, a disabled item, a submenu,
* a checkbox and a group of radios. The area is focusable and named, so
* Shift + F10 opens the menu from the keyboard.
*/
@Component({
selector: 'example-context-menu-demo',
imports: [
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
NdIcon,
],
template: `
<div
[ndContextMenuTrigger]="menu"
tabindex="0"
aria-label="Page preview"
class="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
Right click here
</div>
<ng-template #menu>
<div ndContextMenuContent class="w-52">
<div ndContextMenuItem inset>
Back <span ndContextMenuShortcut>⌘[</span>
</div>
<div ndContextMenuItem inset disabled>
Forward <span ndContextMenuShortcut>⌘]</span>
</div>
<div ndContextMenuItem inset>
Reload <span ndContextMenuShortcut>⌘R</span>
</div>
<div [ndContextMenuSubTrigger]="tools" inset>
More tools
<svg ndIcon="chevron-right" class="ms-auto rtl:rotate-180"></svg>
</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuCheckboxItem [(checked)]="bookmarks">
<span ndContextMenuItemIndicator><svg ndIcon="check"></svg></span>
Show bookmarks
</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuLabel inset id="context-menu-demo-people">People</div>
<div
ndContextMenuRadioGroup
aria-labelledby="context-menu-demo-people"
[(value)]="person"
>
@for (name of people; track name) {
<div ndContextMenuRadioItem [value]="name">
<span ndContextMenuItemIndicator>
<svg ndIcon="circle" class="size-2 fill-current"></svg>
</span>
{{ name }}
</div>
}
</div>
</div>
</ng-template>
<ng-template #tools>
<div ndContextMenuSubContent class="w-44">
<div ndContextMenuItem>Save page…</div>
<div ndContextMenuItem>Developer tools</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuItem variant="destructive">Delete</div>
</div>
</ng-template>
`,
})
export class ContextMenuDemoExample {
protected readonly bookmarks = signal(true);
protected readonly people = ['Pedro Duarte', 'Colm Tuite'];
protected readonly person = signal<string | null>('Pedro Duarte');
}
A context menu lists actions for an area, opened at the pointer with a right click or a long press, or at the area's corner with Shift + F10 or the keyboard's menu key. Inside, it works like any menu: arrow keys, typing, Enter and Escape, which returns focus to the area. Items can be checkboxes, radios or submenus.
Installation
npx @ndop/cli add context-menupnpm dlx @ndop/cli add context-menuyarn dlx @ndop/cli add context-menubunx @ndop/cli add context-menuInstall the dependencies
npm install @ndop/primitives @ndop/corepnpm add @ndop/primitives @ndop/coreyarn add @ndop/primitives @ndop/corebun add @ndop/primitives @ndop/coreCopy the code into your project
At the paths your
ndop.jsonaliases point to; these use the defaults.src/app/ui/context-menu/context-menu.tsimport { booleanAttribute, computed, Directive, input } from '@angular/core'; import { NdContextMenuTrigger } from '@ndop/primitives/context-menu'; import { NdMenuContent, NdMenuGroup, NdMenuItem, NdMenuItemCheckbox, NdMenuItemIndicator, NdMenuItemRadio, NdMenuLabel, NdMenuRadioGroup, NdMenuSeparator, NdMenuSubTrigger, } from '@ndop/primitives/menu'; import { cn } from '@/lib/utils'; /** * An area with a context menu (`@ndop/primitives/context-menu`): its value * is the menu's `ng-template`, opened at the pointer on a right click or * long press, or at its corner on Shift + F10. Make it focusable and named. * * @example * ```html * <div [ndContextMenuTrigger]="menu" tabindex="0" aria-label="Canvas">…</div> * ``` */ @Directive({ selector: '[ndContextMenuTrigger]', hostDirectives: [ { directive: NdContextMenuTrigger, inputs: ['ndContextMenuTrigger', 'disabled'], }, ], host: { 'data-slot': 'context-menu-trigger' }, }) export class ContextMenuTrigger {} /** * A context menu's list (`@ndop/primitives/menu`), in the `ng-template` a * `[ndContextMenuTrigger]` area opens at the pointer on a right click or * long press, or at its corner on Shift + F10. * * @example * ```html * <div ndContextMenuContent><div ndContextMenuItem>Back</div></div> * ``` */ @Directive({ selector: '[ndContextMenuContent]', hostDirectives: [{ directive: NdMenuContent, outputs: ['closed'] }], host: { 'data-slot': 'context-menu-content', '[class]': 'classes()' }, }) export class ContextMenuContent { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'z-50 max-h-[80vh] min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=start]:slide-in-from-end-2 data-[side=end]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', this.class(), ), ); } /** * A submenu, opened beside its sub-trigger: at the end side, so on the left * in right-to-left layouts. * * @example * ```html * <div ndContextMenuSubContent>…</div> * ``` */ @Directive({ selector: '[ndContextMenuSubContent]', hostDirectives: [{ directive: NdMenuContent, outputs: ['closed'] }], host: { 'data-slot': 'context-menu-sub-content', '[class]': 'classes()' }, }) export class ContextMenuSubContent { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=start]:slide-in-from-end-2 data-[side=end]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', this.class(), ), ); } /** * Groups related items (`role="group"`). Name it with `aria-labelledby` * pointing at a label. * * @example * ```html * <div ndContextMenuGroup aria-labelledby="account">…</div> * ``` */ @Directive({ selector: '[ndContextMenuGroup]', hostDirectives: [NdMenuGroup], host: { 'data-slot': 'context-menu-group' }, }) export class ContextMenuGroup {} /** * One action (`role="menuitem"`): activating it emits `triggered` and * closes the menu. Disabled items stay focusable and do nothing. Use a * `div`: on a `button`, Space would close the menu. * * @example * ```html * <div ndContextMenuItem (triggered)="edit()">Edit</div> * ``` */ @Directive({ selector: '[ndContextMenuItem]', hostDirectives: [ { directive: NdMenuItem, inputs: ['disabled'], outputs: ['triggered'] }, ], host: { 'data-slot': 'context-menu-item', '[attr.data-inset]': 'inset() ? "" : null', '[attr.data-variant]': 'variant()', '[class]': 'classes()', }, }) export class ContextMenuItem { /** Indents it to line up with items that have an indicator. */ readonly inset = input(false, { transform: booleanAttribute }); /** `destructive` colors a dangerous action, such as Delete. */ readonly variant = input<'default' | 'destructive'>('default'); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:ps-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!", this.class(), ), ); } /** * An item that is on or off (`role="menuitemcheckbox"`), bound with * `[(checked)]`. Put an indicator with a check icon in it. * * @example * ```html * <div ndContextMenuCheckboxItem [(checked)]="statusBar">…</div> * ``` */ @Directive({ selector: '[ndContextMenuCheckboxItem]', hostDirectives: [ { directive: NdMenuItemCheckbox, inputs: ['checked', 'disabled'], outputs: ['checkedChange', 'triggered'], }, ], host: { 'data-slot': 'context-menu-checkbox-item', '[class]': 'classes()' }, }) export class ContextMenuCheckboxItem { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pe-2 ps-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", this.class(), ), ); } /** * Groups radio items (`role="group"`) and holds the checked value, bound * with `[(value)]`. * * @example * ```html * <div ndContextMenuRadioGroup aria-label="Panel position" [(value)]="position">…</div> * ``` */ @Directive({ selector: '[ndContextMenuRadioGroup]', hostDirectives: [ { directive: NdMenuRadioGroup, inputs: ['value'], outputs: ['valueChange'], }, ], host: { 'data-slot': 'context-menu-radio-group' }, }) export class ContextMenuRadioGroup {} /** * One choice of a radio group (`role="menuitemradio"`). Put an indicator * with a small filled circle in it. * * @example * ```html * <div ndContextMenuRadioItem value="top">Top</div> * ``` */ @Directive({ selector: '[ndContextMenuRadioItem]', hostDirectives: [ { directive: NdMenuItemRadio, inputs: ['value', 'disabled'], outputs: ['triggered'], }, ], host: { 'data-slot': 'context-menu-radio-item', '[class]': 'classes()' }, }) export class ContextMenuRadioItem { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pe-2 ps-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", this.class(), ), ); } /** * The check or dot at the start of a checkbox or radio item, shown while it * is checked. * * @example * ```html * <span ndContextMenuItemIndicator><svg ndIcon="check"></svg></span> * ``` */ @Directive({ selector: '[ndContextMenuItemIndicator]', hostDirectives: [NdMenuItemIndicator], host: { 'data-slot': 'context-menu-item-indicator', '[class]': 'classes()' }, }) export class ContextMenuItemIndicator { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'pointer-events-none absolute start-2 flex size-3.5 items-center justify-center', this.class(), ), ); } /** * A heading for a group of items; point the group's `aria-labelledby` at * it. * * @example * ```html * <div ndContextMenuLabel id="account">My account</div> * ``` */ @Directive({ selector: '[ndContextMenuLabel]', hostDirectives: [NdMenuLabel], host: { 'data-slot': 'context-menu-label', '[attr.data-inset]': 'inset() ? "" : null', '[class]': 'classes()', }, }) export class ContextMenuLabel { /** Indents it to line up with items that have an indicator. */ readonly inset = input(false, { transform: booleanAttribute }); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'px-2 py-1.5 text-sm font-medium text-foreground data-[inset]:ps-8', this.class(), ), ); } /** * A line between groups of items (`role="separator"`). * * @example * ```html * <div ndContextMenuSeparator></div> * ``` */ @Directive({ selector: '[ndContextMenuSeparator]', hostDirectives: [NdMenuSeparator], host: { 'data-slot': 'context-menu-separator', '[class]': 'classes()' }, }) export class ContextMenuSeparator { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn('-mx-1 my-1 h-px bg-border', this.class()), ); } /** * The keyboard shortcut of an item, at its end, as a reminder; the menu * doesn't handle it. * * @example * ```html * <span ndContextMenuShortcut>⌘S</span> * ``` */ @Directive({ selector: '[ndContextMenuShortcut]', host: { 'data-slot': 'context-menu-shortcut', '[class]': 'classes()' }, }) export class ContextMenuShortcut { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn('ms-auto text-xs tracking-widest text-muted-foreground', this.class()), ); } /** * An item that opens the submenu in its template. Put a chevron icon at its * end with `ms-auto`, turned around in right-to-left layouts. * * @example * ```html * <div [ndContextMenuSubTrigger]="more">More tools</div> * ``` */ @Directive({ selector: '[ndContextMenuSubTrigger]', hostDirectives: [ { directive: NdMenuSubTrigger, inputs: ['ndMenuSubTrigger: ndContextMenuSubTrigger', 'disabled'], }, ], host: { 'data-slot': 'context-menu-sub-trigger', '[attr.data-inset]': 'inset() ? "" : null', '[class]': 'classes()', }, }) export class ContextMenuSubTrigger { /** Indents it to line up with items that have an indicator. */ readonly inset = input(false, { transform: booleanAttribute }); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:ps-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", this.class(), ), ); }src/app/lib/utils.ts/** * Class helpers used by every styled item. They come from `@ndop/core`; * this file is yours, so it is the place to customize them, for example by * wrapping `cn` with an extended tailwind-merge configuration for your own * utilities. * * @packageDocumentation */ export { cn, variants, type ClassValue, type VariantOf } from '@ndop/core';Update the import paths to match your project
The files import each other through the
@/uiand@/libaliases.
Usage
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@/ui/context-menu/context-menu';<div
[ndContextMenuTrigger]="menu"
tabindex="0"
aria-label="Page preview"
class="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
Right click here
</div>
<ng-template #menu>
<div ndContextMenuContent class="w-52">
<div ndContextMenuItem inset>
Back <span ndContextMenuShortcut>⌘[</span>
</div>
<div ndContextMenuItem inset disabled>
Forward <span ndContextMenuShortcut>⌘]</span>
</div>
<div ndContextMenuItem inset>
Reload <span ndContextMenuShortcut>⌘R</span>
</div>
<div [ndContextMenuSubTrigger]="tools" inset>
More tools
<svg ndIcon="chevron-right" class="ms-auto rtl:rotate-180"></svg>
</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuCheckboxItem [(checked)]="bookmarks">
<span ndContextMenuItemIndicator><svg ndIcon="check"></svg></span>
Show bookmarks
</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuLabel inset id="context-menu-demo-people">People</div>
<div
ndContextMenuRadioGroup
aria-labelledby="context-menu-demo-people"
[(value)]="person"
>
@for (name of people; track name) {
<div ndContextMenuRadioItem [value]="name">
<span ndContextMenuItemIndicator>
<svg ndIcon="circle" class="size-2 fill-current"></svg>
</span>
{{ name }}
</div>
}
</div>
</div>
</ng-template>
<ng-template #tools>
<div ndContextMenuSubContent class="w-44">
<div ndContextMenuItem>Save page…</div>
<div ndContextMenuItem>Developer tools</div>
<div ndContextMenuSeparator></div>
<div ndContextMenuItem variant="destructive">Delete</div>
</div>
</ng-template>Examples
Context Menu checkboxes
Checkbox items on a right click, for showing and hiding layers.
import { Component, signal } from '@angular/core';
import { NdIcon } from '@ndop/core';
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItemIndicator,
ContextMenuTrigger,
} from '@/ui/context-menu/context-menu';
/**
* Checkbox items on a right click, for showing and hiding a map's
* layers, which the area reflects.
*/
@Component({
selector: 'example-context-menu-checkboxes',
imports: [
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItemIndicator,
ContextMenuTrigger,
NdIcon,
],
template: `
<div
[ndContextMenuTrigger]="layers"
tabindex="0"
aria-label="Map"
class="grid h-[150px] w-[300px] place-content-center gap-1 rounded-md border text-center text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
<span>Map</span>
<span class="text-muted-foreground">
{{ traffic() ? 'Traffic' : '' }} {{ transit() ? 'Transit' : '' }}
</span>
</div>
<ng-template #layers>
<div ndContextMenuContent class="w-44">
<div ndContextMenuCheckboxItem [(checked)]="traffic">
<span ndContextMenuItemIndicator><svg ndIcon="check"></svg></span>
Traffic
</div>
<div ndContextMenuCheckboxItem [(checked)]="transit">
<span ndContextMenuItemIndicator><svg ndIcon="check"></svg></span>
Transit
</div>
</div>
</ng-template>
`,
})
export class ContextMenuCheckboxesExample {
protected readonly traffic = signal(true);
protected readonly transit = signal(false);
}
Accessibility
Implements the Menu pattern of the WAI-ARIA Authoring Practices.
Keyboard
Keys the component handles.
| Key | Action |
|---|---|
| ShiftF10 | On the focused area, opens the menu and focuses its first item. |
| ArrowDownorArrowUp | Moves focus to the next or previous item, wrapping. |
| HomeorEnd | Moves focus to the first or last item. |
| Enter | Activates the focused item and closes the menu. |
| Space | Activates the focused item; checkbox and radio items keep the menu open. |
| ArrowRight | On a sub-trigger, opens its menu and focuses the first item. |
| ArrowLeft | In a submenu, closes it and focuses its sub-trigger. |
| Escape | Closes the current menu and returns focus to what opened it. |
| Tab | Closes every open menu and moves focus on from the trigger. |
| a | Typeahead: moves focus to the next item whose label starts with the typed letters. |
Focus
- Opening focuses the menu; closing returns focus to the area.
- Opened from the keyboard, a menu focuses its first item (its last with
ArrowUpon a Dropdown Menu trigger); opened with the pointer, the menu itself. - Closing returns focus to the trigger or sub-trigger.
Right-to-left
Submenus follow Menu's RTL behavior. ArrowLeft opens submenus and ArrowRight closes them, and submenus open on the left. Direction comes from the app's Directionality: the document's dir, or a CDK [dir] directive.
API reference
ContextMenuTrigger
[ndContextMenuTrigger]An area with a context menu (@ndop/primitives/context-menu): its value
is the menu's ng-template, opened at the pointer on a right click or
long press, or at its corner on Shift + F10. Make it focusable and named.
| ContextMenuTrigger binding | Type | Default |
|---|---|---|
[ndContextMenuTrigger]The ng-template of the menu it opens. | TemplateRef<unknown> | null | null |
[disabled]Leaves the browser's own context menu. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | context-menu-trigger |
data-state | — |
data-disabled | — |
ContextMenuContent
[ndContextMenuContent]role menuA context menu's list (@ndop/primitives/menu), in the ng-template a
[ndContextMenuTrigger] area opens at the pointer on a right click or
long press, or at its corner on Shift + F10.
Manages aria-orientation.
| ContextMenuContent binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-content |
data-state | — |
data-side | — |
ContextMenuSubContent
[ndContextMenuSubContent]role menuA submenu, opened beside its sub-trigger: at the end side, so on the left in right-to-left layouts.
Manages aria-orientation.
| ContextMenuSubContent binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-sub-content |
data-state | — |
data-side | — |
ContextMenuGroup
[ndContextMenuGroup]role groupGroups related items (role="group"). Name it with aria-labelledby
pointing at a label.
| Data attribute | Values |
|---|---|
data-slot | context-menu-group |
ContextMenuItem
[ndContextMenuItem]role menuitemOne action (role="menuitem"): activating it emits triggered and
closes the menu. Disabled items stay focusable and do nothing. Use a
div: on a button, Space would close the menu.
Manages aria-disabled.
| ContextMenuItem binding | Type | Default |
|---|---|---|
[inset]Indents it to line up with items that have an indicator. | boolean | false |
[variant]destructive colors a dangerous action, such as Delete. | 'default' | 'destructive' | 'default' |
[class]Extra classes, merged last so they win. | string | '' |
[disabled]Makes it inert but focusable, with aria-disabled. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | context-menu-item |
data-inset | — |
data-variant | — |
data-disabled | — |
ContextMenuCheckboxItem
[ndContextMenuCheckboxItem]role menuitemcheckboxAn item that is on or off (role="menuitemcheckbox"), bound with
[(checked)]. Put an indicator with a check icon in it.
Manages aria-checked, aria-disabled.
| ContextMenuCheckboxItem binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
[disabled]Makes it inert but focusable, with aria-disabled. | boolean | false |
[(checked)]Whether it's on. Two-way bindable with [(checked)]. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | context-menu-checkbox-item |
data-state | — |
data-disabled | — |
ContextMenuRadioGroup
[ndContextMenuRadioGroup]role groupGroups radio items (role="group") and holds the checked value, bound
with [(value)].
| ContextMenuRadioGroup binding | Type | Default |
|---|---|---|
[(value)]The checked radio item's value, or null. Two-way bindable with
[(value)]. | string | null | null |
| Data attribute | Values |
|---|---|
data-slot | context-menu-radio-group |
ContextMenuRadioItem
[ndContextMenuRadioItem]role menuitemradioOne choice of a radio group (role="menuitemradio"). Put an indicator
with a small filled circle in it.
Manages aria-checked, aria-disabled.
| ContextMenuRadioItem binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
[value]requiredThe value its group takes when it's checked. | string | — |
[disabled]Makes it inert but focusable, with aria-disabled. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | context-menu-radio-item |
data-state | — |
data-disabled | — |
ContextMenuItemIndicator
[ndContextMenuItemIndicator]The check or dot at the start of a checkbox or radio item, shown while it is checked.
| ContextMenuItemIndicator binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-item-indicator |
data-state | — |
ContextMenuLabel
[ndContextMenuLabel]A heading for a group of items; point the group's aria-labelledby at
it.
| ContextMenuLabel binding | Type | Default |
|---|---|---|
[inset]Indents it to line up with items that have an indicator. | boolean | false |
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-label |
data-inset | — |
ContextMenuSeparator
[ndContextMenuSeparator]role separatorA line between groups of items (role="separator").
| ContextMenuSeparator binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-separator |
ContextMenuShortcut
[ndContextMenuShortcut]The keyboard shortcut of an item, at its end, as a reminder; the menu doesn't handle it.
| ContextMenuShortcut binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | context-menu-shortcut |
ContextMenuSubTrigger
[ndContextMenuSubTrigger]role menuitemAn item that opens the submenu in its template. Put a chevron icon at its
end with ms-auto, turned around in right-to-left layouts.
Manages aria-haspopup, aria-expanded, aria-controls.
| ContextMenuSubTrigger binding | Type | Default |
|---|---|---|
[inset]Indents it to line up with items that have an indicator. | boolean | false |
[class]Extra classes, merged last so they win. | string | '' |
[ndContextMenuSubTrigger]The ng-template of the submenu. | TemplateRef<unknown> | null | null |
[disabled]Makes it inert but focusable, with aria-disabled. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | context-menu-sub-trigger |
data-inset | — |
data-state | — |
data-disabled | — |
Agent notes
Do
- Make the area focusable and give it a name, so keyboard users can open the menu.
- Offer the same actions somewhere visible too, such as a toolbar or a Dropdown Menu.
Don't
- Don't hide actions people need only in a context menu.
- Don't replace the browser's menu on text people may want to copy.