NumberCombo (ความหนา/ความทึบ/ขนาด) เป็น combobox เต็มความกว้าง — dropdown preset เปิด "ขึ้นบน" (แถวท้ายเมนูไม่ล้นเขต scroll ของเมนูอีก — L2 แก้ตาม feedback ที่ต้องเป็น dropdown + กรอกเพิ่มได้) พิมพ์ค่าเอง/▲▼ ได้ตามเดิม + แถวสีปรับใหม่: วงกลม 12 วง (เพิ่มสีน้ำตาล #b45309) grid 4 คอลัมน์ 3 แถว ช่อง 32px เอา label "สี" ออกให้ตารางเต็มแถว + เส้นคั่น slate-100 ระหว่างแถวตั้งค่าทุกแถว (.panel-row + .panel-row — เทียบเท่าแทรก div my-1.5 border-t)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
33c7002ffd
commit
1ecb8efcea
4 changed files with 92 additions and 45 deletions
|
|
@ -108,10 +108,11 @@
|
||||||
|
|
||||||
/* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px
|
/* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px
|
||||||
คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼
|
คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼
|
||||||
จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px
|
จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px แทน
|
||||||
แทน (:first-child — แถว chips preset ของ L2 โดน rule ปุ่ม 36px ของเมนู
|
(:first-child — เว้น dropdown ของ preset ที่เป็นลูก div ตัวที่สอง ไม่ให้ถูก
|
||||||
ครอบอยู่แล้ว ห้ามบังคับสูงซ้ำ): แตะ chips preset = touch path หลัก
|
บังคับความสูงจนตัดรายการ): แตะ input = เปิด preset dropdown (เปิดขึ้นบน — L2:
|
||||||
ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */
|
แถวท้ายเมนูเปิดลงจะล้นเขต scroll ของเมนู) คือ touch path หลัก ส่วน ▲▼
|
||||||
|
(ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */
|
||||||
[data-tool-menu] .number-combo > div:first-child {
|
[data-tool-menu] .number-combo > div:first-child {
|
||||||
height: 2.25rem;
|
height: 2.25rem;
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +245,15 @@ html {
|
||||||
touch-action: auto;
|
touch-action: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* เส้นคั่นบางระหว่างแถวตั้งค่า (PanelRow) ที่ต่อกันในเมนู tool/แผง selection —
|
||||||
|
ระยะ/เส้นเท่ากับแทรก <div class="my-1.5 border-t border-slate-100"> ไว้ระหว่างแถว
|
||||||
|
ทุกจุด (แถวแรกของกลุ่มไม่มีเส้น — ไม่ซ้ำกับ 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
|
/* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta
|
||||||
ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */
|
ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */
|
||||||
header {
|
header {
|
||||||
|
|
|
||||||
|
|
@ -512,7 +512,7 @@ export default function AnnotationSelectionLayer({
|
||||||
>
|
>
|
||||||
{highlightData && (
|
{highlightData && (
|
||||||
<>
|
<>
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={highlightData.color} onChange={commitColor} />
|
<ColorSwatches value={highlightData.color} onChange={commitColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="รูปแบบ">
|
<PanelRow label="รูปแบบ">
|
||||||
|
|
@ -550,7 +550,7 @@ export default function AnnotationSelectionLayer({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{strokeData && (
|
{strokeData && (
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={strokeData.strokeColor} onChange={commitColor} />
|
<ColorSwatches value={strokeData.strokeColor} onChange={commitColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
)}
|
)}
|
||||||
|
|
@ -568,7 +568,7 @@ export default function AnnotationSelectionLayer({
|
||||||
)}
|
)}
|
||||||
{stampData && (
|
{stampData && (
|
||||||
<>
|
<>
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches
|
<ColorSwatches
|
||||||
value={stampData.color ?? STAMP_COLOR}
|
value={stampData.color ?? STAMP_COLOR}
|
||||||
onChange={commitColor}
|
onChange={commitColor}
|
||||||
|
|
@ -601,7 +601,7 @@ export default function AnnotationSelectionLayer({
|
||||||
แก้ไขข้อความ
|
แก้ไขข้อความ
|
||||||
</button>
|
</button>
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches
|
<ColorSwatches
|
||||||
value={textboxData.color}
|
value={textboxData.color}
|
||||||
onChange={(c) => commitTextboxPatch({ color: c })}
|
onChange={(c) => commitTextboxPatch({ color: c })}
|
||||||
|
|
|
||||||
|
|
@ -3039,7 +3039,7 @@ export default function PDFViewer({
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="my-1.5 border-t border-slate-100" />
|
<div className="my-1.5 border-t border-slate-100" />
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={highlightColor} onChange={setHighlightColor} />
|
<ColorSwatches value={highlightColor} onChange={setHighlightColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="ความทึบ">
|
<PanelRow label="ความทึบ">
|
||||||
|
|
@ -3127,7 +3127,7 @@ export default function PDFViewer({
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="my-1.5 border-t border-slate-100" />
|
<div className="my-1.5 border-t border-slate-100" />
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={drawColor} onChange={setDrawColor} />
|
<ColorSwatches value={drawColor} onChange={setDrawColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="ความหนา">
|
<PanelRow label="ความหนา">
|
||||||
|
|
@ -3220,7 +3220,7 @@ export default function PDFViewer({
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="my-1.5 border-t border-slate-100" />
|
<div className="my-1.5 border-t border-slate-100" />
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={shapeColor} onChange={setShapeColor} />
|
<ColorSwatches value={shapeColor} onChange={setShapeColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="ความหนา">
|
<PanelRow label="ความหนา">
|
||||||
|
|
@ -3298,7 +3298,7 @@ export default function PDFViewer({
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="my-1.5 border-t border-slate-100" />
|
<div className="my-1.5 border-t border-slate-100" />
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={stampColor} onChange={setStampColor} />
|
<ColorSwatches value={stampColor} onChange={setStampColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="ขนาด">
|
<PanelRow label="ขนาด">
|
||||||
|
|
@ -3360,7 +3360,7 @@ export default function PDFViewer({
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PanelRow label="สี">
|
<PanelRow>
|
||||||
<ColorSwatches value={textboxColor} onChange={setTextboxColor} />
|
<ColorSwatches value={textboxColor} onChange={setTextboxColor} />
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow label="ขนาด">
|
<PanelRow label="ขนาด">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState, type ComponentType, type ReactNode } from "react";
|
import { useEffect, useRef, useState, type ComponentType, type ReactNode } from "react";
|
||||||
import { ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react";
|
import { Check, ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react";
|
||||||
|
|
||||||
import type { HighlightStyle } from "@/db/schema";
|
import type { HighlightStyle } from "@/db/schema";
|
||||||
|
|
||||||
|
|
@ -25,31 +25,37 @@ export const INK_COLORS = [
|
||||||
"#3b82f6", // น้ำเงิน
|
"#3b82f6", // น้ำเงิน
|
||||||
"#8b5cf6", // ม่วง
|
"#8b5cf6", // ม่วง
|
||||||
"#ec4899", // ชมพู
|
"#ec4899", // ชมพู
|
||||||
|
"#b45309", // น้ำตาล
|
||||||
"#111827", // ดำ (default textbox)
|
"#111827", // ดำ (default textbox)
|
||||||
"#6b7280", // เทา
|
"#6b7280", // เทา
|
||||||
];
|
];
|
||||||
|
|
||||||
/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย + ตัวควบคุมขวา */
|
/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย (optional: แถวสีไม่ใส่ ให้ตารางกินเต็มแถว)
|
||||||
export function PanelRow({ label, children }: { label: string; children: ReactNode }) {
|
* + ตัวควบคุมขวา */
|
||||||
|
export function PanelRow({ label, children }: { label?: string; children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-2 px-1 py-1">
|
<div className="panel-row flex items-start gap-2 px-1 py-1">
|
||||||
<span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span>
|
{label ? (
|
||||||
|
<span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span>
|
||||||
|
) : null}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** แถวปุ่มสีวงกลม 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 }) {
|
export function ColorSwatches({ value, onChange }: { value: string; onChange: (c: string) => void }) {
|
||||||
return (
|
return (
|
||||||
<div className="color-swatches flex flex-1 flex-wrap items-center gap-1.5">
|
<div className="color-swatches grid flex-1 grid-cols-4 gap-1.5">
|
||||||
{INK_COLORS.map((c) => (
|
{INK_COLORS.map((c) => (
|
||||||
<button
|
<button
|
||||||
key={c}
|
key={c}
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={`เลือกสี ${c}`}
|
aria-label={`เลือกสี ${c}`}
|
||||||
title={c}
|
title={c}
|
||||||
className={`h-4 w-4 rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${
|
className={`aspect-square max-w-8 w-full justify-self-center rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${
|
||||||
value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110"
|
value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110"
|
||||||
}`}
|
}`}
|
||||||
style={{ backgroundColor: c }}
|
style={{ backgroundColor: c }}
|
||||||
|
|
@ -58,7 +64,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c
|
||||||
))}
|
))}
|
||||||
<label
|
<label
|
||||||
title="เลือกสีอื่น"
|
title="เลือกสีอื่น"
|
||||||
className={`relative h-4 w-4 cursor-pointer rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${
|
className={`relative aspect-square max-w-8 w-full cursor-pointer justify-self-center rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${
|
||||||
INK_COLORS.includes(value) ? "hover:scale-110" : "ring-2 ring-primary ring-offset-1"
|
INK_COLORS.includes(value) ? "hover:scale-110" : "ring-2 ring-primary ring-offset-1"
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -102,9 +108,11 @@ export function NumberCombo({
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
onChange: (v: number) => void;
|
onChange: (v: number) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
// FIX-3: ถือ draft ระหว่างพิมพ์ — commit ครั้งเดียวตอน blur/Enter
|
// FIX-3: ถือ draft ระหว่างพิมพ์ — commit ครั้งเดียวตอน blur/Enter
|
||||||
// ไม่ยิง server action + version snapshot ต่อ keystroke; sync กลับเมื่อค่าจากภายนอกเปลี่ยน
|
// ไม่ยิง server action + version snapshot ต่อ keystroke; sync กลับเมื่อค่าจากภายนอกเปลี่ยน
|
||||||
const [draft, setDraft] = useState(String(value));
|
const [draft, setDraft] = useState(String(value));
|
||||||
|
const rootRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => setDraft(String(value)), [value]);
|
useEffect(() => setDraft(String(value)), [value]);
|
||||||
|
|
||||||
|
|
@ -124,6 +132,16 @@ export function NumberCombo({
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง
|
||||||
}, [draft]);
|
}, [draft]);
|
||||||
|
|
||||||
|
// คลิกนอกตัวควบคุม = ปิด dropdown (คลิกใน dropdown เองไม่นับ — อยู่ใน rootRef)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
|
if (!rootRef.current?.contains(e.target as Node)) setOpen(false);
|
||||||
|
};
|
||||||
|
document.addEventListener("mousedown", onMouseDown);
|
||||||
|
return () => document.removeEventListener("mousedown", onMouseDown);
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
/** ยืนยันค่าที่พิมพ์หนึ่งครั้ง — clamp เข้า min–max; พิมพ์ไม่เป็นตัวเลข = คืนค่าเดิม
|
/** ยืนยันค่าที่พิมพ์หนึ่งครั้ง — clamp เข้า min–max; พิมพ์ไม่เป็นตัวเลข = คืนค่าเดิม
|
||||||
* ค่าเท่าเดิมไม่ยิง onChange — focus/blur เฉย ๆ ต้องไม่เกิด server action + snapshot ขยะ */
|
* ค่าเท่าเดิมไม่ยิง onChange — focus/blur เฉย ๆ ต้องไม่เกิด server action + snapshot ขยะ */
|
||||||
function commitDraft() {
|
function commitDraft() {
|
||||||
|
|
@ -148,7 +166,17 @@ export function NumberCombo({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="number-combo">
|
<div
|
||||||
|
ref={rootRef}
|
||||||
|
className="number-combo relative flex-1"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
// Esc ปิดแค่ dropdown นี้ก่อน (stopPropagation ไม่ให้ปิดทั้งเมนู/กรอบ)
|
||||||
|
if (e.key === "Escape" && open) {
|
||||||
|
e.stopPropagation();
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex items-stretch overflow-hidden rounded-md border border-slate-300 bg-white">
|
<div className="flex items-stretch overflow-hidden rounded-md border border-slate-300 bg-white">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
|
@ -158,12 +186,15 @@ export function NumberCombo({
|
||||||
step={step}
|
step={step}
|
||||||
onChange={(e) => setDraft(e.target.value)}
|
onChange={(e) => setDraft(e.target.value)}
|
||||||
onBlur={commitDraft}
|
onBlur={commitDraft}
|
||||||
onFocus={(e) => e.target.select()} // พิมพ์ทับค่าเดิมได้ทันที
|
onFocus={(e) => {
|
||||||
|
setOpen(true); // กดที่ช่อง = เปิดรายการ preset ให้เลือกเลย (พิมพ์ค่าอื่นเองก็ได้)
|
||||||
|
e.target.select(); // พิมพ์ทับค่าเดิมได้ทันที
|
||||||
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู)
|
if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู)
|
||||||
if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur
|
if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur
|
||||||
}}
|
}}
|
||||||
className="w-14 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
className="min-w-0 flex-1 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col border-l border-slate-300">
|
<div className="flex flex-col border-l border-slate-300">
|
||||||
{/* combo-step: ปุ่ม ▲▼ ซ้อนแนวตั้ง 2 ชั้น — ต้องหลุดจาก rule touch 36px
|
{/* combo-step: ปุ่ม ▲▼ ซ้อนแนวตั้ง 2 ชั้น — ต้องหลุดจาก rule touch 36px
|
||||||
|
|
@ -188,34 +219,40 @@ export function NumberCombo({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* L2 (UX audit 2026-09-16): preset เดิมเป็น dropdown เปิดลงจากช่อง — แถวท้าย
|
{open && (
|
||||||
ของเมนู tool (ความทึบ/ความหนา) ล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น
|
<div
|
||||||
ครบ เปลี่ยนเป็น chips แถวเดียวใต้ช่อง: เห็นค่าที่เลือกได้ทันที ไม่มี popup
|
role="listbox"
|
||||||
ซ้อนในเมนูอีก (จอสัมผัสได้ min 36px จาก rule [data-tool-menu] โดยอัตโนมัติ) */}
|
// L2 (UX audit 2026-09-16): เปิด "ขึ้นบน" (bottom-full) เสมอ — แถวท้ายเมนู
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
// tool (ความทึบ/ความหนา) เปิดลงจะล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น
|
||||||
{options.map((opt) => {
|
// ครบ / ทุกจุดใช้งานมีแถวอื่นอยู่ข้างบนเสมอ จึงไม่หลุดขอบบนของเมนูเช่นกัน
|
||||||
const active = value === opt;
|
className="absolute bottom-full right-0 z-[60] mb-1 w-full overflow-hidden rounded-md border border-slate-200 bg-white py-0.5 shadow-lg"
|
||||||
return (
|
>
|
||||||
|
{options.map((opt) => (
|
||||||
<button
|
<button
|
||||||
key={opt}
|
key={opt}
|
||||||
type="button"
|
type="button"
|
||||||
aria-pressed={active}
|
role="option"
|
||||||
className={`rounded border px-1.5 py-0.5 text-[11px] transition ${FOCUS_RING_CLS} ${
|
aria-selected={value === opt}
|
||||||
active
|
onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input ก่อนคลิก
|
||||||
? "border-primary bg-primary/10 font-medium text-primary"
|
className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${
|
||||||
: "border-slate-300 text-slate-500 hover:bg-slate-100"
|
value === opt
|
||||||
|
? "bg-slate-100 text-slate-900"
|
||||||
|
: "text-slate-600 hover:bg-slate-100"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDraft(String(opt));
|
|
||||||
onChange(opt);
|
onChange(opt);
|
||||||
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{opt}
|
<span className="flex-1">
|
||||||
{suffix}
|
{opt}
|
||||||
|
{suffix}
|
||||||
|
</span>
|
||||||
|
{value === opt && <Check className="h-3.5 w-3.5 shrink-0" />}
|
||||||
</button>
|
</button>
|
||||||
);
|
))}
|
||||||
})}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue