Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/react-zpl/src/commands/graphicCircle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineCommand } from './base';

import { COLOR } from '../constants';
import { ObjectValues } from '../types';

interface GraphicCircleParams {
diameter: number;
thickness?: number;
lineColor?: ObjectValues<typeof COLOR>;
}

export const graphicCircle = defineCommand<GraphicCircleParams>(
({ diameter, thickness = 1, lineColor = COLOR.BLACK }) =>
`^GC${diameter},${thickness},${lineColor}^FS`
);
16 changes: 16 additions & 0 deletions apps/react-zpl/src/commands/graphicEllipse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineCommand } from './base';

import { COLOR } from '../constants';
import { ObjectValues } from '../types';

interface GraphicEllipseParams {
width: number;
height: number;
thickness?: number;
lineColor?: ObjectValues<typeof COLOR>;
}

export const graphicEllipse = defineCommand<GraphicEllipseParams>(
({ width, height, thickness = 1, lineColor = COLOR.BLACK }) =>
`^GE${width},${height},${thickness},${lineColor}^FS`
);
2 changes: 2 additions & 0 deletions apps/react-zpl/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export * from './changeDefaultFont';
export * from './changeInternationalEncoding';
export * from './graphicBox';
export * from './graphicDiagonal';
export * from './graphicCircle';
export * from './graphicEllipse';
48 changes: 48 additions & 0 deletions apps/react-zpl/src/components/Circle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { fieldOrigin, graphicCircle, newLine } from '../commands';
import { COLOR } from '../constants';
import { ObjectValues, ZplElement } from '../types';

const MIN_DIAMETER = 3;
const MAX_DIAMETER = 4095;
const MIN_THICKNESS = 1;
const MAX_THICKNESS = 4095;

interface CircleProps {
diameter: number;
fieldOriginX?: number;
fieldOriginY?: number;
thickness?: number;
lineColor?: ObjectValues<typeof COLOR>;
}

export const Circle: ZplElement<CircleProps> = () => <span />;

Circle.displayName = 'Circle';

Circle.print = (element, _context) => {
const {
diameter,
fieldOriginX = 0,
fieldOriginY = 0,
thickness = MIN_THICKNESS,
lineColor = COLOR.BLACK,
} = element.props;

if (diameter < MIN_DIAMETER || diameter > MAX_DIAMETER) {
throw new Error(
`Circle: diameter는 ${MIN_DIAMETER}~${MAX_DIAMETER} 사이여야 합니다. (diameter=${diameter})`
);
}
if (thickness < MIN_THICKNESS || thickness > MAX_THICKNESS) {
throw new Error(
`Circle: thickness는 ${MIN_THICKNESS}~${MAX_THICKNESS} 사이여야 합니다. (thickness=${thickness})`
);
}

const output: string[] = [];

output.push(fieldOrigin({ offsetX: fieldOriginX, offsetY: fieldOriginY }));
output.push(graphicCircle({ diameter, thickness, lineColor }));

return output.flat().join(newLine());
};
50 changes: 50 additions & 0 deletions apps/react-zpl/src/components/Ellipse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { fieldOrigin, graphicEllipse, newLine } from '../commands';
import { COLOR } from '../constants';
import { ObjectValues, ZplElement } from '../types';

const MIN_SIZE = 3;
const MAX_SIZE = 4095;
const MIN_THICKNESS = 1;
const MAX_THICKNESS = 4095;

interface EllipseProps {
width: number;
height: number;
fieldOriginX?: number;
fieldOriginY?: number;
thickness?: number;
lineColor?: ObjectValues<typeof COLOR>;
}

export const Ellipse: ZplElement<EllipseProps> = () => <span />;

Ellipse.displayName = 'Ellipse';

Ellipse.print = (element, _context) => {
const {
width,
height,
fieldOriginX = 0,
fieldOriginY = 0,
thickness = MIN_THICKNESS,
lineColor = COLOR.BLACK,
} = element.props;

if (width < MIN_SIZE || width > MAX_SIZE || height < MIN_SIZE || height > MAX_SIZE) {
throw new Error(
`Ellipse: width와 height는 ${MIN_SIZE}~${MAX_SIZE} 사이여야 합니다. (width=${width}, height=${height})`
);
}
if (thickness < MIN_THICKNESS || thickness > MAX_THICKNESS) {
throw new Error(
`Ellipse: thickness는 ${MIN_THICKNESS}~${MAX_THICKNESS} 사이여야 합니다. (thickness=${thickness})`
);
}

const output: string[] = [];

output.push(fieldOrigin({ offsetX: fieldOriginX, offsetY: fieldOriginY }));
output.push(graphicEllipse({ width, height, thickness, lineColor }));

return output.flat().join(newLine());
};
2 changes: 2 additions & 0 deletions apps/react-zpl/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from './ZplLabel';
export * from './DiagonalLine';
export * from './Line';
export * from './Text';
export * from './Circle';
export * from './Ellipse';
4 changes: 3 additions & 1 deletion demos/electron/src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from 'react'

import { DiagonalLine, Line, Text, ZplLabel } from '@zpl-kit/react-zpl'
import { Circle, DiagonalLine, Ellipse, Line, Text, ZplLabel } from '@zpl-kit/react-zpl'

const TestLabel = ({ text }: { text: string }) => {
return (
Expand Down Expand Up @@ -41,6 +41,8 @@ const TestLabel = ({ text }: { text: string }) => {
fieldOriginY={20}
thickness={2}
/>
<Circle diameter={100} fieldOriginX={10} fieldOriginY={20} thickness={1} />
<Ellipse width={100} height={50} fieldOriginX={10} fieldOriginY={20} thickness={2} />
</ZplLabel>
)
}
Expand Down
22 changes: 21 additions & 1 deletion demos/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { useState } from 'react';

import { DiagonalLine, Line, Text, ZplLabel } from '@zpl-kit/react-zpl';
import {
Circle,
DiagonalLine,
Ellipse,
Line,
Text,
ZplLabel,
} from '@zpl-kit/react-zpl';

import './App.css';

Expand Down Expand Up @@ -55,6 +62,19 @@ const TestLabel = ({ text }: { text: string }) => {
fieldOriginY={20}
thickness={2}
/>
<Circle
diameter={100}
fieldOriginX={10}
fieldOriginY={20}
thickness={1}
/>
<Ellipse
width={100}
height={50}
fieldOriginX={10}
fieldOriginY={20}
thickness={2}
/>
</ZplLabel>
);
};
Expand Down
77 changes: 77 additions & 0 deletions tests/unit/Circle.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { createElement } from 'react';
import { describe, it, expect } from 'vitest';

import { Circle, type ZplElementContext } from '@zpl-kit/react-zpl';

const defaultContext: ZplElementContext = {
labelOrientation: 'N',
defaultFontName: 'J',
defaultFontWidth: 30,
defaultFontHeight: 30,
};

describe('Circle', () => {
describe('print', () => {
it('기본값으로 ^FO0,0\\&^GC{diameter},1,B^FS 생성', () => {
const el = createElement(Circle, { diameter: 50 });
expect(Circle.print(el, defaultContext)).toBe('^FO0,0\\&^GC50,1,B^FS');
});

it('fieldOrigin, thickness, lineColor 지정 시 올바른 ZPL 생성', () => {
const el = createElement(Circle, {
diameter: 100,
fieldOriginX: 10,
fieldOriginY: 20,
thickness: 5,
lineColor: 'B',
});
expect(Circle.print(el, defaultContext)).toBe('^FO10,20\\&^GC100,5,B^FS');
});

it('흰색 원(lineColor=W) 생성', () => {
const el = createElement(Circle, { diameter: 60, lineColor: 'W' });
expect(Circle.print(el, defaultContext)).toContain('^GC60,1,W^FS');
});

it('채워진 원 (thickness >= diameter) 허용', () => {
const el = createElement(Circle, { diameter: 50, thickness: 50 });
expect(Circle.print(el, defaultContext)).toBe('^FO0,0\\&^GC50,50,B^FS');
});
});

describe('print - 검증', () => {
it('diameter < 3 시 에러 throw', () => {
const el = createElement(Circle, { diameter: 2 });
expect(() => Circle.print(el, defaultContext)).toThrow(
'diameter는 3~4095 사이여야 합니다'
);
});

it('diameter > 4095 시 에러 throw', () => {
const el = createElement(Circle, { diameter: 4096 });
expect(() => Circle.print(el, defaultContext)).toThrow(
'diameter는 3~4095 사이여야 합니다'
);
});

it('thickness < 1 시 에러 throw', () => {
const el = createElement(Circle, { diameter: 50, thickness: 0 });
expect(() => Circle.print(el, defaultContext)).toThrow(
'thickness는 1~4095 사이여야 합니다'
);
});

it('thickness > 4095 시 에러 throw', () => {
const el = createElement(Circle, { diameter: 50, thickness: 4096 });
expect(() => Circle.print(el, defaultContext)).toThrow(
'thickness는 1~4095 사이여야 합니다'
);
});
});

describe('displayName', () => {
it('Circle으로 설정됨', () => {
expect(Circle.displayName).toBe('Circle');
});
});
});
80 changes: 80 additions & 0 deletions tests/unit/Ellipse.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { createElement } from 'react';
import { describe, it, expect } from 'vitest';

import { Ellipse, type ZplElementContext } from '@zpl-kit/react-zpl';

const defaultContext: ZplElementContext = {
labelOrientation: 'N',
defaultFontName: 'J',
defaultFontWidth: 30,
defaultFontHeight: 30,
};

describe('Ellipse', () => {
describe('print', () => {
it('기본값으로 ^FO0,0\\&^GE{width},{height},1,B^FS 생성', () => {
const el = createElement(Ellipse, { width: 80, height: 40 });
expect(Ellipse.print(el, defaultContext)).toBe('^FO0,0\\&^GE80,40,1,B^FS');
});

it('fieldOrigin, thickness, lineColor 지정 시 올바른 ZPL 생성', () => {
const el = createElement(Ellipse, {
width: 100,
height: 60,
fieldOriginX: 10,
fieldOriginY: 20,
thickness: 3,
lineColor: 'B',
});
expect(Ellipse.print(el, defaultContext)).toBe('^FO10,20\\&^GE100,60,3,B^FS');
});

it('흰색 타원(lineColor=W) 생성', () => {
const el = createElement(Ellipse, { width: 80, height: 40, lineColor: 'W' });
expect(Ellipse.print(el, defaultContext)).toContain('^GE80,40,1,W^FS');
});
});

describe('print - 검증', () => {
it('width < 3 시 에러 throw', () => {
const el = createElement(Ellipse, { width: 2, height: 40 });
expect(() => Ellipse.print(el, defaultContext)).toThrow(
'width와 height는 3~4095 사이여야 합니다'
);
});

it('height < 3 시 에러 throw', () => {
const el = createElement(Ellipse, { width: 80, height: 2 });
expect(() => Ellipse.print(el, defaultContext)).toThrow(
'width와 height는 3~4095 사이여야 합니다'
);
});

it('width > 4095 시 에러 throw', () => {
const el = createElement(Ellipse, { width: 4096, height: 40 });
expect(() => Ellipse.print(el, defaultContext)).toThrow(
'width와 height는 3~4095 사이여야 합니다'
);
});

it('thickness < 1 시 에러 throw', () => {
const el = createElement(Ellipse, { width: 80, height: 40, thickness: 0 });
expect(() => Ellipse.print(el, defaultContext)).toThrow(
'thickness는 1~4095 사이여야 합니다'
);
});

it('thickness > 4095 시 에러 throw', () => {
const el = createElement(Ellipse, { width: 80, height: 40, thickness: 4096 });
expect(() => Ellipse.print(el, defaultContext)).toThrow(
'thickness는 1~4095 사이여야 합니다'
);
});
});

describe('displayName', () => {
it('Ellipse으로 설정됨', () => {
expect(Ellipse.displayName).toBe('Ellipse');
});
});
});