aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
443 lines (440 loc) • 14.2 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { forwardRef, useRef, useState, useEffect, useCallback } from 'react';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { cn } from '../../lib/utilsComprehensive.js';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import { MotionFramer } from '../../primitives/motion/MotionFramer.js';
import { useA11yId, createButtonA11y } from '../../utils/a11y.js';
import { usePredictiveEngine, useInteractionRecorder } from '../advanced/GlassPredictiveEngine.js';
import { useAchievements } from '../advanced/GlassAchievementSystem.js';
import { useBiometricAdaptation } from '../advanced/GlassBiometricAdaptation.js';
import { useEyeTracking } from '../advanced/GlassEyeTracking.js';
import { useSpatialAudio } from '../advanced/GlassSpatialAudio.js';
// Get size classes
const getSizeClasses = (size, variant) => {
const isExtended = variant === "extended";
switch (size) {
case "small":
return {
width: isExtended ? "w-auto min-w-20" : "w-10",
height: "h-10",
padding: isExtended ? "px-3 py-2" : "p-0",
fontSize: "glass-text-xl"
};
case "large":
return {
width: isExtended ? "w-auto min-w-32" : "w-16",
height: "h-16",
padding: isExtended ? "px-5 py-3" : "p-0",
fontSize: "text-3xl"
};
default:
// medium
return {
width: isExtended ? "w-auto min-w-28" : "w-14",
height: "h-14",
padding: isExtended ? "px-4 py-2.5" : "p-0",
fontSize: "glass-text-2xl"
};
}
};
// Get position classes
const getPositionClasses = position => {
switch (position) {
case "bottomRight":
return "fixed bottom-4 right-4";
case "bottomLeft":
return "fixed bottom-4 left-4";
case "topRight":
return "fixed top-4 right-4";
case "topLeft":
return "fixed top-4 left-4";
case "center":
return "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2";
case "none":
default:
return "";
}
};
// Get color classes for variants
const getColorClasses = (color, variant) => {
if (variant === "glass") {
return "glass-text-primary bg-white/10 glass-glass-backdrop-blur-md border border-white/20";
}
switch (color) {
case "primary":
return "bg-primary-500 glass-text-primary hover:bg-primary-600";
case "secondary":
return "bg-secondary-500 glass-text-primary hover:bg-secondary-600";
case "success":
return "bg-success-500 glass-text-primary hover:bg-success-600";
case "error":
return "bg-danger-500 glass-text-primary hover:bg-danger-600";
case "warning":
return "bg-warning-500 glass-text-primary hover:bg-warning-600";
case "info":
return "bg-info-500 glass-text-primary hover:bg-info-600";
default:
return "bg-gray-700 glass-text-primary hover:bg-gray-600";
}
};
// Get pulse animation classes
const getPulseClasses = (pulse, color) => {
if (!pulse) return "";
const baseClasses = "animate-pulse-ring";
switch (color) {
case "primary":
return `${baseClasses} ring-primary-400`;
case "secondary":
return `${baseClasses} ring-secondary-400`;
case "success":
return `${baseClasses} ring-success-400`;
case "error":
return `${baseClasses} ring-danger-400`;
case "warning":
return `${baseClasses} ring-warning-400`;
case "info":
return `${baseClasses} ring-info-400`;
default:
return `${baseClasses} ring-gray-400`;
}
};
// Get variant-specific glass props
const getGlassVariantProps = (variant, enhanced) => {
if (variant === "glass") {
return {
variant: "frosted",
intensity: enhanced ? "ultra" : "strong",
border: "glow",
lighting: "volumetric",
caustics: enhanced,
refraction: enhanced
};
}
return {
variant: "clear",
intensity: "medium",
border: "subtle",
lighting: "ambient"
};
};
// Tooltip component
const TooltipComponent = ({
tooltip,
position,
show
}) => {
const getTooltipPosition = position => {
if (position === "bottomRight" || position === "bottomLeft") {
return "absolute -top-8 left-1/2 -translate-x-1/2";
}
if (position === "topRight" || position === "topLeft") {
return "absolute -bottom-8 left-1/2 -translate-x-1/2";
}
return "absolute -top-8 left-1/2 -translate-x-1/2";
};
return jsx("span", {
"data-glass-component": true,
className: cn("bg-gray-900/90 glass-text-primary px-2 py-1 glass-radius-md glass-text-xs whitespace-nowrap", "pointer-events-none z-50 transition-opacity duration-200", getTooltipPosition(position), show ? "opacity-100 visible" : "opacity-0 invisible"),
children: tooltip
});
};
// Wrapper component to handle tooltip
const FabWrapper = ({
position,
children,
className
}) => {
return jsx("div", {
className: cn(position === "none" ? "relative" : "static", "inline-block", className),
children: children
});
};
/**
* Fab Component
*
* A floating action button (FAB) performs the primary action in an application.
*/
const Fab = /*#__PURE__*/forwardRef((props, ref) => {
useReducedMotion();
const {
children,
color = "primary",
disabled = false,
href,
size = "medium",
variant = "standard",
onClick,
position = "bottomRight",
tooltip,
pulse = false,
className,
enhanced = false,
zIndex = 1050,
type = "button",
isVisible = true,
animationConfig,
disableAnimation,
style,
intent = "primary",
elevation = "level4",
tier = "high",
description,
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledBy,
"aria-describedby": ariaDescribedBy,
"aria-pressed": ariaPressed,
"aria-expanded": ariaExpanded,
"aria-controls": ariaControls,
"aria-haspopup": ariaHaspopup,
// Consciousness features
predictive = false,
preloadContent = false,
eyeTracking = false,
gazeResponsive = false,
adaptive = false,
biometricResponsive = false,
spatialAudio = false,
audioFeedback = false,
trackAchievements = false,
achievementId,
usageContext = "fab",
...rest
} = props;
const fabRef = useRef(null);
const [isHovered, setIsHovered] = useState(false);
const [clickCount, setClickCount] = useState(0);
const [shouldRender, setShouldRender] = useState(isVisible);
// Consciousness feature hooks - only initialize if features are enabled
predictive ? usePredictiveEngine() : null;
const eyeTracker = eyeTracking ? useEyeTracking() : null;
adaptive ? useBiometricAdaptation() : null;
const spatialAudioEngine = spatialAudio ? useSpatialAudio() : null;
const achievementTracker = trackAchievements ? useAchievements() : null;
const interactionRecorder = predictive || trackAchievements ? useInteractionRecorder(`glass-fab-${variant}-${usageContext}`) : null;
// Generate unique ID for accessibility
const componentId = useA11yId("glass-fab");
const descriptionId = description ? useA11yId("glass-fab-desc") : undefined;
// Handle visibility changes
useEffect(() => {
if (isVisible) {
setShouldRender(true);
} else {
// Delay hiding to allow exit animation
const timeout = setTimeout(() => setShouldRender(false), 300);
return () => clearTimeout(timeout);
}
}, [isVisible]);
// Eye tracking effects
useEffect(() => {
if (!gazeResponsive || !eyeTracker || !fabRef.current) return;
// Note: Eye tracking event handlers not yet implemented
// eyeTracker.onGazeEnter?.(fabRef.current, handleGazeEnter);
// eyeTracker.onGazeLeave?.(fabRef.current, handleGazeExit);
return () => {
// Note: Eye tracking cleanup not yet implemented
// if (fabRef.current) {
// eyeTracker.offGazeEnter?.(fabRef.current, handleGazeEnter);
// eyeTracker.offGazeLeave?.(fabRef.current, handleGazeExit);
// }
};
}, [gazeResponsive, eyeTracker, disabled, spatialAudioEngine, audioFeedback]);
// Enhanced interaction tracking
const handleInteraction = useCallback(event => {
if (disabled) return;
setClickCount(prev => prev + 1);
// Record interaction for predictive learning
if (interactionRecorder) {
interactionRecorder.recordClick(event);
}
// Play spatial audio feedback
if (spatialAudioEngine && audioFeedback) {
spatialAudioEngine.playGlassSound("fab_click_success", {
x: fabRef.current?.offsetLeft || 0,
y: fabRef.current?.offsetTop || 0,
z: 0 // Default z position
});
}
// Track achievements
if (achievementTracker && trackAchievements) {
achievementTracker.recordAction(achievementId || "fab_interaction", {
variant,
context: usageContext,
clickCount,
timestamp: Date.now()
});
}
// Call original onClick handler
onClick?.(event);
}, [disabled, interactionRecorder, spatialAudioEngine, audioFeedback, achievementTracker, trackAchievements, achievementId, variant, usageContext, clickCount, onClick]);
// Create accessibility attributes
const a11yProps = createButtonA11y({
id: componentId,
label: ariaLabel,
description,
pressed: ariaPressed,
expanded: ariaExpanded,
controls: ariaControls,
haspopup: ariaHaspopup === "true" ? true : ariaHaspopup === "false" ? false : ariaHaspopup,
disabled: disabled,
descriptionId
});
const sizeClasses = getSizeClasses(size, variant);
const positionClasses = getPositionClasses(position);
const colorClasses = getColorClasses(color, variant);
const pulseClasses = getPulseClasses(pulse, color);
const glassVariantProps = getGlassVariantProps(variant, enhanced);
const baseClasses = cn(
// Base styles
"inline-flex items-center justify-center", "font-medium cursor-pointer outline-none", "select-none box-border transition-all duration-200", "will-change-transform",
// Size classes
sizeClasses.width, sizeClasses.height, sizeClasses.padding, sizeClasses.fontSize,
// Position classes
positionClasses,
// Shape
variant === "extended" ? "glass-radius-full" : "glass-radius-full",
// Color classes (for non-glass variants)
variant !== "glass" && colorClasses,
// Pulse classes
pulseClasses,
// Hover and interaction states
!disabled && "hover:scale-105 active:scale-95",
// Disabled state
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
// Visibility animation
isVisible ? "opacity-100 scale-100" : "opacity-0 scale-75",
// Consciousness feature styles
gazeResponsive && isHovered && "ring-2 ring-blue-400/40 shadow-lg shadow-blue-400/20", className);
const combinedStyle = {
...style,
zIndex,
transition: "opacity 0.3s ease-in-out, transform 0.3s ease-in-out"
};
const Component = href ? "a" : "button";
const fabButton = jsx(MotionFramer, {
preset: "scaleIn",
duration: 0.3,
animateOnMount: isVisible,
animateOnHover: !disabled,
className: 'inline-block',
children: variant === "glass" ? jsxs(OptimizedGlassCore, {
as: href ? "a" : "button",
type: href ? undefined : type,
...(href ? {
href
} : {}),
...(!href ? {
disabled
} : {}),
intent: intent,
elevation: elevation,
tier: tier,
...glassVariantProps,
interactive: true,
hoverSheen: true,
liftOnHover: true,
press: true,
className: baseClasses,
style: combinedStyle,
onClick: handleInteraction,
onMouseEnter: () => setIsHovered(true),
onMouseLeave: () => setIsHovered(false),
ref: node => {
if (typeof ref === "function") {
ref(node);
} else if (ref) {
ref.current = node;
}
if (fabRef.current !== undefined) {
fabRef.current = node;
}
},
...a11yProps,
...rest,
children: [jsx("span", {
className: 'relative z-10 glass-focus glass-touch-target glass-contrast-guard',
children: children
}), description && jsx("span", {
id: descriptionId,
className: 'sr-only',
children: description
})]
}) : jsxs(Component, {
className: baseClasses,
style: combinedStyle,
href: href,
disabled: disabled,
onClick: handleInteraction,
type: href ? undefined : type,
onMouseEnter: () => setIsHovered(true),
onMouseLeave: () => setIsHovered(false),
ref: node => {
if (typeof ref === "function") {
ref(node);
} else if (ref) {
ref.current = node;
}
if (fabRef.current !== undefined) {
fabRef.current = node;
}
},
...a11yProps,
...rest,
children: [children, description && jsx("span", {
id: descriptionId,
className: 'sr-only glass-focus glass-touch-target glass-contrast-guard',
children: description
})]
})
});
// --- Conditional Rendering Logic ---
if (!shouldRender) {
return null;
}
if (!tooltip) {
return fabButton;
}
return jsxs(FabWrapper, {
position: position,
children: [fabButton, jsx(TooltipComponent, {
tooltip: tooltip,
position: position,
show: isHovered
})]
});
});
Fab.displayName = "Fab";
/**
* GlassFab Component
*
* A floating action button with glass morphism styling.
*/
const GlassFab = /*#__PURE__*/forwardRef((props, ref) => {
const {
className,
variant = "glass",
intent = "primary",
elevation = "level4",
tier = "high",
enhanced = true,
...rest
} = props;
return jsx(Fab, {
ref: ref,
className: cn("glass-fab", className),
variant: variant,
intent: intent,
elevation: elevation,
tier: tier,
enhanced: enhanced,
...rest
});
});
GlassFab.displayName = "GlassFab";
export { Fab, GlassFab, Fab as default };
//# sourceMappingURL=GlassFab.js.map