From 1ecb8efcea177de5a1243dca06b41cba239331d7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 16 Sep 2026 16:43:17 +0700 Subject: [PATCH] =?UTF-8?q?NumberCombo=20(=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B8=B2/=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=97=E0=B8=B6=E0=B8=9A/=E0=B8=82?= =?UTF-8?q?=E0=B8=99=E0=B8=B2=E0=B8=94)=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20combobox=20=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20=E2=80=94=20dropdown=20preset=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=20"=E0=B8=82=E0=B8=B6=E0=B9=89?= =?UTF-8?q?=E0=B8=99=E0=B8=9A=E0=B8=99"=20(=E0=B9=81=E0=B8=96=E0=B8=A7?= =?UTF-8?q?=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=A1=E0=B8=99?= =?UTF-8?q?=E0=B8=B9=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A5=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=82=E0=B8=95=20scroll=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=E0=B8=AD=E0=B8=B5?= =?UTF-8?q?=E0=B8=81=20=E2=80=94=20L2=20=E0=B9=81=E0=B8=81=E0=B9=89?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=20feedback=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20dropdown=20+=20=E0=B8=81=E0=B8=A3=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89)=20=E0=B8=9E=E0=B8=B4=E0=B8=A1=E0=B8=9E?= =?UTF-8?q?=E0=B9=8C=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B9=80=E0=B8=AD=E0=B8=87?= =?UTF-8?q?/=E2=96=B2=E2=96=BC=20=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20+=20?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=AA=E0=B8=B5=E0=B8=9B=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88:=20?= =?UTF-8?q?=E0=B8=A7=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=A1=2012=20=E0=B8=A7?= =?UTF-8?q?=E0=B8=87=20(=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B8=AA=E0=B8=B5=E0=B8=99=E0=B9=89=E0=B8=B3=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A5=20#b45309)=20grid=204=20=E0=B8=84=E0=B8=AD=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=A1=E0=B8=99=E0=B9=8C=203=20=E0=B9=81=E0=B8=96?= =?UTF-8?q?=E0=B8=A7=20=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87=2032px=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=B2=20label=20"=E0=B8=AA=E0=B8=B5"=20?= =?UTF-8?q?=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=95=E0=B9=87?= =?UTF-8?q?=E0=B8=A1=E0=B9=81=E0=B8=96=E0=B8=A7=20+=20=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99=20slate-?= =?UTF-8?q?100=20=E0=B8=A3=E0=B8=B0=E0=B8=AB=E0=B8=A7=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B8=E0=B8=81?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=A7=20(.panel-row=20+=20.panel-row=20?= =?UTF-8?q?=E2=80=94=20=E0=B9=80=E0=B8=97=E0=B8=B5=E0=B8=A2=E0=B8=9A?= =?UTF-8?q?=E0=B9=80=E0=B8=97=E0=B9=88=E0=B8=B2=E0=B9=81=E0=B8=97=E0=B8=A3?= =?UTF-8?q?=E0=B8=81=20div=20my-1.5=20border-t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- app/globals.css | 18 +++- components/pdf/AnnotationSelectionLayer.tsx | 8 +- components/pdf/PDFViewer.tsx | 10 +- components/pdf/ToolControls.tsx | 101 +++++++++++++------- 4 files changed, 92 insertions(+), 45 deletions(-) diff --git a/app/globals.css b/app/globals.css index 0673981..9862123 100644 --- a/app/globals.css +++ b/app/globals.css @@ -108,10 +108,11 @@ /* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼ - จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px - แทน (:first-child — แถว chips preset ของ L2 โดน rule ปุ่ม 36px ของเมนู - ครอบอยู่แล้ว ห้ามบังคับสูงซ้ำ): แตะ chips preset = touch path หลัก - ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ + จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px แทน + (:first-child — เว้น dropdown ของ preset ที่เป็นลูก div ตัวที่สอง ไม่ให้ถูก + บังคับความสูงจนตัดรายการ): แตะ input = เปิด preset dropdown (เปิดขึ้นบน — L2: + แถวท้ายเมนูเปิดลงจะล้นเขต scroll ของเมนู) คือ touch path หลัก ส่วน ▲▼ + (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ [data-tool-menu] .number-combo > div:first-child { height: 2.25rem; } @@ -244,6 +245,15 @@ html { touch-action: auto; } +/* เส้นคั่นบางระหว่างแถวตั้งค่า (PanelRow) ที่ต่อกันในเมนู tool/แผง selection — + ระยะ/เส้นเท่ากับแทรก
ไว้ระหว่างแถว + ทุกจุด (แถวแรกของกลุ่มไม่มีเส้น — ไม่ซ้ำกับ divider ที่เมนูใส่ไว้เองแล้ว) */ +.panel-row + .panel-row { + margin-top: 0.375rem; + padding-top: 0.625rem; + border-top: 1px solid #f1f5f9; +} + /* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */ header { diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 1035ab7..3ae33c1 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -512,7 +512,7 @@ export default function AnnotationSelectionLayer({ > {highlightData && ( <> - + @@ -550,7 +550,7 @@ export default function AnnotationSelectionLayer({ )} {strokeData && ( - + )} @@ -568,7 +568,7 @@ export default function AnnotationSelectionLayer({ )} {stampData && ( <> - + - + commitTextboxPatch({ color: c })} diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 447d431..aacf4ae 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -3039,7 +3039,7 @@ export default function PDFViewer({ ))}
- + @@ -3127,7 +3127,7 @@ export default function PDFViewer({ ); })}
- + @@ -3220,7 +3220,7 @@ export default function PDFViewer({ ))}
- + @@ -3298,7 +3298,7 @@ export default function PDFViewer({ ))}
- + @@ -3360,7 +3360,7 @@ export default function PDFViewer({ overflowY: "auto", }} > - + diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index b052441..95ba3d2 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -1,7 +1,7 @@ "use client"; -import { useEffect, useState, type ComponentType, type ReactNode } from "react"; -import { ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; +import { useEffect, useRef, useState, type ComponentType, type ReactNode } from "react"; +import { Check, ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; import type { HighlightStyle } from "@/db/schema"; @@ -25,31 +25,37 @@ export const INK_COLORS = [ "#3b82f6", // น้ำเงิน "#8b5cf6", // ม่วง "#ec4899", // ชมพู + "#b45309", // น้ำตาล "#111827", // ดำ (default textbox) "#6b7280", // เทา ]; -/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย + ตัวควบคุมขวา */ -export function PanelRow({ label, children }: { label: string; children: ReactNode }) { +/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย (optional: แถวสีไม่ใส่ ให้ตารางกินเต็มแถว) + * + ตัวควบคุมขวา */ +export function PanelRow({ label, children }: { label?: string; children: ReactNode }) { return ( -
- {label} +
+ {label ? ( + {label} + ) : null} {children}
); } -/** แถวปุ่มสีวงกลม preset + ปุ่มสีรุ้ง (color picker เลือกสีอื่นได้ไม่จำกัด) */ +/** แถวปุ่มสีวงกลม preset + ปุ่มสีรุ้งวงกลม (color picker เลือกสีอื่นได้ไม่จำกัด) + * grid 4 ต่อแถว — 11 สี preset + สีรุ้ง = 12 วง เรียงจบพอดี 3 แถว (4+4+4) + * ช่อง ~37px พ้นมาตรฐาน touch 36px เลยไม่ต้องปลด min ใน CSS จอสัมผัส */ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c: string) => void }) { return ( -
+
{INK_COLORS.map((c) => (
- {/* L2 (UX audit 2026-09-16): preset เดิมเป็น dropdown เปิดลงจากช่อง — แถวท้าย - ของเมนู tool (ความทึบ/ความหนา) ล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น - ครบ เปลี่ยนเป็น chips แถวเดียวใต้ช่อง: เห็นค่าที่เลือกได้ทันที ไม่มี popup - ซ้อนในเมนูอีก (จอสัมผัสได้ min 36px จาก rule [data-tool-menu] โดยอัตโนมัติ) */} -
- {options.map((opt) => { - const active = value === opt; - return ( + {open && ( +
+ {options.map((opt) => ( - ); - })} -
+ ))} +
+ )}
); }