PB07▸🪟 Overlay · 覆蓋層▸選單
Dropdown Menu
下拉選單
難度 ★★★★★標籤 dropdown · menu · 鍵盤3 min read
模式簡述
Dropdown:按鈕點開後跟著的選單。和 popover 像但通常只有列表,並支援鍵盤上下選擇。
Claude Code Prompt
參考:PB07 dropdown 組件:Dropdown.tsx Dropdown:按鈕點開後跟著的選單。和 popover 像但通常只有列表,並支援鍵盤上下選擇。 限制: - 沒處理鍵盤導航 - 外點關閉但點按鈕也關閉 - 寬度跟 trigger 對不齊
完整程式碼
"use client";
import { useEffect, useState } from "react";
import { ChevronDown, User, Settings, LogOut, CreditCard } from "lucide-react";
type State = "closed" | "open" | "grouped";
export function Dropdown({ state = "open" }: { state?: State }) {
const [open, setOpen] = useState(state !== "closed");
useEffect(() => setOpen(state !== "closed"), [state]);
const grouped = state === "grouped";
return (
<div className="grid h-[280px] w-full max-w-md place-items-center">
<div className="relative">
<button
onClick={() => setOpen((o) => !o)}
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-background/50 px-3 py-1.5 text-xs hover:border-lab-accent"
>
帳戶 <ChevronDown className="h-3 w-3" />
</button>
{open && (
<div className="absolute left-0 top-full z-10 mt-1 w-48 overflow-hidden rounded-md border border-border bg-background py-1 shadow-xl animate-in fade-in zoom-in-95 duration-100">
{grouped && (
<p className="px-2.5 py-1 font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70">
帳戶
</p>
)}
<button className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-lab-accent/10">
<User className="h-3.5 w-3.5" /> 個人資料
</button>
<button className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-lab-accent/10">
<CreditCard className="h-3.5 w-3.5" /> 帳單
</button>
<button className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-lab-accent/10">
<Settings className="h-3.5 w-3.5" /> 設定
</button>
{grouped && (
<>
<div className="my-1 border-t border-border" />
<p className="px-2.5 py-1 font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70">
危險區
</p>
</>
)}
<button className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs text-red-500 hover:bg-red-500/10">
<LogOut className="h-3.5 w-3.5" /> 登出
</button>
</div>
)}
</div>
</div>
);
}何時用
- ✅ 使用者頭像下拉(登出、設定)
- ✅ 次要操作集合(more menu)
- ✅ 排序、篩選的選項列表
何時別用
- ❌ 選項超過 8 個(用 combobox + search)
- ❌ 需要多選(用 multi-select)
- ❌ 選項需要圖示和描述(用 mega menu)
我踩過的坑
-
沒處理鍵盤導航:tab 直接跳過所有選項。要綁 ArrowUp/Down 切 focus。
-
外點關閉但點按鈕也關閉:點按鈕應該 toggle,外點才是 close。注意 e.stopPropagation。
-
寬度跟 trigger 對不齊:用 floating-ui 的 size middleware 抓 anchor 寬。
相關模式
- PB05
- PE01