{"name":"context-menu","title":"Context Menu","description":"Displays a menu to the user — such as a set of actions or functions — triggered by right click.","installCommand":"bunx marko-ui add context-menu -y","usageTags":"<ContextMenu>","importSnippet":"import ContextMenu from \"@/components/ui/context-menu/context-menu.marko\";","usageSnippet":"<ContextMenu items=[{ value: \"copy\", label: \"Copy\" }]>\n  Right click here\n</ContextMenu>","tags":["ContextMenu"],"parts":[{"name":"item","attributes":[{"name":"type","type":"\"item\" | \"separator\" | \"label\" | \"checkbox\" | \"radio\" | \"sub\"","required":false},{"name":"value","type":"string","required":false},{"name":"label","type":"string","required":false},{"name":"disabled","type":"boolean","required":false},{"name":"shortcut","type":"string","required":false},{"name":"variant","type":"\"default\" | \"destructive\"","required":false,"description":"Upstream `variant: \"default\" | \"destructive\"`, emitted as `data-variant`."},{"name":"inset","type":"boolean","required":false,"description":"Emits `data-inset` — indents the entry to align with icon-bearing siblings."},{"name":"checked","type":"boolean","required":false,"description":"Checked state for `type=\"checkbox\"` and `type=\"radio\"` entries."},{"name":"radioGroup","type":"string","required":false,"description":"Groups radio items — required for `type: \"radio\"` entries."},{"name":"radioValue","type":"string","required":false,"description":"`type: \"radio\"` entries only — this entry's value within its `radioGroup`."},{"name":"subEntries","type":"ContextMenuItem[]","required":false,"description":"`type: \"sub\"` entries only — the nested submenu's own item list. One level deep: a `type: \"sub\"` entry inside a submenu's own `subEntries` is not wired (same limit as menubar's submenu)."}],"repeatable":true}],"props":[{"name":"class","type":"string","required":false},{"name":"items","type":"ContextMenuItem[]","required":false},{"name":"select","type":"(value: string) => void","required":false}],"events":[],"keyboard":[{"keys":"Right click / long press","description":"Opens the menu at the pointer position, anchored to the trigger region."},{"keys":"ArrowDown / ArrowUp","description":"Moves the highlight to the next / previous item, wrapping at the ends."},{"keys":"ArrowRight","description":"On a submenu trigger item, opens the submenu and moves focus into it."},{"keys":"ArrowLeft / Escape","description":"Closes an open submenu (ArrowLeft) or the whole menu (Escape) and returns focus to the trigger."},{"keys":"Enter / Space","description":"Activates the highlighted item."},{"keys":"Home / End","description":"Moves the highlight to the first / last item."},{"keys":"A–Z","description":"Typeahead: jumps the highlight to the next item whose label starts with the typed characters."}],"accessibilityNotes":["The trigger and content wire up `aria-haspopup`, `aria-expanded`, and `aria-controls` automatically via the underlying Zag menu machine — no manual ARIA authoring needed.","Checkbox and radio items expose `role=\"menuitemcheckbox\"` / `role=\"menuitemradio\"` with `aria-checked` reflecting the `checked` field you pass in.","Disabled items (`disabled: true`) are marked `aria-disabled` and skipped by both keyboard navigation and typeahead."],"examples":[{"id":"context-menu-basic","title":"Basic","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"back\", label: \"Back\" },\n  { value: \"forward\", label: \"Forward\", disabled: true },\n  { value: \"reload\", label: \"Reload\" },\n];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"A simple context menu with a few actions, one of them `disabled`."},{"id":"context-menu-submenu","title":"Submenu","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"copy\", label: \"Copy\", shortcut: \"⌘C\" },\n  { value: \"cut\", label: \"Cut\", shortcut: \"⌘X\" },\n  {\n    type: \"sub\",\n    label: \"More Tools\",\n    subEntries: [\n      { value: \"save-page\", label: \"Save Page...\" },\n      { value: \"create-shortcut\", label: \"Create Shortcut...\" },\n      { value: \"name-window\", label: \"Name Window...\" },\n      { type: \"separator\" },\n      { value: \"developer-tools\", label: \"Developer Tools\" },\n      { type: \"separator\" },\n      { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n    ],\n  },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Use a `type: \"sub\"` entry with `subEntries` to nest a secondary menu (one level deep)."},{"id":"context-menu-shortcuts","title":"Shortcuts","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"cut\", label: \"Cut\", shortcut: \"⌘X\" },\n  { value: \"copy\", label: \"Copy\", shortcut: \"⌘C\" },\n  { value: \"paste\", label: \"Paste\", shortcut: \"⌘V\" },\n];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Add a `shortcut` string to an item to show a keyboard hint on the right."},{"id":"context-menu-groups","title":"Groups","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { type: \"label\", label: \"Actions\" },\n  { value: \"edit\", label: \"Edit\" },\n  { value: \"duplicate\", label: \"Duplicate\" },\n  { type: \"separator\" },\n  { type: \"label\", label: \"Danger Zone\" },\n  { value: \"archive\", label: \"Archive\" },\n  { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Use `type: \"label\"` and `type: \"separator\"` entries to group related actions."},{"id":"context-menu-icons","title":"Icons","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<ContextMenu>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n  <@item value=\"copy\">\n    <Icon name=\"Copy\"/>\n    Copy\n  </@item>\n  <@item value=\"cut\">\n    <Icon name=\"Scissors\"/>\n    Cut\n  </@item>\n  <@item value=\"paste\">\n    <Icon name=\"ClipboardPaste\"/>\n    Paste\n  </@item>\n  <@item type=\"separator\"/>\n  <@item value=\"delete\" variant=\"destructive\">\n    <Icon name=\"Trash\"/>\n    Delete\n  </@item>\n</ContextMenu>","description":"Use the `<@item>` attribute-tag form to combine icons with labels for quick scanning — item bodies render in place of the `label` string."},{"id":"context-menu-checkboxes","title":"Checkboxes","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\n<let/showBookmarksBar=true/>\n<let/showFullUrls=false/>\n<let/showDeveloperTools=true/>\n\n<const/items=[\n  { type: \"checkbox\", value: \"bookmarks-bar\", label: \"Show Bookmarks Bar\", checked: showBookmarksBar },\n  { type: \"checkbox\", value: \"full-urls\", label: \"Show Full URLs\", checked: showFullUrls },\n  { type: \"checkbox\", value: \"developer-tools\", label: \"Show Developer Tools\", checked: showDeveloperTools },\n] satisfies ContextMenuItem[]/>\n\n<ContextMenu\n  items=items\n  select(value) {\n    if (value === \"bookmarks-bar\") showBookmarksBar = !showBookmarksBar;\n    else if (value === \"full-urls\") showFullUrls = !showFullUrls;\n    else if (value === \"developer-tools\") showDeveloperTools = !showDeveloperTools;\n  }\n>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Use `type: \"checkbox\"` entries for toggles. `checked` is driven by your own state — flip it in the `select` handler."},{"id":"context-menu-radio","title":"Radio","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\n<let/user=\"pedro\"/>\n<let/theme=\"light\"/>\n\n<const/items=[\n  { type: \"label\", label: \"People\" },\n  { type: \"radio\", value: \"pedro\", label: \"Pedro Duarte\", radioGroup: \"user\", checked: user === \"pedro\" },\n  { type: \"radio\", value: \"colm\", label: \"Colm Tuite\", radioGroup: \"user\", checked: user === \"colm\" },\n  { type: \"separator\" },\n  { type: \"label\", label: \"Theme\" },\n  { type: \"radio\", value: \"light\", label: \"Light\", radioGroup: \"theme\", checked: theme === \"light\" },\n  { type: \"radio\", value: \"dark\", label: \"Dark\", radioGroup: \"theme\", checked: theme === \"dark\" },\n  { type: \"radio\", value: \"system\", label: \"System\", radioGroup: \"theme\", checked: theme === \"system\" },\n] satisfies ContextMenuItem[]/>\n\n<ContextMenu\n  items=items\n  select(value) {\n    if (value === \"pedro\" || value === \"colm\") user = value;\n    else if (value === \"light\" || value === \"dark\" || value === \"system\") theme = value;\n  }\n>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Use `type: \"radio\"` entries for exclusive choices grouped by `radioGroup`. Exclusivity is your own state, not automatic — set exactly one entry's `checked` per group in the `select` handler."},{"id":"context-menu-destructive","title":"Destructive","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"rename\", label: \"Rename\" },\n  { value: \"duplicate\", label: \"Duplicate\" },\n  { type: \"separator\" },\n  { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Set `variant: \"destructive\"` on an item to style it as a destructive action."},{"id":"context-menu-demo","title":"Demo","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"back\", label: \"Back\", shortcut: \"⌘[\" },\n  { value: \"forward\", label: \"Forward\", shortcut: \"⌘]\", disabled: true },\n  { value: \"reload\", label: \"Reload\", shortcut: \"⌘R\" },\n  { type: \"separator\" },\n  { type: \"label\", label: \"More Tools\" },\n  { value: \"save\", label: \"Save Page As...\", shortcut: \"⌘S\" },\n  { value: \"cast\", label: \"Cast...\" },\n  { type: \"separator\" },\n  { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Pass an `items` array of entries; `type: \"separator\"` and `type: \"label\"` entries organize the list."},{"id":"context-menu-rtl","title":"RTL","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\n<let/people=\"pedro\"/>\n<let/showBookmarks=true/>\n<let/showFullUrls=false/>\n\n<const/items=[\n  {\n    type: \"sub\",\n    label: \"التنقل\",\n    subEntries: [\n      { value: \"back\", label: \"رجوع\", shortcut: \"⌘[\" },\n      { value: \"forward\", label: \"تقدم\", shortcut: \"⌘]\", disabled: true },\n      { value: \"reload\", label: \"إعادة تحميل\", shortcut: \"⌘R\" },\n    ],\n  },\n  {\n    type: \"sub\",\n    label: \"المزيد من الأدوات\",\n    subEntries: [\n      { value: \"save-page\", label: \"حفظ الصفحة...\" },\n      { value: \"create-shortcut\", label: \"إنشاء اختصار...\" },\n      { value: \"name-window\", label: \"تسمية النافذة...\" },\n      { type: \"separator\" },\n      { value: \"developer-tools\", label: \"أدوات المطور\" },\n      { type: \"separator\" },\n      { value: \"delete\", label: \"حذف\", variant: \"destructive\" },\n    ],\n  },\n  { type: \"separator\" },\n  { type: \"checkbox\", value: \"show-bookmarks\", label: \"إظهار الإشارات المرجعية\", checked: showBookmarks },\n  { type: \"checkbox\", value: \"show-full-urls\", label: \"إظهار عناوين URL الكاملة\", checked: showFullUrls },\n  { type: \"separator\" },\n  { type: \"label\", label: \"الأشخاص\" },\n  { type: \"radio\", value: \"pedro\", label: \"Pedro Duarte\", radioGroup: \"people\", checked: people === \"pedro\" },\n  { type: \"radio\", value: \"colm\", label: \"Colm Tuite\", radioGroup: \"people\", checked: people === \"colm\" },\n] satisfies ContextMenuItem[]/>\n\n<div dir=\"rtl\" class=\"flex justify-center\">\n  <ContextMenu\n    items=items\n    select(value) {\n      if (value === \"show-bookmarks\") showBookmarks = !showBookmarks;\n      else if (value === \"show-full-urls\") showFullUrls = !showFullUrls;\n      else if (value === \"pedro\" || value === \"colm\") people = value;\n    }\n    class=\"w-48\"\n  >\n    <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n      انقر بزر الماوس الأيمن هنا\n    </div>\n  </ContextMenu>\n</div>","description":"Wrap the trigger in a `dir=\"rtl\"` container for RTL text direction and logical-property layout. Real deviation from upstream: our `Input` type doesn't re-expose the machine's own `dir` prop, so the floating-ui positioner itself doesn't auto-flip placement under RTL the way upstream's demo does."},{"id":"context-menu-simple","title":"Simple","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"copy\", label: \"Copy\" },\n  { value: \"paste\", label: \"Paste\" },\n  { type: \"separator\" },\n  { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"A minimal menu with a `danger` entry styled as destructive."},{"id":"context-menu-disabled","title":"Disabled","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"copy\", label: \"Copy\" },\n  { value: \"paste\", label: \"Paste\", disabled: true },\n  { type: \"separator\" },\n  { value: \"delete\", label: \"Delete\", disabled: true, variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<ContextMenu items=ITEMS>\n  <div class=\"flex h-[100px] w-[250px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n</ContextMenu>","description":"Set `disabled: true` on an item to skip it during keyboard navigation and pointer input."},{"id":"context-menu-controlled","title":"Controlled","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\nimport type { ContextMenuItem } from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\nstatic const ITEMS = [\n  { value: \"copy\", label: \"Copy\" },\n  { value: \"paste\", label: \"Paste\" },\n  { type: \"separator\" },\n  { value: \"delete\", label: \"Delete\", variant: \"destructive\" },\n] satisfies ContextMenuItem[];\n\n<let/selected=\"\"/>\n\n<div class=\"flex items-center gap-4\">\n  <ContextMenu items=ITEMS select(value) { selected = value; }>\n    <div class=\"flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n      Right click here\n    </div>\n  </ContextMenu>\n  <span class=\"text-muted-foreground text-sm\">selected: ${selected || \"none\"}</span>\n</div>","description":"Pass a `select` handler to react to the chosen item's `value`.","essential":true},{"id":"context-menu-compound","title":"Compound (attr tags)","source":"import ContextMenu from \"@marko-ui/shadcn/ui/context-menu/context-menu.marko\";\n\n<ContextMenu>\n  <div class=\"flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground\">\n    Right click here\n  </div>\n  <@item type=\"label\" label=\"Actions\"/>\n  <@item value=\"copy\" label=\"Copy\" shortcut=\"⌘C\"/>\n  <@item value=\"paste\" label=\"Paste\" shortcut=\"⌘V\"/>\n  <@item type=\"separator\"/>\n  <@item value=\"delete\" label=\"Delete\" variant=\"destructive\"/>\n</ContextMenu>","description":"Use `<@item>` attribute tags instead of `items=` to compose the menu directly in markup — pass `type=\"separator\"` for a divider or `type=\"label\"` for a group label. Entries render in the order they are written. An item's text can come from either a `label=` attribute or a markup body, whichever you prefer.","essential":true}],"importInstallCommand":"bun add @marko-ui/shadcn marko-zag","body":{},"itemTypes":[{"prop":"items","typeName":"ContextMenuItem","fields":[{"name":"type","type":"\"item\" | \"separator\" | \"label\" | \"checkbox\" | \"radio\" | \"sub\"","required":false},{"name":"value","type":"string","required":false},{"name":"label","type":"string","required":false},{"name":"disabled","type":"boolean","required":false},{"name":"shortcut","type":"string","required":false},{"name":"variant","type":"\"default\" | \"destructive\"","required":false,"description":"Upstream `variant: \"default\" | \"destructive\"`, emitted as `data-variant`."},{"name":"inset","type":"boolean","required":false,"description":"Emits `data-inset` — indents the entry to align with icon-bearing siblings."},{"name":"checked","type":"boolean","required":false,"description":"Checked state for `type=\"checkbox\"` and `type=\"radio\"` entries."},{"name":"radioGroup","type":"string","required":false,"description":"Groups radio items — required for `type: \"radio\"` entries."},{"name":"radioValue","type":"string","required":false,"description":"`type: \"radio\"` entries only — this entry's value within its `radioGroup`."},{"name":"subEntries","type":"ContextMenuItem[]","required":false,"description":"`type: \"sub\"` entries only — the nested submenu's own item list. One level deep: a `type: \"sub\"` entry inside a submenu's own `subEntries` is not wired (same limit as menubar's submenu)."}]}]}
