when minimap is hidden, only scroll timeline when needed

This commit is contained in:
Josh Hawkins 2024-03-21 11:00:15 -05:00
parent ca85c317e8
commit 191ebfe930
3 changed files with 70 additions and 6 deletions

View File

@ -11,6 +11,7 @@ import EventSegment from "./EventSegment";
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
import { ReviewSegment, ReviewSeverity } from "@/types/review";
import ReviewTimeline from "./ReviewTimeline";
import scrollIntoView from "scroll-into-view-if-needed";
export type EventReviewTimelineProps = {
segmentDuration: number;
@ -60,6 +61,7 @@ export function EventReviewTimeline({
const [isDragging, setIsDragging] = useState(false);
const [exportStartPosition, setExportStartPosition] = useState(0);
const [exportEndPosition, setExportEndPosition] = useState(0);
const [visibleThumbnails, setVisibleThumbnails] = useState<number[]>([]);
const internalTimelineRef = useRef<HTMLDivElement>(null);
const handlebarRef = useRef<HTMLDivElement>(null);
@ -68,6 +70,9 @@ export function EventReviewTimeline({
const exportStartTimeRef = useRef<HTMLDivElement>(null);
const exportEndRef = useRef<HTMLDivElement>(null);
const exportEndTimeRef = useRef<HTMLDivElement>(null);
const selectedTimelineRef = timelineRef || internalTimelineRef;
const observer = useRef<IntersectionObserver | null>(null);
const timelineDuration = useMemo(
() => timelineStart - timelineEnd,
@ -77,7 +82,7 @@ export function EventReviewTimeline({
const { alignStartDateToTimeline, alignEndDateToTimeline } = useTimelineUtils(
segmentDuration,
timelineDuration,
timelineRef || internalTimelineRef,
selectedTimelineRef,
);
const timelineStartAligned = useMemo(
@ -103,7 +108,7 @@ export function EventReviewTimeline({
handleMouseMove: handlebarMouseMove,
} = useDraggableElement({
contentRef,
timelineRef: timelineRef || internalTimelineRef,
timelineRef: selectedTimelineRef,
draggableElementRef: handlebarRef,
segmentDuration,
showDraggableElement: showHandlebar,
@ -122,7 +127,7 @@ export function EventReviewTimeline({
handleMouseMove: exportStartMouseMove,
} = useDraggableElement({
contentRef,
timelineRef: timelineRef || internalTimelineRef,
timelineRef: selectedTimelineRef,
draggableElementRef: exportStartRef,
segmentDuration,
showDraggableElement: showExportHandles,
@ -143,7 +148,7 @@ export function EventReviewTimeline({
handleMouseMove: exportEndMouseMove,
} = useDraggableElement({
contentRef,
timelineRef: timelineRef || internalTimelineRef,
timelineRef: selectedTimelineRef,
draggableElementRef: exportEndRef,
segmentDuration,
showDraggableElement: showExportHandles,
@ -216,9 +221,67 @@ export function EventReviewTimeline({
}
}, [isDragging, onHandlebarDraggingChange]);
useEffect(() => {
if (contentRef.current && segments) {
observer.current = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const segmentStartId =
entry.target.getAttribute("data-segment-start");
if (segmentStartId) {
setVisibleThumbnails((prevState) => [
...prevState,
parseInt(segmentStartId),
]);
}
} else {
const segmentStartId =
entry.target.getAttribute("data-segment-start");
if (segmentStartId) {
setVisibleThumbnails((prevState) =>
prevState.filter(
(timestamp) => timestamp !== parseInt(segmentStartId),
),
);
}
}
});
},
{ threshold: 0.5 },
);
const reviewItems = contentRef.current.querySelectorAll(".review-item");
reviewItems.forEach((item) => {
observer.current?.observe(item);
});
}
return () => {
observer.current?.disconnect();
};
}, [contentRef, segments]);
useEffect(() => {
if (
selectedTimelineRef.current &&
segments &&
visibleThumbnails?.length > 0 &&
!showMinimap
) {
const element = selectedTimelineRef.current?.querySelector(
`[data-segment-id="${Math.max(...visibleThumbnails)}"]`,
);
scrollIntoView(element as HTMLDivElement, {
scrollMode: "if-needed",
behavior: "smooth",
});
}
}, [visibleThumbnails, selectedTimelineRef, segments, showMinimap]);
return (
<ReviewTimeline
timelineRef={timelineRef || internalTimelineRef}
timelineRef={selectedTimelineRef}
handlebarRef={handlebarRef}
handlebarTimeRef={handlebarTimeRef}
handlebarMouseMove={handlebarMouseMove}

View File

@ -201,6 +201,7 @@ export function EventSegment({
return (
<div
key={segmentKey}
data-segment-id={segmentKey}
className={segmentClasses}
onClick={segmentClick}
onTouchEnd={(event) => handleTouchStart(event, segmentClick)}

View File

@ -508,7 +508,7 @@ function DetectionReview({
data-segment-start={
alignStartDateToTimeline(value.start_time) - segmentDuration
}
className={`outline outline-offset-1 rounded-lg shadow-none transition-all my-1 md:my-0 ${selected ? `outline-4 shadow-[0_0_6px_1px] outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-0 duration-500"}`}
className={`review-item outline outline-offset-1 rounded-lg shadow-none transition-all my-1 md:my-0 ${selected ? `outline-4 shadow-[0_0_6px_1px] outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-0 duration-500"}`}
>
<div className="aspect-video rounded-lg overflow-hidden">
<PreviewThumbnailPlayer