Merged PR 2038: feat(shared,crm): add Code 128 barcode generation library

feat(shared,crm): add Code 128 barcode generation library

Implements new @isa/shared/barcode library with directive and component
for generating Code 128 barcodes using JsBarcode.

Features:
- Standalone Angular directive (svg[sharedBarcode])
- Standalone Angular component (<shared-barcode>)
- Signal-based reactive inputs
- SVG-based vector rendering
- Customizable colors, size, margins, fonts
- Comprehensive Vitest test coverage (39 tests)
- Storybook stories for both directive and component
- Integrated into customer loyalty card component

Changes:
- Created @isa/shared/barcode library with directive and component
- Added JsBarcode dependency (v3.12.1)
- Integrated barcode into customer loyalty card display
- Added Storybook stories for interactive documentation
- Fixed ui-switch story component reference
- Updated library reference documentation

Refs #5496

Related work items: #5496
This commit is contained in:
Lorenz Hilpert
2025-11-21 13:42:32 +00:00
committed by Nino Righi
parent 5f1d3a2c7b
commit 7a6a2dc49d
23 changed files with 2184 additions and 340 deletions

View File

@@ -0,0 +1,214 @@
import {
argsToTemplate,
type Meta,
type StoryObj,
moduleMetadata,
} from '@storybook/angular';
import { BarcodeComponent } from '@isa/shared/barcode';
type BarcodeInputs = {
value: string;
format: string;
width: number;
height: number;
displayValue: boolean;
lineColor: string;
background: string;
fontSize: number;
margin: number;
};
const meta: Meta<BarcodeInputs> = {
title: 'shared/barcode/BarcodeComponent',
decorators: [
moduleMetadata({
imports: [BarcodeComponent],
}),
],
argTypes: {
value: {
control: {
type: 'text',
},
description: 'The barcode value to encode',
},
format: {
control: {
type: 'select',
},
options: ['CODE128', 'CODE39', 'EAN13', 'EAN8', 'UPC'],
description: 'Barcode format',
},
width: {
control: {
type: 'number',
min: 1,
max: 10,
},
description: 'Width of a single bar in pixels',
},
height: {
control: {
type: 'number',
min: 20,
max: 300,
},
description: 'Height of the barcode in pixels',
},
displayValue: {
control: {
type: 'boolean',
},
description: 'Whether to display the human-readable text',
},
lineColor: {
control: {
type: 'color',
},
description: 'Color of the barcode bars and text',
},
background: {
control: {
type: 'color',
},
description: 'Background color',
},
fontSize: {
control: {
type: 'number',
min: 10,
max: 40,
},
description: 'Font size for the human-readable text',
},
margin: {
control: {
type: 'number',
min: 0,
max: 50,
},
description: 'Margin around the barcode in pixels',
},
},
render: (args) => ({
props: args,
template: `<shared-barcode ${argsToTemplate(args)} />`,
}),
};
export default meta;
type Story = StoryObj<BarcodeComponent>;
export const Default: Story = {
args: {
value: '123456789012',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 10,
},
};
export const ProductEAN: Story = {
args: {
value: '9783161484100',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 10,
},
};
export const CompactNoText: Story = {
args: {
value: '987654321',
format: 'CODE128',
width: 1,
height: 60,
displayValue: false,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 5,
},
};
export const LargeBarcode: Story = {
args: {
value: 'WAREHOUSE2024',
format: 'CODE128',
width: 4,
height: 200,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 28,
margin: 20,
},
};
export const ColoredBarcode: Story = {
args: {
value: 'PRODUCT001',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#0066CC',
background: '#F0F8FF',
fontSize: 20,
margin: 10,
},
};
export const RedBarcode: Story = {
args: {
value: 'URGENT2024',
format: 'CODE128',
width: 3,
height: 120,
displayValue: true,
lineColor: '#CC0000',
background: '#FFEEEE',
fontSize: 22,
margin: 15,
},
};
export const MinimalMargin: Story = {
args: {
value: '1234567890',
format: 'CODE128',
width: 2,
height: 80,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 16,
margin: 0,
},
};
export const SmallFont: Story = {
args: {
value: '555123456789',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 12,
margin: 10,
},
};

View File

@@ -0,0 +1,214 @@
import {
argsToTemplate,
type Meta,
type StoryObj,
moduleMetadata,
} from '@storybook/angular';
import { BarcodeDirective } from '@isa/shared/barcode';
type BarcodeInputs = {
value: string;
format: string;
width: number;
height: number;
displayValue: boolean;
lineColor: string;
background: string;
fontSize: number;
margin: number;
};
const meta: Meta<BarcodeInputs> = {
title: 'shared/barcode/Barcode',
decorators: [
moduleMetadata({
imports: [BarcodeDirective],
}),
],
argTypes: {
value: {
control: {
type: 'text',
},
description: 'The barcode value to encode',
},
format: {
control: {
type: 'select',
},
options: ['CODE128', 'CODE39', 'EAN13', 'EAN8', 'UPC'],
description: 'Barcode format',
},
width: {
control: {
type: 'number',
min: 1,
max: 10,
},
description: 'Width of a single bar in pixels',
},
height: {
control: {
type: 'number',
min: 20,
max: 300,
},
description: 'Height of the barcode in pixels',
},
displayValue: {
control: {
type: 'boolean',
},
description: 'Whether to display the human-readable text',
},
lineColor: {
control: {
type: 'color',
},
description: 'Color of the barcode bars and text',
},
background: {
control: {
type: 'color',
},
description: 'Background color',
},
fontSize: {
control: {
type: 'number',
min: 10,
max: 40,
},
description: 'Font size for the human-readable text',
},
margin: {
control: {
type: 'number',
min: 0,
max: 50,
},
description: 'Margin around the barcode in pixels',
},
},
render: (args) => ({
props: args,
template: `<svg sharedBarcode ${argsToTemplate(args)}></svg>`,
}),
};
export default meta;
type Story = StoryObj<BarcodeDirective>;
export const Default: Story = {
args: {
value: '123456789012',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 10,
},
};
export const ProductEAN: Story = {
args: {
value: '9783161484100',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 10,
},
};
export const CompactNoText: Story = {
args: {
value: '987654321',
format: 'CODE128',
width: 1,
height: 60,
displayValue: false,
lineColor: '#000000',
background: '#ffffff',
fontSize: 20,
margin: 5,
},
};
export const LargeBarcode: Story = {
args: {
value: 'WAREHOUSE2024',
format: 'CODE128',
width: 4,
height: 200,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 28,
margin: 20,
},
};
export const ColoredBarcode: Story = {
args: {
value: 'PRODUCT001',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#0066CC',
background: '#F0F8FF',
fontSize: 20,
margin: 10,
},
};
export const RedBarcode: Story = {
args: {
value: 'URGENT2024',
format: 'CODE128',
width: 3,
height: 120,
displayValue: true,
lineColor: '#CC0000',
background: '#FFEEEE',
fontSize: 22,
margin: 15,
},
};
export const MinimalMargin: Story = {
args: {
value: '1234567890',
format: 'CODE128',
width: 2,
height: 80,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 16,
margin: 0,
},
};
export const SmallFont: Story = {
args: {
value: '555123456789',
format: 'CODE128',
width: 2,
height: 100,
displayValue: true,
lineColor: '#000000',
background: '#ffffff',
fontSize: 12,
margin: 10,
},
};

View File

@@ -1,5 +1,5 @@
import { argsToTemplate, type Meta, type StoryObj } from '@storybook/angular';
import { IconSwitchComponent, IconSwitchColor } from '@isa/ui/switch';
import { SwitchComponent, IconSwitchColor } from '@isa/ui/switch';
import { provideIcons } from '@ng-icons/core';
import { isaFiliale, IsaIcons, isaNavigationDashboard } from '@isa/icons';
@@ -11,7 +11,7 @@ type IconSwitchComponentInputs = {
};
const meta: Meta<IconSwitchComponentInputs> = {
component: IconSwitchComponent,
component: SwitchComponent,
title: 'ui/switch/IconSwitch',
decorators: [
(story) => ({
@@ -49,12 +49,12 @@ const meta: Meta<IconSwitchComponentInputs> = {
},
render: (args) => ({
props: args,
template: `<ui-icon-switch ${argsToTemplate(args)}></ui-icon-switch>`,
template: `<ui-switch ${argsToTemplate(args)}></ui-switch>`,
}),
};
export default meta;
type Story = StoryObj<IconSwitchComponent>;
type Story = StoryObj<SwitchComponent>;
export const Default: Story = {
args: {},