- Created vite.standalone.config.js for optimized bundle building - Added build scripts for standalone bundle generation - Generated three formats: IIFE (28KB), UMD (28KB), ES Module (61KB) - Includes all dependencies: qrcode and imagetracer - Added comprehensive documentation and examples - Created build script with detailed bundle information - Added terser for minification optimization - Fixed package.json dependencies and scripts
270 lines
7.7 KiB
JavaScript
270 lines
7.7 KiB
JavaScript
import React, { useState, useEffect, useRef } from 'react'
|
|
import {
|
|
generateQRCode,
|
|
generateSVGQRCode,
|
|
addImageToQRCode,
|
|
addImageToSVG,
|
|
generateCompleteSVGQRCode,
|
|
downloadSVG,
|
|
fileToDataURL,
|
|
validateImageFile
|
|
} from '../utils/qrCodeUtils'
|
|
|
|
const TextAreaComponent = () => {
|
|
const [text, setText] = useState('')
|
|
const [qrCodeUrl, setQrCodeUrl] = useState('')
|
|
const [customImage, setCustomImage] = useState(null)
|
|
const [customImageUrl, setCustomImageUrl] = useState('')
|
|
const [imageSize, setImageSize] = useState(25) // Size as percentage of QR code
|
|
const [foregroundColor, setForegroundColor] = useState('#0000ff') // QR code foreground color
|
|
const [backgroundColor, setBackgroundColor] = useState('#FFFFFF') // QR code background color
|
|
const canvasRef = useRef(null)
|
|
|
|
// Generate QR code when text, custom image, image size, or colors change
|
|
useEffect(() => {
|
|
if (text.trim()) {
|
|
generateQRCodeLocal(text)
|
|
} else {
|
|
setQrCodeUrl('')
|
|
}
|
|
}, [text, customImage, imageSize, foregroundColor, backgroundColor])
|
|
|
|
const generateQRCodeLocal = async (inputText) => {
|
|
try {
|
|
// Generate QR code as data URL using utility function
|
|
const url = await generateQRCode(inputText, foregroundColor, backgroundColor)
|
|
|
|
if (customImage) {
|
|
// Create QR code with custom image overlay
|
|
const qrWithImage = await addImageToQRCode(url, customImageUrl, imageSize)
|
|
setQrCodeUrl(qrWithImage)
|
|
} else {
|
|
setQrCodeUrl(url)
|
|
}
|
|
} catch (error) {
|
|
console.error('Error generating QR code:', error)
|
|
setQrCodeUrl('')
|
|
}
|
|
}
|
|
|
|
const generateSVGQRCodeLocal = async (inputText) => {
|
|
try {
|
|
console.log('Base SVG generated, length:', inputText.length)
|
|
|
|
if (customImage) {
|
|
console.log('Custom image detected, adding to SVG...')
|
|
// Add custom image to SVG using utility function
|
|
const svgWithImage = await addImageToSVG(inputText, customImageUrl, imageSize)
|
|
console.log('SVG with image generated, length:', svgWithImage.length)
|
|
return svgWithImage
|
|
} else {
|
|
console.log('No custom image, returning base SVG')
|
|
return inputText
|
|
}
|
|
} catch (error) {
|
|
console.error('Error generating SVG QR code:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const exportAsSVG = async () => {
|
|
if (!text.trim()) {
|
|
alert('Please enter some text to generate a QR code')
|
|
return
|
|
}
|
|
|
|
// Generate base SVG QR code using utility function
|
|
const baseSvg = await generateSVGQRCode(text, foregroundColor, backgroundColor)
|
|
if (!baseSvg) {
|
|
alert('Error generating QR code')
|
|
return
|
|
}
|
|
|
|
// Add custom image if present
|
|
const svgContent = await generateSVGQRCodeLocal(baseSvg)
|
|
if (svgContent) {
|
|
// Debug: Log the SVG content to see what's being generated
|
|
console.log('Generated SVG content:', svgContent)
|
|
|
|
// Use utility function to download SVG
|
|
downloadSVG(svgContent, 'qrcode.svg')
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleImageUpload = async (event) => {
|
|
const file = event.target.files[0]
|
|
if (file) {
|
|
// Validate file using utility function
|
|
const validation = validateImageFile(file)
|
|
if (!validation.success) {
|
|
alert(validation.error)
|
|
return
|
|
}
|
|
|
|
try {
|
|
// Convert file to data URL using utility function
|
|
const dataUrl = await fileToDataURL(file)
|
|
setCustomImage(file)
|
|
setCustomImageUrl(dataUrl)
|
|
} catch (error) {
|
|
console.error('Error processing image file:', error)
|
|
alert('Error processing image file')
|
|
}
|
|
}
|
|
}
|
|
|
|
const removeCustomImage = () => {
|
|
setCustomImage(null)
|
|
setCustomImageUrl('')
|
|
}
|
|
|
|
const handleTextChange = (e) => {
|
|
setText(e.target.value)
|
|
}
|
|
|
|
const handleClear = () => {
|
|
setText('')
|
|
}
|
|
|
|
return (
|
|
<div className="textarea-container">
|
|
<label htmlFor="text-input" className="textarea-label">
|
|
Enter text to generate QR code:
|
|
</label>
|
|
<textarea
|
|
id="text-input"
|
|
value={text}
|
|
onChange={handleTextChange}
|
|
placeholder="Enter your text here..."
|
|
className="textarea-input"
|
|
rows={6}
|
|
cols={50}
|
|
/>
|
|
|
|
<div className="color-controls">
|
|
<div className="color-input-group">
|
|
<label htmlFor="foreground-color" className="color-label">
|
|
QR Code Color:
|
|
</label>
|
|
<input
|
|
id="foreground-color"
|
|
type="color"
|
|
value={foregroundColor}
|
|
onChange={(e) => setForegroundColor(e.target.value)}
|
|
className="color-input"
|
|
/>
|
|
<span className="color-value">{foregroundColor}</span>
|
|
</div>
|
|
|
|
<div className="color-input-group">
|
|
<label htmlFor="background-color" className="color-label">
|
|
Background Color:
|
|
</label>
|
|
<input
|
|
id="background-color"
|
|
type="color"
|
|
value={backgroundColor}
|
|
onChange={(e) => setBackgroundColor(e.target.value)}
|
|
className="color-input"
|
|
/>
|
|
<span className="color-value">{backgroundColor}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="image-upload-section">
|
|
<label htmlFor="image-upload" className="image-upload-label">
|
|
Add custom image or SVG to QR code center:
|
|
</label>
|
|
<div className="image-upload-controls">
|
|
<input
|
|
id="image-upload"
|
|
type="file"
|
|
accept="image/*,.svg"
|
|
onChange={handleImageUpload}
|
|
className="image-upload-input"
|
|
/>
|
|
{customImage && (
|
|
<button
|
|
onClick={removeCustomImage}
|
|
className="remove-image-button"
|
|
>
|
|
Remove Image
|
|
</button>
|
|
)}
|
|
</div>
|
|
{customImage && (
|
|
<div className="image-preview">
|
|
<img
|
|
src={customImageUrl}
|
|
alt="Custom image preview"
|
|
className="preview-image"
|
|
/>
|
|
<span className="image-name">{customImage.name}</span>
|
|
</div>
|
|
)}
|
|
|
|
{customImage && (
|
|
<div className="image-size-control">
|
|
<label htmlFor="image-size" className="size-label">
|
|
Image Size: {imageSize}%
|
|
</label>
|
|
<input
|
|
id="image-size"
|
|
type="range"
|
|
min="10"
|
|
max="40"
|
|
value={imageSize}
|
|
onChange={(e) => setImageSize(parseInt(e.target.value))}
|
|
className="size-slider"
|
|
/>
|
|
<div className="size-labels">
|
|
<span>Small</span>
|
|
<span>Large</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="textarea-actions">
|
|
<button
|
|
onClick={handleClear}
|
|
className="clear-button"
|
|
disabled={!text}
|
|
>
|
|
Clear
|
|
</button>
|
|
<span className="character-count">
|
|
Characters: {text.length}
|
|
</span>
|
|
</div>
|
|
|
|
{qrCodeUrl && (
|
|
<div className="qr-code-container">
|
|
<h3>QR Code {customImage && '(with custom image)'}</h3>
|
|
<img
|
|
src={qrCodeUrl}
|
|
alt="QR Code"
|
|
className="qr-code-image"
|
|
/>
|
|
<div className="qr-code-actions">
|
|
<button
|
|
onClick={exportAsSVG}
|
|
className="export-svg-button"
|
|
>
|
|
Export as SVG
|
|
</button>
|
|
</div>
|
|
<p className="qr-code-info">
|
|
Scan this QR code with any QR code reader app
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default TextAreaComponent
|