Prisma
Live Preview

Dropdown Menu Component Generation Skill

Skill này hướng dẫn bạn (AI Agent) tạo component Dropdown Menu / Context Menu — danh sách actions hiển thị khi click trigger element.

1. Mục tiêu (Objective)

Tạo component Menu hỗ trợ nested submenus, keyboard shortcuts, grouped items, checkbox/radio items, và tích hợp 100% Design Tokens.

2. AI Context & Intent (Ngữ cảnh cho AI)

Khi nào dùng Dropdown Menu?

  • Action list: Click button → hiện list actions (Edit, Delete, Share...)
  • More actions: "⋯" button → hiện thêm options
  • User menu: Avatar click → Profile, Settings, Logout
  • Context menu: Right-click → context actions

Phân biệt với component khác

Tình huống Component đúng Lý do
Danh sách actions khi click Dropdown Menu Action items
Chọn giá trị từ list (form) Select Form input, selection
Interactive content (form, rich) Popover Rich content, not just actions
Confirm hành động Modal (alert) Blocking confirmation
Chú thích khi hover Tooltip Text-only, non-interactive

Decision Tree cho AI

text
Cần hiển thị list khi click?
├─ Danh sách actions (Edit, Delete...) → Dropdown Menu
│   ├─ Có sub-categories → Dropdown Menu with submenus
│   ├─ Có toggleable items → Dropdown Menu with checkbox/radio items
│   └─ Right-click triggered → Context Menu (same component)
├─ Chọn value cho form → Select
├─ Rich interactive content → Popover
└─ Info khi hover → Tooltip

3. Ngữ nghĩa & Phân loại (Semantics)

3.1. Item Types

Type Mô tả Visual
item Action item thường Icon + Label + Shortcut
checkbox Toggle on/off ☑/☐ + Label
radio Select 1 trong group ◉/○ + Label
label Section header (non-interactive) Uppercase, muted text
separator Visual divider Horizontal line
submenu Item mở sub-menu Label + Arrow →

3.2. Item Anatomy

text
┌─────────────────────────────────┐
│  [✓] [pencil] Edit          Ctrl+E  │ ← check + icon + label + shortcut
│       [copy] Copy            Ctrl+C  │
│       [clipboard] Paste      Ctrl+V  │
│  ─────────────────────────────  │ ← separator
│  GROUP LABEL                    │ ← label
│       [share] Share           ►    │ ← submenu arrow
│  ─────────────────────────────  │
│       [trash] Delete          ⌫     │ ← destructive item (red text)
└─────────────────────────────────┘

Icon: Tất cả icon trong Dropdown Menu PHẢI dùng Lucide SVG từ assets/icons/. CẤM dùng text emoji. Xem icon.md mục 10.

3.3. Destructive Items

  • Text và icon màu destructive (đỏ)
  • Hover background cũng khác (nhẹ đỏ)
  • Dùng cho: Delete, Remove, Revoke, Ban

3.5. Slot Map (Figma ↔ Code)

📎 Source: slot-manifest.jsondropdown-menu · Layer: surface

Figma Slot data-slot CSS Class Required Accepts
Root dropdown-menu .dropdown-menu
Trigger dropdown-menu-trigger button, icon-button
Content dropdown-menu-content .dropdown-menu-content dropdown-menu-item-group
Item dropdown-menu-item .dropdown-menu-item ✅ (n×) menu-item
Item Icon dropdown-menu-item-icon icon
Item Label dropdown-menu-item-label text
Shortcut dropdown-menu-item-shortcut text
Separator dropdown-menu-separator divider

4. Token Mapping

?? Atomic Mapping: Xem ATOMIC-MAPPING.md ? m?c dropdown-menu. Component token JSON files d� deprecated.

Semantic color tokens (qua Mode layer) và state-specific colors được define đầy đủ trong file .tokens.json.

5. Props & API

typescript
interface DropdownMenuProps {
  /** Trigger element */
  trigger: ReactNode;
  /** Align popup relative to trigger */
  align?: 'start' | 'center' | 'end';
  /** Side of trigger to show */
  side?: 'top' | 'bottom' | 'left' | 'right';
  children: ReactNode;
}

interface MenuItemProps {
  /** Label */
  label: string;
  /** Leading icon */
  icon?: ReactNode;
  /** Keyboard shortcut hint */
  shortcut?: string;
  /** Disabled */
  disabled?: boolean;
  /** Destructive styling */
  destructive?: boolean;
  /** Click handler */
  onClick?: () => void;
}

interface MenuCheckboxItemProps {
  label: string;
  checked?: boolean;
  onChange?: (checked: boolean) => void;
}

interface MenuRadioGroupProps {
  value?: string;
  onChange?: (value: string) => void;
  children: ReactNode;
}

interface MenuSubProps {
  /** Sub-menu trigger label */
  label: string;
  icon?: ReactNode;
  children: ReactNode;
}

6. Accessibility (a11y)

  • Trigger: aria-haspopup="menu", aria-expanded="true"/"false".
  • Menu: role="menu".
  • Item: role="menuitem". Checkbox: role="menuitemcheckbox". Radio: role="menuitemradio".
  • Keyboard: Enter/Space activate item, Arrow Up/Down navigate, Arrow Right open submenu, Arrow Left close submenu, Escape close menu.
  • Disabled: aria-disabled="true", skip khi arrow-key navigate.
  • Shortcut: Hiển thị visual + aria-keyshortcuts attribute.
  • Focus: Auto-focus first item khi menu opens. Return focus to trigger khi close.
  • Typeahead: Gõ ký tự → focus item có label bắt đầu bằng ký tự đó.

7. Best Practices & Rules

  • Portal: Render menu qua Portal — tránh overflow/z-index issues.
  • Collision detection: Auto-flip position khi không đủ viewport space.
  • Close triggers: Click item, click outside, Escape, scroll page.
  • Submenu delay: 200ms delay trước khi mở submenu (tránh flash khi di chuột ngang).
  • Max items visible: ~8-10 items, scroll nếu nhiều hơn.
  • Shortcut alignment: Keyboard shortcuts right-aligned, mono font.
  • Destructive cuối cùng: Đặt destructive items ở cuối menu, sau separator.
  • Không Hardcode: Mọi giá trị từ Token.
  • Icon: CHỈ dùng Lucide icon từ assets/icons/. CẤM dùng text emoji. Xem icon.md mục 10.

8. Example Usage

jsx
{/* Basic dropdown */}
<DropdownMenu trigger={<Button variant="ghost" icon={<MoreIcon />} />}>
  <MenuItem icon={<EditIcon />} label="Chỉnh sửa" shortcut="Ctrl+E" onClick={edit} />
  <MenuItem icon={<CopyIcon />} label="Sao chép" shortcut="Ctrl+C" onClick={copy} />
  <MenuSeparator />
  <MenuLabel>Chia sẻ</MenuLabel>
  <MenuSub label="Chia sẻ qua..." icon={<ShareIcon />}>
    <MenuItem label="Email" onClick={shareEmail} />
    <MenuItem label="Link" onClick={shareLink} />
  </MenuSub>
  <MenuSeparator />
  <MenuItem icon={<TrashIcon />} label="Xoá" destructive onClick={del} />
</DropdownMenu>

{/* User menu with checkbox */}
<DropdownMenu trigger={<Avatar src="/user.jpg" size="sm" />} align="end">
  <MenuLabel>Ngọc Lê</MenuLabel>
  <MenuItem label="Hồ sơ" href="/profile" />
  <MenuItem label="Cài đặt" href="/settings" />
  <MenuSeparator />
  <MenuCheckboxItem label="Chế độ tối" checked={isDark} onChange={setIsDark} />
  <MenuSeparator />
  <MenuItem label="Đăng xuất" onClick={logout} />
</DropdownMenu>