import { css, type Handle } from '@remix-run/ui' import * as menu from '@remix-run/ui/menu' import { MenuItem, MenuList, onMenuSelect, Submenu } from '@remix-run/ui/menu' import { separatorStyle } from '@remix-run/ui/separator' import { theme } from '@remix-run/ui/theme' type FileAction = 'copyPath' | 'duplicate' | 'move' | 'rename' | 'reveal' | 'trash' const actionLabelByName: Record = { copyPath: 'Copied path', duplicate: 'Duplicated file', move: 'Moved file', rename: 'Renamed file', reveal: 'Revealed in Finder', trash: 'Moved to trash', } /** * @name Context Menu Trigger * @description A lower-level menu composition that opens from right-click coordinates while keeping standard menu selection and submenu behavior. * @layout center */ export default function Example(handle: Handle) { let latestAction = 'Right-click the card.' return () => (
TS context-menu.tsx Right-click or press Shift+F10

{latestAction}

{ latestAction = actionLabelByName[event.item.name as FileAction] ?? `Selected ${event.item.label}` void handle.update() })} > Rename Duplicate Copy path
Drafts Archive Reveal in Finder
Move to trash
) } const layoutCss = css({ display: 'grid', justifyItems: 'start', gap: theme.space.md, }) const fileCardCss = css({ display: 'grid', gridTemplateColumns: 'auto minmax(0, 1fr)', alignItems: 'center', gap: theme.space.md, width: 'min(100%, 21rem)', padding: theme.space.md, border: `1px solid ${theme.colors.border.subtle}`, borderRadius: theme.radius.lg, backgroundColor: theme.surface.lvl1, color: theme.colors.text.primary, boxShadow: theme.shadow.xs, cursor: 'context-menu', userSelect: 'none', '&:focus-visible': { outline: `2px solid ${theme.colors.focus.ring}`, outlineOffset: '2px', }, }) const fileIconCss = css({ display: 'inline-grid', placeItems: 'center', width: theme.control.height.lg, height: theme.control.height.lg, borderRadius: theme.radius.md, backgroundColor: theme.colors.action.primary.background, color: theme.colors.action.primary.foreground, fontSize: theme.fontSize.xs, fontWeight: theme.fontWeight.bold, }) const fileTextCss = css({ display: 'grid', gap: theme.space.px, minWidth: 0, }) const fileNameCss = css({ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.normal, }) const fileMetaCss = css({ color: theme.colors.text.secondary, fontSize: theme.fontSize.xs, lineHeight: theme.lineHeight.normal, }) const statusCss = css({ margin: 0, minHeight: theme.lineHeight.normal, color: theme.colors.text.secondary, fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.normal, })