Skip to content

Commit bc8ee7b

Browse files
committed
fix(canvas): post-feature review fixes
Three rounds of review feedback consolidated: - Single hit-Rect at the symbol Group level: the click-target Rect previously lived inside the vector branch only, so D/E logo placeholders weren't selectable. Hoisting to the Group level gives both branches one consistent hit area. - GS_VECTOR_CODES set + GsVectorCode type replace the magic `'A' | 'B' | 'C'` cast at the canvas lookup. Adding D/E to the vector set later is a one-line change rather than a silently- broken cast. - DEFAULT_GS_SYMBOL_META switches from positional `GS_SYMBOLS[1]` to find() on the canonical code so reordering the symbol table doesn't break the fallback. - Defensive `rel[p.rotation] ?? rel.N` in the sceneFunc handles malformed JSON imports with an unexpected rotation string. - gsSymbolPaths.ts (and the regen-from-cache template) reuse ZplRotation from registry/rotation.ts and ship an AUTO-GENERATED header so the next maintainer doesn't hand-edit them. - KonvaObject reaches the native 2D context via Konva's idiomatic `ctx._context.fill(path, "evenodd")` instead of the unknown-cast. - All 32 locale files get proper symbol-block translations — English fallback broke the convention every other entry follows. Height and Width pulled from each locale's existing barcode entries to keep unit terminology consistent. Deferred (separate concern): memoise `new Path2D` outside sceneFunc — perf nit at very high symbol counts, not a correctness issue.
1 parent 025c691 commit bc8ee7b

34 files changed

Lines changed: 275 additions & 254 deletions

‎src/components/Canvas/KonvaObject.tsx‎

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { ZPL_FONT_HEIGHT_TO_CSS_RATIO } from "./textPositionTransforms";
1616
import { getTextRenderMetrics } from "./textRenderMetrics";
1717
import { selectionHandlers, type KonvaObjectProps } from "./konvaObjectProps";
1818
import { DEFAULT_GS_SYMBOL_META, GS_SYMBOLS } from "../../registry/symbol";
19-
import { GS_SYMBOL_PATHS } from "../../registry/gsSymbolPaths";
19+
import { GS_SYMBOL_PATHS, GS_VECTOR_CODES, type GsVectorCode } from "../../registry/gsSymbolPaths";
2020

2121
type Props = KonvaObjectProps;
2222

@@ -391,8 +391,11 @@ function KonvaObjectInner({
391391
// to Zebra firmware). D/E are trademarked UL/CSA logos we can't
392392
// ship — render a clearly-placeholder dashed gray box with the
393393
// letters so the user knows the printer will substitute the real
394-
// logo. Falls back to the © path if an unknown code slips through.
395-
const vectorPath = GS_SYMBOL_PATHS[p.symbol as 'A' | 'B' | 'C'] ?? null;
394+
// logo. Unknown codes (D/E or malformed import) take the same
395+
// placeholder branch via the null path here.
396+
const vectorPath = GS_VECTOR_CODES.has(p.symbol)
397+
? GS_SYMBOL_PATHS[p.symbol as GsVectorCode]
398+
: null;
396399
// The field bbox itself doesn't rotate — Zebra keeps the ^FO
397400
// anchor and (w, h) extent fixed and rotates only the glyph
398401
// inside. Skip the outer rotatedGroupTransform; sceneFunc bakes
@@ -408,6 +411,13 @@ function KonvaObjectInner({
408411
onDragMove={handleDragMove}
409412
onDragEnd={handleDragEnd}
410413
>
414+
{/* Single hit area for the whole field bbox — picks up clicks
415+
both for the vector-path branch and the placeholder branch
416+
below (both of which render with listening={false}). One
417+
source of truth for "where in this Group counts as a hit"
418+
instead of duplicating a transparent Rect inside each
419+
branch. */}
420+
<Rect width={w} height={h} fill="transparent" />
411421
{vectorPath ? (() => {
412422
// Konva.Path has no fillRule prop, so the holes in ® / ©
413423
// (inner ring carved out of the outer disc) can't be
@@ -420,21 +430,19 @@ function KonvaObjectInner({
420430
// inside the declared bbox, measured directly from
421431
// Labelary so Zebra's per-rotation anchor offsets match
422432
// pixel-for-pixel.
423-
const rel = vectorPath.rel[p.rotation];
433+
// Defensive ?? rel.N for the malformed-import case where
434+
// a JSON-restored object slips a non-N/R/I/B rotation
435+
// string past the type system; falling back to the
436+
// upright entry keeps the canvas from crashing on bad
437+
// data instead of throwing inside sceneFunc.
438+
const rel = vectorPath.rel[p.rotation] ?? vectorPath.rel.N;
424439
const glyphW = vectorPath.bbox.maxX - vectorPath.bbox.minX;
425440
const glyphH = vectorPath.bbox.maxY - vectorPath.bbox.minY;
426441
const renderW = w * rel.w;
427442
const renderH = h * rel.h;
428443
const renderX = w * rel.x;
429444
const renderY = h * rel.y;
430445
return (
431-
<>
432-
{/* Hit area: a plain Rect catches clicks for the whole
433-
bbox so the user can hit anywhere in the field, not
434-
only the glyph ink. Rendered first so the visible
435-
Shape draws on top. `fill="transparent"` keeps it
436-
invisible but Konva-listenable. */}
437-
<Rect width={w} height={h} fill="transparent" />
438446
<Shape
439447
listening={false}
440448
sceneFunc={(ctx) => {
@@ -463,14 +471,14 @@ function KonvaObjectInner({
463471
ctx.translate(-vectorPath.bbox.minX, -vectorPath.bbox.minY);
464472
const path = new Path2D(vectorPath.d);
465473
ctx.fillStyle = "#000000";
466-
// 2D ctx fill(rule) is unaware of the Konva wrapper,
467-
// so cast through unknown to reach the underlying
468-
// CanvasRenderingContext2D method.
469-
(ctx as unknown as CanvasRenderingContext2D).fill(path, "evenodd");
474+
// Konva.Context exposes the underlying 2D canvas
475+
// context as `_context`; reach for it directly so
476+
// we can call `fill(path, "evenodd")` — the Konva
477+
// wrapper doesn't surface the path/rule overload.
478+
ctx._context.fill(path, "evenodd");
470479
ctx.restore();
471480
}}
472481
/>
473-
</>
474482
);
475483
})() : (
476484
<>

‎src/locales/ar.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const ar = {
327327
storeInline: 'تضمين مباشر',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
330+
symbol: 'رمز',
331+
height: 'الارتفاع (نقطة)',
332+
width: 'العرض (نقطة)',
333+
symbolRegistered: 'مسجل (®)',
334+
symbolCopyright: 'حقوق الطبع (©)',
335+
symbolTrademark: 'علامة تجارية (™)',
336+
symbolUL: 'اعتماد UL',
337+
symbolCSA: 'اعتماد CSA',
338338
},
339339
upca: {
340340
content: 'المحتوى (11 رقم)',

‎src/locales/bg.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const bg = {
327327
storeInline: 'Вграждане',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
330+
symbol: 'Символ',
331+
height: 'Височина (точки)',
332+
width: 'Ширина (точки)',
333+
symbolRegistered: 'Регистрирано (®)',
334+
symbolCopyright: 'Авторско право (©)',
335+
symbolTrademark: 'Търговска марка (™)',
336+
symbolUL: 'UL одобрение',
337+
symbolCSA: 'CSA одобрение',
338338
},
339339
upca: {
340340
content: 'Съдържание (11 цифри)',

‎src/locales/cs.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,13 @@ const cs = {
328328
},
329329
symbol: {
330330
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
331+
height: 'Výška (body)',
332+
width: 'Šířka (body)',
333+
symbolRegistered: 'Registrováno (®)',
334+
symbolCopyright: 'Autorská práva (©)',
335+
symbolTrademark: 'Ochranná známka (™)',
336+
symbolUL: 'Schválení UL',
337+
symbolCSA: 'Schválení CSA',
338338
},
339339
upca: {
340340
content: 'Obsah (11 číslic)',

‎src/locales/da.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,13 @@ const da = {
328328
},
329329
symbol: {
330330
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
331+
height: 'Højde (punkter)',
332+
width: 'Bredde (punkter)',
333+
symbolRegistered: 'Registreret (®)',
334+
symbolCopyright: 'Ophavsret (©)',
335+
symbolTrademark: 'Varemærke (™)',
336+
symbolUL: 'UL-godkendelse',
337+
symbolCSA: 'CSA-godkendelse',
338338
},
339339
upca: {
340340
content: 'Indhold (11 cifre)',

‎src/locales/de.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,9 @@ const de = {
349349
},
350350
symbol: {
351351
symbol: 'Symbol',
352-
height: 'Höhe (Dots)',
353-
width: 'Breite (Dots)',
354-
symbolRegistered: 'Eingetragen (®)',
352+
height: 'Höhe (Punkte)',
353+
width: 'Breite (Punkte)',
354+
symbolRegistered: 'Registriert (®)',
355355
symbolCopyright: 'Copyright (©)',
356356
symbolTrademark: 'Markenzeichen (™)',
357357
symbolUL: 'UL-Zulassung',

‎src/locales/el.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const el = {
327327
storeInline: 'Ενσωμάτωση',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
330+
symbol: 'Σύμβολο',
331+
height: 'Ύψος (κουκκίδες)',
332+
width: 'Πλάτος (κουκκίδες)',
333+
symbolRegistered: 'Καταχωρισμένο (®)',
334+
symbolCopyright: 'Πνευματικά δικαιώματα (©)',
335+
symbolTrademark: 'Εμπορικό σήμα (™)',
336+
symbolUL: 'Έγκριση UL',
337+
symbolCSA: 'Έγκριση CSA',
338338
},
339339
upca: {
340340
content: 'Περιεχόμενο (11 ψηφία)',

‎src/locales/es.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const es = {
327327
storeInline: 'Incrustar en línea',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
330+
symbol: 'Símbolo',
331+
height: 'Altura (puntos)',
332+
width: 'Ancho (puntos)',
333+
symbolRegistered: 'Registrado (®)',
334334
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
335+
symbolTrademark: 'Marca registrada (™)',
336+
symbolUL: 'Aprobación UL',
337+
symbolCSA: 'Aprobación CSA',
338338
},
339339
upca: {
340340
content: 'Contenido (11 dígitos)',

‎src/locales/et.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const et = {
327327
storeInline: 'Manusta otse',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
330+
symbol: 'Sümbol',
331+
height: 'Kõrgus (punkti)',
332+
width: 'Laius (punkti)',
333+
symbolRegistered: 'Registreeritud (®)',
334+
symbolCopyright: 'Autoriõigus (©)',
335+
symbolTrademark: 'Kaubamärk (™)',
336+
symbolUL: 'UL heakskiit',
337+
symbolCSA: 'CSA heakskiit',
338338
},
339339
upca: {
340340
content: 'Sisu (11 numbrit)',

‎src/locales/fa.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,14 @@ const fa = {
327327
storeInline: 'جاسازی درون‌خطی',
328328
},
329329
symbol: {
330-
symbol: 'Symbol',
331-
height: 'Height (dots)',
332-
width: 'Width (dots)',
333-
symbolRegistered: 'Registered (®)',
334-
symbolCopyright: 'Copyright (©)',
335-
symbolTrademark: 'Trademark (™)',
336-
symbolUL: 'UL approval',
337-
symbolCSA: 'CSA approval',
330+
symbol: 'نماد',
331+
height: 'ارتفاع (نقطه)',
332+
width: 'عرض (نقطه)',
333+
symbolRegistered: 'ثبت‌شده (®)',
334+
symbolCopyright: 'حق نشر (©)',
335+
symbolTrademark: 'علامت تجاری (™)',
336+
symbolUL: 'تأیید UL',
337+
symbolCSA: 'تأیید CSA',
338338
},
339339
upca: {
340340
content: 'محتوا (۱۱ رقم)',

0 commit comments

Comments
 (0)