make object lifecycle scrollable in tracking details

This commit is contained in:
Josh Hawkins 2025-12-20 07:51:29 -06:00
parent 081e47f6d7
commit 73cd48262f
2 changed files with 102 additions and 101 deletions

View File

@ -599,9 +599,14 @@ export default function SearchDetailDialog({
<Content
ref={isDesktop ? dialogContentRef : undefined}
className={cn(
"scrollbar-container overflow-y-auto",
isDesktop && "max-h-[95dvh] max-w-[85%] xl:max-w-[70%]",
isMobile && "flex h-full flex-col px-4",
isDesktop && [
"max-h-[95dvh] max-w-[85%] xl:max-w-[70%]",
pageToggle === "tracking_details"
? "flex flex-col overflow-hidden"
: "scrollbar-container overflow-y-auto",
],
isMobile &&
"scrollbar-container flex h-full flex-col overflow-y-auto px-4",
)}
onEscapeKeyDown={(event) => {
if (isPopoverOpen) {

View File

@ -526,7 +526,7 @@ export function TrackingDetails({
<div
className={cn(
"flex items-center justify-center",
"flex items-start justify-center",
isDesktop && "overflow-hidden",
cameraAspect === "tall" ? "max-h-[50dvh] lg:max-h-[70dvh]" : "w-full",
cameraAspect === "tall" && isMobileOnly && "w-full",
@ -622,7 +622,10 @@ export function TrackingDetails({
<div
className={cn(
isDesktop && "justify-between overflow-hidden lg:basis-2/5",
isDesktop && "justify-start overflow-hidden",
aspectRatio > 1 && aspectRatio < 1.5
? "lg:basis-3/5"
: "lg:basis-2/5",
)}
>
{isDesktop && tabs && (
@ -632,121 +635,114 @@ export function TrackingDetails({
)}
<div
className={cn(
isDesktop && "scrollbar-container h-full overflow-y-auto",
isDesktop && "scrollbar-container max-h-[70vh] overflow-y-auto",
)}
>
{config?.cameras[event.camera]?.onvif.autotracking
.enabled_in_config && (
<div className="mb-2 ml-3 text-sm text-danger">
<div className="mb-4 ml-3 text-sm text-danger">
{t("trackingDetails.autoTrackingTips")}
</div>
)}
<div className="mt-4">
<div
className={cn("rounded-md bg-background_alt px-0 py-3 md:px-2")}
>
<div className="flex w-full items-center justify-between">
<div className={cn("rounded-md bg-background_alt px-0 py-3 md:px-2")}>
<div className="flex w-full items-center justify-between">
<div
className="flex items-center gap-2 font-medium"
onClick={(e) => {
e.stopPropagation();
// event.start_time is detect time, convert to record
handleSeekToTime(
(event.start_time ?? 0) + annotationOffset / 1000,
);
}}
role="button"
>
<div
className="flex items-center gap-2 font-medium"
onClick={(e) => {
e.stopPropagation();
// event.start_time is detect time, convert to record
handleSeekToTime(
(event.start_time ?? 0) + annotationOffset / 1000,
);
}}
role="button"
className={cn(
"relative ml-2 rounded-full bg-muted-foreground p-2",
)}
>
<div
className={cn(
"relative ml-2 rounded-full bg-muted-foreground p-2",
)}
>
{getIconForLabel(
event.sub_label ? event.label + "-verified" : event.label,
"size-4 text-white",
)}
</div>
<div className="flex items-center gap-2">
<span className="capitalize">{label}</span>
<div className="md:text-md flex items-center text-xs text-secondary-foreground">
{formattedStart ?? ""}
{event.end_time != null ? (
<> - {formattedEnd}</>
) : (
<div className="inline-block">
<ActivityIndicator className="ml-3 size-4" />
</div>
)}
</div>
{event.data?.recognized_license_plate && (
<>
<span className="text-secondary-foreground">·</span>
<div className="text-sm text-secondary-foreground">
<Link
to={`/explore?recognized_license_plate=${event.data.recognized_license_plate}`}
className="text-sm"
>
{event.data.recognized_license_plate}
</Link>
</div>
</>
{getIconForLabel(
event.sub_label ? event.label + "-verified" : event.label,
"size-4 text-white",
)}
</div>
<div className="flex items-center gap-2">
<span className="capitalize">{label}</span>
<div className="md:text-md flex items-center text-xs text-secondary-foreground">
{formattedStart ?? ""}
{event.end_time != null ? (
<> - {formattedEnd}</>
) : (
<div className="inline-block">
<ActivityIndicator className="ml-3 size-4" />
</div>
)}
</div>
{event.data?.recognized_license_plate && (
<>
<span className="text-secondary-foreground">·</span>
<div className="text-sm text-secondary-foreground">
<Link
to={`/explore?recognized_license_plate=${event.data.recognized_license_plate}`}
className="text-sm"
>
{event.data.recognized_license_plate}
</Link>
</div>
</>
)}
</div>
</div>
</div>
<div className="mt-2">
{!eventSequence ? (
<ActivityIndicator className="size-2" size={2} />
) : eventSequence.length === 0 ? (
<div className="py-2 text-muted-foreground">
{t("detail.noObjectDetailData", { ns: "views/events" })}
</div>
) : (
<div className="mt-2">
{!eventSequence ? (
<ActivityIndicator className="size-2" size={2} />
) : eventSequence.length === 0 ? (
<div className="py-2 text-muted-foreground">
{t("detail.noObjectDetailData", { ns: "views/events" })}
</div>
) : (
<div className="-pb-2 relative mx-0" ref={timelineContainerRef}>
<div
className="-pb-2 relative mx-0"
ref={timelineContainerRef}
>
className="absolute -top-2 left-6 z-0 w-0.5 -translate-x-1/2 bg-secondary-foreground"
style={{ bottom: lineBottomOffsetPx }}
/>
{isWithinEventRange && (
<div
className="absolute -top-2 left-6 z-0 w-0.5 -translate-x-1/2 bg-secondary-foreground"
style={{ bottom: lineBottomOffsetPx }}
className="absolute left-6 z-[5] w-0.5 -translate-x-1/2 bg-selected transition-all duration-300"
style={{
top: `${lineTopOffsetPx}px`,
height: `${blueLineHeightPx}px`,
}}
/>
{isWithinEventRange && (
<div
className="absolute left-6 z-[5] w-0.5 -translate-x-1/2 bg-selected transition-all duration-300"
style={{
top: `${lineTopOffsetPx}px`,
height: `${blueLineHeightPx}px`,
}}
/>
)}
<div className="space-y-2">
{eventSequence.map((item, idx) => {
return (
<div
key={`${item.timestamp}-${item.source_id ?? ""}-${idx}`}
ref={(el) => {
rowRefs.current[idx] = el;
}}
>
<LifecycleIconRow
item={item}
event={event}
onClick={() => handleLifecycleClick(item)}
setSelectedZone={setSelectedZone}
getZoneColor={getZoneColor}
effectiveTime={effectiveTime}
isTimelineActive={isWithinEventRange}
/>
</div>
);
})}
</div>
)}
<div className="space-y-2">
{eventSequence.map((item, idx) => {
return (
<div
key={`${item.timestamp}-${item.source_id ?? ""}-${idx}`}
ref={(el) => {
rowRefs.current[idx] = el;
}}
>
<LifecycleIconRow
item={item}
event={event}
onClick={() => handleLifecycleClick(item)}
setSelectedZone={setSelectedZone}
getZoneColor={getZoneColor}
effectiveTime={effectiveTime}
isTimelineActive={isWithinEventRange}
/>
</div>
);
})}
</div>
)}
</div>
</div>
)}
</div>
</div>
</div>