perf(pdf-viewer): limit drag preview re-render to its page
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 45s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 3s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 45s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 3s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
แก้ "ลากย้ายเส้น/กล่องข้อความ/ตรายาง/pin ไม่ smooth เป็น step"
(คำขอผู้ใช้ 2026-09-29):
- สาเหตุ: preview ของการลาก (onPreview → setPreviewData) เป็น state
ระดับ root ของ PDFViewer — ทุก pointermove ระหว่างลาก re-render ทั้ง
ต้นไม้ (toolbar + sidebar + ทุกหน้าที่ mounted ตาม virtualization)
ทั้งที่ผู้ใช้ลากอยู่หน้าเดียว และค่าถูกอ่านแค่จุดเดียวคือ substitution
ของหน้าที่ลาก — เทียบกับการวาดเส้นที่ smooth เพราะ live state อยู่ใน
InkLayer เอง + rAF throttle (FIX-8) อยู่แล้ว
- แก้: ย้าย preview state ลงเป็น state ท้องถิ่นของ PdfPage (substitution
ย้ายตามมาอยู่ก่อน derive layers ทั้งหมด) — ระหว่างลาก re-render เฉพาะ
หน้าที่ลาก; root สั่งเคลียร์ผ่าน counter previewClear (useReducer)
แทน setter ตรง ครบทุกจุดเดิม 9 จุด (ปิด popup/Escape/auto-select/
delete/ก่อนคิว update/deselect/goToAnnotation/เลือกรายการ)
- onPreview ของ AnnotationSelectionLayer throttle ด้วย rAF ≤1 ครั้ง/
เฟรม (pattern FIX-8 ของ InkLayer): ทุก pointermove ยังคำนวณ + เก็บ
latestRef เสมอ (ค่า commit ตอน pointerup เป็นค่าล่าสุดสุดเสมอ) เฉพาะ
การ render ที่จัดคิวตามเฟรม + cancel ครบทุกทางออกของ drag
(pointerup/pointercancel/unmount)
- แก้ตาม code review 2026-09-29: preview เก็บเป็น {id, data} เทียบ id
เจ้าของตอน substitute แทน selectedId — กันจอสัมผัสเปลี่ยน selection
กลาง drag (นิ้วหนึ่งลากค้าง อีกนิ้วเลือกอื่น) แล้ว preview ของรายการเก่า
ไปทดแทนรายการใหม่หนึ่งเฟรม (การเคลียร์จาก root เป็น passive effect
หลัง paint)
- sync สถาปัตยกรรมใหม่ใน .claude/rules/pdf-viewer.md (หัวข้อกรอบ
selection F1)
ผ่าน code-reviewer + plan-reviewer (ไม่มี bug ค้าง/ไม่ละเมิด invariant) +
tsc + vitest 207/207 + e2e Playwright: ลาก DRAW FPS≈60.6 (commit
เลื่อนตรงระยะ/undo/redo/reload คงอยู่) + ลาก TEXTBOX FPS≈59-60.9 ไม่มี
เฟรม drop (commit/reload ตรงเป๊ะ)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
0681e056fd
commit
e4ecc026b0
3 changed files with 80 additions and 24 deletions
File diff suppressed because one or more lines are too long
|
|
@ -183,6 +183,26 @@ export default function AnnotationSelectionLayer({
|
||||||
const dragRef = useRef<DragState | null>(null);
|
const dragRef = useRef<DragState | null>(null);
|
||||||
const movedRef = useRef(false);
|
const movedRef = useRef(false);
|
||||||
const latestRef = useRef<AnnotationData | null>(null);
|
const latestRef = useRef<AnnotationData | null>(null);
|
||||||
|
// rAF ของ preview ระหว่างลาก — ยิง onPreview ≤1 ครั้ง/เฟรม (pattern เดียวกับ FIX-8
|
||||||
|
// ของ InkLayer): pointermove ยิงถี่กว่าเฟรมได้ ทุก event ยังคำนวณ+เก็บค่าล่าสุดใน
|
||||||
|
// latestRef เสมอ (ค่าตอน commit ต้องล่าสุดสุด) เฉพาะการ render ที่จัดคิวตามเฟรม
|
||||||
|
const previewRafRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
/** ยกเลิก rAF preview ที่ค้าง — เรียกก่อนเคลียร์ preview ทุกทางออกของ drag (ไม่งั้น
|
||||||
|
* callback ยิงตามหลัง ดัน preview เก่ากลับมาตอน dragRef เคลียร์ไปแล้ว) */
|
||||||
|
function cancelPreviewRaf() {
|
||||||
|
if (previewRafRef.current === null) return;
|
||||||
|
cancelAnimationFrame(previewRafRef.current);
|
||||||
|
previewRafRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// เลิกลากกลางคันแล้ว unmount — ยกเลิก rAF ที่ค้างอยู่ด้วย (เหมือน InkLayer)
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
cancelPreviewRaf();
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
// อุปกรณ์จอสัมผัส (pointer หลัก coarse) — คำนวณครั้งเดียวตอน mount เหมือน
|
// อุปกรณ์จอสัมผัส (pointer หลัก coarse) — คำนวณครั้งเดียวตอน mount เหมือน
|
||||||
// coarseTouch ของ PDFViewer: .coarse-36 ขยายปุ่ม pin เป็น 36px บนพวกนี้
|
// coarseTouch ของ PDFViewer: .coarse-36 ขยายปุ่ม pin เป็น 36px บนพวกนี้
|
||||||
|
|
@ -395,7 +415,14 @@ export default function AnnotationSelectionLayer({
|
||||||
}
|
}
|
||||||
movedRef.current = true;
|
movedRef.current = true;
|
||||||
latestRef.current = next;
|
latestRef.current = next;
|
||||||
onPreview(next);
|
// render preview ครั้งเดียวต่อ animation frame — callback อ่าน latestRef ตอนเฟรม
|
||||||
|
// ยิงจริง ๆ เพื่อให้ได้ค่าใหม่สุดเสมอแม้ event ถี่ (แก้ลากไม่ smooth 2026-09-29)
|
||||||
|
if (previewRafRef.current === null) {
|
||||||
|
previewRafRef.current = requestAnimationFrame(() => {
|
||||||
|
previewRafRef.current = null;
|
||||||
|
if (dragRef.current && latestRef.current) onPreview(latestRef.current);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function endDrag(e: ReactPointerEvent<HTMLElement>) {
|
function endDrag(e: ReactPointerEvent<HTMLElement>) {
|
||||||
|
|
@ -403,6 +430,7 @@ export default function AnnotationSelectionLayer({
|
||||||
if (!st) return;
|
if (!st) return;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
dragRef.current = null;
|
dragRef.current = null;
|
||||||
|
cancelPreviewRaf();
|
||||||
onPreview(null);
|
onPreview(null);
|
||||||
if (movedRef.current && latestRef.current) onCommit(annotation.id, latestRef.current);
|
if (movedRef.current && latestRef.current) onCommit(annotation.id, latestRef.current);
|
||||||
movedRef.current = false;
|
movedRef.current = false;
|
||||||
|
|
@ -412,6 +440,7 @@ export default function AnnotationSelectionLayer({
|
||||||
function cancelDrag() {
|
function cancelDrag() {
|
||||||
if (!dragRef.current) return;
|
if (!dragRef.current) return;
|
||||||
dragRef.current = null;
|
dragRef.current = null;
|
||||||
|
cancelPreviewRaf();
|
||||||
movedRef.current = false;
|
movedRef.current = false;
|
||||||
latestRef.current = null;
|
latestRef.current = null;
|
||||||
onPreview(null);
|
onPreview(null);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useOptimistic,
|
useOptimistic,
|
||||||
|
useReducer,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
startTransition,
|
startTransition,
|
||||||
|
|
@ -600,11 +601,14 @@ function PdfPage({
|
||||||
liveMark,
|
liveMark,
|
||||||
liveMarkStyle,
|
liveMarkStyle,
|
||||||
selectPreview,
|
selectPreview,
|
||||||
annotations,
|
annotations: annotationsProp,
|
||||||
replies,
|
replies,
|
||||||
openPinId,
|
openPinId,
|
||||||
ownUserId,
|
ownUserId,
|
||||||
selectedId,
|
selectedId,
|
||||||
|
/** root สั่งเคลียร์ preview ของการลาก (bump counter — ปิด popup/เปลี่ยน selection/ก่อน
|
||||||
|
* คิว update): ทุกครั้งที่ค่าเปลี่ยน หน้าเคลียร์ previewData ท้องถิ่นของตัวเอง */
|
||||||
|
previewClear,
|
||||||
editingCommentId,
|
editingCommentId,
|
||||||
commentEditDraft,
|
commentEditDraft,
|
||||||
onCommentEditDraft,
|
onCommentEditDraft,
|
||||||
|
|
@ -612,7 +616,6 @@ function PdfPage({
|
||||||
textbox,
|
textbox,
|
||||||
onSelectAnnotation,
|
onSelectAnnotation,
|
||||||
onPinToggle,
|
onPinToggle,
|
||||||
onAnnotationPreview,
|
|
||||||
onAnnotationUpdate,
|
onAnnotationUpdate,
|
||||||
onAnnotationPatch,
|
onAnnotationPatch,
|
||||||
onSetResolved,
|
onSetResolved,
|
||||||
|
|
@ -659,6 +662,8 @@ function PdfPage({
|
||||||
openPinId: string | null;
|
openPinId: string | null;
|
||||||
ownUserId: string;
|
ownUserId: string;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
|
/** root สั่งเคลียร์ preview ของการลาก (bump counter — ดูตัวแปร previewClear ของ viewer) */
|
||||||
|
previewClear: number;
|
||||||
/** F13/A6: comment ที่กำลังแก้ไขข้อความอยู่ (null = ไม่มี) */
|
/** F13/A6: comment ที่กำลังแก้ไขข้อความอยู่ (null = ไม่มี) */
|
||||||
editingCommentId: string | null;
|
editingCommentId: string | null;
|
||||||
commentEditDraft: string;
|
commentEditDraft: string;
|
||||||
|
|
@ -669,7 +674,6 @@ function PdfPage({
|
||||||
/** คลิกรายการ overlay (เช่น TEXTBOX) → เลือกรายการนั้น (F1) */
|
/** คลิกรายการ overlay (เช่น TEXTBOX) → เลือกรายการนั้น (F1) */
|
||||||
onSelectAnnotation: (id: string) => void;
|
onSelectAnnotation: (id: string) => void;
|
||||||
onPinToggle: (id: string | null) => void;
|
onPinToggle: (id: string | null) => void;
|
||||||
onAnnotationPreview: (data: AnnotationData | null) => void;
|
|
||||||
onAnnotationUpdate: (id: string, data: AnnotationData) => void;
|
onAnnotationUpdate: (id: string, data: AnnotationData) => void;
|
||||||
/** ตัวควบคุมเล็กของแผง commit เป็น patch — viewer ผสานกับ data ล่าสุดตอนถึงคิว
|
/** ตัวควบคุมเล็กของแผง commit เป็น patch — viewer ผสานกับ data ล่าสุดตอนถึงคิว
|
||||||
* update กัน stale base ทับค่าที่เพิ่งเปลี่ยน (AnnotationPatch, code review 2026-09-28) */
|
* update กัน stale base ทับค่าที่เพิ่งเปลี่ยน (AnnotationPatch, code review 2026-09-28) */
|
||||||
|
|
@ -690,6 +694,22 @@ function PdfPage({
|
||||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
const textLayerRef = useRef<HTMLDivElement | null>(null);
|
const textLayerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
// ระหว่างลากย้าย/ปรับขนาดรายการที่เลือก: data ชั่วคราวให้ layer เดิมของชนิดนั้น render
|
||||||
|
// ตาม (null = เลิก preview) — state อยู่ที่หน้านี้ ไม่ใช่ root ของ viewer: ต่อ pointermove
|
||||||
|
// ตอนลากต้อง re-render เฉพาะหน้าที่ลากเท่านั้น (เดิม setPreviewData อยู่ root แล้วแทนค่า
|
||||||
|
// กลับลงมาให้ทุกหน้า = จ่าย render ทั้งต้นไม้ต่อเฟรม จนลากเป็น step — แก้ 2026-09-29)
|
||||||
|
// เก็บพร้อม id เจ้าของไม่อิง selectedId ตอน substitute: การเคลียร์จาก root มาเป็น
|
||||||
|
// passive effect หลัง paint — อิง selectedId แล้วจอสัมผัสเปลี่ยน selection กลาง drag
|
||||||
|
// (นิ้วหนึ่งลากค้าง อีกนิ้วเลือกอื่น) จะเอา preview ของรายการเก่าไปทดแทนรายการใหม่
|
||||||
|
// หนึ่งเฟรม (code review 2026-09-29)
|
||||||
|
const [preview, setPreview] = useState<{ id: string; data: AnnotationData } | null>(null);
|
||||||
|
|
||||||
|
// root สั่งเคลียร์ preview ผ่าน counter (ปิด popup/เปลี่ยน selection/ก่อนคิว update) —
|
||||||
|
// ครั้งแรกตอน mount preview ยัง null อยู่แล้ว จึงเป็น no-op
|
||||||
|
useEffect(() => {
|
||||||
|
setPreview(null);
|
||||||
|
}, [previewClear]);
|
||||||
|
|
||||||
// N3: หน้ารูปภาพไม่มี canvas/textLayer — effect ของ pdfjs ทำงานเฉพาะหน้า pdf
|
// N3: หน้ารูปภาพไม่มี canvas/textLayer — effect ของ pdfjs ทำงานเฉพาะหน้า pdf
|
||||||
const pdfjsLib = content.kind === "pdf" ? content.pdfjs : null;
|
const pdfjsLib = content.kind === "pdf" ? content.pdfjs : null;
|
||||||
const pdfDoc = content.kind === "pdf" ? content.doc : null;
|
const pdfDoc = content.kind === "pdf" ? content.doc : null;
|
||||||
|
|
@ -761,6 +781,15 @@ function PdfPage({
|
||||||
if (content.kind === "image") onTextLayerRendered?.(pageNumber - 1, 0);
|
if (content.kind === "image") onTextLayerRendered?.(pageNumber - 1, 0);
|
||||||
}, [content.kind, pageNumber]);
|
}, [content.kind, pageNumber]);
|
||||||
|
|
||||||
|
// ระหว่างลาก: แทน data ของรายการที่เลือกด้วย preview ก่อนแยกไป layer ต่าง ๆ +
|
||||||
|
// กรอบ selection (เดิมแทนตอน root ส่ง prop — ย้ายลงมาพร้อม state ของหน้า, 2026-09-29)
|
||||||
|
// เทียบ id กับเจ้าของ preview โดยตรง (ไม่ใช่ selectedId ปัจจุบัน) — ดู comment ของ state
|
||||||
|
const annotations = preview
|
||||||
|
? annotationsProp.map((a) =>
|
||||||
|
a.id === preview.id ? { ...a, data: preview.data } : a,
|
||||||
|
)
|
||||||
|
: annotationsProp;
|
||||||
|
|
||||||
const highlights = annotations.filter(isHighlight);
|
const highlights = annotations.filter(isHighlight);
|
||||||
const inkStrokes: InkStroke[] = annotations
|
const inkStrokes: InkStroke[] = annotations
|
||||||
.filter(isDraw)
|
.filter(isDraw)
|
||||||
|
|
@ -1158,7 +1187,8 @@ function PdfPage({
|
||||||
editable={ownUserId !== "" && selected.userId === ownUserId}
|
editable={ownUserId !== "" && selected.userId === ownUserId}
|
||||||
pageBaseWidth={pageBaseWidth}
|
pageBaseWidth={pageBaseWidth}
|
||||||
disabled={overlayFrozen}
|
disabled={overlayFrozen}
|
||||||
onPreview={onAnnotationPreview}
|
// ผูก id เจ้าของ preview ตอนรับค่า (ไม่ใช่ตอน substitute) — ดู comment ของ state preview
|
||||||
|
onPreview={(d) => setPreview(d && selected ? { id: selected.id, data: d } : null)}
|
||||||
onCommit={onAnnotationUpdate}
|
onCommit={onAnnotationUpdate}
|
||||||
onDelete={onErase}
|
onDelete={onErase}
|
||||||
onBookmark={(y) => onAnnotationBookmark(selected, y)}
|
onBookmark={(y) => onAnnotationBookmark(selected, y)}
|
||||||
|
|
@ -1358,7 +1388,11 @@ export default function PDFViewer({
|
||||||
const bookmarkSavedTimerRef = useRef<number | null>(null);
|
const bookmarkSavedTimerRef = useRef<number | null>(null);
|
||||||
/** L6: สถานะกำลังบันทึกของปุ่มใน popup bookmark — กันกดซ้ำเพราะคิดว่ากดไม่เข้า */
|
/** L6: สถานะกำลังบันทึกของปุ่มใน popup bookmark — กันกดซ้ำเพราะคิดว่ากดไม่เข้า */
|
||||||
const [bookmarkSaving, setBookmarkSaving] = useState(false);
|
const [bookmarkSaving, setBookmarkSaving] = useState(false);
|
||||||
const [previewData, setPreviewData] = useState<AnnotationData | null>(null);
|
// "เคลียร์ preview ของการลาก" จาก root — preview เป็น state ท้องถิ่นของหน้า (PdfPage)
|
||||||
|
// ตั้งแต่ 2026-09-29 root จึงสั่งเคลียร์ผ่าน counter นี้แทน setter ตรง ๆ: ทุกจุดที่เดิม
|
||||||
|
// setPreviewData(null) (ปิด popup/เปลี่ยน selection/ก่อนคิว update — กัน preview เก่าค้าง
|
||||||
|
// ทับรายการที่เลือกใหม่) bump ค่านี้ แล้วทุกหน้า effect เคลียร์ previewData ตัวเอง
|
||||||
|
const [previewClear, bumpPreviewClear] = useReducer((c: number) => c + 1, 0);
|
||||||
// F13/A6: แก้ไขข้อความ comment ของตัวเองใน popover — บันทึกผ่าน updateAnnotation (reuse F1)
|
// F13/A6: แก้ไขข้อความ comment ของตัวเองใน popover — บันทึกผ่าน updateAnnotation (reuse F1)
|
||||||
const [editingCommentId, setEditingCommentId] = useState<string | null>(null);
|
const [editingCommentId, setEditingCommentId] = useState<string | null>(null);
|
||||||
const [commentEditDraft, setCommentEditDraft] = useState("");
|
const [commentEditDraft, setCommentEditDraft] = useState("");
|
||||||
|
|
@ -1941,7 +1975,7 @@ export default function PDFViewer({
|
||||||
setComposer(null);
|
setComposer(null);
|
||||||
setOpenPinId(null);
|
setOpenPinId(null);
|
||||||
setSelectedId(null);
|
setSelectedId(null);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
setEditingCommentId(null);
|
setEditingCommentId(null);
|
||||||
setTextboxComposer(null);
|
setTextboxComposer(null);
|
||||||
setEditingTextboxId(null);
|
setEditingTextboxId(null);
|
||||||
|
|
@ -2136,7 +2170,7 @@ export default function PDFViewer({
|
||||||
// optimistic row ถูกถอดตอน transition จบ เลือก id นั้นไฮไลต์จะหายเอง)
|
// optimistic row ถูกถอดตอน transition จบ เลือก id นั้นไฮไลต์จะหายเอง)
|
||||||
if (AUTO_SELECT_TYPES.has(type)) {
|
if (AUTO_SELECT_TYPES.has(type)) {
|
||||||
setSelectedId(saved.id);
|
setSelectedId(saved.id);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
// แผง selection กำลังจะโผล่ = ปิดเมนู tool/popup bookmark ที่เปิดค้างก่อน — บน iOS
|
// แผง selection กำลังจะโผล่ = ปิดเมนู tool/popup bookmark ที่เปิดค้างก่อน — บน iOS
|
||||||
// การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนู
|
// การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนู
|
||||||
// เอง (Android/desktop ปิดไปแล้วตั้งแต่เริ่มกด) ไม่ปิดตรงนี้ = เมนูซ้อนแผงบนจอสัมผัส
|
// เอง (Android/desktop ปิดไปแล้วตั้งแต่เริ่มกด) ไม่ปิดตรงนี้ = เมนูซ้อนแผงบนจอสัมผัส
|
||||||
|
|
@ -2184,12 +2218,12 @@ export default function PDFViewer({
|
||||||
if (pending) {
|
if (pending) {
|
||||||
pending.cancelled = true;
|
pending.cancelled = true;
|
||||||
setSelectedId(null);
|
setSelectedId(null);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = annotations.find((a) => a.id === id); // เก็บข้อมูลก่อนลบไว้ทำ undo
|
const target = annotations.find((a) => a.id === id); // เก็บข้อมูลก่อนลบไว้ทำ undo
|
||||||
setSelectedId(null);
|
setSelectedId(null);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
await startTransition(async () => {
|
await startTransition(async () => {
|
||||||
applyAnnotation({ kind: "remove", id });
|
applyAnnotation({ kind: "remove", id });
|
||||||
try {
|
try {
|
||||||
|
|
@ -2245,7 +2279,7 @@ export default function PDFViewer({
|
||||||
if (!user) return resolve();
|
if (!user) return resolve();
|
||||||
const current = annotations.find((a) => a.id === id);
|
const current = annotations.find((a) => a.id === id);
|
||||||
if (!current || current.userId !== user.userId) return resolve(); // ของคนอื่นแก้ไม่ได้ (D9)
|
if (!current || current.userId !== user.userId) return resolve(); // ของคนอื่นแก้ไม่ได้ (D9)
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
applyAnnotation({
|
applyAnnotation({
|
||||||
|
|
@ -2935,7 +2969,7 @@ export default function PDFViewer({
|
||||||
// ตาม hit-test ทีหลังจุดนี้เหมือนเดิม พฤติกรรมจึงเท่าเดิม
|
// ตาม hit-test ทีหลังจุดนี้เหมือนเดิม พฤติกรรมจึงเท่าเดิม
|
||||||
if (selectedId !== null) {
|
if (selectedId !== null) {
|
||||||
setSelectedId(null);
|
setSelectedId(null);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
}
|
}
|
||||||
if (tool === "comment" && user) {
|
if (tool === "comment" && user) {
|
||||||
const position = getPointerPercent(e, pageEl);
|
const position = getPointerPercent(e, pageEl);
|
||||||
|
|
@ -3019,7 +3053,7 @@ export default function PDFViewer({
|
||||||
// ของรายการอยู่ใต้ sticky header (เดิมเลื่อนไปขอบบนหน้าตรง ๆ เลยถูก header บัง
|
// ของรายการอยู่ใต้ sticky header (เดิมเลื่อนไปขอบบนหน้าตรง ๆ เลยถูก header บัง
|
||||||
// กรณีรายการอยู่ส่วนบนสุดของหน้า)
|
// กรณีรายการอยู่ส่วนบนสุดของหน้า)
|
||||||
setSelectedId(a.id);
|
setSelectedId(a.id);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
setOpenPinId(null); // กระโดดไปเลือกรายการอื่น = ปิด popover ของ pin เดิมทิ้ง
|
setOpenPinId(null); // กระโดดไปเลือกรายการอื่น = ปิด popover ของ pin เดิมทิ้ง
|
||||||
const el = pageWrapRefs.current[a.pageIndex];
|
const el = pageWrapRefs.current[a.pageIndex];
|
||||||
const main = mainRef.current;
|
const main = mainRef.current;
|
||||||
|
|
@ -3048,7 +3082,7 @@ export default function PDFViewer({
|
||||||
/** คลิกรายการ overlay (TEXTBOX ฯลฯ) = เลือกรายการนั้น (F1) */
|
/** คลิกรายการ overlay (TEXTBOX ฯลฯ) = เลือกรายการนั้น (F1) */
|
||||||
function handleSelectAnnotation(id: string) {
|
function handleSelectAnnotation(id: string) {
|
||||||
setSelectedId(id);
|
setSelectedId(id);
|
||||||
setPreviewData(null);
|
bumpPreviewClear();
|
||||||
// เลือกรายการอื่น = ปิด popover ของ pin เดิม (คลิก pin จะไม่ผ่านที่นี่ — มัน toggle เอง)
|
// เลือกรายการอื่น = ปิด popover ของ pin เดิม (คลิก pin จะไม่ผ่านที่นี่ — มัน toggle เอง)
|
||||||
setOpenPinId(null);
|
setOpenPinId(null);
|
||||||
}
|
}
|
||||||
|
|
@ -4390,18 +4424,12 @@ export default function PDFViewer({
|
||||||
: {}),
|
: {}),
|
||||||
}}
|
}}
|
||||||
selectPreview={selectPreview}
|
selectPreview={selectPreview}
|
||||||
annotations={visibleAnnotations
|
annotations={visibleAnnotations.filter((a) => a.pageIndex === i)}
|
||||||
.filter((a) => a.pageIndex === i)
|
|
||||||
// ระหว่างลาก: แทน data ด้วย preview ให้ layer เดิม (highlight/ink/pin) render ตาม
|
|
||||||
.map((a) =>
|
|
||||||
a.id === selectedId && previewData
|
|
||||||
? { ...a, data: previewData }
|
|
||||||
: a,
|
|
||||||
)}
|
|
||||||
replies={visibleReplies}
|
replies={visibleReplies}
|
||||||
openPinId={openPinId}
|
openPinId={openPinId}
|
||||||
ownUserId={user?.userId ?? ""}
|
ownUserId={user?.userId ?? ""}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
|
previewClear={previewClear}
|
||||||
editingCommentId={editingCommentId}
|
editingCommentId={editingCommentId}
|
||||||
commentEditDraft={commentEditDraft}
|
commentEditDraft={commentEditDraft}
|
||||||
onCommentEditDraft={setCommentEditDraft}
|
onCommentEditDraft={setCommentEditDraft}
|
||||||
|
|
@ -4416,7 +4444,6 @@ export default function PDFViewer({
|
||||||
}}
|
}}
|
||||||
onSelectAnnotation={handleSelectAnnotation}
|
onSelectAnnotation={handleSelectAnnotation}
|
||||||
onPinToggle={handlePinToggle}
|
onPinToggle={handlePinToggle}
|
||||||
onAnnotationPreview={setPreviewData}
|
|
||||||
onAnnotationUpdate={(id, data) => {
|
onAnnotationUpdate={(id, data) => {
|
||||||
void handleUpdateAnnotation(id, data);
|
void handleUpdateAnnotation(id, data);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue