UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

690 lines (687 loc) 22.8 kB
'use client'; import { jsx } from 'react/jsx-runtime'; import { useState, useRef, useCallback, createContext, useContext } from 'react'; const ImageProcessingContext = /*#__PURE__*/createContext(null); // Default templates for common use cases const defaultTemplates = [ // Social Media { id: 'instagram_post', name: 'Instagram Post', category: 'social', width: 1080, height: 1080, description: 'Square format perfect for Instagram posts', thumbnail: 'https://via.placeholder.com/100x100/e91e63/ffffff?text=IG', preset: { saturation: 10, contrast: 5 } }, { id: 'instagram_story', name: 'Instagram Story', category: 'social', width: 1080, height: 1920, description: 'Vertical format for Instagram and Facebook stories', thumbnail: 'https://via.placeholder.com/56x100/e91e63/ffffff?text=Story' }, { id: 'facebook_cover', name: 'Facebook Cover', category: 'social', width: 1200, height: 630, description: 'Facebook cover photo dimensions', thumbnail: 'https://via.placeholder.com/100x52/1877f2/ffffff?text=FB' }, { id: 'twitter_header', name: 'Twitter Header', category: 'social', width: 1500, height: 500, description: 'Twitter profile header image', thumbnail: 'https://via.placeholder.com/100x33/1da1f2/ffffff?text=TW' }, // Web { id: 'blog_banner', name: 'Blog Banner', category: 'web', width: 1200, height: 400, description: 'Wide banner for blog posts and articles', thumbnail: 'https://via.placeholder.com/100x33/6c757d/ffffff?text=Blog' }, { id: 'web_thumbnail', name: 'Web Thumbnail', category: 'web', width: 400, height: 300, description: 'Standard web thumbnail size', thumbnail: 'https://via.placeholder.com/100x75/17a2b8/ffffff?text=Thumb' }, // Print { id: 'business_card', name: 'Business Card', category: 'print', width: 1050, height: 600, description: 'Standard business card size (3.5" x 2")', thumbnail: 'https://via.placeholder.com/100x57/28a745/ffffff?text=Card' }, { id: 'poster_a4', name: 'A4 Poster', category: 'print', width: 2480, height: 3508, description: 'A4 poster size at 300 DPI', thumbnail: 'https://via.placeholder.com/71x100/ffc107/ffffff?text=A4' }]; // Mock AI image processing functions const mockImageProcessor = { async analyzeImage(imageUrl) { // Simulate AI analysis await new Promise(resolve => setTimeout(resolve, 1000)); return { dominantColors: ['var(--glass-color-primary)', '#1e40af', '#93c5fd'], brightness: 65 + Math.random() * 30, contrast: 50 + Math.random() * 30, saturation: 40 + Math.random() * 40, sharpness: 60 + Math.random() * 30 }; }, async detectFaces(imageUrl) { await new Promise(resolve => setTimeout(resolve, 1500)); // Mock face detection - return 0-3 random faces const faceCount = Math.floor(Math.random() * 4); const faces = []; for (let i = 0; i < faceCount; i++) { faces.push({ id: `face_${i}`, x: Math.random() * 0.7, y: Math.random() * 0.7, width: 0.1 + Math.random() * 0.2, height: 0.1 + Math.random() * 0.2, confidence: 0.7 + Math.random() * 0.3, landmarks: { leftEye: { x: Math.random(), y: Math.random() }, rightEye: { x: Math.random(), y: Math.random() }, nose: { x: Math.random(), y: Math.random() }, mouth: { x: Math.random(), y: Math.random() } } }); } return faces; }, async optimizeImage(imageUrl, options) { await new Promise(resolve => setTimeout(resolve, 2000)); // In a real implementation, this would call an AI service // For demo, we'll return the same URL with a query parameter return `${imageUrl}?optimized=true&q=${options.quality || 85}`; } }; const ImageProcessingProvider = ({ children }) => { const [images, setImages] = useState([]); const [uploadProgresses, setUploadProgresses] = useState([]); const [templates] = useState(defaultTemplates); const [defaultOptimizations, setDefaultOptimizations] = useState({ quality: 85, format: 'auto', removeMetadata: true, progressive: true }); const [autoOptimize, setAutoOptimize] = useState(true); useRef(); const generateId = useCallback(() => { return `img_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }, []); const createImageFromFile = useCallback(async file => { return new Promise((resolve, reject) => { const img = new Image(); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); img.onload = async () => { canvas.width = img.width; canvas.height = img.height; ctx?.drawImage(img, 0, 0); const url = URL.createObjectURL(file); const metadata = await mockImageProcessor.analyzeImage(url); const imageFile = { id: generateId(), file, name: file.name, originalName: file.name, size: file.size, type: file.type, width: img.width, height: img.height, aspectRatio: img.width / img.height, url, uploadedAt: new Date(), editHistory: [], metadata: { format: file.type.split('/')[1], quality: 100, colorSpace: 'sRGB', hasAlpha: file.type.includes('png'), fileSize: file.size, dominantColors: ['var(--glass-black)'], brightness: 50, contrast: 50, saturation: 50, sharpness: 50, ...metadata }, tags: [] }; resolve(imageFile); }; img.onerror = () => reject(new Error('Failed to load image')); img.src = URL.createObjectURL(file); }); }, [generateId]); const addImage = useCallback(async file => { const imageFile = await createImageFromFile(file); setImages(prev => [...prev, imageFile]); // Auto-optimize if enabled if (autoOptimize) { setTimeout(() => { optimizeImage(imageFile.id, defaultOptimizations); }, 100); } return imageFile; }, [createImageFromFile, autoOptimize, defaultOptimizations]); const addImages = useCallback(async files => { const imageFiles = []; for (let i = 0; i < files.length; i++) { const file = files[i]; if (file.type.startsWith('image/')) { try { const imageFile = await createImageFromFile(file); imageFiles.push(imageFile); // Update progress setUploadProgresses(prev => [...prev, { imageId: imageFile.id, progress: 100, status: 'completed' }]); } catch (error) { console.error('Failed to process image:', file.name, error); } } } setImages(prev => [...prev, ...imageFiles]); return imageFiles; }, [createImageFromFile]); const removeImage = useCallback(id => { setImages(prev => { const image = prev.find(img => img.id === id); if (image) { URL.revokeObjectURL(image.url); if (image.thumbnailUrl) { URL.revokeObjectURL(image.thumbnailUrl); } } return prev.filter(img => img.id !== id); }); setUploadProgresses(prev => prev.filter(p => p.imageId !== id)); }, []); const getImage = useCallback(id => { return images.find(img => img.id === id); }, [images]); const updateImage = useCallback((id, updates) => { setImages(prev => prev.map(img => img.id === id ? { ...img, ...updates } : img)); }, []); const optimizeImage = useCallback(async (imageId, options = {}) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); // Update progress setUploadProgresses(prev => [...prev, { imageId, progress: 0, status: 'processing', message: 'Analyzing image...' }]); const optimizationOptions = { ...defaultOptimizations, ...options }; try { // Simulate optimization progress for (let progress = 10; progress <= 90; progress += 20) { await new Promise(resolve => setTimeout(resolve, 200)); setUploadProgresses(prev => prev.map(p => p.imageId === imageId ? { ...p, progress } : p)); } const optimizedUrl = await mockImageProcessor.optimizeImage(image.url, optimizationOptions); const optimizedImage = { ...image, url: optimizedUrl, optimizedAt: new Date(), metadata: { ...image.metadata, quality: optimizationOptions.quality || 85, compressionRatio: 0.3 + Math.random() * 0.4 }, editHistory: [...image.editHistory, { id: generateId(), type: 'color', timestamp: new Date(), parameters: optimizationOptions }] }; setImages(prev => prev.map(img => img.id === imageId ? optimizedImage : img)); setUploadProgresses(prev => prev.map(p => p.imageId === imageId ? { ...p, progress: 100, status: 'completed', message: 'Optimization complete!' } : p)); return optimizedImage; } catch (error) { setUploadProgresses(prev => prev.map(p => p.imageId === imageId ? { ...p, status: 'error', message: 'Optimization failed' } : p)); throw error; } }, [getImage, defaultOptimizations, generateId]); const batchOptimize = useCallback(async (imageIds, options = {}) => { const results = []; for (const imageId of imageIds) { try { const optimized = await optimizeImage(imageId, options); results.push(optimized); } catch (error) { console.error(`Failed to optimize image ${imageId}:`, error); } } return results; }, [optimizeImage]); const detectFaces = useCallback(async imageId => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); const faces = await mockImageProcessor.detectFaces(image.url); updateImage(imageId, { faces }); return faces; }, [getImage, updateImage]); const applyFilter = useCallback(async (imageId, filters) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); // Simulate filter processing await new Promise(resolve => setTimeout(resolve, 1000)); const editOperation = { id: generateId(), type: 'filter', timestamp: new Date(), parameters: filters }; const updatedImage = { ...image, editHistory: [...image.editHistory, editOperation], metadata: { ...image.metadata, brightness: image.metadata.brightness + (filters.brightness || 0), contrast: image.metadata.contrast + (filters.contrast || 0), saturation: image.metadata.saturation + (filters.saturation || 0) } }; setImages(prev => prev.map(img => img.id === imageId ? updatedImage : img)); return updatedImage; }, [getImage, generateId]); const cropImage = useCallback(async (imageId, cropArea) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); // Simulate crop processing await new Promise(resolve => setTimeout(resolve, 800)); const editOperation = { id: generateId(), type: 'crop', timestamp: new Date(), parameters: cropArea }; const croppedImage = { ...image, width: Math.round(image.width * cropArea.width), height: Math.round(image.height * cropArea.height), aspectRatio: cropArea.width / cropArea.height, editHistory: [...image.editHistory, editOperation] }; setImages(prev => prev.map(img => img.id === imageId ? croppedImage : img)); return croppedImage; }, [getImage, generateId]); const resizeImage = useCallback(async (imageId, width, height, maintainAspect = true) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); let newWidth = width; let newHeight = height; if (maintainAspect) { const aspectRatio = image.width / image.height; if (width / height > aspectRatio) { newWidth = height * aspectRatio; } else { newHeight = width / aspectRatio; } } await new Promise(resolve => setTimeout(resolve, 600)); const editOperation = { id: generateId(), type: 'resize', timestamp: new Date(), parameters: { width: newWidth, height: newHeight, maintainAspect } }; const resizedImage = { ...image, width: Math.round(newWidth), height: Math.round(newHeight), aspectRatio: newWidth / newHeight, editHistory: [...image.editHistory, editOperation] }; setImages(prev => prev.map(img => img.id === imageId ? resizedImage : img)); return resizedImage; }, [getImage, generateId]); const addWatermark = useCallback(async (imageId, watermark) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); await new Promise(resolve => setTimeout(resolve, 1200)); const editOperation = { id: generateId(), type: 'watermark', timestamp: new Date(), parameters: watermark }; const watermarkedImage = { ...image, editHistory: [...image.editHistory, editOperation] }; setImages(prev => prev.map(img => img.id === imageId ? watermarkedImage : img)); return watermarkedImage; }, [getImage, generateId]); const removeBackground = useCallback(async imageId => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); await new Promise(resolve => setTimeout(resolve, 3000)); const editOperation = { id: generateId(), type: 'background', timestamp: new Date(), parameters: { action: 'remove' } }; const processedImage = { ...image, metadata: { ...image.metadata, hasAlpha: true }, editHistory: [...image.editHistory, editOperation] }; setImages(prev => prev.map(img => img.id === imageId ? processedImage : img)); return processedImage; }, [getImage, generateId]); const smartCrop = useCallback(async (imageId, aspectRatio) => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); // If faces are detected, use them for smart cropping let faces = image.faces; if (!faces || faces.length === 0) { faces = await detectFaces(imageId); } let cropArea; if (faces.length > 0) { // Center crop around faces const facesCenterX = faces.reduce((sum, face) => sum + face.x + face.width / 2, 0) / faces.length; const facesCenterY = faces.reduce((sum, face) => sum + face.y + face.height / 2, 0) / faces.length; const currentAspect = image.width / image.height; if (aspectRatio > currentAspect) { // Crop height const newHeight = image.width / aspectRatio; cropArea = { x: 0, y: Math.max(0, facesCenterY - newHeight / 2), width: 1, height: newHeight / image.height }; } else { // Crop width const newWidth = image.height * aspectRatio; cropArea = { x: Math.max(0, facesCenterX - newWidth / 2), y: 0, width: newWidth / image.width, height: 1 }; } } else { // Center crop const currentAspect = image.width / image.height; if (aspectRatio > currentAspect) { const newHeight = image.width / aspectRatio; cropArea = { x: 0, y: (image.height - newHeight) / 2 / image.height, width: 1, height: newHeight / image.height }; } else { const newWidth = image.height * aspectRatio; cropArea = { x: (image.width - newWidth) / 2 / image.width, y: 0, width: newWidth / image.width, height: 1 }; } } return cropImage(imageId, cropArea); }, [getImage, detectFaces, cropImage]); const enhanceImage = useCallback(async imageId => { const image = getImage(imageId); if (!image) throw new Error('Image not found'); // Apply AI enhancement (brightness, contrast, sharpness optimization) const enhancementFilters = { brightness: Math.max(-20, Math.min(20, 10 - image.metadata.brightness * 0.2)), contrast: Math.max(-20, Math.min(20, 15 - image.metadata.contrast * 0.3)), sharpen: Math.max(0, Math.min(10, 8 - image.metadata.sharpness * 0.1)), saturation: Math.max(-15, Math.min(15, 5 - image.metadata.saturation * 0.15)) }; return applyFilter(imageId, enhancementFilters); }, [getImage, applyFilter]); const getOptimizationStats = useCallback(() => { const processedImages = images.filter(img => img.metadata.compressionRatio); const totalOriginalSize = processedImages.reduce((sum, img) => sum + img.size, 0); const totalOptimizedSize = processedImages.reduce((sum, img) => sum + img.size * (1 - (img.metadata.compressionRatio || 0)), 0); return { totalSaved: Math.round((totalOriginalSize - totalOptimizedSize) / 1024), // KB averageReduction: processedImages.length > 0 ? Math.round(processedImages.reduce((sum, img) => sum + (img.metadata.compressionRatio || 0), 0) / processedImages.length * 100) : 0, imagesProcessed: processedImages.length, mostUsedFormat: 'JPEG' // Mock data }; }, [images]); const clearProgress = useCallback(imageId => { setUploadProgresses(prev => prev.filter(p => p.imageId !== imageId)); }, []); // Placeholder implementations for other methods const rotateImage = useCallback(async (imageId, degrees) => { // Implementation would go here return getImage(imageId); }, [getImage]); const replaceBackground = useCallback(async (imageId, backgroundImage) => { // Implementation would go here return getImage(imageId); }, [getImage]); const upscaleImage = useCallback(async (imageId, factor) => { // Implementation would go here return getImage(imageId); }, [getImage]); const batchResize = useCallback(async (imageIds, width, height) => { const results = []; for (const imageId of imageIds) { try { const resized = await resizeImage(imageId, width, height); results.push(resized); } catch (error) { console.error(`Failed to resize image ${imageId}:`, error); } } return results; }, [resizeImage]); const batchFilter = useCallback(async (imageIds, filters) => { const results = []; for (const imageId of imageIds) { try { const filtered = await applyFilter(imageId, filters); results.push(filtered); } catch (error) { console.error(`Failed to filter image ${imageId}:`, error); } } return results; }, [applyFilter]); const batchWatermark = useCallback(async (imageIds, watermark) => { const results = []; for (const imageId of imageIds) { try { const watermarked = await addWatermark(imageId, watermark); results.push(watermarked); } catch (error) { console.error(`Failed to watermark image ${imageId}:`, error); } } return results; }, [addWatermark]); const uploadToCloud = useCallback(async (imageId, provider = 'cloudinary') => { // Mock cloud upload await new Promise(resolve => setTimeout(resolve, 2000)); const baseUrl = `https://${provider}.example.com`; return { original: `${baseUrl}/original/${imageId}`, optimized: `${baseUrl}/optimized/${imageId}`, thumbnail: `${baseUrl}/thumb/${imageId}`, responsive: { small: `${baseUrl}/w_400/${imageId}`, medium: `${baseUrl}/w_800/${imageId}`, large: `${baseUrl}/w_1200/${imageId}`, xlarge: `${baseUrl}/w_1600/${imageId}` } }; }, []); const generateResponsiveImages = useCallback(async (imageId, sizes) => { return uploadToCloud(imageId); }, [uploadToCloud]); const applyTemplate = useCallback(async (imageId, templateId) => { const template = templates.find(t => t.id === templateId); if (!template) throw new Error('Template not found'); let result = await resizeImage(imageId, template.width, template.height, false); if (template.preset) { result = await applyFilter(imageId, template.preset); } return result; }, [templates, resizeImage, applyFilter]); const createCustomTemplate = useCallback(template => { const newTemplate = { ...template, id: generateId() }; // In a real app, you'd save this to state or backend return newTemplate; }, [generateId]); const getImageInsights = useCallback(imageId => { const image = getImage(imageId); if (!image) return { colorPalette: [], dominantColor: 'var(--glass-black)', brightness: 0, complexity: 0, recommendedFormats: [] }; return { colorPalette: image.metadata.dominantColors, dominantColor: image.metadata.dominantColors[0] || 'var(--glass-black)', brightness: image.metadata.brightness, complexity: Math.random() * 100, // Mock complexity score recommendedFormats: image.metadata.hasAlpha ? ['PNG', 'WebP'] : ['JPEG', 'WebP', 'AVIF'] }; }, [getImage]); const value = { images, addImage, addImages, removeImage, getImage, updateImage, optimizeImage, batchOptimize, cropImage, resizeImage, applyFilter, rotateImage, addWatermark, detectFaces, removeBackground, replaceBackground, smartCrop, enhanceImage, upscaleImage, batchResize, batchFilter, batchWatermark, uploadToCloud, generateResponsiveImages, templates, applyTemplate, createCustomTemplate, getOptimizationStats, getImageInsights, uploadProgresses, clearProgress, defaultOptimizations, setDefaultOptimizations, autoOptimize, setAutoOptimize }; return jsx(ImageProcessingContext.Provider, { "data-glass-component": true, value: value, children: children }); }; const useImageProcessing = () => { const context = useContext(ImageProcessingContext); if (!context) { throw new Error('useImageProcessing must be used within an ImageProcessingProvider'); } return context; }; export { ImageProcessingProvider as GlassImageProcessingProvider, ImageProcessingProvider, useImageProcessing }; //# sourceMappingURL=GlassImageProcessingProvider.js.map