@qr-styled/browser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * QR code data types for specialized content
3
+ */
4
+ export type QRDataType = 'url' | 'text' | 'vcard' | 'wifi' | 'email' | 'sms' | 'geo';
5
+ /**
6
+ * vCard contact data
7
+ */
8
+ export interface VCardData {
9
+ firstName?: string;
10
+ lastName?: string;
11
+ organization?: string;
12
+ title?: string;
13
+ phone?: string;
14
+ email?: string;
15
+ url?: string;
16
+ address?: string;
17
+ city?: string;
18
+ state?: string;
19
+ zip?: string;
20
+ country?: string;
21
+ }
22
+ /**
23
+ * WiFi configuration data
24
+ */
25
+ export interface WiFiData {
26
+ ssid: string;
27
+ password?: string;
28
+ encryption?: 'WPA' | 'WEP' | 'nopass';
29
+ hidden?: boolean;
30
+ }
31
+ /**
32
+ * Email data
33
+ */
34
+ export interface EmailData {
35
+ email: string;
36
+ subject?: string;
37
+ body?: string;
38
+ }
39
+ /**
40
+ * SMS data
41
+ */
42
+ export interface SMSData {
43
+ phone: string;
44
+ message?: string;
45
+ }
46
+ /**
47
+ * Geolocation data
48
+ */
49
+ export interface GeoData {
50
+ latitude: number;
51
+ longitude: number;
52
+ }
53
+ /**
54
+ * QR generation options interface
55
+ */
56
+ export interface QROptions {
57
+ /** URL or text content for the QR code */
58
+ url?: string;
59
+ /** Type of QR code data */
60
+ type?: QRDataType;
61
+ /** Structured data for specialized QR types */
62
+ data?: VCardData | WiFiData | EmailData | SMSData | GeoData;
63
+ /** Canvas size in pixels */
64
+ size?: number;
65
+ /** QR code quiet zone margin (in modules, typically 4) */
66
+ margin?: number;
67
+ /** Canvas padding in pixels */
68
+ padding?: number;
69
+ /** QR code foreground color (hex format) */
70
+ foregroundColor?: string;
71
+ /** @deprecated Use foregroundColor instead */
72
+ color?: string;
73
+ /** Path, URL, or HTMLImageElement for logo */
74
+ logo?: string | HTMLImageElement;
75
+ /** Logo size in pixels */
76
+ logoSize?: number;
77
+ /** Use rounded module corners */
78
+ rounded?: boolean;
79
+ /** Module corner radius (0.0-0.5) */
80
+ moduleRadius?: number;
81
+ /** Padding around logo */
82
+ logoPadding?: number;
83
+ /** Logo background shape */
84
+ logoShape?: 'circle' | 'square';
85
+ /** Logo background color */
86
+ logoBackgroundColor?: string;
87
+ /** Corner radius for square logo background */
88
+ logoRadius?: number;
89
+ /** Use gradient colors */
90
+ gradient?: boolean;
91
+ /** Comma-separated gradient colors */
92
+ gradientColors?: string;
93
+ /** Gradient angle in degrees (0-360) */
94
+ gradientAngle?: number;
95
+ /** Background corner radius */
96
+ cornerRadius?: number;
97
+ /** Background color */
98
+ backgroundColor?: string;
99
+ /** QR error correction level */
100
+ errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
101
+ /** Custom eye (finder pattern) color */
102
+ eyeColor?: string;
103
+ /** Custom eye radius (0.0-0.5) */
104
+ eyeRadius?: number;
105
+ }
106
+ /**
107
+ * Default configuration options for QR code generation
108
+ */
109
+ export declare const DEFAULT_OPTIONS: Required<Omit<QROptions, 'url' | 'data' | 'color'>>;
110
+ /**
111
+ * Valid logo shapes
112
+ */
113
+ export declare const VALID_LOGO_SHAPES: readonly ["circle", "square"];
114
+ /**
115
+ * Valid error correction levels
116
+ */
117
+ export declare const VALID_ERROR_CORRECTION_LEVELS: readonly ["L", "M", "Q", "H"];
118
+ export type LogoShape = (typeof VALID_LOGO_SHAPES)[number];
119
+ export type ErrorCorrectionLevel = (typeof VALID_ERROR_CORRECTION_LEVELS)[number];
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Default configuration options for QR code generation
3
+ */
4
+ export const DEFAULT_OPTIONS = {
5
+ type: 'text',
6
+ size: 600,
7
+ margin: 4,
8
+ padding: 40,
9
+ foregroundColor: '#000000',
10
+ logo: '',
11
+ logoSize: 120,
12
+ rounded: true,
13
+ moduleRadius: 0.35,
14
+ logoPadding: 10,
15
+ logoShape: 'circle',
16
+ logoBackgroundColor: '#ffffff',
17
+ logoRadius: 20,
18
+ gradient: false,
19
+ gradientColors: '#FEDA75,#FA7E1E,#D62976,#962FBF,#4F5BD5',
20
+ gradientAngle: 45,
21
+ cornerRadius: 60,
22
+ backgroundColor: '#ffffff',
23
+ errorCorrectionLevel: 'H',
24
+ eyeColor: '',
25
+ eyeRadius: 0
26
+ };
27
+ /**
28
+ * Valid logo shapes
29
+ */
30
+ export const VALID_LOGO_SHAPES = ['circle', 'square'];
31
+ /**
32
+ * Valid error correction levels
33
+ */
34
+ export const VALID_ERROR_CORRECTION_LEVELS = ['L', 'M', 'Q', 'H'];
@@ -0,0 +1,10 @@
1
+ import { QROptions } from './types.js';
2
+ /**
3
+ * Validates QR generation options
4
+ * @throws {Error} If options are invalid
5
+ */
6
+ export declare function validateOptions(options: QROptions): void;
7
+ /**
8
+ * Normalizes and merges options with defaults
9
+ */
10
+ export declare function normalizeOptions<T extends Partial<QROptions>>(options: T, defaults: Partial<QROptions>): T & Partial<QROptions>;
@@ -0,0 +1,126 @@
1
+ import { VALID_LOGO_SHAPES, VALID_ERROR_CORRECTION_LEVELS } from './types.js';
2
+ /**
3
+ * Validates QR generation options
4
+ * @throws {Error} If options are invalid
5
+ */
6
+ export function validateOptions(options) {
7
+ // Validate that either url or data is provided
8
+ if (!options.url && !options.data) {
9
+ throw new Error('URL, text content, or structured data is required');
10
+ }
11
+ // Validate specialized data types
12
+ if (options.type &&
13
+ options.type !== 'url' &&
14
+ options.type !== 'text' &&
15
+ !options.data) {
16
+ throw new Error(`Structured data is required for type '${options.type}'`);
17
+ }
18
+ if (options.size && (options.size < 100 || options.size > 5000)) {
19
+ throw new Error('Size must be between 100 and 5000 pixels');
20
+ }
21
+ if (options.margin !== undefined &&
22
+ (options.margin < 0 || options.margin > 10)) {
23
+ throw new Error('Margin must be between 0 and 10 modules');
24
+ }
25
+ if (options.moduleRadius !== undefined &&
26
+ (options.moduleRadius < 0 || options.moduleRadius > 0.5)) {
27
+ throw new Error('Module radius must be between 0.0 and 0.5');
28
+ }
29
+ if (options.eyeRadius !== undefined &&
30
+ (options.eyeRadius < 0 || options.eyeRadius > 0.5)) {
31
+ throw new Error('Eye radius must be between 0.0 and 0.5');
32
+ }
33
+ if (options.gradientAngle &&
34
+ (options.gradientAngle < 0 || options.gradientAngle > 360)) {
35
+ throw new Error('Gradient angle must be between 0 and 360 degrees');
36
+ }
37
+ if (options.logoShape &&
38
+ !VALID_LOGO_SHAPES.includes(options.logoShape)) {
39
+ throw new Error(`Logo shape must be one of: ${VALID_LOGO_SHAPES.join(', ')}`);
40
+ }
41
+ if (options.logoSize && (options.logoSize < 50 || options.logoSize > 500)) {
42
+ throw new Error('Logo size must be between 50 and 500 pixels');
43
+ }
44
+ if (options.errorCorrectionLevel &&
45
+ !VALID_ERROR_CORRECTION_LEVELS.includes(options.errorCorrectionLevel)) {
46
+ throw new Error(`Error correction level must be one of: ${VALID_ERROR_CORRECTION_LEVELS.join(', ')}`);
47
+ }
48
+ // Support both deprecated 'color' and new 'foregroundColor'
49
+ const colorToValidate = options.foregroundColor || options.color;
50
+ if (colorToValidate && !/^#([0-9A-F]{3}){1,2}$/i.test(colorToValidate)) {
51
+ throw new Error('Color must be in hex format (e.g., #000000)');
52
+ }
53
+ if (options.eyeColor && !/^#([0-9A-F]{3}){1,2}$/i.test(options.eyeColor)) {
54
+ throw new Error('Eye color must be in hex format (e.g., #000000)');
55
+ }
56
+ if (options.backgroundColor &&
57
+ !/^#([0-9A-F]{3}){1,2}$/i.test(options.backgroundColor)) {
58
+ throw new Error('Background color must be in hex format (e.g., #ffffff)');
59
+ }
60
+ if (options.logoBackgroundColor &&
61
+ !/^#([0-9A-F]{3}){1,2}$/i.test(options.logoBackgroundColor)) {
62
+ throw new Error('Logo background color must be in hex format (e.g., #ffffff)');
63
+ }
64
+ if (options.gradientColors) {
65
+ const colors = options.gradientColors.split(',').map(c => c.trim());
66
+ colors.forEach(color => {
67
+ if (!/^#([0-9A-F]{3}){1,2}$/i.test(color)) {
68
+ throw new Error(`Invalid gradient color: ${color}. Must be in hex format`);
69
+ }
70
+ });
71
+ }
72
+ // Validate specialized data structures
73
+ if (options.data && options.type) {
74
+ switch (options.type) {
75
+ case 'vcard':
76
+ {
77
+ const vcard = options.data;
78
+ if (!vcard.firstName && !vcard.lastName) {
79
+ throw new Error('vCard requires at least a first name or last name');
80
+ }
81
+ }
82
+ break;
83
+ case 'wifi':
84
+ {
85
+ const wifi = options.data;
86
+ if (!wifi.ssid) {
87
+ throw new Error('WiFi QR code requires SSID');
88
+ }
89
+ }
90
+ break;
91
+ case 'email':
92
+ {
93
+ const email = options.data;
94
+ if (!email.email) {
95
+ throw new Error('Email QR code requires email address');
96
+ }
97
+ }
98
+ break;
99
+ case 'sms':
100
+ {
101
+ const sms = options.data;
102
+ if (!sms.phone) {
103
+ throw new Error('SMS QR code requires phone number');
104
+ }
105
+ }
106
+ break;
107
+ case 'geo':
108
+ {
109
+ const geo = options.data;
110
+ if (geo.latitude === undefined || geo.longitude === undefined) {
111
+ throw new Error('Geo QR code requires latitude and longitude');
112
+ }
113
+ }
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Normalizes and merges options with defaults
120
+ */
121
+ export function normalizeOptions(options, defaults) {
122
+ return {
123
+ ...defaults,
124
+ ...options
125
+ };
126
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@qr-styled/browser",
3
+ "version": "1.0.0",
4
+ "description": "A professional browser QR code generator library with advanced styling options including gradients, rounded modules, logo support, specialized QR types (vCard, WiFi, Email, SMS, Geo), and eye customization",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "prepublishOnly": "npm run build",
19
+ "demo": "python3 -m http.server 3000",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "test:ui": "vitest --ui",
23
+ "test:coverage": "vitest run --coverage"
24
+ },
25
+ "keywords": [
26
+ "qr",
27
+ "qrcode",
28
+ "qr-code",
29
+ "generator",
30
+ "styled",
31
+ "gradient",
32
+ "logo",
33
+ "custom",
34
+ "canvas",
35
+ "browser",
36
+ "html5",
37
+ "vcard",
38
+ "wifi",
39
+ "email",
40
+ "sms",
41
+ "geo",
42
+ "contact-card",
43
+ "typescript"
44
+ ],
45
+ "author": "Luis Manuel Yerena Sosa",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/Luisma92/qr-styled.git",
50
+ "directory": "packages/browser"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/Luisma92/qr-styled/issues"
54
+ },
55
+ "homepage": "https://github.com/Luisma92/qr-styled/tree/main/packages/browser#readme",
56
+ "dependencies": {
57
+ "qrcode": "^1.5.4"
58
+ },
59
+ "files": [
60
+ "dist/",
61
+ "README.md",
62
+ "LICENSE"
63
+ ],
64
+ "devDependencies": {
65
+ "@types/qrcode": "^1.5.5",
66
+ "@vitest/ui": "^4.0.18",
67
+ "happy-dom": "^15.0.0",
68
+ "typescript": "^5.9.3",
69
+ "vitest": "^4.0.18"
70
+ }
71
+ }