mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 20:03:52 +00:00
feat(views): keep group visible as header for the running event
In the countdown view a user can subscribe to a group and to events inside it. Once an event within the group started, the auto scroll pushed the group card above the viewport, losing the context of which group the running event belongs to. The operator view had the same problem. Group and its events are now wrapped in a section, and the group card is pinned with position sticky, so it stays above the running event however far down the group that event sits. The follow scroll offsets the sticky header, and the follow button threshold discounts it so it keeps its meaning. A group holding the running event is now marked as active. In the countdown the card keeps its background and gains a green outline, the green fill stays reserved for the running event itself. In the operator the card is already filled with the group colour, so the ring uses the lighter active indicator over a dark frame to stay legible against any group colour. Neither state was visible before: .sub--group overrode both .sub--live and .sub--armed at equal specificity. Also fixes the countdown handing the same selectedRef to both a group row and its running child. React detaches refs before attaching them, so the child unsetting the ref left it null while the group never re-attached, silently disabling auto scroll. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GjX7D56AW8cL3FXjvtnjtL
This commit is contained in:
@@ -22,6 +22,11 @@ interface UseFollowComponentProps {
|
||||
scrollRef: RefObject<HTMLElement | null>;
|
||||
doFollow: boolean;
|
||||
topOffset?: number;
|
||||
/**
|
||||
* Resolved at scroll time, for offsets which can only be measured from the DOM (eg. a sticky header).
|
||||
* Takes precedence over topOffset. Keep the reference stable, it is a dependency of the follow effect.
|
||||
*/
|
||||
getTopOffset?: () => number;
|
||||
setScrollFlag?: (newValue: boolean) => void;
|
||||
followTrigger?: MaybeString; // this would be an entry id or null
|
||||
}
|
||||
@@ -31,6 +36,7 @@ export default function useFollowComponent({
|
||||
scrollRef,
|
||||
doFollow,
|
||||
topOffset = 100,
|
||||
getTopOffset,
|
||||
setScrollFlag,
|
||||
followTrigger,
|
||||
}: UseFollowComponentProps) {
|
||||
@@ -44,21 +50,23 @@ export default function useFollowComponent({
|
||||
setScrollFlag?.(true);
|
||||
// Use requestAnimationFrame to ensure the component is fully loaded
|
||||
window.requestAnimationFrame(() => {
|
||||
scrollToComponent(followRef as RefObject<HTMLElement>, scrollRef as RefObject<HTMLElement>, topOffset);
|
||||
// resolve the offset after layout, so that measured values are up to date
|
||||
const offset = getTopOffset?.() ?? topOffset;
|
||||
scrollToComponent(followRef as RefObject<HTMLElement>, scrollRef as RefObject<HTMLElement>, offset);
|
||||
setScrollFlag?.(false);
|
||||
});
|
||||
}
|
||||
}, [followTrigger, doFollow, followRef, scrollRef, setScrollFlag, topOffset]);
|
||||
}, [followTrigger, doFollow, followRef, scrollRef, setScrollFlag, topOffset, getTopOffset]);
|
||||
|
||||
const scrollToRefComponent = useCallback(
|
||||
(componentRef = followRef, containerRef = scrollRef, offset = topOffset) => {
|
||||
(componentRef = followRef, containerRef = scrollRef, offset?: number) => {
|
||||
if (componentRef && containerRef) {
|
||||
// @ts-expect-error -- we know this are not null
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
scrollToComponent(componentRef!, containerRef!, offset);
|
||||
scrollToComponent(componentRef!, containerRef!, offset ?? getTopOffset?.() ?? topOffset);
|
||||
}
|
||||
},
|
||||
[followRef, scrollRef, topOffset],
|
||||
[followRef, scrollRef, topOffset, getTopOffset],
|
||||
);
|
||||
|
||||
return scrollToRefComponent;
|
||||
|
||||
Reference in New Issue
Block a user