UNPKG

aura-glass

Version:

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

199 lines (196 loc) 5.97 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { forwardRef, useState, useRef, useCallback, useMemo } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { GlassButton } from '../button/GlassButton.js'; import '../button/GlassFab.js'; import '../button/GlassMagneticButton.js'; import { createGlassStyle } from '../../core/mixins/glassMixins.js'; import styles from './RippleButton.module.css.js'; // Convert color string to RGB values const colorToRgb = color => { // Convert named colors to their RGB values switch (color) { case 'primary': return '99, 102, 241'; // Indigo case 'secondary': return '156, 39, 176'; // Purple case 'error': return '240, 82, 82'; // Red case 'info': return '3, 169, 244'; // Light Blue case 'success': return '76, 175, 80'; // Green case 'warning': return '255, 152, 0'; // Orange case 'white': return '255, 255, 255'; default: // If it's already an RGB value in format 'r, g, b' if (/^\d+,\s*\d+,\s*\d+$/.test(color)) { return color; } // Default color (white) return '255, 255, 255'; } }; // Get ripple size based on button size const getRippleSize = size => { switch (size) { case 'small': return 100; case 'large': return 200; case 'medium': default: return 150; } }; // Get ripple duration based on speed - Adjusted default const getRippleDuration = speed => { switch (speed) { case 'slow': return 900; case 'fast': return 400; case 'medium': default: return 300; // Shortened default duration further for better sync with physics } }; /** * RippleButton Component Implementation * Wraps the base Button component to add a ripple effect. */ function RippleButtonComponent( // Inherit ButtonProps and add Ripple-specific props props, ref) { const { children, disabled = false, rippleColor = 'white', rippleSize: rippleSizeProp, // Rename prop to avoid conflict rippleSpeed = 'medium', centerRipple = false, onClick, onMouseDown, // Capture original onMouseDown // Extract Button props variant = 'primary', size = 'md', fullWidth = false, className, style, ...rest // Rest are Button props } = props; const prefersReducedMotion = useReducedMotion(); const [ripples, setRipples] = useState([]); const rippleCount = useRef(0); const wrapperRef = useRef(null); // Ref for the wrapper // Handle ripple creation on mouse down const handleMouseDown = useCallback(event => { if (disabled || prefersReducedMotion) { if (onMouseDown) onMouseDown(event); // Call original handler return; } const wrapper = wrapperRef.current; if (!wrapper) return; const rect = wrapper.getBoundingClientRect(); let x, y; if (centerRipple) { x = rect.width / 2; y = rect.height / 2; } else { x = event.clientX - rect.left; y = event.clientY - rect.top; } const id = rippleCount.current++; const duration = getRippleDuration(rippleSpeed); // Start ripple animation setRipples(prev => [...prev, { id, x, y }]); // Schedule ripple removal after animation duration setTimeout(() => { setRipples(prev => prev.filter(r => r.id !== id)); }, duration); // Call the original onMouseDown handler if provided if (onMouseDown) { onMouseDown(event); } }, [disabled, prefersReducedMotion, centerRipple, rippleSpeed, onMouseDown]); // Added dependencies const rippleSizeValue = rippleSizeProp ? getRippleSize(rippleSizeProp) : getRippleSize(size); // Use prop or button size const rippleDuration = getRippleDuration(rippleSpeed); const rippleColorRgb = colorToRgb(rippleColor); const wrapperStyle = useMemo(() => { const base = createGlassStyle({ intent: 'neutral', elevation: 'level2', tier: 'high' }); const { position, ...rest } = base; return { ...rest, '--ripple-color-rgb': rippleColorRgb }; }, [rippleColorRgb]); return ( // Apply ripple color CSS variable to the wrapper jsxs("div", { ref: wrapperRef, style: wrapperStyle, className: cn(styles.wrapper, 'glass-ripple-button glass-contrast-guard', className), children: [jsx(GlassButton, { ref: ref, variant: variant, size: size, disabled: disabled, fullWidth: fullWidth, className: "glass-focus glass-touch-target", style: style, onClick: onClick, onMouseDown: handleMouseDown, ...rest, 'aria-pressed': rest['aria-pressed'] === 'true' ? true : rest['aria-pressed'] === 'false' ? false : rest['aria-pressed'] === 'mixed' ? undefined : rest['aria-pressed'], 'aria-expanded': rest['aria-expanded'] === 'true' ? true : rest['aria-expanded'] === 'false' ? false : rest['aria-expanded'], children: children }), !prefersReducedMotion && jsx("div", { className: styles.ripples, "aria-hidden": "true", children: ripples.map(ripple => jsx("span", { className: styles.ripple, style: { left: ripple.x - rippleSizeValue / 2, top: ripple.y - rippleSizeValue / 2, width: rippleSizeValue, height: rippleSizeValue, animationDuration: `${rippleDuration}ms` } }, ripple.id)) })] }) ); } /** * RippleButton Component * * A button component that wraps the base Button to add a ripple effect feedback. * It inherits physics interactions from the base Button. */ const RippleButton = /*#__PURE__*/forwardRef(RippleButtonComponent); export { RippleButton as default }; //# sourceMappingURL=RippleButton.js.map