diff --git a/app/globals.css b/app/globals.css index e0de124..d0b394e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -90,10 +90,5 @@ header .touch-44 > div > a { min-height: 2.75rem; min-width: 2.75rem; - /* min 44px ทำให้ปุ่ม icon เดี่ยวกว้างเกิน content — ต้อง center icon - ไม่งั้นชิดซ้าย+ลอยตาม baseline (ปุ่ม dropdown มี flex อยู่แล้ว ไม่กระทบ) */ - display: inline-flex; - align-items: center; - justify-content: center; } } diff --git a/components/pdf/InkLayer.tsx b/components/pdf/InkLayer.tsx index d26a1ce..37cbfbe 100644 --- a/components/pdf/InkLayer.tsx +++ b/components/pdf/InkLayer.tsx @@ -20,8 +20,7 @@ import { type Rect, type Size, } from "@/utils/coordinates"; -import { annotationBBox, hitTestPath, hitTestShape } from "@/utils/geometry"; -import type { InkStamp } from "./TextboxStampLayer"; +import { hitTestPath, hitTestShape } from "@/utils/geometry"; /** เส้นวาดที่เก็บใน DB — จุดทุกจุดเป็น % ของหน้า, strokeWidth เป็น % ของความกว้าง */ export type InkStroke = { @@ -80,7 +79,6 @@ export default function InkLayer({ size, strokes, shapes, - stamps, redactions, active, nextStroke, @@ -99,8 +97,6 @@ export default function InkLayer({ strokes: InkStroke[]; /** รูปทรงเดิมจาก DB (พิกัด %) */ shapes: InkShape[]; - /** ตรายางเดิมจาก DB (พิกัด %) — render ที่ TextboxStampLayer แต่ eraser hit-test ที่นี่ */ - stamps: InkStamp[]; /** ปิดพรางเดิมจาก DB (พิกัด %) — วาดทับบังข้อความจริงบนจอ (F8) */ redactions: InkRedaction[]; /** tool วาด/รูปทรง/eraser/redact → รับ pointer events */ @@ -293,27 +289,11 @@ export default function InkLayer({ } } - /** hit-test ตำแหน่ง pointer กับเส้น/รูปทรง/ตรายาง **ของตัวเอง** — โดนเมื่อไรลบทันทีทีละรายการ (F6) */ + /** hit-test ตำแหน่ง pointer กับเส้น/รูปทรง **ของตัวเอง** — โดนเมื่อไรลบทันทีทีละรายการ (F6) */ function eraseAt(e: ReactPointerEvent) { if (!onErase || ownUserId === "") return; - const lp = toLocalPx(e); - const p = pointToPercent(lp, measured); + const p = pointToPercent(toLocalPx(e), measured); const pad = 6; // เผื่อความคาดหวังของผู้ใช้นอกเส้นจริง (px) - // ตรายาง render อยู่ layer บน (TextboxStampLayer) → เช็คก่อน ให้ตัวบนสุดชนะ - // (bbox = สูตรเดียวกับ render และกรอบ selection — annotationBBox) - for (const s of stamps) { - if (s.userId !== ownUserId) continue; - const b = annotationBBox({ type: "STAMP", data: s.data }, measured); - if ( - lp.x >= b.x - pad && - lp.x <= b.x + b.width + pad && - lp.y >= b.y - pad && - lp.y <= b.y + b.height + pad - ) { - onErase(s.id); - return; - } - } for (const s of strokes) { if (s.userId !== ownUserId) continue; const threshold = toPx(s.strokeWidthPct, measured.width) / 2 + pad; diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index d57f2af..9ac4d47 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -853,7 +853,6 @@ function PdfPage({ strokes={inkStrokes} shapes={inkShapes} redactions={inkRedactions} - stamps={inkStamps} active={inkActive} nextStroke={{ color: drawStyle.color, diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 550285a..2fd2909 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -139,10 +139,9 @@ Index: `(file_id)` ### F6 · B3 Eraser -- Tool `erase`: InkLayer active (รับ pointer, `touch-action: none`), `pointerdown/move` ทำ hit-test ตำแหน่ง pointer กับเส้น/รูปทรง/ตรายาง **ของตัวเองเท่านั้น** (ส่ง `userId` มากับ strokes): +- Tool `erase`: InkLayer active (รับ pointer, `touch-action: none`), `pointerdown/move` ทำ hit-test ตำแหน่ง pointer กับเส้น/รูปทรง **ของตัวเองเท่านั้น** (ส่ง `userId` มากับ strokes): - DRAW: ระยะจากจุดถึง segment ทุกท่อน ≤ `strokeWidth/2 + 6px` - SHAPE: ระยะถึงเส้นขอบ (stroke) — ลากผ่าน "พื้นที่" รูปทรงไม่โดน (เหมือนสัมผัสเส้นจริง) - - STAMP: จุดอยู่ใน bbox ของตรา (`annotationBBox` สูตรเดียวกับ render) + 6px — เช็ค**ก่อน**เส้น/รูปทรงเพราะตรา render อยู่ layer บน (TextboxStampLayer) ให้ตัวบนสุดชนะ - โดน = `deleteAnnotation` ทันที (ลากผ่านหลายเส้น = ลบทีละเส้นระหว่างเคลื่อน) — เส้นของคนอื่นไม่ผ่าน hit-test จึงลบไม่ได้ ### F8 · B4+C1 Redaction + Export PDF (ทำคู่กันเสมอ — D4)