From 312dc951565011d7620520c0f6f9a105a82423e8 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 3 Mar 2024 10:32:35 -0600 Subject: [PATCH] More intelligent timeline scrolling (#10209) * more intelligent timeline scrolling * keep as div --- web/package-lock.json | 14 +++++++++++++ web/package.json | 1 + .../components/filter/ReviewFilterGroup.tsx | 4 ++-- web/src/components/timeline/EventSegment.tsx | 20 +++++++++---------- web/src/hooks/use-handle-dragging.ts | 10 +++++----- web/src/views/events/EventView.tsx | 6 +++--- web/src/views/live/LiveDashboardView.tsx | 2 +- 7 files changed, 35 insertions(+), 22 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index fa86be074..b0ac58397 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -52,6 +52,7 @@ "react-transition-group": "^4.4.5", "react-use-websocket": "^4.7.0", "recoil": "^0.7.7", + "scroll-into-view-if-needed": "^3.1.0", "sonner": "^1.4.0", "sort-by": "^1.2.0", "strftime": "^0.10.2", @@ -3761,6 +3762,11 @@ "node": ">= 6" } }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -7213,6 +7219,14 @@ "loose-envify": "^1.1.0" } }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", diff --git a/web/package.json b/web/package.json index 8b11e7799..6c8a698c6 100644 --- a/web/package.json +++ b/web/package.json @@ -57,6 +57,7 @@ "react-transition-group": "^4.4.5", "react-use-websocket": "^4.7.0", "recoil": "^0.7.7", + "scroll-into-view-if-needed": "^3.1.0", "sonner": "^1.4.0", "sort-by": "^1.2.0", "strftime": "^0.10.2", diff --git a/web/src/components/filter/ReviewFilterGroup.tsx b/web/src/components/filter/ReviewFilterGroup.tsx index bdce6bd58..6fb5c7d42 100644 --- a/web/src/components/filter/ReviewFilterGroup.tsx +++ b/web/src/components/filter/ReviewFilterGroup.tsx @@ -71,7 +71,7 @@ export default function ReviewFilterGroup({ ); return ( -
+
- diff --git a/web/src/components/timeline/EventSegment.tsx b/web/src/components/timeline/EventSegment.tsx index 21c3dcfb5..eed161804 100644 --- a/web/src/components/timeline/EventSegment.tsx +++ b/web/src/components/timeline/EventSegment.tsx @@ -16,6 +16,7 @@ import { HoverCardTrigger, } from "../ui/hover-card"; import { HoverCardPortal } from "@radix-ui/react-hover-card"; +import scrollIntoView from "scroll-into-view-if-needed"; type EventSegmentProps = { events: ReviewSegment[]; @@ -225,20 +226,14 @@ export function EventSegment({ const firstMinimapSegmentRef = useRef(null); - let debounceTimer: ReturnType; - - function debounceScrollIntoView(element: HTMLElement) { - clearTimeout(debounceTimer); - debounceTimer = setTimeout(() => { - element.scrollIntoView({ behavior: "smooth", block: "center" }); - }, 100); - } - useEffect(() => { // Check if the first segment is out of view const firstSegment = firstMinimapSegmentRef.current; if (firstSegment && showMinimap && isFirstSegmentInMinimap) { - debounceScrollIntoView(firstSegment); + scrollIntoView(firstSegment, { + scrollMode: "if-needed", + behavior: "smooth", + }); } // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps @@ -276,7 +271,10 @@ export function EventSegment({ `[data-segment-start="${startTimestamp - segmentDuration}"]`, ); if (element instanceof HTMLElement) { - debounceScrollIntoView(element); + scrollIntoView(element, { + scrollMode: "if-needed", + behavior: "smooth", + }); element.classList.add( `outline-severity_${severityType}`, `shadow-severity_${severityType}`, diff --git a/web/src/hooks/use-handle-dragging.ts b/web/src/hooks/use-handle-dragging.ts index b208526d0..cf2f2e0a0 100644 --- a/web/src/hooks/use-handle-dragging.ts +++ b/web/src/hooks/use-handle-dragging.ts @@ -1,4 +1,5 @@ import { useCallback, useEffect } from "react"; +import scrollIntoView from "scroll-into-view-if-needed"; type DragHandlerProps = { contentRef: React.RefObject; @@ -75,6 +76,10 @@ function useDraggableHandler({ minute: "2-digit", ...(segmentDuration < 60 && { second: "2-digit" }), }); + scrollIntoView(thumb, { + scrollMode: "if-needed", + behavior: "smooth", + }); } }); if (setHandlebarTime) { @@ -167,11 +172,6 @@ function useDraggableHandler({ scrolled; updateHandlebarPosition(newHandlePosition - segmentHeight, handlebarTime); - - scrollTimeRef.current.scrollIntoView({ - behavior: "smooth", - block: "center", - }); } // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index a2086063e..f35608c7d 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -247,12 +247,12 @@ export default function EventView({ return (
-
+
{isMobile && ( )} - There are no {severity} items to review + There are no {severity.replace(/_/g, " ")} items to review
)} diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx index 5aa5ca6de..b31d79100 100644 --- a/web/src/views/live/LiveDashboardView.tsx +++ b/web/src/views/live/LiveDashboardView.tsx @@ -120,7 +120,7 @@ export default function LiveDashboardView({ )}
{cameras.map((camera) => { let grow;