Navigation
Context Menu
Usage#
import { WdsContextMenuComponent } from '@wds/angular/context-menu';wds-dropdown-item, wds-dropdown-label, wds-dropdown-separator, wds-dropdown-group, wds-dropdown-radio-group and a nested wds-dropdown as a submenu. Importing @wds/core/context-menu registers them as well.
Examples#
Default#
trigger slot is the area. A right click on it (a long press on a touch screen, or the context menu key) opens the menu at the pointer instead of the browser menu; near the edge of the window the menu opens on the other side of the pointer. Selecting an item fires wds-select with its value and closes the menu.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-default-demo',
imports: [WdsContextMenuComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-item value="back">Back</wds-dropdown-item>
<wds-dropdown-item value="forward" disabled>Forward</wds-dropdown-item>
<wds-dropdown-item value="reload">Reload</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="save">Save as…</wds-dropdown-item>
<wds-dropdown-item value="print">Print…</wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuDefaultDemo {}Icons#
<svg> before the text gets the icon size and spacing.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-icons-demo',
imports: [WdsContextMenuComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-item value="copy">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" /><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></svg>
Copy
</wds-dropdown-item>
<wds-dropdown-item value="cut">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6" cy="6" r="3" /><path d="M8.12 8.12 12 12" /><path d="M20 4 8.12 15.88" /><circle cx="6" cy="18" r="3" /><path d="M14.8 14.8 20 20" /></svg>
Cut
</wds-dropdown-item>
<wds-dropdown-item value="paste">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="8" height="4" x="8" y="2" rx="1" /><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" /></svg>
Paste
</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="share">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3" /><circle cx="6" cy="12" r="3" /><circle cx="18" cy="19" r="3" /><path d="m8.59 13.51 6.83 3.98" /><path d="m15.41 6.51-6.82 3.98" /></svg>
Share
</wds-dropdown-item>
<wds-dropdown-item value="download">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="m7 10 5 5 5-5" /><path d="M12 15V3" /></svg>
Download
</wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuIconsDemo {}Shortcuts#
shortcut slot shows a keyboard shortcut on the right. It is only displayed: handle the keys yourself.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-shortcuts-demo',
imports: [WdsContextMenuComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-item value="back">Back<span slot="shortcut">⌘[</span></wds-dropdown-item>
<wds-dropdown-item value="forward" disabled>Forward<span slot="shortcut">⌘]</span></wds-dropdown-item>
<wds-dropdown-item value="reload">Reload<span slot="shortcut">⌘R</span></wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="save">Save as…<span slot="shortcut">⇧⌘S</span></wds-dropdown-item>
<wds-dropdown-item value="print">Print…<span slot="shortcut">⌘P</span></wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuShortcutsDemo {}Submenu#
wds-dropdown with a wds-dropdown-item in its trigger slot opens a submenu on hover, a click or ArrowRight.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-submenu-demo',
imports: [WdsContextMenuComponent, WdsDropdownItemComponent, WdsDropdownComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-item value="back">Back<span slot="shortcut">⌘[</span></wds-dropdown-item>
<wds-dropdown-item value="reload">Reload<span slot="shortcut">⌘R</span></wds-dropdown-item>
<wds-dropdown>
<wds-dropdown-item slot="trigger">More tools</wds-dropdown-item>
<wds-dropdown-item value="save-page">Save page…</wds-dropdown-item>
<wds-dropdown-item value="create-shortcut">Create shortcut…</wds-dropdown-item>
<wds-dropdown-item value="name-window">Name window…</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="developer-tools">Developer tools</wds-dropdown-item>
</wds-dropdown>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="print">Print…<span slot="shortcut">⌘P</span></wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuSubmenuDemo {}Labels and groups#
wds-dropdown-label heads a section and wds-dropdown-separator divides sections; wds-dropdown-group with aria-label names a group for screen readers.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownGroupComponent, WdsDropdownItemComponent, WdsDropdownLabelComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-groups-demo',
imports: [WdsContextMenuComponent, WdsDropdownGroupComponent, WdsDropdownLabelComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-group aria-label="File">
<wds-dropdown-label>File</wds-dropdown-label>
<wds-dropdown-item value="new">New file<span slot="shortcut">⌘N</span></wds-dropdown-item>
<wds-dropdown-item value="open">Open…<span slot="shortcut">⌘O</span></wds-dropdown-item>
<wds-dropdown-item value="save">Save<span slot="shortcut">⌘S</span></wds-dropdown-item>
</wds-dropdown-group>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-group aria-label="Edit">
<wds-dropdown-label>Edit</wds-dropdown-label>
<wds-dropdown-item value="undo">Undo<span slot="shortcut">⌘Z</span></wds-dropdown-item>
<wds-dropdown-item value="redo">Redo<span slot="shortcut">⇧⌘Z</span></wds-dropdown-item>
</wds-dropdown-group>
</wds-context-menu>
`,
})
export class ContextMenuGroupsDemo {}Checkboxes#
type="checkbox" toggles checked on selection; detail.checked in wds-select is the new state. inset on the label lines it up with the items.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownLabelComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-checkboxes-demo',
imports: [WdsContextMenuComponent, WdsDropdownLabelComponent, WdsDropdownItemComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-label inset>View</wds-dropdown-label>
<wds-dropdown-item type="checkbox" value="bookmarks" checked>Show bookmarks bar<span slot="shortcut">⇧⌘B</span></wds-dropdown-item>
<wds-dropdown-item type="checkbox" value="urls">Show full URLs</wds-dropdown-item>
<wds-dropdown-item type="checkbox" value="status" disabled>Show status bar</wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuCheckboxesDemo {}Radio group#
type="radio" in a wds-dropdown-radio-group select one value; the group fires wds-change with it.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownLabelComponent, WdsDropdownRadioGroupComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-radio-group-demo',
imports: [WdsContextMenuComponent, WdsDropdownLabelComponent, WdsDropdownSeparatorComponent, WdsDropdownRadioGroupComponent, WdsDropdownItemComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-label inset>People</wds-dropdown-label>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-radio-group value="pedro" aria-label="People">
<wds-dropdown-item type="radio" value="pedro">Pedro Duarte</wds-dropdown-item>
<wds-dropdown-item type="radio" value="colm">Colm Tuite</wds-dropdown-item>
<wds-dropdown-item type="radio" value="jamie">Jamie Hall</wds-dropdown-item>
</wds-dropdown-radio-group>
</wds-context-menu>
`,
})
export class ContextMenuRadioGroupDemo {}Destructive#
variant="destructive" marks an item that deletes or cannot be undone.
import { Component } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-destructive-demo',
imports: [WdsContextMenuComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent],
template: `
<wds-context-menu>
<div slot="trigger" class="flex h-37.5 w-75 max-w-full items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">Right click here</div>
<wds-dropdown-item value="rename">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9" /><path d="M16.38 3.62a1 1 0 0 1 3 3L7.37 18.64a2 2 0 0 1-.86.5l-2.87.84a.5.5 0 0 1-.62-.62l.84-2.87a2 2 0 0 1 .5-.86z" /></svg>
Rename
</wds-dropdown-item>
<wds-dropdown-item value="duplicate">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" /><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></svg>
Duplicate
</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="delete" variant="destructive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18" /><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" /><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" /></svg>
Delete
<span slot="shortcut">⌘⌫</span>
</wds-dropdown-item>
</wds-context-menu>
`,
})
export class ContextMenuDestructiveDemo {}List#
contextmenu event reaches the clicked row before the menu opens, so remember which item it is for and act on it in wds-select. The label at the top names the item.
- report.pdf2.4 MB
- budget.xlsx840 KB
- logo.svg12 KB
Right click a file.
import { Component, signal } from '@angular/core';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WDS_DROPDOWN } from '@wds/angular/dropdown';
interface FileRow {
name: string;
size: string;
}
@Component({
selector: 'app-context-menu-list-demo',
imports: [...WDS_DROPDOWN, WdsContextMenuComponent],
template: `
<div class="flex w-80 max-w-full flex-col gap-3">
<wds-context-menu (wdsSelect)="run($event.value)">
<ul slot="trigger" class="m-0 list-none divide-y divide-border rounded-md border border-border p-0 text-sm select-none">
@for (file of files(); track file.name) {
<li class="flex justify-between px-3 py-2" [class.bg-accent]="target() === file.name" (contextmenu)="target.set(file.name)">
<span>{{ file.name }}</span>
<span class="text-muted-foreground">{{ file.size }}</span>
</li>
}
</ul>
<wds-dropdown-label>{{ target() }}</wds-dropdown-label>
<wds-dropdown-item value="open">Open</wds-dropdown-item>
<wds-dropdown-item value="duplicate">Duplicate</wds-dropdown-item>
<wds-dropdown-separator />
<wds-dropdown-item value="delete" variant="destructive">Delete</wds-dropdown-item>
</wds-context-menu>
<p class="m-0 text-sm text-muted-foreground">{{ status() }}</p>
</div>
`,
})
export class ContextMenuListDemo {
protected readonly files = signal<FileRow[]>([
{ name: 'report.pdf', size: '2.4 MB' },
{ name: 'budget.xlsx', size: '840 KB' },
{ name: 'logo.svg', size: '12 KB' },
]);
protected readonly target = signal('');
protected readonly status = signal('Right click a file.');
protected run(action: string) {
const name = this.target();
if (action === 'duplicate') {
const copy = name.replace(/(\.[^.]+)$/, ' copy$1');
this.files.update((list) => [...list, { ...list.find((f) => f.name === name)!, name: copy }]);
}
if (action === 'delete') this.files.update((list) => list.filter((f) => f.name !== name));
this.status.set(`${action[0].toUpperCase()}${action.slice(1)}: ${name}`);
}
}In a dialog#
wds-dialog and a click outside the menu closes only the menu.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsContextMenuComponent } from '@wds/angular/context-menu';
import { WdsDialogComponent } from '@wds/angular/dialog';
import { WDS_DROPDOWN } from '@wds/angular/dropdown';
@Component({
selector: 'app-context-menu-dialog-demo',
imports: [...WDS_DROPDOWN, WdsButtonComponent, WdsContextMenuComponent, WdsDialogComponent],
template: `
<wds-button variant="outline" (click)="dialog.show()">Open dialog</wds-button>
<wds-dialog #dialog heading="Shared files" description="Right click the area to manage the folder.">
<wds-context-menu>
<div slot="trigger" class="flex h-32 items-center justify-center rounded-md border border-dashed border-border text-sm text-muted-foreground select-none">
Right click here
</div>
<wds-dropdown-item value="new-folder">New folder<span slot="shortcut">⇧⌘N</span></wds-dropdown-item>
<wds-dropdown-item value="upload">Upload files…</wds-dropdown-item>
<wds-dropdown-separator />
<wds-dropdown-item value="sort">Sort by name</wds-dropdown-item>
</wds-context-menu>
<wds-button slot="footer" variant="outline" (click)="dialog.close()">Close</wds-button>
</wds-dialog>
`,
})
export class ContextMenuDialogDemo {}API References#
wds-context-menu#
wds-dropdown-item (also type="checkbox" and type="radio"),
wds-dropdown-label, wds-dropdown-separator, wds-dropdown-group, wds-dropdown-radio-group, and a nested
wds-dropdown with a wds-dropdown-item in its trigger slot as a submenu. Importing @wds/core/context-menu
registers them as well.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| open | boolean | false | show(), close() and toggle(). |
| disabled | boolean | false |
Events
| Event | Description |
|---|---|
| wds-open-changeCustomEvent<WdsDropdownOpenChangeDetail> | |
| wds-selectCustomEvent<WdsDropdownSelectDetail> | wds-dropdown-item). |
Methods
| Method | Description |
|---|---|
| show() | |
| showAt(x: number, y: number) | clientX, clientY). |
| close() | |
| toggle() |
Slots
| Slot | Description |
|---|---|
| trigger | |
| (default) | wds-dropdown-item, -label, -separator, -group, -radio-group, submenus. |
CSS parts (::part)
| Part | Description |
|---|---|
| content |
CSS custom properties
| Property | Description |
|---|---|
| --wds-dropdown-min-width |
Accessibility#
Screen reader#
role="menu"; items have role="menuitem", or menuitemcheckbox / menuitemradio with aria-checked, as in the Dropdown.
Keyboard support#
| Key | Function |
|---|---|