assets_js_controllers_qrcode-controller.js
import { Controller } from "@hotwired/stimulus";
import QRCode from 'qrcode';
/**
* QR Code Generator Controller
*
* Generates QR codes dynamically using the qrcode library.
* Generates the QR code when the modal/container is shown to avoid unnecessary generation.
*
* Usage:
* <div data-controller="qrcode"
* data-qrcode-url-value="https://example.com"
* data-qrcode-size-value="256"
* data-qrcode-modal-id-value="qrCodeModal">
* <div data-qrcode-target="canvas"></div>
* </div>
*
* Or with a modal:
* <div class="modal" id="qrCodeModal" data-controller="qrcode"
* data-qrcode-url-value="https://example.com">
* <div data-qrcode-target="canvas"></div>
* </div>
*/
class QrcodeController extends Controller {
static targets = ["canvas"]
static values = {
url: String, // The URL to encode in QR code
size: { type: Number, default: 256 }, // QR code size in pixels
modalId: String, // Optional modal ID to detect when to generate
colorDark: { type: String, default: '#000000' },
colorLight: { type: String, default: '#ffffff' },
errorCorrectionLevel: { type: String, default: 'H' } // L, M, Q, H
}
// Initialize the generated flag
generated = false;
connect() {
this.generated = false;
// If modal ID is provided, wait for modal to show
if (this.hasModalIdValue) {
const modal = document.getElementById(this.modalIdValue);
if (modal) {
// Store the listener as an instance property
this._onModalShown = () => this.generate();
modal.addEventListener('shown.bs.modal', this._onModalShown);
}
} else {
// Generate immediately if no modal
this.generate();
}
}
disconnect() {
// Remove event listener to prevent memory leak
if (this.hasModalIdValue && this._onModalShown) {
const modal = document.getElementById(this.modalIdValue);
if (modal) {
modal.removeEventListener('shown.bs.modal', this._onModalShown);
}
}
this.generated = false;
}
/**
* Generate the QR code
* Returns a Promise that resolves when generation is complete
*/
generate() {
// Only generate once
if (this.generated) {
return Promise.resolve();
}
if (!this.hasUrlValue) {
throw new Error('QR Code: URL value is required');
}
if (!this.hasCanvasTarget) {
throw new Error('QR Code: Canvas target is required');
}
// Clear any existing content
this.canvasTarget.innerHTML = '';
// Create canvas element
const canvas = document.createElement('canvas');
this.canvasTarget.appendChild(canvas);
// Return a Promise that resolves when QR code generation is complete
return new Promise((resolve, reject) => {
QRCode.toCanvas(canvas, this.urlValue, {
width: this.sizeValue,
margin: 2,
color: {
dark: this.colorDarkValue,
light: this.colorLightValue
},
errorCorrectionLevel: this.errorCorrectionLevelValue
}, (error) => {
if (error) {
this.canvasTarget.innerHTML = '<p class="text-danger">Error generating QR code</p>';
reject(error);
} else {
this.generated = true;
resolve();
}
});
});
}
/**
* Regenerate the QR code (useful if URL changes)
*/
regenerate() {
this.generated = false;
this.generate();
}
/**
* Download the QR code as PNG
*/
async download() {
try {
// Wait for generation to complete
await this.generate();
const canvas = this.canvasTarget.querySelector('canvas');
if (!canvas) {
console.error('QR Code Controller: Canvas not found');
return;
}
// Create download link
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = canvas.toDataURL('image/png');
link.click();
} catch (error) {
console.error('QR Code generation failed:', error);
}
}
/**
* Copy QR code to clipboard as image
*/
async copyToClipboard() {
try {
// Wait for generation to complete
await this.generate();
const canvas = this.canvasTarget.querySelector('canvas');
if (!canvas) {
console.error('QR Code Controller: Canvas not found');
return;
}
// Convert canvas to blob using Promise
const blob = await new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Failed to create blob from canvas'));
}
});
});
// Copy to clipboard
const item = new ClipboardItem({ 'image/png': blob });
await navigator.clipboard.write([item]);
} catch (error) {
console.error('Failed to copy QR code:', error);
}
}
}
// Register controller globally
if (!window.Controllers) {
window.Controllers = {};
}
window.Controllers["qrcode"] = QrcodeController;
export default QrcodeController;