diff --git a/apps/client/src/common/hooks/useFollowComponent.ts b/apps/client/src/common/hooks/useFollowComponent.ts index 60f187f87..dafdcdc58 100644 --- a/apps/client/src/common/hooks/useFollowComponent.ts +++ b/apps/client/src/common/hooks/useFollowComponent.ts @@ -1,6 +1,5 @@ -import { RefObject, useCallback, useEffect, useRef } from 'react'; - -import { useSelectedEventId } from './useSocket'; +import { RefObject, useCallback, useEffect } from 'react'; +import { MaybeString } from 'ontime-types'; function scrollToComponent( componentRef: RefObject, @@ -18,37 +17,26 @@ function scrollToComponent( - componentRef: RefObject, - scrollRef: RefObject, - topOffset: number, -) { - if (!componentRef.current || !scrollRef.current) { - return; - } - - const componentRect = componentRef.current.getBoundingClientRect(); - const scrollRect = scrollRef.current.getBoundingClientRect(); - const top = componentRect.top - scrollRect.top + scrollRef.current.scrollTop - topOffset; - - // maintain current x scroll position - scrollRef.current.scrollTo(scrollRef.current.scrollLeft, top); -} - interface UseFollowComponentProps { followRef: RefObject; scrollRef: RefObject; doFollow: boolean; topOffset?: number; setScrollFlag?: (newValue: boolean) => void; + followTrigger?: MaybeString; // this would be an entry id or null } -export default function useFollowComponent(props: UseFollowComponentProps) { - const { followRef, scrollRef, doFollow, topOffset = 100, setScrollFlag } = props; - - // when cursor moves, view should follow +export default function useFollowComponent({ + followRef, + scrollRef, + doFollow, + topOffset = 100, + setScrollFlag, + followTrigger, +}: UseFollowComponentProps) { + // when trigger moves, view should follow useEffect(() => { - if (!doFollow) { + if (!doFollow || !followTrigger) { return; } @@ -60,16 +48,14 @@ export default function useFollowComponent(props: UseFollowComponentProps) { setScrollFlag?.(false); }); } - - // eslint-disable-next-line -- the prompt seems incorrect - }, [followRef?.current, scrollRef?.current]); + }, [followTrigger, doFollow, followRef, scrollRef, setScrollFlag, topOffset]); const scrollToRefComponent = useCallback( (componentRef = followRef, containerRef = scrollRef, offset = topOffset) => { - if (componentRef.current && containerRef.current) { + if (componentRef && containerRef) { // @ts-expect-error -- we know this are not null // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - scrollToComponent(componentRef!, scrollRef!, offset); + scrollToComponent(componentRef!, containerRef!, offset); } }, [followRef, scrollRef, topOffset], @@ -77,32 +63,3 @@ export default function useFollowComponent(props: UseFollowComponentProps) { return scrollToRefComponent; } - -export function useFollowSelected(doFollow: boolean, topOffset = 100) { - const selectedEvenId = useSelectedEventId(); - - const selectedRef = useRef(null); - const scrollRef = useRef(null); - - useEffect(() => { - if (!doFollow) { - return; - } - - if (selectedEvenId && selectedRef.current && scrollRef.current) { - // Use requestAnimationFrame to ensure the component is fully loaded - window.requestAnimationFrame(() => { - snapToComponent( - { current: selectedRef.current } as RefObject, - { current: scrollRef.current } as RefObject, - topOffset, - ); - }); - } - }, [doFollow, selectedEvenId, topOffset]); - - return { - selectedRef, - scrollRef, - }; -} diff --git a/apps/client/src/features/operator/Operator.tsx b/apps/client/src/features/operator/Operator.tsx index 22406b07c..887f8027e 100644 --- a/apps/client/src/features/operator/Operator.tsx +++ b/apps/client/src/features/operator/Operator.tsx @@ -49,6 +49,7 @@ export default function Operator() { scrollRef, doFollow: !lockAutoScroll, topOffset: selectedOffset, + followTrigger: selectedEventId, }); useWindowTitle('Operator'); diff --git a/apps/client/src/features/rundown/Rundown.tsx b/apps/client/src/features/rundown/Rundown.tsx index 3c60f2cf3..1075b4c01 100644 --- a/apps/client/src/features/rundown/Rundown.tsx +++ b/apps/client/src/features/rundown/Rundown.tsx @@ -82,7 +82,12 @@ export default function Rundown({ data, rundownMetadata }: RundownProps) { const cursorRef = useRef(null); const scrollRef = useRef(null); - useFollowComponent({ followRef: cursorRef, scrollRef, doFollow: editorMode === AppMode.Run }); + useFollowComponent({ + followRef: cursorRef, + scrollRef, + doFollow: true, + followTrigger: editorMode === AppMode.Edit ? cursor : featureData?.selectedEventId, + }); // DND KIT const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } })); @@ -312,7 +317,7 @@ export default function Rundown({ data, rundownMetadata }: RundownProps) { setMetadata(rundownMetadata); }, [order, entries, rundownMetadata]); - // in run mode, we follow selection + // in run mode, we follow the playback selection and open groups as needed useEffect(() => { if (editorMode !== AppMode.Run || !featureData?.selectedEventId) { return; diff --git a/apps/client/src/views/countdown/CountdownSubscriptions.tsx b/apps/client/src/views/countdown/CountdownSubscriptions.tsx index b14f1041a..d51ea298c 100644 --- a/apps/client/src/views/countdown/CountdownSubscriptions.tsx +++ b/apps/client/src/views/countdown/CountdownSubscriptions.tsx @@ -53,6 +53,7 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode scrollRef, doFollow: !lockAutoScroll, topOffset: 0, + followTrigger: selectedEventId, }); // reset scroll if nothing is selected