Context Menu
A context menu component that displays a popover with menu items on right-click. It suppresses the default context menu.
Project Report Q1
2.4 MB · Modified today
Rename
Move to folder
Duplicate
Delete
HTML
Angular
React
Vue
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " file-earmark-text" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Project Report Q1</ div>
< div class = " ctx-demo-item__meta" > 2.4 MB · Modified today</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " files" > </ afp-icon> Duplicate</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' file-earmark-text'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Project Report Q1</ div>
< div class = " ctx-demo-item__meta" > 2.4 MB · Modified today</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' files'" > </ afp-icon> Duplicate</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " file-earmark-text" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Project Report Q1</ div>
< div class = " ctx-demo-item__meta" > 2.4 MB · Modified today</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " files" > </ AfpIcon> Duplicate</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' file-earmark-text'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Project Report Q1</ div>
< div class = " ctx-demo-item__meta" > 2.4 MB · Modified today</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' files'" > </ AfpIcon> Duplicate</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
Show code
Edit
Examples #
Icons & Shortcuts #
Use afp-icon inside menu items and the details slot for keyboard shortcuts.
index.ts
TypeScript · 4.2 KB
Cut
Copy
Paste
Rename
Delete
HTML
Angular
React
Vue
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " file-earmark-code" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > index.ts</ div>
< div class = " ctx-demo-item__meta" > TypeScript · 4.2 KB</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " scissors" > </ afp-icon> Cut</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " copy" > </ afp-icon> Copy</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " clipboard" > </ afp-icon> Paste</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' file-earmark-code'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > index.ts</ div>
< div class = " ctx-demo-item__meta" > TypeScript · 4.2 KB</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' scissors'" > </ afp-icon> Cut</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' copy'" > </ afp-icon> Copy</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' clipboard'" > </ afp-icon> Paste</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " file-earmark-code" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > index.ts</ div>
< div class = " ctx-demo-item__meta" > TypeScript · 4.2 KB</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " scissors" > </ AfpIcon> Cut</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " copy" > </ AfpIcon> Copy</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " clipboard" > </ AfpIcon> Paste</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' file-earmark-code'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > index.ts</ div>
< div class = " ctx-demo-item__meta" > TypeScript · 4.2 KB</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' scissors'" > </ AfpIcon> Cut</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' copy'" > </ AfpIcon> Copy</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' clipboard'" > </ AfpIcon> Paste</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
Show code
Edit
Badges #
Use afp-badge in the details slot to show status labels or counters alongside menu items.
Mark all as read
12
Archive all
Flag for follow-up
3
Delete all
HTML
Angular
React
Vue
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " envelope" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Inbox</ div>
< div class = " ctx-demo-item__meta" > 12 unread messages</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item>
< afp-icon name = " envelope-open" > </ afp-icon> Mark all as read
< afp-badge slot = " details" variant = " brand" shape = " pill" > 12</ afp-badge>
</ afp-dropdown-item>
< afp-dropdown-item>
< afp-icon name = " archive" > </ afp-icon> Archive all
</ afp-dropdown-item>
< afp-dropdown-item>
< afp-icon name = " flag" > </ afp-icon> Flag for follow-up
< afp-badge slot = " details" variant = " warning" appearance = " filled" > 3</ afp-badge>
</ afp-dropdown-item>
< afp-dropdown-item variant = " error" >
< afp-icon name = " trash" > </ afp-icon> Delete all
</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' envelope'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Inbox</ div>
< div class = " ctx-demo-item__meta" > 12 unread messages</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item>
< afp-icon [name] = " ' envelope-open'" > </ afp-icon> Mark all as read
< afp-badge [slot] = " ' details'" [variant] = " ' brand'" [shape] = " ' pill'" > 12</ afp-badge>
</ afp-dropdown-item>
< afp-dropdown-item>
< afp-icon [name] = " ' archive'" > </ afp-icon> Archive all
</ afp-dropdown-item>
< afp-dropdown-item>
< afp-icon [name] = " ' flag'" > </ afp-icon> Flag for follow-up
< afp-badge [slot] = " ' details'" [variant] = " ' warning'" [appearance] = " ' filled'" > 3</ afp-badge>
</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" >
< afp-icon [name] = " ' trash'" > </ afp-icon> Delete all
</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " envelope" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Inbox</ div>
< div class = " ctx-demo-item__meta" > 12 unread messages</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem>
< AfpIcon name = " envelope-open" > </ AfpIcon> Mark all as read
< AfpBadge slot = " details" variant = " brand" shape = " pill" > 12</ AfpBadge>
</ AfpDropdownItem>
< AfpDropdownItem>
< AfpIcon name = " archive" > </ AfpIcon> Archive all
</ AfpDropdownItem>
< AfpDropdownItem>
< AfpIcon name = " flag" > </ AfpIcon> Flag for follow-up
< AfpBadge slot = " details" variant = " warning" appearance = " filled" > 3</ AfpBadge>
</ AfpDropdownItem>
< AfpDropdownItem variant = " error" >
< AfpIcon name = " trash" > </ AfpIcon> Delete all
</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' envelope'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Inbox</ div>
< div class = " ctx-demo-item__meta" > 12 unread messages</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem>
< AfpIcon :name = " ' envelope-open'" > </ AfpIcon> Mark all as read
< AfpBadge :slot = " ' details'" :variant = " ' brand'" :shape = " ' pill'" > 12</ AfpBadge>
</ AfpDropdownItem>
< AfpDropdownItem>
< AfpIcon :name = " ' archive'" > </ AfpIcon> Archive all
</ AfpDropdownItem>
< AfpDropdownItem>
< AfpIcon :name = " ' flag'" > </ AfpIcon> Flag for follow-up
< AfpBadge :slot = " ' details'" :variant = " ' warning'" :appearance = " ' filled'" > 3</ AfpBadge>
</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" >
< AfpIcon :name = " ' trash'" > </ AfpIcon> Delete all
</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
Show code
Edit
Sliders #
The menu slot accepts any content — for example sliders for quick inline adjustments.
HTML
Angular
React
Vue
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " image" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > hero.jpg</ div>
< div class = " ctx-demo-item__meta" > JPEG · 3.2 MB</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< afp-slider label = " Opacity" value = " 80" hint = " Adjust the image opacity." > </ afp-slider>
</ div>
</ afp-context-menu>
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' image'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > hero.jpg</ div>
< div class = " ctx-demo-item__meta" > JPEG · 3.2 MB</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< afp-slider [label] = " ' Opacity'" [value] = 80 [hint] = " ' Adjust the image opacity.'" > </ afp-slider>
</ div>
</ afp-context-menu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " image" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > hero.jpg</ div>
< div class = " ctx-demo-item__meta" > JPEG · 3.2 MB</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< AfpSlider label = " Opacity" value = " 80" hint = " Adjust the image opacity." > </ AfpSlider>
</ div>
</ AfpContextMenu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' image'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > hero.jpg</ div>
< div class = " ctx-demo-item__meta" > JPEG · 3.2 MB</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< AfpSlider :label = " ' Opacity'" :value = 80 :hint = " ' Adjust the image opacity.'" > </ AfpSlider>
</ div>
</ AfpContextMenu>
Show code
Edit
Switches #
Use switches inside the menu slot for inline preference toggles.
Document
Right-click for settings
HTML
Angular
React
Vue
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " file-earmark-richtext" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Document</ div>
< div class = " ctx-demo-item__meta" > Right-click for settings</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< afp-switch checked hint = " Automatically saves your document every few minutes." > Auto-save</ afp-switch>
</ div>
</ afp-context-menu>
< afp-context-menu>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' file-earmark-richtext'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Document</ div>
< div class = " ctx-demo-item__meta" > Right-click for settings</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< afp-switch checked [hint] = " ' Automatically saves your document every few minutes.'" > Auto-save</ afp-switch>
</ div>
</ afp-context-menu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " file-earmark-richtext" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Document</ div>
< div class = " ctx-demo-item__meta" > Right-click for settings</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< AfpSwitch checked hint = " Automatically saves your document every few minutes." > Auto-save</ AfpSwitch>
</ div>
</ AfpContextMenu>
< AfpContextMenu>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' file-earmark-richtext'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Document</ div>
< div class = " ctx-demo-item__meta" > Right-click for settings</ div>
</ div>
</ div>
< div slot = " menu" class = " ctx-demo-panel" >
< AfpSwitch checked :hint = " ' Automatically saves your document every few minutes.'" > Auto-save</ AfpSwitch>
</ div>
</ AfpContextMenu>
Show code
Edit
Click Handler #
Listen for the afp-click event on menu items to react to selections. Use value to identify which item was clicked.
Rename
Move to folder
Delete
HTML
Angular
React
Vue
< afp-context-menu id = " ctx-file" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " file-earmark-pdf" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > report.pdf</ div>
< div class = " ctx-demo-item__meta" > PDF · 1.8 MB</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item value = " rename" > < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item value = " move" > < afp-icon name = " folder" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item value = " delete" variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< script>
document. getElementById ( 'ctx-file' ) . addEventListener ( 'afp-click' , ( e ) => {
console. log ( 'Action:' , e. detail) ;
} ) ;
</ script>
< afp-context-menu [id] = " ' ctx-file'" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' file-earmark-pdf'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > report.pdf</ div>
< div class = " ctx-demo-item__meta" > PDF · 1.8 MB</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item [value] = " ' rename'" > < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item [value] = " ' move'" > < afp-icon [name] = " ' folder'" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item [value] = " ' delete'" [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< script>
document. getElementById ( 'ctx-file' ) . addEventListener ( 'afp-click' , ( e ) => {
console. log ( 'Action:' , e. detail) ;
} ) ;
</ script>
< AfpContextMenu id = " ctx-file" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " file-earmark-pdf" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > report.pdf</ div>
< div class = " ctx-demo-item__meta" > PDF · 1.8 MB</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem value = " rename" > < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem value = " move" > < AfpIcon name = " folder" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem value = " delete" variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< script>
document. getElementById ( 'ctx-file' ) . addEventListener ( 'afp-click' , ( e ) => {
console. log ( 'Action:' , e. detail) ;
} ) ;
</ script>
< AfpContextMenu :id = " ' ctx-file'" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' file-earmark-pdf'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > report.pdf</ div>
< div class = " ctx-demo-item__meta" > PDF · 1.8 MB</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem :value = " ' rename'" > < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem :value = " ' move'" > < AfpIcon :name = " ' folder'" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem :value = " ' delete'" :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< script>
document. getElementById ( 'ctx-file' ) . addEventListener ( 'afp-click' , ( e ) => {
console. log ( 'Action:' , e. detail) ;
} ) ;
</ script>
Show code
Placement #
Use the placement attribute to control which side of the cursor the menu appears on. Leave it empty (default) for automatic placement based on available space.
HTML
Angular
React
Vue
< div style = " display : flex; gap : var ( --afp-space-s) ; flex-wrap : wrap; " >
< afp-context-menu placement = " bottom-start" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " folder2" > </ afp-icon>
< div class = " ctx-demo-item__name" > Bottom start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu placement = " top-start" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " folder2" > </ afp-icon>
< div class = " ctx-demo-item__name" > Top start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu placement = " right-start" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " folder2" > </ afp-icon>
< div class = " ctx-demo-item__name" > Right start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu placement = " left-start" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " folder2" > </ afp-icon>
< div class = " ctx-demo-item__name" > Left start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
</ div>
< div style = " display : flex; gap : var ( --afp-space-s) ; flex-wrap : wrap; " >
< afp-context-menu [placement] = " ' bottom-start'" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' folder2'" > </ afp-icon>
< div class = " ctx-demo-item__name" > Bottom start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu [placement] = " ' top-start'" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' folder2'" > </ afp-icon>
< div class = " ctx-demo-item__name" > Top start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu [placement] = " ' right-start'" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' folder2'" > </ afp-icon>
< div class = " ctx-demo-item__name" > Right start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu [placement] = " ' left-start'" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' folder2'" > </ afp-icon>
< div class = " ctx-demo-item__name" > Left start</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
</ div>
< div style = " display : flex; gap : var ( --afp-space-s) ; flex-wrap : wrap; " >
< AfpContextMenu placement = " bottom-start" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " folder2" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Bottom start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu placement = " top-start" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " folder2" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Top start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu placement = " right-start" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " folder2" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Right start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu placement = " left-start" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " folder2" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Left start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
</ div>
< div style = " display : flex; gap : var ( --afp-space-s) ; flex-wrap : wrap; " >
< AfpContextMenu :placement = " ' bottom-start'" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' folder2'" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Bottom start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu :placement = " ' top-start'" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' folder2'" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Top start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu :placement = " ' right-start'" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' folder2'" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Right start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu :placement = " ' left-start'" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' folder2'" > </ AfpIcon>
< div class = " ctx-demo-item__name" > Left start</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
</ div>
Show code
Edit
Available placements #
Placements can be combined and will be restricted to the provided options if more than two are given.
Primary
Variants
top
top-start, top-end
right
right-start, right-end
bottom
bottom-start, bottom-end
left
left-start, left-end
Duration #
Use the duration attribute to control the open/close animation in milliseconds. Set to 0 to disable animations.
archive.zip
Slow animation (500ms)
Rename
Move to folder
Delete
HTML
Angular
React
Vue
< afp-context-menu duration = " 500" >
< div slot = " content" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " file-earmark-zip" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > archive.zip</ div>
< div class = " ctx-demo-item__meta" > Slow animation (500ms)</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< afp-context-menu [duration] = 500>
< div slot = " content" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' file-earmark-zip'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > archive.zip</ div>
< div class = " ctx-demo-item__meta" > Slow animation (500ms)</ div>
</ div>
</ div>
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< AfpContextMenu duration = " 500" >
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " file-earmark-zip" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > archive.zip</ div>
< div class = " ctx-demo-item__meta" > Slow animation (500ms)</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< AfpContextMenu :duration = 500>
< div slot = " content" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' file-earmark-zip'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > archive.zip</ div>
< div class = " ctx-demo-item__meta" > Slow animation (500ms)</ div>
</ div>
</ div>
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
Show code
Edit
Programmatic Usage #
Use showPopoverFromMouseEvent to trigger the context menu from any element — for example inside a canvas or a custom list.
Custom trigger
Right-click me
Rename
Move to folder
Delete
HTML
Angular
React
Vue
< div id = " custom-trigger" class = " ctx-demo-item" >
< afp-icon class = " ctx-demo-item__icon" name = " grid" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Custom trigger</ div>
< div class = " ctx-demo-item__meta" > Right-click me</ div>
</ div>
</ div>
< afp-context-menu id = " ctx-programmatic" >
< div slot = " menu" >
< afp-dropdown-item> < afp-icon name = " pencil" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon name = " folder" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item variant = " error" > < afp-icon name = " trash" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< script>
const trigger = document. getElementById ( 'custom-trigger' ) ;
const ctx = document. getElementById ( 'ctx-programmatic' ) ;
trigger. addEventListener ( 'contextmenu' , ( e ) => ctx. showPopoverFromMouseEvent ( e) ) ;
</ script>
< div id = " custom-trigger" class = " ctx-demo-item" >
< afp-icon [class] = " ' ctx-demo-item__icon'" [name] = " ' grid'" > </ afp-icon>
< div>
< div class = " ctx-demo-item__name" > Custom trigger</ div>
< div class = " ctx-demo-item__meta" > Right-click me</ div>
</ div>
</ div>
< afp-context-menu [id] = " ' ctx-programmatic'" >
< div slot = " menu" >
< afp-dropdown-item> < afp-icon [name] = " ' pencil'" > </ afp-icon> Rename</ afp-dropdown-item>
< afp-dropdown-item> < afp-icon [name] = " ' folder'" > </ afp-icon> Move to folder</ afp-dropdown-item>
< afp-dropdown-item [variant] = " ' error'" > < afp-icon [name] = " ' trash'" > </ afp-icon> Delete</ afp-dropdown-item>
</ div>
</ afp-context-menu>
< script>
const trigger = document. getElementById ( 'custom-trigger' ) ;
const ctx = document. getElementById ( 'ctx-programmatic' ) ;
trigger. addEventListener ( 'contextmenu' , ( e ) => ctx. showPopoverFromMouseEvent ( e) ) ;
</ script>
< div id = " custom-trigger" class = " ctx-demo-item" >
< AfpIcon class = " ctx-demo-item__icon" name = " grid" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Custom trigger</ div>
< div class = " ctx-demo-item__meta" > Right-click me</ div>
</ div>
</ div>
< AfpContextMenu id = " ctx-programmatic" >
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon name = " pencil" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon name = " folder" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem variant = " error" > < AfpIcon name = " trash" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< script>
const trigger = document. getElementById ( 'custom-trigger' ) ;
const ctx = document. getElementById ( 'ctx-programmatic' ) ;
trigger. addEventListener ( 'contextmenu' , ( e ) => ctx. showPopoverFromMouseEvent ( e) ) ;
</ script>
< div id = " custom-trigger" class = " ctx-demo-item" >
< AfpIcon :class = " ' ctx-demo-item__icon'" :name = " ' grid'" > </ AfpIcon>
< div>
< div class = " ctx-demo-item__name" > Custom trigger</ div>
< div class = " ctx-demo-item__meta" > Right-click me</ div>
</ div>
</ div>
< AfpContextMenu :id = " ' ctx-programmatic'" >
< div slot = " menu" >
< AfpDropdownItem> < AfpIcon :name = " ' pencil'" > </ AfpIcon> Rename</ AfpDropdownItem>
< AfpDropdownItem> < AfpIcon :name = " ' folder'" > </ AfpIcon> Move to folder</ AfpDropdownItem>
< AfpDropdownItem :variant = " ' error'" > < AfpIcon :name = " ' trash'" > </ AfpIcon> Delete</ AfpDropdownItem>
</ div>
</ AfpContextMenu>
< script>
const trigger = document. getElementById ( 'custom-trigger' ) ;
const ctx = document. getElementById ( 'ctx-programmatic' ) ;
trigger. addEventListener ( 'contextmenu' , ( e ) => ctx. showPopoverFromMouseEvent ( e) ) ;
</ script>
Show code
Notes #
The Context Menu component is built on top of the Popover component and inherits its positioning options.
placement controls on which side of the cursor the menu opens. duration sets the transition timing. For a full reference of inherited options, see the Popover documentation .
Properties
Learn more about properties .
Name
Description
Reflects
placement
The preferred placement of the context menu. Note that the actual placement may vary as needed to keep the panel
inside of the viewport. If set to an empty array ([]), it enables auto placement. If containing more than two items,
it is automatically restricted to the provided placements. You can also input a space-separated string.
Type
AfpPlacement
Name
Description
Top placement.
Right placement.
Bottom placement.
Left placement.
Top-start placement.
Top-end placement.
Right-start placement.
Right-end placement.
Bottom-start placement.
Bottom-end placement.
Left-start placement.
Left-end placement.
AfpPlacement
Default []
duration
The duration of the show/hide animation in milliseconds. This value is passed to the popover component to control the transition duration.
Methods
Learn more about using methods .
Name
Description
Arguments
hidePopover
Hides the popover if it is currently shown.
-
showPopoverAt
Shows the popover at the specified coordinates.
x<number>
— The horizontal position in viewport pixels.
y<number>
— The vertical position in viewport pixels.
showPopoverFromMouseEvent
Shows the popover at the mouse event coordinates. This is typically called on a right-click event.
e<MouseEvent>
— The mouse event containing the coordinates where the popover should be shown.
Events
Learn more about events .
Name
Return
Description
afp-show
void
Emitted when the context menu is shown.
afp-after-show
void
Emitted after the context menu has been shown and transitions complete.
afp-hide
void
Emitted when the context menu is hidden.
afp-after-hide
void
Emitted after the context menu has been hidden and transitions complete.
Slots
Learn more about slots .
Name
Description
content
The content that triggers the context menu on right-click.
menu
The context menu items to be displayed in the popover.
CSS Parts
Learn more about CSS Parts .
Name
Description
Selector
base
The component's base wrapper.
popover-base
The popover's base container.
CSS Custom Properties
Name
Description
Default
--afp-context-menu-max-height
The maximum height of the context menu panel.
15rem
--afp-context-menu-min-width
The minimum width of the context menu panel.
auto
Dependencies
The component automatically brings in the listed items, including any nested dependencies that may exist.
Component
CSS Part Prefix
afp-popover
popover-base
- The popover's base container.
Importing
Using the bundle is the recommended method to include components. If you’d rather handle imports manually, you can use the code examples below.
import '@afp-design-system/core/dist/components/context-menu/context-menu.js' ;