Context menu
STEP 09 / CONTEXTUAL ACTIONS
A menu at the point of intent
Right-click a row or use its Actions button. The server renders one inert template; Rocket clones it on demand, binds the row’s contextId, handles focus and nested menus, and emits a semantic action. The page decides what that action means and returns a small SSE patch. No menu fetch is needed for these shared, non-sensitive actions.
Choose an action for a record.
<article data-context-id="record-a" data-menu-for="record-menu">
<button data-menu-for="record-menu" aria-haspopup="menu">Actions</button>
</article>
<rocket-context-menu id="record-menu" data-on:rocket-menu-action="$menu = evt.detail; @post('/menu-action')">
<template data-rocket-menu>
<button role="menuitem" data-action="inspect">Inspect {contextId}</button>
<button role="menuitem" data-submenu="more" aria-haspopup="menu">More →</button>
<div id="more" role="menu" popover="auto">
<button role="menuitem" data-action="archive">Archive</button>
</div>
</template>
</rocket-context-menu>
rocket-menu-action → { action, contextId }Menus may nest as deeply as your markup needs. Opening focuses the menu; Down starts at the first item (Up starts at the last). Arrow keys or h/j/k/l move through a level, Right opens a submenu, and Left, Backspace or Escape returns one level. Escape at the root closes the menu; Enter or Space on the menu activates its first item. Home/End and per-host data-key-* overrides also work. On macOS,Ctrl+n / Ctrl+p also move next / previous, following familiar Control-key text navigation; Command-key browser shortcuts remain untouched. On other platforms the Control pair can be opted into with data-key-focus-next="ArrowDown j Ctrl+n" and data-key-focus-previous="ArrowUp k Ctrl+p". Native popovers provide the top layer and light dismiss; CSS anchors position the menu and flip nested panels at viewport edges, with measured coordinates as a fallback. Your CSS owns the presentation. Context placeholders bind in text, data-menu-param-*, aria-label, and title attributes; executable directives are left as server-rendered.
For fresh options, fetch page-owned HTML before opening and morph the template, or render a direct child marked data-rocket-menu-content for live server markup. Then call menu.openFor(trigger). Live markup stays in the host after close; use menu.closeMenu(refocus?) and menu.isOpen() when coordinating a page-owned menu lifecycle. rocket-menu-scope emits { root, active } when its keyboard scope opens or closes. Additional non-sensitive values can be passed as menu.openFor(trigger, undefined, { label: "Example" }) and used as {label} in the template. The Rocket never fetches menus or makes authorization decisions. See menu behavior ↗ and JSX adapter ↗.