UNPKG

aura-glass

Version:

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

383 lines (380 loc) 13.8 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { Palette, Shuffle, Check, Copy, RotateCcw, Upload, Download } from 'lucide-react'; import { useState, useCallback } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import { MotionFramer } from '../../primitives/motion/MotionFramer.js'; import { GlassButton } from '../button/GlassButton.js'; import '../button/GlassFab.js'; import '../button/GlassMagneticButton.js'; import { CardHeader, CardTitle, CardContent } from '../card/index.js'; import { GlassCard } from '../card/GlassCard.js'; /** * GlassGradientPicker component * Advanced gradient picker with presets and custom creation */ const GlassGradientPicker = ({ value, presets = [], enableCustom = true, showTypeSelector = true, showAngleControl = true, showStopsEditor = true, maxStops = 5, onChange, onPresetSelect, className, ...props }) => { const [selectedType, setSelectedType] = useState("linear"); const [angle, setAngle] = useState(45); const [stops, setStops] = useState([{ color: "var(--glass-color-primary)", position: 0 }, { color: "#8b5cf6", position: 100 }]); const [selectedStopIndex, setSelectedStopIndex] = useState(null); const [copied, setCopied] = useState(false); // Default presets const defaultPresets = [{ id: "sunset", name: "Sunset", type: "linear", angle: 45, stops: [{ color: "#ff6b6b", position: 0 }, { color: "#ffa500", position: 50 }, { color: "#ff1493", position: 100 }] }, { id: "ocean", name: "Ocean", type: "linear", angle: 90, stops: [{ color: "#0077be", position: 0 }, { color: "#00a8cc", position: 50 }, { color: "#90e0ef", position: 100 }] }, { id: "forest", name: "Forest", type: "radial", stops: [{ color: "#2d5016", position: 0 }, { color: "#4a7c59", position: 70 }, { color: "#90a955", position: 100 }] }, { id: "fire", name: "Fire", type: "conic", stops: [{ color: "#ff0000", position: 0 }, { color: "#ff8000", position: 25 }, { color: "#ffff00", position: 50 }, { color: "#ff0000", position: 100 }] }]; const allPresets = [...defaultPresets, ...presets]; // Generate CSS gradient string const generateGradient = useCallback(() => { if (stops.length === 0) return "transparent"; const colorStops = stops.sort((a, b) => a.position - b.position).map(stop => `${stop.color} ${stop.position}%`).join(", "); switch (selectedType) { case "linear": return `linear-gradient(${angle}deg, ${colorStops})`; case "radial": return `radial-gradient(circle, ${colorStops})`; case "conic": return `conic-gradient(from 0deg, ${colorStops})`; default: return `linear-gradient(45deg, ${colorStops})`; } }, [stops, selectedType, angle]); // Handle stop change const handleStopChange = useCallback((index, updates) => { setStops(prev => prev.map((stop, i) => i === index ? { ...stop, ...updates } : stop)); }, []); // Add new stop const handleAddStop = useCallback(() => { if (stops.length >= maxStops) return; const newStop = { color: "var(--glass-white)", position: 50 }; setStops(prev => [...prev, newStop]); setSelectedStopIndex(stops.length); }, [stops.length, maxStops]); // Remove stop const handleRemoveStop = useCallback(index => { if (stops.length <= 2) return; // Keep at least 2 stops setStops(prev => prev.filter((_, i) => i !== index)); setSelectedStopIndex(null); }, [stops.length]); // Handle preset selection const handlePresetSelect = useCallback(preset => { setSelectedType(preset.type); if (preset.angle !== undefined) { setAngle(preset.angle); } setStops([...preset.stops]); setSelectedStopIndex(null); const gradient = generateGradient(); onChange?.(gradient, preset.stops, preset.type, preset.angle); onPresetSelect?.(preset); }, [generateGradient, onChange, onPresetSelect]); // Generate random gradient const handleRandomGradient = useCallback(() => { const randomColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16); const newStops = [{ color: randomColor(), position: 0 }, { color: randomColor(), position: 100 }]; if (Math.random() > 0.5) { newStops.push({ color: randomColor(), position: 50 }); } setStops(newStops); setSelectedStopIndex(null); const gradient = generateGradient(); onChange?.(gradient, newStops, selectedType, angle); }, [generateGradient, selectedType, angle, onChange]); // Copy gradient to clipboard const handleCopyGradient = useCallback(async () => { const gradient = generateGradient(); try { await navigator.clipboard.writeText(gradient); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error("Failed to copy gradient:", err); } }, [generateGradient]); // Reset to default const handleReset = useCallback(() => { setSelectedType("linear"); setAngle(45); setStops([{ color: "var(--glass-color-primary)", position: 0 }, { color: "#8b5cf6", position: 100 }]); setSelectedStopIndex(null); }, []); const currentGradient = generateGradient(); return jsx(MotionFramer, { "data-glass-component": true, preset: "fadeIn", className: "glass-w-full", children: jsxs(GlassCard, { className: cn("overflow-hidden", className), ...props, children: [jsx(CardHeader, { className: 'pb-3', children: jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs(CardTitle, { className: 'text-primary glass-text-lg font-semibold glass-flex glass-items-center glass-gap-2', children: [jsx(Palette, { className: 'w-5 h-5' }), "Gradient Picker"] }), jsxs("div", { className: "glass-flex glass-gap-2", children: [jsx(GlassButton, { variant: "ghost", size: "sm", onClick: handleRandomGradient, className: "glass-p-2 glass-focus glass-touch-target", title: "Random Gradient", children: jsx(Shuffle, { className: 'w-4 h-4' }) }), jsx(GlassButton, { variant: "ghost", size: "sm", onClick: handleCopyGradient, className: "glass-p-2 glass-focus glass-touch-target", title: "Copy Gradient", children: copied ? jsx(Check, { className: 'w-4 h-4' }) : jsx(Copy, { className: 'w-4 h-4' }) })] })] }) }), jsxs(CardContent, { className: 'pt-0 glass-auto-gap glass-auto-gap-2xl', children: [jsxs("div", { className: "glass-auto-gap glass-auto-gap-md", children: [jsx("div", { className: 'glass-w-full h-32 glass-radius-lg glass-border glass-border-white/20', style: { background: currentGradient } }), jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsx("code", { className: 'glass-text-xs text-primary/60 glass-surface-subtle/10 glass-px-2 glass-py-1 glass-radius-md font-mono truncate glass-flex-1 glass-mr-2', children: currentGradient }), jsx(GlassButton, { variant: "ghost", size: "sm", onClick: handleReset, className: "glass-p-2 glass-focus glass-touch-target", title: "Reset", children: jsx(RotateCcw, { className: 'w-4 h-4' }) })] })] }), showTypeSelector && jsxs("div", { className: "glass-auto-gap glass-auto-gap-md", children: [jsx("h4", { className: 'text-primary/80 glass-text-sm font-medium', children: "Type" }), jsx("div", { className: "glass-flex glass-gap-2", children: ["linear", "radial", "conic"].map(type => jsx(GlassButton, { variant: selectedType === type ? "primary" : "ghost", size: "sm", onClick: e => setSelectedType(type), className: 'capitalize glass-focus glass-touch-target', children: type }, type)) })] }), showAngleControl && selectedType === "linear" && jsxs("div", { className: "glass-auto-gap glass-auto-gap-md", children: [jsxs("h4", { className: 'text-primary/80 glass-text-sm font-medium', children: ["Angle: ", angle, "\u00B0"] }), jsx("input", { type: "range", min: "0", max: "360", value: angle, onChange: e => setAngle(Number(e.target.value)), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer glass-focus glass-touch-target glass-contrast-guard' })] }), showStopsEditor && enableCustom && jsxs("div", { className: "glass-auto-gap glass-auto-gap-md", children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsx("h4", { className: 'text-primary/80 glass-text-sm font-medium', children: "Color Stops" }), stops.length < maxStops && jsx(GlassButton, { variant: "ghost", size: "sm", onClick: handleAddStop, className: "glass-p-1 glass-focus glass-touch-target", children: jsx(Upload, { className: 'w-4 h-4' }) })] }), jsx("div", { className: "glass-auto-gap glass-auto-gap-sm", children: stops.map((stop, index) => jsxs("div", { className: cn("flex items-center glass-gap-3 glass-p-3 glass-radius-lg border transition-all", selectedStopIndex === index ? "border-primary bg-primary/20" : "border-white/20 bg-white/5"), onClick: e => setSelectedStopIndex(index), children: [jsx("input", { type: "color", value: stop.color, onChange: e => handleStopChange(index, { color: e.target.value }), className: 'w-8 h-8 glass-radius-md glass-border glass-border-white/20 cursor-pointer glass-focus glass-touch-target glass-contrast-guard' }), jsxs("div", { className: "glass-flex-1", children: [jsx("input", { type: "range", min: "0", max: "100", value: stop.position, onChange: e => handleStopChange(index, { position: Number(e.target.value) }), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer glass-focus glass-touch-target glass-contrast-guard' }), jsxs("div", { className: 'glass-text-xs text-primary/60 glass-mt-1', children: ["Position: ", stop.position, "%"] })] }), stops.length > 2 && jsx(GlassButton, { variant: "ghost", size: "sm", onClick: e => handleRemoveStop(index), className: 'glass-p-1 text-primary hover:glass-text-secondary glass-focus glass-touch-target', children: jsx(Download, { className: 'w-4 h-4' }) })] }, index)) })] }), allPresets.length > 0 && jsxs("div", { className: "glass-auto-gap glass-auto-gap-md", children: [jsx("h4", { className: 'text-primary/80 glass-text-sm font-medium', children: "Presets" }), jsx("div", { className: "glass-grid glass-grid-cols-2 glass-gap-3", children: allPresets.map(preset => jsxs("div", { className: 'cursor-pointer group glass-focus glass-touch-target', onClick: e => handlePresetSelect(preset), children: [jsx("div", { className: 'glass-w-full h-16 glass-radius-lg glass-border glass-border-white/20 group-hover:border-white/40 transition-all', style: { background: preset.stops.length > 0 ? `${preset.type}-gradient(${preset.angle || 0}deg, ${preset.stops.map(s => `${s.color} ${s.position}%`).join(", ")})` : "transparent" } }), jsx("p", { className: 'text-primary/80 glass-text-xs glass-mt-2 text-center group-hover:text-primary transition-colors', children: preset.name })] }, preset.id)) })] })] })] }) }); }; export { GlassGradientPicker, GlassGradientPicker as default }; //# sourceMappingURL=GlassGradientPicker.js.map