เลือก annotation บนหน้า PDF → sidebar ตามไปเน้นรายการนั้น + สร้างเสร็จเลือกให้ทันที + กัน duplicate ชั่วขณะตอนสร้าง: (1) ส่ง selectedId ให้ AnnotationSidebar — ไฮไลต์แถว (ทั้งลิสต์หลักและผลค้นหา) + scrollIntoView block:nearest + สลับมาแท็บ Annotations ให้เองถ้ากำลังอยู่ Bookmarks (rAF รอแถว render หลังสลับแท็บค่อยเลื่อน) (2) AUTO_SELECT_TYPES — สร้าง DRAW/SHAPE/TEXTBOX/STAMP/HIGHLIGHT/REDACTION เสร็จ = เลือกตัวใหม่ทันทีด้วย saved.id (กรอบ selection + sidebar ตามไปเลย; COMMENT ยกเว้นเพราะ composer/pin ทำหน้าที่นั้นอยู่แล้ว) — แก้ปัญหา "วาดเสร็จคลิกที่เส้น tool ยังเป็นวาด sidebar นิ่ง" (3) กันอาการ row โผล่แล้วหาย (0→2→1): เดิม optimistic row กับ row จาก server คนละ id ทำให้ SSE event มาก่อน action เสร็จ dedupe ไม่ได้ — ตอนนี้ client ส่ง id ของ optimistic row ให้ createAnnotation ใช้ซ้ำ (validate UUID, ไม่ส่ง = สุ่มเองตามเดิมสำหรับ redo path) ทุกชั้น dedupe ด้วย id เดียวกันตั้งแต่แรก — sync PLAN.md §4 + rules/realtime.md ให้ตรงพฤติกรรมใหม่
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
4934b9d0b3
commit
6ddc35d6d5
5 changed files with 67 additions and 12 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useRef, useState, type FormEvent } from "react";
|
||||
import { useEffect, useRef, useState, type FormEvent } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
ArrowRight,
|
||||
|
|
@ -132,6 +132,7 @@ export default function AnnotationSidebar({
|
|||
onUpdateBookmark,
|
||||
onGoToBookmark,
|
||||
onGoToAnnotation,
|
||||
selectedId,
|
||||
}: {
|
||||
annotations: AnnotationDTO[];
|
||||
bookmarks: BookmarkDTO[];
|
||||
|
|
@ -157,9 +158,26 @@ export default function AnnotationSidebar({
|
|||
) => void;
|
||||
onGoToBookmark: (b: BookmarkDTO) => void;
|
||||
onGoToAnnotation: (a: AnnotationDTO) => void;
|
||||
/** id ของ annotation ที่ถูกเลือกอยู่บนหน้า PDF (null = ไม่มี) — แถวในแผงนี้ไฮไลต์ตาม */
|
||||
selectedId: string | null;
|
||||
}) {
|
||||
const [tab, setTab] = useState<"bookmarks" | "annotations">("bookmarks");
|
||||
|
||||
// คลิกเลือก annotation บนหน้าเอกสาร = สลับมาแท็บ annotations (ถ้ายังอยู่ bookmarks)
|
||||
// แล้วเลื่อนแถวนั้นเข้ามุมมอง — แถว render หลัง setTab จึงค่อยเลื่อนใน rAF ถัดไป
|
||||
// (dep รวม tab ตาม lint — ผลข้างเคียง: เปิดแท็บนี้ตอนยังมีรายการถูกเลือก = เลื่อนไปแถวนั้นเช่นกัน)
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (!selectedId) return;
|
||||
if (tab !== "annotations") setTab("annotations");
|
||||
const raf = requestAnimationFrame(() => {
|
||||
listRef.current
|
||||
?.querySelector(`[data-ann-id="${CSS.escape(selectedId)}"]`)
|
||||
?.scrollIntoView({ block: "nearest" });
|
||||
});
|
||||
return () => cancelAnimationFrame(raf);
|
||||
}, [selectedId, tab]);
|
||||
|
||||
// ─── F14/D1: ค้นหาคำในหมายเหตุทั้งไฟล์ (highlight text / comment message) ────
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [searchResults, setSearchResults] = useState<AnnotationDTO[] | null>(null);
|
||||
|
|
@ -488,9 +506,12 @@ export default function AnnotationSidebar({
|
|||
return (
|
||||
<li
|
||||
key={a.id}
|
||||
className={`group rounded px-2 py-1.5 hover:bg-slate-100 ${
|
||||
a.resolved ? "opacity-60" : ""
|
||||
}`}
|
||||
data-ann-id={a.id}
|
||||
className={`group rounded px-2 py-1.5 ${
|
||||
a.id === selectedId
|
||||
? "bg-primary/10 ring-1 ring-inset ring-primary/30"
|
||||
: "hover:bg-slate-100"
|
||||
} ${a.resolved ? "opacity-60" : ""}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
|
|
@ -677,7 +698,11 @@ export default function AnnotationSidebar({
|
|||
<li key={a.id}>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-start gap-2 rounded px-1 py-1 text-left hover:bg-slate-100"
|
||||
className={`flex w-full items-start gap-2 rounded px-1 py-1 text-left ${
|
||||
a.id === selectedId
|
||||
? "bg-primary/10 ring-1 ring-inset ring-primary/30"
|
||||
: "hover:bg-slate-100"
|
||||
}`}
|
||||
onClick={() => onGoToAnnotation(a)}
|
||||
>
|
||||
<TypeIcon type={a.type} data={a.data} />
|
||||
|
|
@ -717,7 +742,7 @@ export default function AnnotationSidebar({
|
|||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-2">
|
||||
<div ref={listRef} className="flex-1 overflow-y-auto p-2">
|
||||
{tab === "bookmarks" ? (
|
||||
bookmarks.length === 0 ? (
|
||||
<p className="p-4 text-center text-xs text-slate-400">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue