"use client"; import React, { forwardRef } from 'react'; import { useInteractiveState } from '../hooks/use-interactive-state'; import { cn } from "../utils/cn"; interface InteractiveOptions { enableHover?: boolean; enableFocus?: boolean; enablePress?: boolean; enableRipple?: boolean; enableAnimations?: boolean; enableColorShifts?: boolean; enableAccessibilityEnhancements?: boolean; animations?: boolean; enableHapticFeedback?: boolean; } interface InteractiveWrapperProps extends React.HTMLAttributes { /** * Interactive state options */ interactive?: InteractiveOptions; /** * Element type to render */ as?: keyof React.JSX.IntrinsicElements; /** * Whether to render ripple effect */ showRipple?: boolean; /** * Custom ripple color */ rippleColor?: string; /** * Accessibility role */ role?: string; /** * Whether element should be focusable */ focusable?: boolean; /** * Loading state */ loading?: boolean; /** * Disabled state */ disabled?: boolean; /** * Active state */ active?: boolean; /** * Callback for state changes */ onStateChange?: (state: any) => void; } export const InteractiveWrapper = forwardRef(({ interactive = {}, as = 'div', showRipple = true, rippleColor, role, focusable = false, loading = false, disabled = false, active = false, onStateChange, className, children, onClick, ...props }, ref) => { const { state, handlers, getStateStyles, getStateClasses, ripplePosition, setLoading, setDisabled, ref: interactiveRef } = useInteractiveState(); // Update loading and disabled states React.useEffect(() => { setLoading(loading); }, [loading, setLoading]); React.useEffect(() => { setDisabled(disabled); }, [disabled, setDisabled]); // Notify parent of state changes React.useEffect(() => { if (onStateChange) { onStateChange(state); } }, [state, onStateChange]); // Merge refs React.useEffect(() => { if (ref) { if (typeof ref === 'function') { ref(interactiveRef.current); } else { ref.current = interactiveRef.current; } } }, [ref, interactiveRef]); const Component = as as any; const mergedHandlers = { ...handlers, onClick: (event: React.MouseEvent) => { handlers.onClick(); if (onClick && !event.defaultPrevented) { onClick(event); } } }; const computedClassName = cn( 'interactive-wrapper', 'relative overflow-hidden transition-all duration-200', getStateClasses(), focusable && 'focus:outline-none', className ); return ( {children} {/* Ripple Effect */} {showRipple && ripplePosition && ( )} {/* Loading Overlay */} {loading && (
)} ); }); InteractiveWrapper.displayName = 'InteractiveWrapper'; /** * Ripple effect component */ function RippleEffect({ x, y, color = 'currentColor' }: { x: number; y: number; color?: string; }) { return (
); } /** * Pre-configured interactive components */ export const InteractivePresets = { /** * Card with hover and click interactions */ Card: forwardRef>((props, ref) => ( )), /** * Button-like interactive element */ Button: forwardRef>((props, ref) => ( )), /** * Navigation item with subtle interactions */ NavItem: forwardRef>((props, ref) => ( )), /** * Input field with enhanced focus states */ Input: forwardRef>((props, ref) => ( )), /** * Toggle switch with visual feedback */ Toggle: forwardRef & { checked?: boolean }>((props, ref) => { const { checked, ...restProps } = props; return (
); }) }; // Export individual preset components with display names InteractivePresets.Card.displayName = 'InteractiveCard'; InteractivePresets.Button.displayName = 'InteractiveButton'; InteractivePresets.NavItem.displayName = 'InteractiveNavItem'; InteractivePresets.Input.displayName = 'InteractiveInput'; InteractivePresets.Toggle.displayName = 'InteractiveToggle';