code-craft-studio
Version:
A comprehensive QR code and barcode scanning/generation library for React. Works with or without Capacitor. Supports 22+ QR data types and 14+ barcode formats (EAN, UPC, Code 128, etc.), with customizable designs, analytics, and React components. Provider
305 lines • 12.1 kB
JavaScript
import { QRType } from '../../definitions';
import { WebStorageAdapter } from '../storage/web-storage';
import QrScanner from 'qr-scanner';
import QRCode from 'qrcode';
import JsBarcode from 'jsbarcode';
import { BrowserMultiFormatReader } from '@zxing/browser';
import { validateQRCodeData, validateBarcodeData } from '../../utils/validation';
import { formatQRData } from '../../utils/qr-formatter';
import { logger } from '../../utils/logger';
export class WebPlatformAdapter {
constructor(storage) {
this.name = 'web';
this.capabilities = {
hasNativeScanning: false,
hasNativeGeneration: false,
hasOfflineStorage: true,
hasFileAccess: true,
hasCameraAccess: true,
supportedBarcodeFormats: [
'EAN_13', 'EAN_8', 'UPC_A', 'UPC_E', 'CODE_128',
'CODE_39', 'CODE_93', 'ITF', 'CODABAR', 'QR_CODE',
'DATA_MATRIX', 'PDF_417', 'AZTEC'
],
supportedExportFormats: ['PNG', 'JPG', 'SVG', 'PDF', 'JSON', 'WEBP']
};
this.qrScanner = null;
this.barcodeReader = null;
this.currentStream = null;
this.storage = storage || new WebStorageAdapter();
}
async scanQRCode(_options) {
try {
const video = document.createElement('video');
video.style.display = 'none';
document.body.appendChild(video);
let scanResolve = null;
this.qrScanner = new QrScanner(video, result => {
if (scanResolve) {
scanResolve({
content: result.data,
format: 'QR_CODE',
type: QRType.TEXT,
timestamp: Date.now()
});
this.stopScanning();
document.body.removeChild(video);
}
}, {
highlightScanRegion: true,
highlightCodeOutline: true,
});
const result = await new Promise((resolve, reject) => {
if (!this.qrScanner) {
reject(new Error('Scanner not initialized'));
return;
}
scanResolve = resolve;
this.qrScanner.start().catch(reject);
});
return result;
}
catch (error) {
throw new Error(`Failed to scan QR code: ${error}`);
}
}
async generateQRCode(data, options) {
try {
const formattedData = formatQRData(data);
const qrOptions = {
errorCorrectionLevel: (options === null || options === void 0 ? void 0 : options.errorCorrectionLevel) || 'M',
type: 'image/png',
quality: (options === null || options === void 0 ? void 0 : options.quality) || 0.92,
margin: (options === null || options === void 0 ? void 0 : options.margin) || 1,
color: {
dark: (options === null || options === void 0 ? void 0 : options.color) || '#000000',
light: (options === null || options === void 0 ? void 0 : options.backgroundColor) || '#FFFFFF'
},
width: (options === null || options === void 0 ? void 0 : options.width) || 256,
};
const dataUrl = await QRCode.toDataURL(formattedData, qrOptions);
if (options === null || options === void 0 ? void 0 : options.logo) {
// Add logo overlay logic here if needed
}
return { dataUrl };
}
catch (error) {
throw new Error(`Failed to generate QR code: ${error}`);
}
}
async validateQRData(data) {
return validateQRCodeData(data);
}
async scanBarcode(_options) {
var _a;
try {
if (!this.barcodeReader) {
this.barcodeReader = new BrowserMultiFormatReader();
}
const videoInputDevices = await BrowserMultiFormatReader.listVideoInputDevices();
const selectedDeviceId = (_a = videoInputDevices[0]) === null || _a === void 0 ? void 0 : _a.deviceId;
const video = document.createElement('video');
video.style.position = 'fixed';
video.style.top = '0';
video.style.left = '0';
video.style.width = '100%';
video.style.height = '100%';
video.style.objectFit = 'cover';
video.style.zIndex = '9999';
document.body.appendChild(video);
const result = await new Promise((resolve, reject) => {
this.barcodeReader.decodeFromVideoDevice(selectedDeviceId, video, (result, _error) => {
if (result) {
this.stopScanning();
document.body.removeChild(video);
resolve(result);
}
}).catch(reject);
});
return {
content: result.getText(),
format: result.getBarcodeFormat().toString(),
type: QRType.TEXT,
timestamp: Date.now()
};
}
catch (error) {
throw new Error(`Failed to scan barcode: ${error}`);
}
}
async generateBarcode(data, format, options) {
try {
const canvas = document.createElement('canvas');
const barcodeOptions = {
format: this.mapBarcodeFormat(format),
width: (options === null || options === void 0 ? void 0 : options.width) || 2,
height: (options === null || options === void 0 ? void 0 : options.height) || 100,
displayValue: (options === null || options === void 0 ? void 0 : options.showText) !== false,
text: data,
fontSize: (options === null || options === void 0 ? void 0 : options.fontSize) || 20,
textMargin: (options === null || options === void 0 ? void 0 : options.textMargin) || 2,
margin: (options === null || options === void 0 ? void 0 : options.margin) || 10,
background: (options === null || options === void 0 ? void 0 : options.backgroundColor) || '#FFFFFF',
lineColor: (options === null || options === void 0 ? void 0 : options.color) || '#000000'
};
JsBarcode(canvas, data, barcodeOptions);
const dataUrl = canvas.toDataURL('image/png');
return { dataUrl };
}
catch (error) {
throw new Error(`Failed to generate barcode: ${error}`);
}
}
async validateBarcode(data, format) {
return validateBarcodeData(data, format);
}
async saveToHistory(item) {
try {
const historyJson = await this.storage.get('history');
const history = historyJson ? JSON.parse(historyJson) : [];
history.unshift(item);
// Keep only last 100 items
if (history.length > 100) {
history.length = 100;
}
await this.storage.set('history', JSON.stringify(history));
}
catch (error) {
logger.error('Failed to save to history:', error);
throw error;
}
}
async getHistory(options) {
try {
const historyJson = await this.storage.get('history');
let history = historyJson ? JSON.parse(historyJson) : [];
if (options === null || options === void 0 ? void 0 : options.type) {
history = history.filter((item) => item.type === options.type);
}
if (options === null || options === void 0 ? void 0 : options.limit) {
history = history.slice(0, options.limit);
}
return history;
}
catch (error) {
logger.error('Failed to get history:', error);
return [];
}
}
async clearHistory() {
await this.storage.remove('history');
}
async getAnalytics() {
try {
const analyticsJson = await this.storage.get('analytics');
return analyticsJson ? JSON.parse(analyticsJson) : {
totalScans: 0,
totalGenerations: 0,
scansByType: {},
generationsByType: {},
exportsByFormat: {},
lastUpdated: new Date().toISOString()
};
}
catch (error) {
logger.error('Failed to get analytics:', error);
return {
totalScans: 0,
totalGenerations: 0,
scansByType: {},
generationsByType: {},
exportsByFormat: {},
lastUpdated: new Date().toISOString()
};
}
}
async exportCode(dataUrl, options) {
try {
const response = await fetch(dataUrl);
const blob = await response.blob();
if (options.format === 'png' || options.format === 'jpg' || options.format === 'webp') {
// Convert if needed
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = dataUrl;
});
canvas.width = img.width;
canvas.height = img.height;
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(img, 0, 0);
const convertedBlob = await new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob);
}, `image/${options.format.toLowerCase()}`);
});
return { blob: convertedBlob };
}
return { blob };
}
catch (error) {
throw new Error(`Failed to export code: ${error}`);
}
}
async checkPermissions() {
const permissions = {};
try {
const cameraResult = await navigator.permissions.query({ name: 'camera' });
permissions.camera = cameraResult.state;
}
catch (_error) {
permissions.camera = 'prompt';
}
permissions.storage = 'granted'; // Always granted for localStorage
return permissions;
}
async requestPermissions() {
const permissions = {};
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
stream.getTracks().forEach(track => track.stop());
permissions.camera = 'granted';
}
catch (_error) {
permissions.camera = 'denied';
}
permissions.storage = 'granted';
return permissions;
}
stopScanning() {
if (this.qrScanner) {
this.qrScanner.stop();
this.qrScanner.destroy();
this.qrScanner = null;
}
if (this.barcodeReader) {
// Reset barcode reader if needed
this.barcodeReader = null;
}
if (this.currentStream) {
this.currentStream.getTracks().forEach(track => track.stop());
this.currentStream = null;
}
}
mapBarcodeFormat(format) {
const formatMap = {
'EAN_13': 'EAN13',
'EAN_8': 'EAN8',
'UPC_A': 'UPC',
'UPC_E': 'UPC_E',
'CODE_128': 'CODE128',
'CODE_39': 'CODE39',
'CODE_93': 'CODE93',
'ITF': 'ITF',
'CODABAR': 'codabar',
'QR_CODE': 'qrcode',
'DATA_MATRIX': 'datamatrix',
'PDF_417': 'pdf417',
'AZTEC': 'aztec'
};
return formatMap[format] || format.toLowerCase();
}
}
//# sourceMappingURL=web-adapter.js.map