diff --git a/docs/content/docs/components/contextmenu.md b/docs/content/docs/components/contextmenu.md new file mode 100644 index 000000000..d520e46db --- /dev/null +++ b/docs/content/docs/components/contextmenu.md @@ -0,0 +1,22 @@ +# ContextMenu + +A floating menu that appears upon right-click or long-press, providing a set of +actions related to the clicked element. + +## Basic + + + +## Groups + + + +## Submenus + + + +## Themes + + + + diff --git a/src/components/ContextMenu/ContextMenu.story.vue b/src/components/ContextMenu/ContextMenu.story.vue new file mode 100644 index 000000000..76478fcca --- /dev/null +++ b/src/components/ContextMenu/ContextMenu.story.vue @@ -0,0 +1,238 @@ + + + + + + + Interactive Controls + + + + User Logged In (shows "Save to Cloud") + + + + Has Edit Permission (enables "Open in New Window", shows "Cut") + + + + Can Delete (enables "Delete" button) + + + + + + + + Right-click here + Long-press here + + + Try the context menu with groups, submenus, themes, and conditional items + Touch-optimized menu + + + + + + Features Demonstrated: + + Group labels and separators + Nested submenus (3 levels deep - see Social Media) + Keyboard shortcuts display (hidden on mobile) + Text truncation with tooltips for long labels + Conditional visibility (hidden property) + Disabled states + Multiple themes (gray, red, green, blue) + Icons and labels + Fixed-width responsive design (192px → 224px → 240px) + Collision detection prevents viewport overflow + Mobile-optimized touch targets (40px height) + Long-press support for mobile devices + + + + + + + + + diff --git a/src/components/ContextMenu/ContextMenu.vue b/src/components/ContextMenu/ContextMenu.vue new file mode 100644 index 000000000..53b5a2dad --- /dev/null +++ b/src/components/ContextMenu/ContextMenu.vue @@ -0,0 +1,480 @@ + + + + + + + + + + + + {{ group.group }} + + + + + + + + + + + + + + + + {{ item.label }} + + + + {{ item.label }} + + + + + + + + + {{ submenuGroup.group }} + + + + + handleItemClick(subItem, event) + " + :disabled="subItem.disabled" + class="data-[disabled]:cursor-not-allowed" + > + + + + + + + + {{ subItem.label }} + + + + {{ subItem.label }} + + + {{ subItem.shortcut }} + + + + + + + + + + + + + + + + + + + + {{ item.label }} + + + + {{ item.label }} + + + {{ item.shortcut }} + + + + + + + + + + + + + + diff --git a/src/components/ContextMenu/index.ts b/src/components/ContextMenu/index.ts new file mode 100644 index 000000000..97770da61 --- /dev/null +++ b/src/components/ContextMenu/index.ts @@ -0,0 +1,2 @@ +export { default as ContextMenu } from './ContextMenu.vue' +export type { ContextMenuProps, ContextMenuItem, ContextMenuItems } from './types' diff --git a/src/components/ContextMenu/stories/Basic.vue b/src/components/ContextMenu/stories/Basic.vue new file mode 100644 index 000000000..527732bcf --- /dev/null +++ b/src/components/ContextMenu/stories/Basic.vue @@ -0,0 +1,22 @@ + + + + + + Right click here + + + diff --git a/src/components/ContextMenu/stories/Groups.vue b/src/components/ContextMenu/stories/Groups.vue new file mode 100644 index 000000000..bfa6b0ba8 --- /dev/null +++ b/src/components/ContextMenu/stories/Groups.vue @@ -0,0 +1,53 @@ + + + + + + Right click for grouped items + + + diff --git a/src/components/ContextMenu/stories/Submenus.vue b/src/components/ContextMenu/stories/Submenus.vue new file mode 100644 index 000000000..de2a7679c --- /dev/null +++ b/src/components/ContextMenu/stories/Submenus.vue @@ -0,0 +1,51 @@ + + + + + + Right click for submenus + + + diff --git a/src/components/ContextMenu/stories/Themes.vue b/src/components/ContextMenu/stories/Themes.vue new file mode 100644 index 000000000..92a548acf --- /dev/null +++ b/src/components/ContextMenu/stories/Themes.vue @@ -0,0 +1,38 @@ + + + + + + Right click for themes + + + diff --git a/src/components/ContextMenu/types.ts b/src/components/ContextMenu/types.ts new file mode 100644 index 000000000..58b0df3d8 --- /dev/null +++ b/src/components/ContextMenu/types.ts @@ -0,0 +1,36 @@ +import { RouterLinkProps } from 'vue-router' +import { type Component } from 'vue' + +export type ContextMenuItem = { + label: string + icon?: string | Component | null + disabled?: boolean + hidden?: boolean + theme?: string + component?: any + onClick?: (val: any) => void + route?: RouterLinkProps['to'] + condition?: () => boolean + submenu?: ContextMenuItems + separator?: boolean + shortcut?: string + showTooltip?: boolean +} + +export type ContextMenuGroupItem = { + key?: number + group: string + items: ContextMenuItem[] + hideLabel?: boolean + theme?: string +} + +export type ContextMenuItemType = ContextMenuItem | ContextMenuGroupItem + +export type ContextMenuItems = Array + +export interface ContextMenuProps { + items?: ContextMenuItems + disabled?: boolean + showTooltip?: boolean | ((label: string) => boolean) +} diff --git a/src/index.ts b/src/index.ts index 597c8c8c8..8b209e641 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export * from './components/Breadcrumbs' export * from './components/Button' export { default as Card } from './components/Card.vue' export * from './components/Combobox' +export * from './components/ContextMenu' export * from './components/Checkbox' export * from './components/DatePicker' export * from './components/MonthPicker'
+ Right-click here + Long-press here +
+ Try the context menu with groups, submenus, themes, and conditional items + Touch-optimized menu +