Frontend Lab
← 回到所有模式
PB06🪟 Overlay · 覆蓋層選單

Context Menu

右鍵選單

難度 ★★★★★標籤 右鍵 · menu · 位置3 min read

模式簡述

Context Menu:右鍵或長按觸發的選單。檔案管理器、編輯器標配。要處理多種觸發方式、多種位置計算。

右鍵點擊此區域

關閉|預設關閉

Claude Code Prompt

💬 Claude Code Prompt
參考:PB06 context-menu
組件:ContextMenu.tsx

Context Menu:右鍵或長按觸發的選單。檔案管理器、編輯器標配。要處理多種觸發方式、多種位置計算。

限制:
- 沒做螢幕邊界判斷
- 手機長按沒實作
- 選單關閉行為不一致

完整程式碼

ContextMenu.tsx
"use client";
import { useEffect, useState } from "react";
import { Copy, Pencil, Trash2, Share2 } from "lucide-react";

type State = "closed" | "open";

export function ContextMenu({ state = "open" }: { state?: State }) {
const [pos, setPos] = useState<{ x: number; y: number } | null>(
  state === "open" ? { x: 80, y: 60 } : null,
);

useEffect(() => {
  setPos(state === "open" ? { x: 80, y: 60 } : null);
}, [state]);

return (
  <div
    onContextMenu={(e) => {
      e.preventDefault();
      const rect = e.currentTarget.getBoundingClientRect();
      setPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
    }}
    onClick={() => setPos(null)}
    className="relative grid h-[260px] w-full max-w-md place-items-center rounded-lg border border-dashed border-border/60 bg-background/30"
  >
    <p className="font-mono text-xs text-muted-foreground">
      右鍵點擊此區域
    </p>
    {pos && (
      <div
        style={{ left: pos.x, top: pos.y }}
        className="absolute z-10 w-44 overflow-hidden rounded-md border border-border bg-background py-1 shadow-xl animate-in fade-in zoom-in-95 duration-100"
      >
        {[
          { icon: Copy, label: "複製", shortcut: "⌘C" },
          { icon: Pencil, label: "重新命名" },
          { icon: Share2, label: "分享" },
        ].map((it, i) => (
          <button
            key={i}
            className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-lab-accent/10"
          >
            <it.icon className="h-3.5 w-3.5" />
            <span className="flex-1">{it.label}</span>
            {it.shortcut && <kbd className="font-mono text-[10px] text-muted-foreground">{it.shortcut}</kbd>}
          </button>
        ))}
        <div className="my-1 border-t border-border" />
        <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">
          <Trash2 className="h-3.5 w-3.5" />
          刪除
        </button>
      </div>
    )}
  </div>
);
}

何時用

  • ✅ 檔案/列表項的次要操作(重新命名、刪除、複製連結)
  • ✅ 編輯器上下文相關的快捷操作
  • ✅ 需要對單一物件做多種操作

何時別用

  • ❌ 手機優先的介面(右鍵概念不存在)
  • ❌ 只有兩三個操作(用 dropdown 或 inline 按鈕)
  • ❌ 操作會破壞性影響全站(用 modal 確認)

我踩過的坑

  1. 沒做螢幕邊界判斷:右下角點擊選單超出視口。算位置時要 clamp 到 vw/vh 內。

  2. 手機長按沒實作:iOS Safari 長按會跳系統選單,要 e.preventDefault()。

  3. 選單關閉行為不一致:使用者點外面就關,但選了選項也要關,按 ESC 也要關。

相關模式

  • PB07
  • PE08

搜尋

按 ⌘K 隨時開啟