'use client' import React, { createContext, useContext, useState, useCallback, useRef, useEffect } from 'react' import { MOTION, LAYERS } from './depth-system' import { usePerformance } from './PerformanceContext' /** * Focus Context - Manages focus mode for the UI * * When an element enters "focus mode", the rest of the UI dims and blurs, * creating a spotlight effect that helps users concentrate on the task at hand. * * This is particularly useful for: * - Replying to messages * - Editing content * - Modal-like interactions without actual modals */ interface FocusContextType { /** Whether focus mode is active */ isFocused: boolean /** The ID of the focused element (if any) */ focusedElementId: string | null /** Enter focus mode */ enterFocus: (elementId: string) => void /** Exit focus mode */ exitFocus: () => void /** Toggle focus mode */ toggleFocus: (elementId: string) => void } const FocusContext = createContext(null) interface FocusProviderProps { children: React.ReactNode /** Duration of the focus transition in ms */ transitionDuration?: number /** Blur amount for unfocused elements (px) */ blurAmount?: number /** Dim amount for unfocused elements (0-1) */ dimAmount?: number } export function FocusProvider({ children, transitionDuration = 300, blurAmount = 4, dimAmount = 0.6, }: FocusProviderProps) { const [isFocused, setIsFocused] = useState(false) const [focusedElementId, setFocusedElementId] = useState(null) const { settings } = usePerformance() const enterFocus = useCallback((elementId: string) => { setFocusedElementId(elementId) setIsFocused(true) }, []) const exitFocus = useCallback(() => { setIsFocused(false) // Delay clearing the ID to allow exit animation setTimeout(() => { setFocusedElementId(null) }, transitionDuration) }, [transitionDuration]) const toggleFocus = useCallback( (elementId: string) => { if (isFocused && focusedElementId === elementId) { exitFocus() } else { enterFocus(elementId) } }, [isFocused, focusedElementId, enterFocus, exitFocus] ) // Keyboard handler for Escape useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isFocused) { exitFocus() } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [isFocused, exitFocus]) // Adaptive values based on performance const adaptiveBlur = settings.enableBlur ? blurAmount * settings.blurIntensity : 0 const adaptiveDuration = Math.round(transitionDuration * settings.animationSpeed) return ( {/* Focus backdrop - dims and blurs unfocused content */}
0 ? `blur(${adaptiveBlur}px)` : 'none', WebkitBackdropFilter: isFocused && adaptiveBlur > 0 ? `blur(${adaptiveBlur}px)` : 'none', transition: ` opacity ${adaptiveDuration}ms ${MOTION.standard.easing}, backdrop-filter ${adaptiveDuration}ms ${MOTION.standard.easing} `, }} onClick={exitFocus} aria-hidden={!isFocused} /> {children} ) } export function useFocus() { const context = useContext(FocusContext) if (!context) { return { isFocused: false, focusedElementId: null, enterFocus: () => {}, exitFocus: () => {}, toggleFocus: () => {}, } } return context } /** * Hook to check if a specific element is focused */ export function useIsFocused(elementId: string): boolean { const { isFocused, focusedElementId } = useFocus() return isFocused && focusedElementId === elementId } /** * FocusTarget - Wrapper that makes children focusable */ interface FocusTargetProps { children: React.ReactNode /** Unique ID for this focus target */ id: string /** Additional class names */ className?: string /** Style overrides */ style?: React.CSSProperties } export function FocusTarget({ children, id, className = '', style }: FocusTargetProps) { const { isFocused, focusedElementId } = useFocus() const { settings } = usePerformance() const isThisElement = focusedElementId === id const shouldElevate = isFocused && isThisElement const duration = Math.round(MOTION.emphasis.duration * settings.animationSpeed) return (
{children}
) }