sidebar จอกลางเป็นคอลัมน์ + bookmark row hover แยกแถว + PDF fit จอเล็ก
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 39s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
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 39s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
- overlay sidebar ลด breakpoint จาก xl (1280) เป็น lg (1024): จอ 1024-1280 เปิดแผงข้างแล้ว content ขยับไปข้าง ๆ ไม่ลอยทับบัง PDF - overlay บนจอ < lg กว้างเป็นสัดส่วนของจอ (75vw, cap 288px) กันบังเกือบเต็มมือถือ - ลบการปิด sidebar อัตโนมัติตอน mount บนจอเล็ก (ต้องกดปิดเองเท่านั้น) - PDF fit ความกว้างอัตโนมัติครั้งเดียวตอนโหลดเมื่อพื้นที่แคบกว่าหน้ากระดาษ - รายการ bookmark: ย้ายพื้นหลัง hover จาก li (ครอบ <ul> ลูกทั้งก้อน ชี้ลูกแล้ว :hover ของแม่ทำงานด้วย ไฮไลต์ทั้ง subtree) ลง div แถวรายการ ให้ไฮไลต์เฉพาะแถว Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
b588343d50
commit
1c2f4c834a
2 changed files with 30 additions and 20 deletions
|
|
@ -1234,11 +1234,6 @@ export default function PDFViewer({
|
||||||
setUser(getLocalUser());
|
setUser(getLocalUser());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// UX review 2026-09-09: จอแคบกว่า xl (< 1280px) sidebar เป็น overlay — ปิดอัตโนมัติตอน mount
|
|
||||||
useEffect(() => {
|
|
||||||
if (!window.matchMedia("(min-width: 1280px)").matches) setSidebarOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// โหลดเอกสาร: dynamic import ภายใน useEffect เท่านั้น (invariant #6)
|
// โหลดเอกสาร: dynamic import ภายใน useEffect เท่านั้น (invariant #6)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
@ -1374,6 +1369,21 @@ export default function PDFViewer({
|
||||||
}
|
}
|
||||||
}, [pageSizes, annotations]);
|
}, [pageSizes, annotations]);
|
||||||
|
|
||||||
|
// จอเล็ก: หน้ากระดาษ (scale 1) กว้างกว่าพื้นที่แสดงผล → ย่อให้พอดีความกว้าง
|
||||||
|
// ครั้งเดียวตอนโหลดเท่านั้น (ผู้ใช้ซูมเองหลังจากนี้แล้วไม่ถูกแทนที่) —
|
||||||
|
// อิงความกว้างหน้าแรกเหมือน basePageWidth และตัด padding px-4 ซ้าย-ขวาของ main ออก
|
||||||
|
const mainRef = useRef<HTMLElement>(null);
|
||||||
|
const fitWidthDoneRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (pageSizes.length === 0 || fitWidthDoneRef.current) return;
|
||||||
|
fitWidthDoneRef.current = true;
|
||||||
|
const avail = (mainRef.current?.clientWidth ?? 0) - 32;
|
||||||
|
const pageW = pageSizes[0].width;
|
||||||
|
if (avail > 0 && pageW > avail) {
|
||||||
|
setScale(clamp(Math.floor((avail / pageW) * 100) / 100, 0.5, 1));
|
||||||
|
}
|
||||||
|
}, [pageSizes]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDraft("");
|
setDraft("");
|
||||||
}, [composer]);
|
}, [composer]);
|
||||||
|
|
@ -2352,22 +2362,23 @@ export default function PDFViewer({
|
||||||
{TEXT_SELECTION_TOOLS.has(tool) && (
|
{TEXT_SELECTION_TOOLS.has(tool) && (
|
||||||
<style>{`.textLayer span::selection, .textLayer br::selection { background: transparent; }`}</style>
|
<style>{`.textLayer span::selection, .textLayer br::selection { background: transparent; }`}</style>
|
||||||
)}
|
)}
|
||||||
{/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < xl)
|
{/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < lg)
|
||||||
z-[45] อยู่เหนือ header sticky z-40 แต่ใต้ aside z-50 / composer z-50 */}
|
z-[45] อยู่เหนือ header sticky z-40 แต่ใต้ aside z-50 / composer z-50 */}
|
||||||
{sidebarOpen && (
|
{sidebarOpen && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[45] bg-slate-900/30 xl:hidden"
|
className="fixed inset-0 z-[45] bg-slate-900/30 lg:hidden"
|
||||||
aria-hidden
|
aria-hidden
|
||||||
onClick={() => setSidebarOpen(false)}
|
onClick={() => setSidebarOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* xl+ = sticky แยกคอลัมน์เหมือนเดิม / < xl = overlay เลื่อนเข้า-ออก
|
{/* lg+ = sticky แยกคอลัมน์ (เปิดแล้ว content ขยับไปข้าง ๆ ไม่บังกัน)
|
||||||
(ปิดบน desktop ใช้ xl:hidden ออกจาก flow → main เต็มจอ) */}
|
/ < lg = overlay เลื่อนเข้า-ออก กว้างเป็นสัดส่วนของจอกว้างกว่า 288px
|
||||||
|
(ปิดบน desktop ใช้ lg:hidden ออกจาก flow → main เต็มจอ) */}
|
||||||
<aside
|
<aside
|
||||||
className={`sticky top-0 h-screen w-72 shrink-0 border-r border-slate-200 bg-white max-xl:fixed max-xl:left-0 max-xl:z-50 max-xl:transition-transform max-xl:duration-200 ${
|
className={`sticky top-0 h-screen w-72 shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${
|
||||||
sidebarOpen
|
sidebarOpen
|
||||||
? "max-xl:translate-x-0"
|
? "max-lg:translate-x-0"
|
||||||
: "max-xl:pointer-events-none max-xl:-translate-x-full xl:hidden"
|
: "max-lg:pointer-events-none max-lg:-translate-x-full lg:hidden"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<AnnotationSidebar
|
<AnnotationSidebar
|
||||||
|
|
@ -3040,6 +3051,7 @@ export default function PDFViewer({
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main
|
<main
|
||||||
|
ref={mainRef}
|
||||||
className="space-y-4 px-4 py-4"
|
className="space-y-4 px-4 py-4"
|
||||||
style={{
|
style={{
|
||||||
cursor:
|
cursor:
|
||||||
|
|
|
||||||
|
|
@ -327,15 +327,13 @@ export default function AnnotationSidebar({
|
||||||
3,
|
3,
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
|
// พื้นหลัง hover อยู่ที่ div แถวรายการ ไม่ใช่ li — li ครอบ <ul> ลูกทั้งก้อน
|
||||||
|
// ทำให้ :hover ของแม่ทำงานด้วยเมื่อชี้รายการลูก (ไฮไลต์ทั้ง subtree เป็นก้อนเดียว)
|
||||||
|
const pad = depth === 1 ? "px-1" : `pr-1 ${depth === 2 ? "pl-5" : "pl-9"}`;
|
||||||
return (
|
return (
|
||||||
<li
|
<li key={b.id}>
|
||||||
key={b.id}
|
|
||||||
className={`group rounded hover:bg-slate-100 ${
|
|
||||||
depth === 1 ? "px-1 py-0.5" : `py-0.5 pr-1 ${depth === 2 ? "pl-5" : "pl-9"}`
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{eb ? (
|
{eb ? (
|
||||||
<div className="py-1">
|
<div className={`rounded py-1 ${pad}`}>
|
||||||
<input
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
value={eb.title}
|
value={eb.title}
|
||||||
|
|
@ -394,7 +392,7 @@ export default function AnnotationSidebar({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-1.5 py-1">
|
<div className={`group flex items-center gap-1.5 rounded py-1 hover:bg-slate-100 ${pad}`}>
|
||||||
{hasChildren ? (
|
{hasChildren ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue