Skip to content

Commit 2a6daae

Browse files
authored
Merge pull request #31 from u8array/refactor/barcode1d-typed-locale
Refactor/barcode1d typed locale
2 parents 6fc1763 + a9cb371 commit 2a6daae

22 files changed

Lines changed: 63 additions & 295 deletions

‎src/lib/useT.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useLabelStore } from '../store/labelStore';
2-
import { locales } from '../locales';
2+
import { locales, type Translations } from '../locales';
33

4-
export function useT() {
4+
export function useT(): Translations {
55
const locale = useLabelStore((s) => s.locale);
66
return locales[locale];
77
}

‎src/lib/zplParser.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL {
429429
height: bcHeight,
430430
moduleWidth: byModuleWidth,
431431
printInterpretation: bcInterp,
432+
checkDigit: false, // EAN-13 has no user-controlled check digit (^BE auto-appends).
432433
rotation: bcRotation,
433434
} satisfies Ean13Props,
434435
posType,

‎src/locales/index.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export const locales = {
7878

7979
export type LocaleCode = keyof typeof locales;
8080

81+
/** Shape of a single locale dictionary, derived from the registered locales.
82+
* Use this when a non-React module needs the translation type without
83+
* pulling in the useT hook. */
84+
export type Translations = (typeof locales)[LocaleCode];
85+
8186
export const localeNames: Record<LocaleCode, string> = {
8287
en: 'English',
8388
de: 'Deutsch',

‎src/registry/barcode1d.tsx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ObjectTypeDefinition, ObjectGroup, LabelObjectBase } from '../types/ObjectType';
22
import { useT } from '../lib/useT';
3+
import type { Translations } from '../locales';
34
import { inputCls, labelCls } from '../components/Properties/styles';
45
import { fieldPos, fdField } from './zplHelpers';
56
import { commitHeightTransform } from './transformHelpers';
@@ -24,8 +25,10 @@ interface Barcode1DConfig {
2425
hasCheckDigit: boolean;
2526
/** Build the ZPL barcode command (e.g. `^BUN,100,Y,N,N`). */
2627
zplCommand: (p: Barcode1DProps) => string;
27-
/** Locale key under `t.registry[localeKey]`. Must match en.ts shape. */
28-
localeKey: string;
28+
/** Per-symbology locale block selector — TS verifies the returned shape
29+
* conforms to BarcodeLocale at every call site, so a missing or renamed
30+
* `t.registry.<key>` is a compile error rather than a runtime undefined. */
31+
locale: (t: Translations) => BarcodeLocale;
2932
group: ObjectGroup;
3033
/**
3134
* Explicit wide-to-narrow ratio for the ^BY command.
@@ -90,7 +93,7 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition<B
9093

9194
PropertiesPanel: ({ obj, onChange }) => {
9295
const t = useT();
93-
const loc = (t.registry as unknown as Record<string, BarcodeLocale>)[config.localeKey] ?? {} as BarcodeLocale;
96+
const loc = config.locale(t);
9497
const p = obj.props;
9598
return (
9699
<div className="flex flex-col gap-3">

‎src/registry/codabar.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const codabar = createBarcode1D({
66
icon: "CBA",
77
defaultContent: "A12345A",
88
hasCheckDigit: true,
9-
localeKey: "codabar",
9+
locale: (t) => t.registry.codabar,
1010
group: 'code-1d',
1111
contentSpec: { charset: '0-9A-Da-d\\-$:/.+' },
1212
zplCommand: (p) => {

‎src/registry/code11.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const code11 = createBarcode1D({
66
icon: "C11",
77
defaultContent: "12345",
88
hasCheckDigit: true,
9-
localeKey: "code11",
9+
locale: (t) => t.registry.code11,
1010
group: 'code-1d',
1111
contentSpec: { charset: '0-9\\-' },
1212
zplCommand: (p) => {

‎src/registry/code128.tsx‎

Lines changed: 10 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,16 @@
1-
import type { ObjectTypeDefinition } from '../types/ObjectType';
2-
import { useT } from '../lib/useT';
3-
import { inputCls, labelCls } from '../components/Properties/styles';
4-
import { fieldPos, fdField } from './zplHelpers';
5-
import { commitHeightTransform } from './transformHelpers';
6-
import { type ZplRotation } from './rotation';
7-
import { RotationSelect } from '../components/Properties/RotationSelect';
8-
import { NumberInput } from '../components/Properties/NumberInput';
1+
import { createBarcode1D } from './barcode1d';
2+
export type { Barcode1DProps as Code128Props } from './barcode1d';
93

10-
export interface Code128Props {
11-
content: string;
12-
height: number;
13-
moduleWidth: number;
14-
printInterpretation: boolean;
15-
checkDigit: boolean;
16-
rotation: ZplRotation;
17-
}
18-
19-
export const code128: ObjectTypeDefinition<Code128Props> = {
4+
export const code128 = createBarcode1D({
205
label: 'Code 128',
216
icon: '|||',
22-
group: 'code-1d' as const,
23-
defaultProps: {
24-
content: '12345678',
25-
height: 100,
26-
moduleWidth: 2,
27-
printInterpretation: true,
28-
checkDigit: false,
29-
rotation: 'N',
30-
},
31-
defaultSize: { width: 300, height: 120 },
32-
33-
commitTransform: commitHeightTransform,
34-
35-
toZPL: (obj) => {
36-
const p = obj.props;
7+
defaultContent: '12345678',
8+
hasCheckDigit: true,
9+
locale: (t) => t.registry.code128,
10+
group: 'code-1d',
11+
zplCommand: (p) => {
3712
const interp = p.printInterpretation ? 'Y' : 'N';
3813
const check = p.checkDigit ? 'Y' : 'N';
39-
return [
40-
`^BY${p.moduleWidth}`,
41-
fieldPos(obj),
42-
`^BC${p.rotation},${p.height},${interp},N,${check}`,
43-
fdField(p.content),
44-
].filter(Boolean).join('');
45-
},
46-
47-
PropertiesPanel: ({ obj, onChange }) => {
48-
const t = useT();
49-
const p = obj.props;
50-
return (
51-
<div className="flex flex-col gap-3">
52-
<div className="flex flex-col gap-1">
53-
<label className={labelCls}>{t.registry.code128.content}</label>
54-
<input
55-
className={inputCls}
56-
value={p.content}
57-
onChange={(e) => onChange({ content: e.target.value })}
58-
/>
59-
</div>
60-
61-
<NumberInput
62-
label={t.registry.code128.height}
63-
value={p.height}
64-
min={1}
65-
onChange={(height) => onChange({ height })}
66-
/>
67-
68-
<NumberInput
69-
label={t.registry.code128.moduleWidth}
70-
value={p.moduleWidth}
71-
min={1}
72-
max={10}
73-
onChange={(moduleWidth) => onChange({ moduleWidth })}
74-
/>
75-
76-
<div className="flex flex-col gap-2">
77-
<label className="flex items-center gap-2 cursor-pointer">
78-
<input
79-
type="checkbox"
80-
className="accent-accent"
81-
checked={p.printInterpretation}
82-
onChange={(e) => onChange({ printInterpretation: e.target.checked })}
83-
/>
84-
<span className={labelCls}>{t.registry.code128.printInterpretation}</span>
85-
</label>
86-
<label className="flex items-center gap-2 cursor-pointer">
87-
<input
88-
type="checkbox"
89-
className="accent-accent"
90-
checked={p.checkDigit}
91-
onChange={(e) => onChange({ checkDigit: e.target.checked })}
92-
/>
93-
<span className={labelCls}>{t.registry.code128.checkDigit}</span>
94-
</label>
95-
</div>
96-
97-
<RotationSelect value={p.rotation} onChange={(rotation) => onChange({ rotation })} />
98-
</div>
99-
);
14+
return `^BC${p.rotation},${p.height},${interp},N,${check}`;
10015
},
101-
};
16+
});

‎src/registry/code39.tsx‎

Lines changed: 11 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,20 @@
1-
import type { ObjectTypeDefinition } from '../types/ObjectType';
2-
import { useT } from '../lib/useT';
3-
import { inputCls, labelCls } from '../components/Properties/styles';
4-
import { fieldPos, fdField } from './zplHelpers';
5-
import { commitHeightTransform } from './transformHelpers';
6-
import { filterContent, type ContentSpec } from './contentSpec';
7-
import { type ZplRotation } from './rotation';
8-
import { RotationSelect } from '../components/Properties/RotationSelect';
9-
import { NumberInput } from '../components/Properties/NumberInput';
1+
import { createBarcode1D } from './barcode1d';
2+
import type { ContentSpec } from './contentSpec';
3+
export type { Barcode1DProps as Code39Props } from './barcode1d';
104

115
const code39Spec: ContentSpec = { charset: '0-9A-Za-z\\-. $/+%' };
126

13-
export interface Code39Props {
14-
content: string;
15-
height: number;
16-
moduleWidth: number;
17-
printInterpretation: boolean;
18-
checkDigit: boolean;
19-
rotation: ZplRotation;
20-
}
21-
22-
export const code39: ObjectTypeDefinition<Code39Props> = {
7+
export const code39 = createBarcode1D({
238
label: 'Code 39',
249
icon: '|·|',
10+
defaultContent: 'CODE39',
11+
hasCheckDigit: true,
12+
locale: (t) => t.registry.code39,
2513
group: 'code-1d',
26-
defaultProps: {
27-
content: 'CODE39',
28-
height: 100,
29-
moduleWidth: 2,
30-
printInterpretation: true,
31-
checkDigit: false,
32-
rotation: 'N',
33-
},
34-
defaultSize: { width: 300, height: 120 },
35-
36-
commitTransform: commitHeightTransform,
37-
38-
toZPL: (obj) => {
39-
const p = obj.props;
14+
contentSpec: code39Spec,
15+
zplCommand: (p) => {
4016
const interp = p.printInterpretation ? 'Y' : 'N';
4117
const check = p.checkDigit ? 'Y' : 'N';
42-
return [
43-
`^BY${p.moduleWidth}`,
44-
fieldPos(obj),
45-
`^B3${p.rotation},${check},${p.height},${interp},N`,
46-
fdField(p.content),
47-
].filter(Boolean).join('');
48-
},
49-
50-
PropertiesPanel: ({ obj, onChange }) => {
51-
const t = useT();
52-
const p = obj.props;
53-
return (
54-
<div className="flex flex-col gap-3">
55-
<div className="flex flex-col gap-1">
56-
<label className={labelCls}>{t.registry.code39.content}</label>
57-
<input
58-
className={inputCls}
59-
value={p.content}
60-
onChange={(e) => onChange({ content: filterContent(e.target.value, code39Spec) })}
61-
/>
62-
</div>
63-
64-
<NumberInput
65-
label={t.registry.code39.height}
66-
value={p.height}
67-
min={1}
68-
onChange={(height) => onChange({ height })}
69-
/>
70-
71-
<NumberInput
72-
label={t.registry.code39.moduleWidth}
73-
value={p.moduleWidth}
74-
min={1}
75-
max={10}
76-
onChange={(moduleWidth) => onChange({ moduleWidth })}
77-
/>
78-
79-
<div className="flex flex-col gap-2">
80-
<label className="flex items-center gap-2 cursor-pointer">
81-
<input
82-
type="checkbox"
83-
className="accent-accent"
84-
checked={p.printInterpretation}
85-
onChange={(e) => onChange({ printInterpretation: e.target.checked })}
86-
/>
87-
<span className={labelCls}>{t.registry.code39.printInterpretation}</span>
88-
</label>
89-
<label className="flex items-center gap-2 cursor-pointer">
90-
<input
91-
type="checkbox"
92-
className="accent-accent"
93-
checked={p.checkDigit}
94-
onChange={(e) => onChange({ checkDigit: e.target.checked })}
95-
/>
96-
<span className={labelCls}>{t.registry.code39.checkDigit}</span>
97-
</label>
98-
</div>
99-
100-
<RotationSelect value={p.rotation} onChange={(rotation) => onChange({ rotation })} />
101-
</div>
102-
);
18+
return `^B3${p.rotation},${check},${p.height},${interp},N`;
10319
},
104-
};
20+
});

‎src/registry/code93.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const code93 = createBarcode1D({
66
icon: '|93|',
77
defaultContent: 'CODE93',
88
hasCheckDigit: true,
9-
localeKey: 'code93',
9+
locale: (t) => t.registry.code93,
1010
group: 'code-1d',
1111
zplCommand: (p) => {
1212
const interp = p.printInterpretation ? 'Y' : 'N';

0 commit comments

Comments
 (0)