NumberCombo: commit อัตโนมัติหยุดพิมพ์ 0.5 วิ + ความหนาเส้นไม่จำกัดขอบบน

- พิมพ์ตัวเลขใน NumberCombo แล้วหยุด ~0.5 วิ ค่าถูกใช้เอง ไม่ต้องกด
  Enter/คลิกที่อื่น (debounce — ยังรวมเป็น commit เดียวต่อชุดที่พิมพ์เสร็จ
  ไม่ยิง server action + version snapshot ต่อ keystroke, FIX-3 ยังคงไว้)
  Enter/blur commit ทันทีเหมือนเดิม
- prop max เป็น optional — ไม่ส่ง = ไม่ clamp ขอบบน (พิมพ์เอง/▲▼/auto-commit
  ครบทุกทาง) และถอด max=30 ออกจากช่องความหนาทั้ง 3 จุด (dropdown ปากกา,
  dropdown รูปทรง, แผง selection) — ช่องที่มีหน่วยจริง (ความทึบ/ขนาดฟอนต์/
  ตรายาง) ยังมีขอบเดิม

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 14:04:58 +07:00
parent fbc78b78e9
commit 0c36b516fa
3 changed files with 20 additions and 6 deletions

View file

@ -509,7 +509,6 @@ export default function AnnotationSelectionLayer({
value={Number((strokeData.strokeWidth * 10).toFixed(2))}
options={[1, 2, 4, 8, 12]}
min={1}
max={30}
step={1}
onChange={(v) => commitStrokeWidth(v / 10)}
/>

View file

@ -2578,7 +2578,6 @@ export default function PDFViewer({
value={Number((drawWidthPct * 10).toFixed(2))}
options={[1, 2, 4, 8, 12]}
min={1}
max={30}
step={1}
onChange={(v) => setDrawWidthPct(v / 10)}
/>
@ -2658,7 +2657,6 @@ export default function PDFViewer({
value={Number((shapeWidthPct * 10).toFixed(2))}
options={[1, 2, 4, 8, 12]}
min={1}
max={30}
step={1}
onChange={(v) => setShapeWidthPct(v / 10)}
/>

View file

@ -87,7 +87,8 @@ export function NumberCombo({
value: number;
options: number[];
min: number;
max: number;
/** ขอบบนตอน clamp — ไม่ส่ง = ไม่จำกัด (เช่น ความหนาเส้นวาด) */
max?: number;
step: number;
/** ละขั้นของปุ่ม ▲▼ (default 1 — เช่น fontSize ใช้ 0.1) */
stepBy?: number;
@ -103,6 +104,22 @@ export function NumberCombo({
useEffect(() => setDraft(String(value)), [value]);
// FIX-3 (ปรับ): หยุดพิมพ์แล้ว ~0.5 วิ commit เอง — ไม่ต้องรอ Enter/blur แต่ยัง
// รวมเป็น commit เดียวต่อชุดที่พิมพ์เสร็จ (ไม่ยิง server action + version
// snapshot ต่อ keystroke — สำคัญกับแผง selection ที่ commit ทัพหลังถึง DB)
useEffect(() => {
if (draft === String(value)) return; // sync กลับจาก value ไม่นับเป็นการพิมพ์
const t = setTimeout(() => {
const v = Number(draft);
if (draft.trim() === "" || !Number.isFinite(v)) return; // คั่นกลางพิมพ์/ลบหมด — ให้ blur จัดการ
const clamped = Math.min(max ?? Infinity, Math.max(min, Number(v.toFixed(2))));
if (clamped !== value) onChange(clamped); // value เปลี่ยน → effect ด้านบน sync draft กลับ
else setDraft(String(clamped)); // เกินขอบแต่ clamped แล้วเท่าค่าเดิม → แสดงค่าจริง
}, 500);
return () => clearTimeout(t);
// eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง
}, [draft]);
useEffect(() => {
if (!open) return;
const onMouseDown = (e: MouseEvent) => {
@ -117,7 +134,7 @@ export function NumberCombo({
function commitDraft() {
const v = Number(draft);
if (draft.trim() !== "" && Number.isFinite(v)) {
const clamped = Math.min(max, Math.max(min, Number(v.toFixed(2))));
const clamped = Math.min(max ?? Infinity, Math.max(min, Number(v.toFixed(2))));
setDraft(String(clamped));
if (clamped !== value) onChange(clamped);
} else {
@ -130,7 +147,7 @@ export function NumberCombo({
function stepValue(dir: 1 | -1) {
const by = stepBy ?? 1;
const base = draft.trim() !== "" && Number.isFinite(Number(draft)) ? Number(draft) : value;
const next = Math.min(max, Math.max(min, Number((base + dir * by).toFixed(2))));
const next = Math.min(max ?? Infinity, Math.max(min, Number((base + dir * by).toFixed(2))));
setDraft(String(next));
if (next !== value) onChange(next);
}