1 ? "hidden" : ""} zoom-in-[0.2] ${secondHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
+ className={`${isDesktop && animationClassesSecondHalf} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
style={{
width: secondHalfSegmentWidth,
}}
@@ -216,7 +221,7 @@ export function MotionSegment({
1 ? "hidden" : ""} zoom-in-[0.2] ${firstHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
+ className={`${isDesktop && animationClassesFirstHalf} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
style={{
width: firstHalfSegmentWidth,
}}
diff --git a/web/src/components/timeline/ReviewTimeline.tsx b/web/src/components/timeline/ReviewTimeline.tsx
index b0c1bc16e..8d6b59471 100644
--- a/web/src/components/timeline/ReviewTimeline.tsx
+++ b/web/src/components/timeline/ReviewTimeline.tsx
@@ -14,16 +14,8 @@ export type ReviewTimelineProps = {
timelineRef: RefObject
;
handlebarRef: RefObject;
handlebarTimeRef: RefObject;
- handlebarMouseMove: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
- handlebarMouseUp: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
+ handlebarMouseMove: (e: MouseEvent | TouchEvent) => void;
+ handlebarMouseUp: (e: MouseEvent | TouchEvent) => void;
handlebarMouseDown: (
e:
| React.MouseEvent
@@ -37,31 +29,15 @@ export type ReviewTimelineProps = {
exportStartTimeRef: RefObject;
exportEndRef: RefObject;
exportEndTimeRef: RefObject;
- exportStartMouseMove: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
- exportStartMouseUp: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
+ exportStartMouseMove: (e: MouseEvent | TouchEvent) => void;
+ exportStartMouseUp: (e: MouseEvent | TouchEvent) => void;
exportStartMouseDown: (
e:
| React.MouseEvent
| React.TouchEvent,
) => void;
- exportEndMouseMove: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
- exportEndMouseUp: (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => void;
+ exportEndMouseMove: (e: MouseEvent | TouchEvent) => void;
+ exportEndMouseUp: (e: MouseEvent | TouchEvent) => void;
exportEndMouseDown: (
e:
| React.MouseEvent
@@ -152,11 +128,7 @@ export function ReviewTimeline({
);
const handleMouseMove = useCallback(
- (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => {
+ (e: MouseEvent | TouchEvent) => {
switch (draggableElementType) {
case "export_start":
exportStartMouseMove(e);
@@ -181,11 +153,7 @@ export function ReviewTimeline({
);
const handleMouseUp = useCallback(
- (
- e:
- | React.MouseEvent
- | React.TouchEvent,
- ) => {
+ (e: MouseEvent | TouchEvent) => {
switch (draggableElementType) {
case "export_start":
exportStartMouseUp(e);
@@ -227,13 +195,32 @@ export function ReviewTimeline({
exportEndPosition,
]);
+ const documentRef = useRef(document);
+ useEffect(() => {
+ const documentInstance = documentRef.current;
+
+ if (isDragging) {
+ documentInstance?.addEventListener("mousemove", handleMouseMove);
+ documentInstance?.addEventListener("touchmove", handleMouseMove);
+ documentInstance?.addEventListener("mouseup", handleMouseUp);
+ documentInstance?.addEventListener("touchend", handleMouseUp);
+ } else {
+ documentInstance?.removeEventListener("mousemove", handleMouseMove);
+ documentInstance?.removeEventListener("touchmove", handleMouseMove);
+ documentInstance?.removeEventListener("mouseup", handleMouseUp);
+ documentInstance?.removeEventListener("touchend", handleMouseUp);
+ }
+ return () => {
+ documentInstance?.removeEventListener("mousemove", handleMouseMove);
+ documentInstance?.removeEventListener("touchmove", handleMouseMove);
+ documentInstance?.removeEventListener("mouseup", handleMouseUp);
+ documentInstance?.removeEventListener("touchend", handleMouseUp);
+ };
+ }, [handleMouseMove, handleMouseUp, isDragging]);
+
return (
- {severity.map((severityValue: number, index: number) => {
- return (
-
+ {severity.map((severityValue: number, index: number) => (
+
+ {severityValue === displaySeverityType && (
-
- );
- })}
+ )}
+
+ ))}
);
}
diff --git a/web/src/components/timeline/SummaryTimeline.tsx b/web/src/components/timeline/SummaryTimeline.tsx
index c2a5fa6d6..8b89b88a8 100644
--- a/web/src/components/timeline/SummaryTimeline.tsx
+++ b/web/src/components/timeline/SummaryTimeline.tsx
@@ -8,7 +8,7 @@ import {
} from "react";
import { SummarySegment } from "./SummarySegment";
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
-import { ReviewSegment } from "@/types/review";
+import { ReviewSegment, ReviewSeverity } from "@/types/review";
import { isMobile } from "react-device-detect";
export type SummaryTimelineProps = {
@@ -17,6 +17,7 @@ export type SummaryTimelineProps = {
timelineEnd: number;
segmentDuration: number;
events: ReviewSegment[];
+ severityType: ReviewSeverity;
};
export function SummaryTimeline({
@@ -25,6 +26,7 @@ export function SummaryTimeline({
timelineEnd,
segmentDuration,
events,
+ severityType,
}: SummaryTimelineProps) {
const summaryTimelineRef = useRef
(null);
const visibleSectionRef = useRef(null);
@@ -35,6 +37,8 @@ export function SummaryTimeline({
const [initialReviewTimelineScrollTop, setInitialReviewTimelineScrollTop] =
useState(0);
+ const observer = useRef(null);
+
const { alignStartDateToTimeline } = useTimelineUtils(segmentDuration);
const timelineStartAligned = useMemo(
@@ -62,6 +66,7 @@ export function SummaryTimeline({
segmentDuration={segmentDuration}
segmentTime={segmentTime}
segmentHeight={segmentHeight}
+ severityType={severityType}
/>
);
});
@@ -72,6 +77,7 @@ export function SummaryTimeline({
events,
reviewTimelineDuration,
segmentHeight,
+ severityType,
]);
const segments = useMemo(
@@ -86,34 +92,73 @@ export function SummaryTimeline({
reviewTimelineDuration,
segmentHeight,
generateSegments,
+ severityType,
],
);
+ const setVisibleSectionStyles = useCallback(() => {
+ if (
+ reviewTimelineRef.current &&
+ summaryTimelineRef.current &&
+ visibleSectionRef.current
+ ) {
+ const content = reviewTimelineRef.current;
+ const summary = summaryTimelineRef.current;
+ const {
+ clientHeight: reviewTimelineVisibleHeight,
+ scrollHeight: reviewTimelineFullHeight,
+ scrollTop: scrolled,
+ } = content;
+ const { clientHeight: summaryTimelineVisibleHeight } = summary;
+
+ visibleSectionRef.current.style.top = `${
+ summaryTimelineVisibleHeight * (scrolled / reviewTimelineFullHeight)
+ }px`;
+ visibleSectionRef.current.style.height = `${
+ reviewTimelineVisibleHeight *
+ (reviewTimelineVisibleHeight / reviewTimelineFullHeight)
+ }px`;
+ }
+ }, [reviewTimelineRef, summaryTimelineRef, visibleSectionRef]);
+
useEffect(() => {
if (reviewTimelineRef.current && summaryTimelineRef.current) {
const content = reviewTimelineRef.current;
- const summary = summaryTimelineRef.current;
const handleScroll = () => {
- const {
- clientHeight: reviewTimelineVisibleHeight,
- scrollHeight: reviewTimelineFullHeight,
- scrollTop: scrolled,
- } = content;
- const { clientHeight: summaryTimelineVisibleHeight } = summary;
-
- if (visibleSectionRef.current) {
- visibleSectionRef.current.style.top = `${summaryTimelineVisibleHeight * (scrolled / reviewTimelineFullHeight)}px`;
- visibleSectionRef.current.style.height = `${reviewTimelineVisibleHeight * (reviewTimelineVisibleHeight / reviewTimelineFullHeight)}px`;
- }
+ setVisibleSectionStyles();
};
+ // Set initial styles
+ setVisibleSectionStyles();
+
+ observer.current = new ResizeObserver(() => {
+ setVisibleSectionStyles();
+ if (summaryTimelineRef.current) {
+ const { clientHeight: summaryTimelineVisibleHeight } =
+ summaryTimelineRef.current;
+
+ setSegmentHeight(
+ summaryTimelineVisibleHeight /
+ (reviewTimelineDuration / segmentDuration),
+ );
+ }
+ });
+ observer.current.observe(content);
+
content.addEventListener("scroll", handleScroll);
+
return () => {
content.removeEventListener("scroll", handleScroll);
};
}
- }, [reviewTimelineRef, summaryTimelineRef]);
+ }, [
+ reviewTimelineRef,
+ summaryTimelineRef,
+ setVisibleSectionStyles,
+ reviewTimelineDuration,
+ segmentDuration,
+ ]);
useEffect(() => {
if (summaryTimelineRef.current) {
diff --git a/web/src/hooks/use-draggable-element.ts b/web/src/hooks/use-draggable-element.ts
index 1bed502c6..fd20ebf85 100644
--- a/web/src/hooks/use-draggable-element.ts
+++ b/web/src/hooks/use-draggable-element.ts
@@ -63,14 +63,12 @@ function useDraggableElement({
}, [clientYPosition, timelineRef, isDragging]);
const getClientYPosition = useCallback(
- (
- e: React.MouseEvent | React.TouchEvent,
- ) => {
+ (e: MouseEvent | TouchEvent) => {
let clientY;
- if (isMobile && e.nativeEvent instanceof TouchEvent) {
- clientY = e.nativeEvent.touches[0].clientY;
- } else if (e.nativeEvent instanceof MouseEvent) {
- clientY = e.nativeEvent.clientY;
+ if (isMobile && e instanceof TouchEvent) {
+ clientY = e.touches[0].clientY;
+ } else if (e instanceof MouseEvent) {
+ clientY = e.clientY;
}
if (clientY) {
@@ -113,9 +111,7 @@ function useDraggableElement({
);
const handleMouseUp = useCallback(
- (
- e: React.MouseEvent | React.TouchEvent,
- ) => {
+ (e: MouseEvent | TouchEvent) => {
e.preventDefault();
e.stopPropagation();
if (isDragging) {
@@ -187,9 +183,7 @@ function useDraggableElement({
);
const handleMouseMove = useCallback(
- (
- e: React.MouseEvent | React.TouchEvent,
- ) => {
+ (e: MouseEvent | TouchEvent) => {
if (
!contentRef.current ||
!timelineRef.current ||
diff --git a/web/src/pages/UIPlayground.tsx b/web/src/pages/UIPlayground.tsx
index c344e0d72..4f44564ad 100644
--- a/web/src/pages/UIPlayground.tsx
+++ b/web/src/pages/UIPlayground.tsx
@@ -405,18 +405,19 @@ function UIPlayground() {
events={mockEvents} // events, including new has_been_reviewed and severity properties
severityType={"alert"} // choose the severity type for the middle line - all other severity types are to the right
contentRef={contentRef} // optional content ref where previews are, can be used for observing/scrolling later
- timelineRef={reviewTimelineRef}
+ timelineRef={reviewTimelineRef} // save a ref to this timeline to connect with the summary timeline
/>
)}
{isEventsReviewTimeline && (
)}
diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx
index c3abcb2a9..d1a372af9 100644
--- a/web/src/views/events/EventView.tsx
+++ b/web/src/views/events/EventView.tsx
@@ -555,6 +555,7 @@ function DetectionReview({
timelineEnd={timeRange.after}
segmentDuration={segmentDuration}
events={reviewItems?.all ?? []}
+ severityType={severity}
/>