diff --git a/src/frontend/panda.config.ts b/src/frontend/panda.config.ts index cce15d18..67147c6e 100644 --- a/src/frontend/panda.config.ts +++ b/src/frontend/panda.config.ts @@ -114,6 +114,10 @@ const config: Config = { clipPath: 'polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 0)', }, }, + overlayIn: { + from: { opacity: 0 }, + to: { opacity: 0.6 }, + }, }, tokens: defineTokens({ /* we take a few things from the panda preset but for now we clear out some stuff. diff --git a/src/frontend/src/features/participantTile/components/participantTileFocus/ParticipantTileFocus.tsx b/src/frontend/src/features/participantTile/components/participantTileFocus/ParticipantTileFocus.tsx index 66c4e762..7facb036 100644 --- a/src/frontend/src/features/participantTile/components/participantTileFocus/ParticipantTileFocus.tsx +++ b/src/frontend/src/features/participantTile/components/participantTileFocus/ParticipantTileFocus.tsx @@ -1,7 +1,7 @@ import { css } from '@/styled-system/css' import { HStack } from '@/styled-system/jsx' import { TrackReferenceOrPlaceholder } from '@livekit/components-core' -import { useEffect, useRef, useState } from 'react' +import { ReactNode, useEffect, useRef, useState } from 'react' import { Track } from 'livekit-client' import { useCanMute } from '@/features/rooms/livekit/hooks/useCanMute' import { FocusButton } from './FocusButton' @@ -11,49 +11,34 @@ import { ZoomButton } from './ZoomButton' const MOUSE_IDLE_TIME = 3000 -export const ParticipantTileFocus = ({ - trackRef, - hasKeyboardFocus, -}: { - trackRef: TrackReferenceOrPlaceholder +type FadeOverlayProps = { + children: ReactNode hasKeyboardFocus: boolean -}) => { - const [hovered, setHovered] = useState(false) - const [opacity, setOpacity] = useState(0) +} +const FadeOverlay = ({ children, hasKeyboardFocus }: FadeOverlayProps) => { + const [active, setActive] = useState(false) const idleTimerRef = useRef(null) - const [isIdleRef, setIsIdleRef] = useState(false) - const isVisible = hasKeyboardFocus || (hovered && !isIdleRef) - - useEffect(() => { - if (isVisible) { - // Wait for next frame to ensure element is mounted - requestAnimationFrame(() => { - setOpacity(0.6) - }) - } else { - setOpacity(0) - } - }, [isVisible]) - - const handleMouseMove = () => { - if (idleTimerRef.current) { - window.clearTimeout(idleTimerRef.current) - } - idleTimerRef.current = window.setTimeout(() => { - setIsIdleRef(true) - }, MOUSE_IDLE_TIME) - setIsIdleRef(false) + const clearIdleTimer = () => { + if (idleTimerRef.current) window.clearTimeout(idleTimerRef.current) } - const participant = trackRef.participant + const armIdleTimer = () => { + clearIdleTimer() + idleTimerRef.current = window.setTimeout(() => { + setActive(false) + }, MOUSE_IDLE_TIME) + } - const isScreenShare = trackRef.source == Track.Source.ScreenShare - const isLocal = trackRef.participant.isLocal + const handleActivity = () => { + setActive(true) + armIdleTimer() + } - const canMute = useCanMute(participant) + useEffect(() => clearIdleTimer, []) + const isVisible = hasKeyboardFocus || active return (
setHovered(true)} - onMouseLeave={() => setHovered(false)} - onMouseMove={handleMouseMove} + onMouseEnter={handleActivity} + onMouseMove={handleActivity} + onMouseLeave={() => { + clearIdleTimer() + setActive(false) + }} > - {isVisible && ( -
- - - {!isScreenShare ? ( - <> - {participant.isLocal ? ( - - ) : ( - canMute && - )} - - ) : ( - !isLocal && - )} - -
- )} + {isVisible && children}
) } + +export const ParticipantTileFocus = ({ + trackRef, + hasKeyboardFocus, +}: { + trackRef: TrackReferenceOrPlaceholder + hasKeyboardFocus: boolean +}) => { + const participant = trackRef.participant + const isScreenShare = trackRef.source == Track.Source.ScreenShare + const isLocal = participant.isLocal + const canMute = useCanMute(participant) + + return ( + +
+ + + {!isScreenShare ? ( + <> + {isLocal ? ( + + ) : ( + canMute && + )} + + ) : ( + !isLocal && + )} + +
+
+ ) +}