TEXTBOX กล่องกว้างคงที่สไตล์ Smallpdf: คลิกเดียวได้กล่อง 30% + ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด ข้อความ wrap ใหม่)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 17:41:22 +07:00
parent 9e6158ed45
commit ca74fb1843
10 changed files with 248 additions and 41 deletions

View file

@ -6,6 +6,7 @@ import {
hitTestPath,
hitTestShape,
scaleAnnotationData,
textboxMaxWidthPct,
translateAnnotationData,
} from "@/utils/geometry";
@ -132,6 +133,100 @@ describe("annotationBBox", () => {
expect(bbox.height).toBeCloseTo(6); // 2 บรรทัด × 3
});
it("TEXTBOX: กล่องใกล้ขอบขวา → bbox ถูก clamp ไม่ยื่นเกินขอบหน้า (เหมือนที่ render บีบ)", () => {
const bbox = annotationBBox(
{
type: "TEXTBOX",
data: { position: { x: 95, y: 50 }, text: "x".repeat(60), color: "#000", fontSize: 2 },
},
S,
);
expect(bbox.x).toBe(95);
expect(bbox.width).toBe(5); // ค่าประมาณกว้าง 50 แต่เหลือพื้นที่แค่ 100 − 95 → clamp ที่ 5
});
it("TEXTBOX: align ไม่ขยับกล่อง — position เป็นมุมซ้ายบนเสมอ (ขยับแค่ข้อความข้างใน)", () => {
for (const align of ["left", "center", "right"] as const) {
const bbox = annotationBBox(
{
type: "TEXTBOX",
data: {
position: { x: 30, y: 20 },
text: "x".repeat(60),
color: "#000",
fontSize: 2,
align,
},
},
S,
);
expect(bbox.x).toBe(30); // ขอบซ้ายกล่อง = position.x ทุก align
expect(bbox.width).toBe(50); // cap เดิม min(50, 100−30=70)
}
});
it("textboxMaxWidthPct: ความกว้างสูงสุด = พื้นที่จริงถึงขอบขวา (cap 50%)", () => {
expect(textboxMaxWidthPct(30)).toBe(50); // กลางหน้า → เต็ม cap
expect(textboxMaxWidthPct(95)).toBe(5); // ใกล้ขอบขวา → เหลือพื้นที่จริง
expect(textboxMaxWidthPct(100)).toBe(1); // ติดขอบเป๊ะ → ขั้นต่ำ 1%
});
it("TEXTBOX: กล่อง width คงที่ — กว้างตามที่กำหนดแม้ข้อความสั้น (สไตล์ Smallpdf)", () => {
const bbox = annotationBBox(
{
type: "TEXTBOX",
data: {
position: { x: 20, y: 20 },
text: "x".repeat(10), // กว้างแค่ 12 < 30 → กล่องยังกว้าง 30
color: "#000",
fontSize: 2,
width: 30,
},
},
S,
);
expect(bbox.x).toBe(20);
expect(bbox.width).toBe(30);
expect(bbox.height).toBeCloseTo(3); // ยัง 1 บรรทัด
});
it("TEXTBOX: width ใหญ่กว่าพื้นที่ถึงขอบขวา → บีบเหลือพื้นที่จริง", () => {
const bbox = annotationBBox(
{
type: "TEXTBOX",
data: {
position: { x: 80, y: 20 },
text: "x".repeat(10),
color: "#000",
fontSize: 2,
width: 40,
},
},
S,
);
expect(bbox.x).toBe(80);
expect(bbox.width).toBe(20); // min(40, 100−80)
});
it("TEXTBOX: width คงที่ + align ใดก็ตาม → กล่องอยู่ที่มุมซ้ายบนเสมอ", () => {
const bbox = annotationBBox(
{
type: "TEXTBOX",
data: {
position: { x: 50, y: 20 },
text: "x".repeat(10),
color: "#000",
fontSize: 2,
align: "right",
width: 30,
},
},
S,
);
expect(bbox.x).toBe(50);
expect(bbox.width).toBe(30);
});
it("TEXTBOX: ข้อความว่าง → กว้าง fallback เท่าตัวอักษรเดียว สูง 1 บรรทัด", () => {
const bbox = annotationBBox(
{