Skip to content

Commit 846441d

Browse files
committed
refactor(label): flatten defaultFont into two top-level optional fields
Replaces the nested defaultFont: { fontId, height } object with two independent optional fields defaultFontId and defaultFontHeight. The nested shape forced an all-or-nothing UI: typing one field at a time either lost the partial input (deadlock) or required a fragile merge helper that conflated 'absent' and 'cleared' states. Flat fields let each input persist independently; the generator emits ^CF only when both are set. Removes mergeDefaultFont; tests updated including a new pair that asserts ^CF is omitted on partial state.
1 parent 0425a31 commit 846441d

6 files changed

Lines changed: 32 additions & 40 deletions

File tree

‎src/components/Properties/PropertiesPanel.tsx‎

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,9 @@ function LabelConfigPanel({
436436
type="text"
437437
className={inputCls}
438438
maxLength={2}
439-
value={label.defaultFont?.fontId ?? ""}
439+
value={label.defaultFontId ?? ""}
440440
onChange={(e) =>
441-
onUpdate({
442-
defaultFont: mergeDefaultFont(label.defaultFont, {
443-
fontId: e.target.value,
444-
}),
445-
})
441+
onUpdate({ defaultFontId: e.target.value || undefined })
446442
}
447443
/>
448444
</div>
@@ -454,12 +450,10 @@ function LabelConfigPanel({
454450
type="number"
455451
className={inputCls}
456452
min={1}
457-
value={label.defaultFont?.height ?? ""}
453+
value={label.defaultFontHeight ?? ""}
458454
onChange={(e) =>
459455
onUpdate({
460-
defaultFont: mergeDefaultFont(label.defaultFont, {
461-
height: parseIntOrUndef(e.target.value),
462-
}),
456+
defaultFontHeight: parseIntOrUndef(e.target.value),
463457
})
464458
}
465459
/>
@@ -476,16 +470,3 @@ function parseIntOrUndef(raw: string): number | undefined {
476470
const n = parseInt(raw, 10);
477471
return isNaN(n) ? undefined : n;
478472
}
479-
480-
function mergeDefaultFont(
481-
current: LabelConfig["defaultFont"],
482-
patch: { fontId?: string; height?: number },
483-
): LabelConfig["defaultFont"] {
484-
// `in` distinguishes "key absent from patch" (keep current value) from
485-
// "key set to undefined" (user just cleared the input). `??` would conflate
486-
// the two and silently keep the old value when the user clears a field.
487-
const fontId = "fontId" in patch ? patch.fontId : current?.fontId;
488-
const height = "height" in patch ? patch.height : current?.height;
489-
if (!fontId || height === undefined) return undefined;
490-
return { fontId, height };
491-
}

‎src/lib/zplGenerator.test.ts‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,24 @@ describe('generateZPL — printer params', () => {
8888
expect(generateZPL(BASE_LABEL, [])).not.toContain('^PO');
8989
});
9090

91-
it('emits ^CF when defaultFont is set', () => {
91+
it('emits ^CF when both defaultFontId and defaultFontHeight are set', () => {
9292
const zpl = generateZPL(
93-
{ ...BASE_LABEL, defaultFont: { fontId: '0', height: 30 } },
93+
{ ...BASE_LABEL, defaultFontId: '0', defaultFontHeight: 30 },
9494
[],
9595
);
9696
expect(zpl).toContain('^CF0,30');
9797
});
9898

99+
it('omits ^CF when defaultFontId is set without defaultFontHeight', () => {
100+
expect(generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, []))
101+
.not.toContain('^CF');
102+
});
103+
104+
it('omits ^CF when defaultFontHeight is set without defaultFontId', () => {
105+
expect(generateZPL({ ...BASE_LABEL, defaultFontHeight: 30 }, []))
106+
.not.toContain('^CF');
107+
});
108+
99109
it('emits printer params in canonical header order before ^LS', () => {
100110
const zpl = generateZPL(
101111
{
@@ -105,7 +115,8 @@ describe('generateZPL — printer params', () => {
105115
printSpeed: 6,
106116
darkness: 10,
107117
printOrientation: 'I',
108-
defaultFont: { fontId: '0', height: 30 },
118+
defaultFontId: '0',
119+
defaultFontHeight: 30,
109120
labelShift: 5,
110121
},
111122
[],
@@ -215,14 +226,16 @@ describe('generateZPL — parse/generate roundtrip', () => {
215226
darkness: 0,
216227
mediaType: 'D',
217228
printOrientation: 'I',
218-
defaultFont: { fontId: '0', height: 30 },
229+
defaultFontId: '0',
230+
defaultFontHeight: 30,
219231
};
220232
const regenerated = generateZPL(label, []);
221233
const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm);
222234
expect(labelConfig.printSpeed).toBe(8);
223235
expect(labelConfig.darkness).toBe(0);
224236
expect(labelConfig.mediaType).toBe('D');
225237
expect(labelConfig.printOrientation).toBe('I');
226-
expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 30 });
238+
expect(labelConfig.defaultFontId).toBe('0');
239+
expect(labelConfig.defaultFontHeight).toBe(30);
227240
});
228241
});

‎src/lib/zplGenerator.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export function generateZPL(label: LabelConfig, objects: LabelObject[]): string
3030
// darkness=0 is a valid value (printer baseline), so check undefined explicitly.
3131
if (label.darkness !== undefined) lines.push(`^MD${label.darkness}`);
3232
if (label.printOrientation) lines.push(`^PO${label.printOrientation}`);
33-
if (label.defaultFont) {
34-
lines.push(`^CF${label.defaultFont.fontId},${label.defaultFont.height}`);
33+
if (label.defaultFontId && label.defaultFontHeight !== undefined) {
34+
lines.push(`^CF${label.defaultFontId},${label.defaultFontHeight}`);
3535
}
3636
if (label.labelShift) lines.push(`^LS${label.labelShift}`);
3737

‎src/lib/zplParser.test.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,10 @@ describe('parseZPL — printer params', () => {
546546
expect(parseZPL('^XA^POI^XZ', 8).labelConfig.printOrientation).toBe('I');
547547
});
548548

549-
it('parses ^CF default font with id and height', () => {
549+
it('parses ^CF into defaultFontId and defaultFontHeight', () => {
550550
const { labelConfig } = parseZPL('^XA^CF0,40^XZ', 8);
551-
expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 40 });
551+
expect(labelConfig.defaultFontId).toBe('0');
552+
expect(labelConfig.defaultFontHeight).toBe(40);
552553
});
553554
});
554555

‎src/lib/zplParser.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,8 +649,9 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL {
649649
const explicitHeight = parseInt(p[1] ?? "", 10);
650650
cfHeight = isNaN(explicitHeight) ? cfHeight : explicitHeight;
651651
cfWidth = int(p[2], cfWidth);
652-
if (fontId && !isNaN(explicitHeight) && explicitHeight > 0) {
653-
labelConfig.defaultFont = { fontId, height: explicitHeight };
652+
if (fontId) labelConfig.defaultFontId = fontId;
653+
if (!isNaN(explicitHeight) && explicitHeight > 0) {
654+
labelConfig.defaultFontHeight = explicitHeight;
654655
}
655656
},
656657

‎src/types/ObjectType.ts‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ export const labelConfigSchema = z.object({
1212
darkness: z.number().int().min(-30).max(30).optional(),
1313
mediaType: z.enum(['T', 'D']).optional(),
1414
printOrientation: z.enum(['N', 'I']).optional(),
15-
defaultFont: z
16-
.object({
17-
fontId: z.string().min(1),
18-
height: z.number().int().positive(),
19-
})
20-
.optional(),
15+
defaultFontId: z.string().min(1).optional(),
16+
defaultFontHeight: z.number().int().positive().optional(),
2117
});
2218

2319
export type LabelConfig = z.infer<typeof labelConfigSchema>;

0 commit comments

Comments
 (0)