# ContextMenu

## Description

A right-click context menu component that displays a list of actions when triggered by a right-click or programmatic event. Built on top of the List component, it provides contextual actions and navigation options with keyboard support and proper positioning relative to the trigger point.

## Aliases

- ContextMenu
- RightClickMenu
- ContextualMenu
- PopupMenu
- ActionMenu

## Props Breakdown

**Extends:** ListProps<T> (inherits all List component properties)

| Prop | Type | Default | Required | Description |
|------|------|---------|----------|-------------|
| `items` | `T[]` | - | Yes | Array of menu items to display |
| `onSelect` | `(item: T) => void` | - | No | Callback fired when a menu item is selected |
| `trigger` | `ReactNode` | - | No | Element that triggers the context menu |
| `position` | `{ x: number, y: number }` | - | No | Position coordinates for the menu |
| `visible` | `boolean` | `false` | No | Controls menu visibility |
| `onClose` | `() => void` | - | No | Callback fired when menu should close |
| `className` | `string` | - | No | Additional CSS class names |
| `component-variant` | `string` | - | No | Override styling variant |

## Examples

### Basic Context Menu
```tsx
import { ContextMenu, ListItem } from '@delightui/components';

function BasicExample() {
  const [menuVisible, setMenuVisible] = useState(false);
  const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });

  const menuItems = [
    { id: '1', label: 'Copy', icon: 'Copy' },
    { id: '2', label: 'Paste', icon: 'Paste' },
    { id: '3', label: 'Delete', icon: 'Delete' }
  ];

  const handleRightClick = (event) => {
    event.preventDefault();
    setMenuPosition({ x: event.clientX, y: event.clientY });
    setMenuVisible(true);
  };

  const handleSelect = (item) => {
    console.log('Selected:', item.label);
    setMenuVisible(false);
  };

  return (
    <div>
      <div 
        onContextMenu={handleRightClick}
        style={{ padding: '50px', border: '1px dashed #ccc' }}
      >
        Right-click here for context menu
      </div>
      
      <ContextMenu
        items={menuItems}
        visible={menuVisible}
        position={menuPosition}
        onSelect={handleSelect}
        onClose={() => setMenuVisible(false)}
      />
    </div>
  );
}
```

### File Manager Context Menu
```tsx
function FileManagerExample() {
  const [selectedFile, setSelectedFile] = useState(null);
  const [menuVisible, setMenuVisible] = useState(false);
  const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });

  const files = [
    { id: '1', name: 'Document.pdf', type: 'pdf' },
    { id: '2', name: 'Image.jpg', type: 'image' },
    { id: '3', name: 'Spreadsheet.xlsx', type: 'excel' }
  ];

  const getContextMenuItems = (file) => {
    const baseItems = [
      { id: 'open', label: 'Open', icon: 'Open' },
      { id: 'rename', label: 'Rename', icon: 'Edit' },
      { id: 'copy', label: 'Copy', icon: 'Copy' },
      { id: 'separator1', type: 'separator' },
      { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
    ];

    if (file.type === 'image') {
      baseItems.splice(1, 0, { id: 'preview', label: 'Preview', icon: 'Eye' });
    }

    return baseItems;
  };

  const handleFileRightClick = (event, file) => {
    event.preventDefault();
    setSelectedFile(file);
    setMenuPosition({ x: event.clientX, y: event.clientY });
    setMenuVisible(true);
  };

  const handleMenuSelect = (item) => {
    if (!selectedFile) return;

    switch (item.id) {
      case 'open':
        console.log('Opening:', selectedFile.name);
        break;
      case 'rename':
        // Show rename dialog
        break;
      case 'copy':
        navigator.clipboard.writeText(selectedFile.name);
        break;
      case 'preview':
        // Show image preview
        break;
      case 'delete':
        // Show delete confirmation
        break;
    }
    
    setMenuVisible(false);
    setSelectedFile(null);
  };

  return (
    <div className="file-manager">
      {files.map(file => (
        <div
          key={file.id}
          className="file-item"
          onContextMenu={(e) => handleFileRightClick(e, file)}
        >
          <Icon icon={file.type === 'pdf' ? 'FilePdf' : file.type === 'image' ? 'FileImage' : 'FileExcel'} />
          <Text>{file.name}</Text>
        </div>
      ))}
      
      <ContextMenu
        items={selectedFile ? getContextMenuItems(selectedFile) : []}
        visible={menuVisible}
        position={menuPosition}
        onSelect={handleMenuSelect}
        onClose={() => {
          setMenuVisible(false);
          setSelectedFile(null);
        }}
      />
    </div>
  );
}
```

### Table Row Context Menu
```tsx
function TableContextExample() {
  const [users] = useState([
    { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User' }
  ]);

  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 },
    user: null
  });

  const getMenuItems = (user) => [
    { id: 'view', label: 'View Profile', icon: 'Person' },
    { id: 'edit', label: 'Edit User', icon: 'Edit' },
    { id: 'separator1', type: 'separator' },
    { id: 'promote', label: user.role === 'Admin' ? 'Demote to User' : 'Promote to Admin', icon: 'Star' },
    { id: 'separator2', type: 'separator' },
    { id: 'delete', label: 'Delete User', icon: 'Delete', style: 'destructive' }
  ];

  const handleRowRightClick = (event, user) => {
    event.preventDefault();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      user
    });
  };

  const handleMenuSelect = (item) => {
    const { user } = contextMenu;
    
    switch (item.id) {
      case 'view':
        console.log('Viewing profile for:', user.name);
        break;
      case 'edit':
        console.log('Editing user:', user.name);
        break;
      case 'promote':
        console.log('Changing role for:', user.name);
        break;
      case 'delete':
        console.log('Deleting user:', user.name);
        break;
    }
    
    setContextMenu({ visible: false, position: { x: 0, y: 0 }, user: null });
  };

  return (
    <div className="user-table">
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Email</th>
            <th>Role</th>
          </tr>
        </thead>
        <tbody>
          {users.map(user => (
            <tr 
              key={user.id}
              onContextMenu={(e) => handleRowRightClick(e, user)}
              className="table-row"
            >
              <td>{user.name}</td>
              <td>{user.email}</td>
              <td>{user.role}</td>
            </tr>
          ))}
        </tbody>
      </table>
      
      <ContextMenu
        items={contextMenu.user ? getMenuItems(contextMenu.user) : []}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 }, user: null })}
      />
    </div>
  );
}
```

### Canvas Context Menu
```tsx
function CanvasContextExample() {
  const [shapes, setShapes] = useState([
    { id: '1', type: 'rectangle', x: 50, y: 50, selected: false },
    { id: '2', type: 'circle', x: 150, y: 100, selected: false }
  ]);
  
  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 },
    target: null
  });

  const getCanvasMenuItems = () => [
    { id: 'paste', label: 'Paste', icon: 'Paste' },
    { id: 'separator1', type: 'separator' },
    { id: 'add-rectangle', label: 'Add Rectangle', icon: 'Rectangle' },
    { id: 'add-circle', label: 'Add Circle', icon: 'Circle' },
    { id: 'separator2', type: 'separator' },
    { id: 'clear-all', label: 'Clear All', icon: 'Delete', style: 'destructive' }
  ];

  const getShapeMenuItems = () => [
    { id: 'copy', label: 'Copy', icon: 'Copy' },
    { id: 'duplicate', label: 'Duplicate', icon: 'Duplicate' },
    { id: 'separator1', type: 'separator' },
    { id: 'bring-front', label: 'Bring to Front', icon: 'ArrowUp' },
    { id: 'send-back', label: 'Send to Back', icon: 'ArrowDown' },
    { id: 'separator2', type: 'separator' },
    { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
  ];

  const handleCanvasRightClick = (event) => {
    event.preventDefault();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      target: 'canvas'
    });
  };

  const handleShapeRightClick = (event, shape) => {
    event.preventDefault();
    event.stopPropagation();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      target: shape
    });
  };

  const handleMenuSelect = (item) => {
    if (contextMenu.target === 'canvas') {
      switch (item.id) {
        case 'add-rectangle':
          const newRect = { 
            id: Date.now().toString(), 
            type: 'rectangle', 
            x: contextMenu.position.x, 
            y: contextMenu.position.y, 
            selected: false 
          };
          setShapes(prev => [...prev, newRect]);
          break;
        case 'clear-all':
          setShapes([]);
          break;
      }
    } else {
      // Handle shape-specific actions
      switch (item.id) {
        case 'delete':
          setShapes(prev => prev.filter(s => s.id !== contextMenu.target.id));
          break;
        case 'duplicate':
          const duplicate = { 
            ...contextMenu.target, 
            id: Date.now().toString(), 
            x: contextMenu.target.x + 20, 
            y: contextMenu.target.y + 20 
          };
          setShapes(prev => [...prev, duplicate]);
          break;
      }
    }
    
    setContextMenu({ visible: false, position: { x: 0, y: 0 }, target: null });
  };

  return (
    <div className="canvas-container">
      <div
        className="canvas"
        onContextMenu={handleCanvasRightClick}
        style={{ width: '400px', height: '300px', border: '1px solid #ccc', position: 'relative' }}
      >
        {shapes.map(shape => (
          <div
            key={shape.id}
            className={`shape ${shape.type}`}
            onContextMenu={(e) => handleShapeRightClick(e, shape)}
            style={{
              position: 'absolute',
              left: shape.x,
              top: shape.y,
              width: '50px',
              height: '50px',
              backgroundColor: shape.type === 'rectangle' ? '#4CAF50' : '#2196F3',
              borderRadius: shape.type === 'circle' ? '50%' : '0'
            }}
          />
        ))}
      </div>
      
      <ContextMenu
        items={contextMenu.target === 'canvas' ? getCanvasMenuItems() : getShapeMenuItems()}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 }, target: null })}
      />
    </div>
  );
}
```

### Text Editor Context Menu
```tsx
function TextEditorContextExample() {
  const [selectedText, setSelectedText] = useState('');
  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 }
  });

  const getTextMenuItems = () => {
    const hasSelection = selectedText.length > 0;
    
    return [
      { id: 'cut', label: 'Cut', icon: 'Cut', disabled: !hasSelection },
      { id: 'copy', label: 'Copy', icon: 'Copy', disabled: !hasSelection },
      { id: 'paste', label: 'Paste', icon: 'Paste' },
      { id: 'separator1', type: 'separator' },
      { id: 'select-all', label: 'Select All', icon: 'SelectAll' },
      { id: 'separator2', type: 'separator' },
      { id: 'bold', label: 'Bold', icon: 'Bold', disabled: !hasSelection },
      { id: 'italic', label: 'Italic', icon: 'Italic', disabled: !hasSelection },
      { id: 'underline', label: 'Underline', icon: 'Underline', disabled: !hasSelection }
    ];
  };

  const handleTextAreaRightClick = (event) => {
    event.preventDefault();
    const textarea = event.target;
    const selection = window.getSelection()?.toString() || '';
    setSelectedText(selection);
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY }
    });
  };

  const handleMenuSelect = (item) => {
    switch (item.id) {
      case 'cut':
        document.execCommand('cut');
        break;
      case 'copy':
        document.execCommand('copy');
        break;
      case 'paste':
        document.execCommand('paste');
        break;
      case 'select-all':
        document.execCommand('selectAll');
        break;
      case 'bold':
        document.execCommand('bold');
        break;
      case 'italic':
        document.execCommand('italic');
        break;
      case 'underline':
        document.execCommand('underline');
        break;
    }
    
    setContextMenu({ visible: false, position: { x: 0, y: 0 } });
  };

  return (
    <div className="text-editor">
      <div
        contentEditable
        className="editor-content"
        onContextMenu={handleTextAreaRightClick}
        style={{
          border: '1px solid #ccc',
          padding: '10px',
          minHeight: '200px',
          outline: 'none'
        }}
      >
        Right-click anywhere in this text editor to see formatting options. 
        Select some text first to enable cut, copy, and formatting commands.
      </div>
      
      <ContextMenu
        items={getTextMenuItems()}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 } })}
      />
    </div>
  );
}
```

### Multi-Level Context Menu
```tsx
function MultiLevelContextExample() {
  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 },
    level: 'main'
  });

  const getMainMenuItems = () => [
    { id: 'new', label: 'New', icon: 'Add', hasSubmenu: true },
    { id: 'edit', label: 'Edit', icon: 'Edit' },
    { id: 'view', label: 'View', icon: 'Eye', hasSubmenu: true },
    { id: 'separator1', type: 'separator' },
    { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
  ];

  const getNewSubmenuItems = () => [
    { id: 'new-file', label: 'File', icon: 'File' },
    { id: 'new-folder', label: 'Folder', icon: 'Folder' },
    { id: 'new-project', label: 'Project', icon: 'Project' },
    { id: 'separator1', type: 'separator' },
    { id: 'back', label: 'Back', icon: 'ArrowBack' }
  ];

  const getViewSubmenuItems = () => [
    { id: 'view-list', label: 'List View', icon: 'List' },
    { id: 'view-grid', label: 'Grid View', icon: 'Grid' },
    { id: 'view-details', label: 'Details View', icon: 'Details' },
    { id: 'separator1', type: 'separator' },
    { id: 'back', label: 'Back', icon: 'ArrowBack' }
  ];

  const getCurrentItems = () => {
    switch (contextMenu.level) {
      case 'new':
        return getNewSubmenuItems();
      case 'view':
        return getViewSubmenuItems();
      default:
        return getMainMenuItems();
    }
  };

  const handleRightClick = (event) => {
    event.preventDefault();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      level: 'main'
    });
  };

  const handleMenuSelect = (item) => {
    if (item.hasSubmenu) {
      setContextMenu(prev => ({ ...prev, level: item.id }));
      return;
    }

    if (item.id === 'back') {
      setContextMenu(prev => ({ ...prev, level: 'main' }));
      return;
    }

    console.log('Selected:', item.label);
    setContextMenu({ visible: false, position: { x: 0, y: 0 }, level: 'main' });
  };

  return (
    <div>
      <div 
        onContextMenu={handleRightClick}
        style={{ 
          padding: '100px', 
          border: '2px dashed #ccc',
          textAlign: 'center'
        }}
      >
        Right-click for multi-level context menu
      </div>
      
      <ContextMenu
        items={getCurrentItems()}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 }, level: 'main' })}
      />
    </div>
  );
}
```

### Image Gallery Context Menu
```tsx
function ImageGalleryContextExample() {
  const [images] = useState([
    { id: '1', src: '/image1.jpg', name: 'Sunset.jpg', favorite: false },
    { id: '2', src: '/image2.jpg', name: 'Mountains.jpg', favorite: true },
    { id: '3', src: '/image3.jpg', name: 'Ocean.jpg', favorite: false }
  ]);

  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 },
    image: null
  });

  const getImageMenuItems = (image) => [
    { id: 'view', label: 'View Full Size', icon: 'Eye' },
    { id: 'favorite', label: image.favorite ? 'Remove from Favorites' : 'Add to Favorites', icon: image.favorite ? 'StarFilled' : 'Star' },
    { id: 'separator1', type: 'separator' },
    { id: 'copy', label: 'Copy Image', icon: 'Copy' },
    { id: 'copy-link', label: 'Copy Link', icon: 'Link' },
    { id: 'separator2', type: 'separator' },
    { id: 'rename', label: 'Rename', icon: 'Edit' },
    { id: 'download', label: 'Download', icon: 'Download' },
    { id: 'separator3', type: 'separator' },
    { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
  ];

  const handleImageRightClick = (event, image) => {
    event.preventDefault();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      image
    });
  };

  const handleMenuSelect = (item) => {
    const { image } = contextMenu;
    
    switch (item.id) {
      case 'view':
        console.log('Viewing full size:', image.name);
        break;
      case 'favorite':
        console.log(image.favorite ? 'Removing from favorites:' : 'Adding to favorites:', image.name);
        break;
      case 'copy':
        console.log('Copying image:', image.name);
        break;
      case 'copy-link':
        navigator.clipboard.writeText(image.src);
        break;
      case 'rename':
        console.log('Renaming:', image.name);
        break;
      case 'download':
        console.log('Downloading:', image.name);
        break;
      case 'delete':
        console.log('Deleting:', image.name);
        break;
    }
    
    setContextMenu({ visible: false, position: { x: 0, y: 0 }, image: null });
  };

  return (
    <div className="image-gallery">
      <div className="gallery-grid">
        {images.map(image => (
          <div
            key={image.id}
            className="gallery-item"
            onContextMenu={(e) => handleImageRightClick(e, image)}
          >
            <Image src={image.src} alt={image.name} />
            <div className="image-info">
              <Text>{image.name}</Text>
              {image.favorite && <Icon icon="Star" className="favorite-icon" />}
            </div>
          </div>
        ))}
      </div>
      
      <ContextMenu
        items={contextMenu.image ? getImageMenuItems(contextMenu.image) : []}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 }, image: null })}
      />
    </div>
  );
}
```

### Tree View Context Menu
```tsx
function TreeViewContextExample() {
  const [treeData] = useState([
    {
      id: '1',
      name: 'Documents',
      type: 'folder',
      children: [
        { id: '1-1', name: 'Resume.pdf', type: 'file' },
        { id: '1-2', name: 'Cover Letter.docx', type: 'file' }
      ]
    },
    {
      id: '2',
      name: 'Images',
      type: 'folder',
      children: [
        { id: '2-1', name: 'Vacation.jpg', type: 'file' }
      ]
    }
  ]);

  const [contextMenu, setContextMenu] = useState({
    visible: false,
    position: { x: 0, y: 0 },
    node: null
  });

  const getFolderMenuItems = () => [
    { id: 'open', label: 'Open', icon: 'FolderOpen' },
    { id: 'separator1', type: 'separator' },
    { id: 'new-folder', label: 'New Folder', icon: 'FolderAdd' },
    { id: 'new-file', label: 'New File', icon: 'FileAdd' },
    { id: 'separator2', type: 'separator' },
    { id: 'rename', label: 'Rename', icon: 'Edit' },
    { id: 'copy', label: 'Copy', icon: 'Copy' },
    { id: 'separator3', type: 'separator' },
    { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
  ];

  const getFileMenuItems = () => [
    { id: 'open', label: 'Open', icon: 'Open' },
    { id: 'open-with', label: 'Open With...', icon: 'Apps' },
    { id: 'separator1', type: 'separator' },
    { id: 'rename', label: 'Rename', icon: 'Edit' },
    { id: 'copy', label: 'Copy', icon: 'Copy' },
    { id: 'cut', label: 'Cut', icon: 'Cut' },
    { id: 'separator2', type: 'separator' },
    { id: 'properties', label: 'Properties', icon: 'Info' },
    { id: 'separator3', type: 'separator' },
    { id: 'delete', label: 'Delete', icon: 'Delete', style: 'destructive' }
  ];

  const handleNodeRightClick = (event, node) => {
    event.preventDefault();
    event.stopPropagation();
    setContextMenu({
      visible: true,
      position: { x: event.clientX, y: event.clientY },
      node
    });
  };

  const handleMenuSelect = (item) => {
    const { node } = contextMenu;
    console.log(`${item.label} on ${node.type}: ${node.name}`);
    setContextMenu({ visible: false, position: { x: 0, y: 0 }, node: null });
  };

  const renderTreeNode = (node, level = 0) => (
    <div key={node.id} style={{ marginLeft: `${level * 20}px` }}>
      <div
        className="tree-node"
        onContextMenu={(e) => handleNodeRightClick(e, node)}
        style={{ padding: '4px', cursor: 'pointer' }}
      >
        <Icon icon={node.type === 'folder' ? 'Folder' : 'File'} />
        <Text>{node.name}</Text>
      </div>
      {node.children?.map(child => renderTreeNode(child, level + 1))}
    </div>
  );

  return (
    <div className="tree-view">
      <div className="tree-container">
        {treeData.map(node => renderTreeNode(node))}
      </div>
      
      <ContextMenu
        items={contextMenu.node?.type === 'folder' ? getFolderMenuItems() : getFileMenuItems()}
        visible={contextMenu.visible}
        position={contextMenu.position}
        onSelect={handleMenuSelect}
        onClose={() => setContextMenu({ visible: false, position: { x: 0, y: 0 }, node: null })}
      />
    </div>
  );
}
```