mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 19:33:46 +00:00
feat: operator view (#440)
* feat: operator view * chore: smoke test operator * refactor: small improvements from deepsource --------- Co-authored-by: arihanv <arihanvaranasi@gmail.com> Co-authored-by: arihanv <63890951+arihanv@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { MutableRefObject, useCallback, useEffect } from 'react';
|
||||
|
||||
function scrollToComponent<ComponentRef extends HTMLElement, ScrollRef extends HTMLElement>(
|
||||
componentRef: MutableRefObject<ComponentRef>,
|
||||
scrollRef: MutableRefObject<ScrollRef>,
|
||||
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;
|
||||
|
||||
scrollRef.current.scrollTo({ top, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
interface UseFollowComponentProps {
|
||||
followRef: MutableRefObject<HTMLElement | null>;
|
||||
scrollRef: MutableRefObject<HTMLElement | null>;
|
||||
doFollow: boolean;
|
||||
topOffset?: number;
|
||||
setScrollFlag?: () => void;
|
||||
}
|
||||
|
||||
export default function useFollowComponent(props: UseFollowComponentProps) {
|
||||
const { followRef, scrollRef, doFollow, topOffset = 100, setScrollFlag } = props;
|
||||
|
||||
// when cursor moves, view should follow
|
||||
useEffect(() => {
|
||||
if (!doFollow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (followRef.current && scrollRef.current) {
|
||||
// Use requestAnimationFrame to ensure the component is fully loaded
|
||||
window.requestAnimationFrame(() => {
|
||||
setScrollFlag?.();
|
||||
scrollToComponent(
|
||||
followRef as MutableRefObject<HTMLElement>,
|
||||
scrollRef as MutableRefObject<HTMLElement>,
|
||||
topOffset,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line -- the prompt seems incorrect
|
||||
}, [followRef?.current, scrollRef?.current]);
|
||||
|
||||
const scrollToRefComponent = useCallback(
|
||||
(componentRef = followRef, containerRef = scrollRef, offset = topOffset) => {
|
||||
if (componentRef.current && containerRef.current) {
|
||||
// @ts-expect-error -- we know this are not null
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
scrollToComponent(componentRef!, scrollRef!, offset);
|
||||
}
|
||||
},
|
||||
[followRef, scrollRef, topOffset],
|
||||
);
|
||||
|
||||
return scrollToRefComponent;
|
||||
}
|
||||
@@ -4,10 +4,17 @@ import { deepCompare, useRuntimeStore } from '../stores/runtime';
|
||||
import { socketSendJson } from '../utils/socket';
|
||||
|
||||
export const useRundownEditor = () => {
|
||||
const featureSelector = (state: RuntimeStore) => ({
|
||||
selectedEventId: state.loaded.selectedEventId,
|
||||
});
|
||||
|
||||
return useRuntimeStore(featureSelector, deepCompare);
|
||||
};
|
||||
|
||||
export const useOperator = () => {
|
||||
const featureSelector = (state: RuntimeStore) => ({
|
||||
playback: state.playback,
|
||||
selectedEventId: state.loaded.selectedEventId,
|
||||
nextEventId: state.loaded.nextEventId,
|
||||
});
|
||||
|
||||
return useRuntimeStore(featureSelector, deepCompare);
|
||||
|
||||
Reference in New Issue
Block a user