// pure module ของแถบ tool หน้า annotate — ย้ายจาก PDFViewer.tsx แบบ // behavior-preserving (PLAN-TOOLBAR.md ขั้น 1) ไม่มี React/state เพื่อ unit // test ได้ (tests/components/toolbar-tools.test.ts) import { Eraser, EyeOff, Hand, Highlighter, MessageSquarePlus, MousePointer2, Pencil, Shapes, Stamp, Type, type LucideIcon, } from "lucide-react"; export type Tool = | "select" | "pan" | "highlight" | "draw" | "comment" | "shape" | "erase" | "redact" | "textbox" | "stamp"; export type ToolEntry = { key: Tool; label: string; text: string; Icon: LucideIcon }; /** label = ชื่อเต็มสำหรับ title/aria-label (พร้อมคีย์ลัด/คำอังกฤษประกอบ) — * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่ม (คำขอผู้ใช้ 2026-09-21: ผู้ใช้ศาล * ไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span ชื่อ * tool-active ของ H3 ที่ถอดออก) — สำหรับปุ่ม dropdown ที่มีตัวเลือกย่อย (มาร์ก * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) เคยแสดงชื่อตัวเลือกล่าสุดแทน text ตลอด (คำขอผู้ใช้ * 2026-09-23) แต่ทำให้ชื่อ tool หายไปจากแถบ ("วาดเส้น" เห็นเป็น "ปากกา" ตั้งแต่ * ยังไม่ได้เลือกอะไร) — 2026-09-29 จึงแสดงค่าล่าสุด**เฉพาะตอน tool active** * (ค่าที่จะใช้จริงเมื่อวาด) ไม่ active กลับมาแสดง text ชื่อ tool เหมือนปุ่มอื่น */ export const TOOLS: ToolEntry[] = [ { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, { key: "comment", label: "คอมเมนต์ (C)", text: "คอมเมนต์", Icon: MessageSquarePlus }, { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, { key: "erase", label: "ยางลบ (เส้น/รูปทรง/ตรายางของตัวเอง)", text: "ยางลบ", Icon: Eraser }, { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, { key: "textbox", label: "กล่องข้อความ", text: "กล่องข้อความ", Icon: Type }, { key: "stamp", label: "ตรายาง (stamp)", text: "ตรายาง", Icon: Stamp }, ]; /** N3 ไฟล์ภาพ + PDF สแกนล้วน: ไม่มีข้อความให้คลุม — ซ่อน tool มาร์กข้อความ * (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบ (hideHighlightTool ของ PDFViewer) */ export function visibleToolsFor(hideHighlight: boolean): ToolEntry[] { return hideHighlight ? TOOLS.filter((t) => t.key !== "highlight") : TOOLS; } /** ไฟล์ภาพ/ไฟล์สแกนล้วน: มาร์กข้อความใช้ไม่ได้ (ไม่มีข้อความให้คลุม) — ปุ่มถูกซ่อน * ไปแล้ว เหลือทางเข้า tool นี้คือคีย์ลัด H จึงกันที่ switchTool ให้จบ (สลับ tool * อื่นไม่กระทบ) */ export function canSwitchTo(t: Tool, hideHighlight: boolean): boolean { return !(t === "highlight" && hideHighlight); } /** หน้าตาปุ่ม tool dropdown ที่มีค่าย่อย (มาร์กข้อความ/วาดเส้น/รูปทรง/ตรายาง) — * ไม่ active → text ชื่อ tool + title = label เหมือนปุ่มเดี่ยวอื่น; * active + มีค่า (ค่าที่จะใช้จริงเมื่อวาด เช่น ชื่อปากกา/preset ตรายาง "กำหนดเอง") * → text = ค่า + title = "label — ค่า" ตาม pattern เดิม; * ไม่ส่งค่า (textbox ไม่มีค่าย่อย) → ชื่อ tool แม้ active */ export function toolButtonFace( active: boolean, tool: Pick, value?: string | null, ): { text: string; title: string } { const showValue = active && value != null; return { text: showValue ? value : tool.text, title: showValue ? `${tool.label} — ${value}` : tool.label, }; } /** จำ intent เปิด/พับเมนูของปุ่ม dropdown ก่อนเรียก switchTool (ซึ่งปิดเมนูทุกตัว): * กดปุ่มจาก tool อื่น = เปิดเมนูของ tool ที่เพิ่ง active, tool เดิม+เมนูปิด = เปิด, * tool เดิม+เมนูเปิด = พับ (toggle) */ export function menuIntentAfterClick(current: Tool, key: Tool, menuOpen: boolean): boolean { return current !== key || !menuOpen; } /** tool ที่ปุ่มเป็น dropdown มีเมนูของตัวเอง — ทุกจุดที่ "ปิดเมนูทั้งชุด" (switchTool/ * outside-click/resize/auto-select/เปิด popup bookmark/reset tool ตรง) ต้องวนจากชุด * ค่านี้จุดเดียว: แต่ก่อน enumerate setter ด้วยมือ 6 แห่ง จนเมนูใหม่/พฤติกรรมใหม่ลืม * แห่งเดียว = drift เงียบ (เคยเกิดจริง: effect เลื่อนปุ่ม active หาปุ่มไม่เจอเงียบ ๆ * ตั้งแต่ 2026-09-23) — ฝั่ง PDFViewer ผูก setter/ref เป็น Record ให้ * ลืมแถว = compile error, ฝั่งนี้ test cross-check กับ TOOLS */ export const TOOL_MENU_KEYS = ["highlight", "draw", "shape", "textbox", "stamp"] as const satisfies readonly Tool[]; export type MenuTool = (typeof TOOL_MENU_KEYS)[number]; /** คีย์ลัดสลับ tool (F3) — จับคู่ด้วย `e.code` (ตำแหน่งกายภาพของปุ่มตาม layout * สหรัฐ) ไม่ใช่ `e.key` (ตัวอักษรที่ layout ปัจจุบันจะพิมพ์): ผู้ใช้ศาลส่วนใหญ่สลับ * keyboard เป็นภาษาไทย ซึ่งปุ่มเดียวกันให้ e.key = "ภ" ไม่ใช่ "h" — เช็ค e.key * ทำให้คีย์ลัดทั้งชุดใช้ไม่ได้เลยบน layout ไทย (PLAN-TOOLBAR.md finding #1, * 2026-09-29) */ export const TOOL_SHORTCUTS: Readonly> = { KeyH: "highlight", KeyD: "draw", KeyC: "comment", };