Components

ContextMenu

Custom right-click context menu with nested submenu support

Overview

ContextMenu is a custom right-click menu. Built without external libraries, it supports keyboard navigation and nested submenus.

  • Compound — Root, Trigger, Content, Item, SubRoot, SubTrigger, SubContent
  • Keyboard — Arrow keys, Home/End, Enter/Space, Escape
  • Submenu — Nested submenus, openable via hover/keyboard
  • Positioning — Displayed at click position, auto-adjusted within viewport

Basic Usage

import { ContextMenu } from '@illog/ui'
 
<ContextMenu.Root>
  <ContextMenu.Trigger>
    <div className="workspace">
      Right-click area
    </div>
  </ContextMenu.Trigger>
  <ContextMenu.Content>
    <ContextMenu.Item onSelect={() => handleEdit()}>
      Edit
    </ContextMenu.Item>
    <ContextMenu.Item onSelect={() => handleDuplicate()}>
      Duplicate
    </ContextMenu.Item>
    <ContextMenu.Item onSelect={() => handleDelete()}>
      Delete
    </ContextMenu.Item>
  </ContextMenu.Content>
</ContextMenu.Root>
<ContextMenu.Content>
  <ContextMenu.Item onSelect={handleCut}>Cut</ContextMenu.Item>
  <ContextMenu.Item onSelect={handleCopy}>Copy</ContextMenu.Item>
  <ContextMenu.SubRoot>
    <ContextMenu.SubTrigger>Sort By</ContextMenu.SubTrigger>
    <ContextMenu.SubContent>
      <ContextMenu.Item onSelect={() => sort('name')}>Name</ContextMenu.Item>
      <ContextMenu.Item onSelect={() => sort('date')}>Date</ContextMenu.Item>
      <ContextMenu.Item onSelect={() => sort('size')}>Size</ContextMenu.Item>
    </ContextMenu.SubContent>
  </ContextMenu.SubRoot>
  <ContextMenu.Item isDisabled>Paste</ContextMenu.Item>
</ContextMenu.Content>

Sub-components

ContextMenu.Root

PropTypeDefaultDescription
modalbooleanfalseModal mode
onOpenChange(isOpen: boolean) => voidOpen state callback

ContextMenu.Trigger

PropTypeDefaultDescription
isDisabledbooleanDisable right-click

ContextMenu.Content

PropTypeDefaultDescription
isLoopbooleantrueKeyboard navigation loop
alignOffsetnumber0Alignment offset
onEscapeKeyDown(e: KeyboardEvent) => voidEscape key handler

ContextMenu.Item

PropTypeDefaultDescription
onSelect() => voidSelect handler (auto-closes after selection)
isDisabledbooleanfalseDisabled state
textValuestringdata-value attribute

ContextMenu.SubRoot

PropTypeDefaultDescription
isOpenbooleanControlled open state
isDefaultOpenbooleanfalseDefault open state
onOpenChange(isOpen: boolean) => voidOpen state callback

ContextMenu.SubTrigger

PropTypeDefaultDescription
isDisabledbooleanDisabled state

ContextMenu.SubContent

PropTypeDefaultDescription
isLoopbooleantrueKeyboard navigation loop
sideOffsetnumber0Gap beside the trigger
alignOffsetnumber0Vertical alignment offset

Keyboard Navigation

KeyAction
/ Move between items
Home / EndFirst/last item
Enter / SpaceSelect item
Open submenu
Close submenu
EscapeClose menu

Guidelines

  • Right-click is ignored when an input/textarea has focus (browser default menu is used)
  • The menu automatically closes on outside click or scroll
  • Submenus open after 100ms hover and close after 150ms mouse leave