diff --git a/src/components/Canvas/hooks/useKonvaTransformer.ts b/src/components/Canvas/hooks/useKonvaTransformer.ts index e5b9ed91..3644277e 100644 --- a/src/components/Canvas/hooks/useKonvaTransformer.ts +++ b/src/components/Canvas/hooks/useKonvaTransformer.ts @@ -190,12 +190,18 @@ export function useKonvaTransformer({ }, [selectedIds, selectedTypesKey, selectedSignature, stageRef, transformerRef]); const resizeEnabled = selectedIds.length <= 1; + const singleType = + selectedIds.length === 1 + ? objects.find((o) => o.id === selectedIds[0])?.type ?? "" + : ""; const enabledAnchors: string[] | undefined = selectedIds.length > 1 ? [] - : BARCODE_1D_TYPES.has(objects.find((o) => o.id === selectedIds[0])?.type ?? "") - ? ["top-center", "bottom-center"] - : undefined; + : ObjectRegistry[singleType]?.heightLocked + ? [] + : BARCODE_1D_TYPES.has(singleType) + ? ["top-center", "bottom-center"] + : undefined; const isFreeResize = enabledAnchors === undefined; /** Reset all transform-time state. Idempotent; safe to call from any exit path. */ diff --git a/src/registry/barcode1d.tsx b/src/registry/barcode1d.tsx index b5d0a693..88fa809a 100644 --- a/src/registry/barcode1d.tsx +++ b/src/registry/barcode1d.tsx @@ -2,6 +2,7 @@ import type { ObjectTypeDefinition, ObjectGroup, LabelObjectBase } from '../type import { useT } from '../lib/useT'; import { inputCls, labelCls } from '../components/Properties/styles'; import { fieldPos } from './zplHelpers'; +import { commitHeightTransform } from './transformHelpers'; export interface Barcode1DProps { content: string; @@ -29,6 +30,8 @@ interface Barcode1DConfig { * here ensures Labelary uses the same ratio as the canvas rendering. */ byRatio?: number; + /** See {@link ObjectTypeDefinition.heightLocked}. */ + heightLocked?: boolean; } interface BarcodeLocale { @@ -53,10 +56,9 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition ({ - height: Math.max(1, snap(Math.round(obj.props.height * sy))), - }), + commitTransform: config.heightLocked ? undefined : commitHeightTransform, toZPL: (obj: LabelObjectBase & { props: Barcode1DProps }) => { const p = obj.props; @@ -95,6 +97,8 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition onChange({ height: Number(e.target.value) })} /> diff --git a/src/registry/code128.tsx b/src/registry/code128.tsx index e029b72c..dd3a8dd8 100644 --- a/src/registry/code128.tsx +++ b/src/registry/code128.tsx @@ -2,6 +2,7 @@ import type { ObjectTypeDefinition } from '../types/ObjectType'; import { useT } from '../lib/useT'; import { inputCls, labelCls } from '../components/Properties/styles'; import { fieldPos } from './zplHelpers'; +import { commitHeightTransform } from './transformHelpers'; export interface Code128Props { content: string; @@ -24,6 +25,8 @@ export const code128: ObjectTypeDefinition = { }, defaultSize: { width: 300, height: 120 }, + commitTransform: commitHeightTransform, + toZPL: (obj) => { const p = obj.props; const interp = p.printInterpretation ? 'Y' : 'N'; diff --git a/src/registry/code39.tsx b/src/registry/code39.tsx index a689a05c..78461428 100644 --- a/src/registry/code39.tsx +++ b/src/registry/code39.tsx @@ -2,6 +2,7 @@ import type { ObjectTypeDefinition } from '../types/ObjectType'; import { useT } from '../lib/useT'; import { inputCls, labelCls } from '../components/Properties/styles'; import { fieldPos } from './zplHelpers'; +import { commitHeightTransform } from './transformHelpers'; export interface Code39Props { content: string; @@ -24,6 +25,8 @@ export const code39: ObjectTypeDefinition = { }, defaultSize: { width: 300, height: 120 }, + commitTransform: commitHeightTransform, + toZPL: (obj) => { const p = obj.props; const interp = p.printInterpretation ? 'Y' : 'N'; diff --git a/src/registry/ean13.tsx b/src/registry/ean13.tsx index 42683bb9..cc0bd197 100644 --- a/src/registry/ean13.tsx +++ b/src/registry/ean13.tsx @@ -2,6 +2,7 @@ import type { ObjectTypeDefinition } from '../types/ObjectType'; import { useT } from '../lib/useT'; import { inputCls, labelCls } from '../components/Properties/styles'; import { fieldPos } from './zplHelpers'; +import { commitHeightTransform } from './transformHelpers'; export interface Ean13Props { content: string; // 12 digits — ZPL appends the check digit automatically @@ -22,6 +23,8 @@ export const ean13: ObjectTypeDefinition = { }, defaultSize: { width: 300, height: 120 }, + commitTransform: commitHeightTransform, + toZPL: (obj) => { const p = obj.props; const interp = p.printInterpretation ? 'Y' : 'N'; diff --git a/src/registry/gs1databar.tsx b/src/registry/gs1databar.tsx index d69d276a..7f96553b 100644 --- a/src/registry/gs1databar.tsx +++ b/src/registry/gs1databar.tsx @@ -8,6 +8,10 @@ export const gs1databar = createBarcode1D({ hasCheckDigit: false, localeKey: "gs1databar", group: 'code-1d', + // GS1 Databar Omnidirectional has a symbology-fixed height; Zebra/Labelary + // ignore the ^BR height parameter for this variant. Disabling resize and the + // height input keeps the designer honest about what affects the print. + heightLocked: true, zplCommand: (p) => { // ^BR{orientation},{symbology},{magnification},{separator},{height},{segments} // symbology 1 = omnidirectional diff --git a/src/registry/transformHelpers.ts b/src/registry/transformHelpers.ts index f862ed40..2b786c13 100644 --- a/src/registry/transformHelpers.ts +++ b/src/registry/transformHelpers.ts @@ -22,6 +22,18 @@ export function commitWidthHeightTransform

( } as Partial

; } +/** Shared commitTransform for 1D barcodes — only the bar height scales (width + * is determined by content + module width, not by the resize anchor). */ +export function commitHeightTransform

( + obj: LabelObjectBase & { props: P }, + ctx: TransformContext, +): Partial

{ + const { sy, snap } = ctx; + return { + height: Math.max(1, snap(Math.round(obj.props.height * sy))), + } as Partial

; +} + interface Stacked2DProps { rowHeight: number; moduleWidth: number; diff --git a/src/types/ObjectType.ts b/src/types/ObjectType.ts index 9149b665..ddbfb71b 100644 --- a/src/types/ObjectType.ts +++ b/src/types/ObjectType.ts @@ -57,6 +57,13 @@ export interface ObjectTypeDefinition

{ * back to the model's top-left convention. */ nodeOrigin?: 'center' | 'top-left'; + /** + * True if the rendered height is fixed by the symbology spec rather than the + * `height` prop (e.g. GS1 Databar Omnidirectional). The transformer disables + * its height anchors and the properties panel renders the height input as + * read-only — both reflect that the value cannot influence the print output. + */ + heightLocked?: boolean; toZPL: (obj: LabelObjectBase & { props: P }) => string; /** * Optional hook to enforce type-specific invariants on incoming changes