UNPKG

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
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