turn annotation settings into a popover

This commit is contained in:
Josh Hawkins 2025-11-05 20:49:28 -06:00
parent 02c5b9a4f5
commit 78e278d5e8
3 changed files with 59 additions and 37 deletions

View File

@ -140,7 +140,7 @@ export function AnnotationSettingsPane({
}
return (
<div className="mb-3 space-y-3 rounded-lg border border-secondary-foreground bg-background_alt p-2">
<div className="space-y-3 p-4">
<Heading as="h4" className="my-2">
{t("trackingDetails.annotationSettings.title")}
</Heading>

View File

@ -32,6 +32,7 @@ import {
FaChevronRight,
} from "react-icons/fa";
import { TrackingDetails } from "./TrackingDetails";
import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
import { LuSettings } from "react-icons/lu";
import { DetailStreamProvider } from "@/context/detail-stream-context";
import {
@ -74,6 +75,7 @@ import { useIsAdmin } from "@/hooks/use-is-admin";
import { getTranslatedLabel } from "@/utils/i18n";
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
import { DialogPortal } from "@radix-ui/react-dialog";
import { useDetailStream } from "@/context/detail-stream-context";
const SEARCH_TABS = ["snapshot", "tracking_details"] as const;
export type SearchTab = (typeof SEARCH_TABS)[number];
@ -147,17 +149,46 @@ function TabsWithActions({
setSearch={setSearch}
setSimilarity={setSimilarity}
/>
{pageToggle === "tracking_details" && (
<AnnotationSettingsPopover
search={search}
showControls={showControls}
setShowControls={setShowControls}
/>
)}
</div>
);
}
type AnnotationSettingsPopoverProps = {
search: SearchResult;
showControls?: boolean;
setShowControls?: (v: boolean) => void;
};
function AnnotationSettingsPopover({
search,
showControls,
setShowControls,
}: AnnotationSettingsPopoverProps) {
const { t } = useTranslation(["views/explore"]);
const { annotationOffset, setAnnotationOffset } = useDetailStream();
const [showZones, setShowZones] = useState(true);
return (
<div className="ml-2">
<Popover modal={true} open={showControls} onOpenChange={setShowControls}>
<Tooltip>
<TooltipTrigger asChild>
<PopoverTrigger asChild>
<Button
variant={showControls ? "select" : "default"}
className="size-7 p-1.5"
aria-label={t("trackingDetails.adjustAnnotationSettings")}
onClick={() => setShowControls?.(!showControls)}
>
<LuSettings className="size-5" />
</Button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>
@ -165,7 +196,23 @@ function TabsWithActions({
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
<PopoverContent className="w-[90vw] max-w-2xl p-0" align="end">
<AnnotationSettingsPane
event={search as unknown as Event}
showZones={showZones}
setShowZones={setShowZones}
annotationOffset={annotationOffset}
setAnnotationOffset={(value) => {
if (typeof value === "function") {
const newValue = value(annotationOffset);
setAnnotationOffset(newValue);
} else {
setAnnotationOffset(value);
}
}}
/>
</PopoverContent>
</Popover>
</div>
);
}
@ -221,8 +268,6 @@ function DialogContentComponent({
/>
) : undefined
}
showControls={showControls}
setShowControls={setShowControls}
/>
);
}

View File

@ -8,8 +8,6 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { getIconForLabel } from "@/utils/iconUtil";
import { LuCircle, LuFolderX } from "react-icons/lu";
import { cn } from "@/lib/utils";
import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
import HlsVideoPlayer from "@/components/player/HlsVideoPlayer";
import { baseUrl } from "@/api/baseUrl";
import { REVIEW_PADDING } from "@/types/review";
@ -40,15 +38,12 @@ type TrackingDetailsProps = {
event: Event;
fullscreen?: boolean;
tabs?: React.ReactNode;
showControls?: boolean;
setShowControls?: (v: boolean) => void;
};
export function TrackingDetails({
className,
event,
tabs,
showControls = false,
}: TrackingDetailsProps) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const { t } = useTranslation(["views/explore"]);
@ -58,8 +53,7 @@ export function TrackingDetails({
const [displaySource, _setDisplaySource] = useState<"video" | "image">(
"video",
);
const { setSelectedObjectIds, annotationOffset, setAnnotationOffset } =
useDetailStream();
const { setSelectedObjectIds, annotationOffset } = useDetailStream();
// manualOverride holds a record-stream timestamp explicitly chosen by the
// user (eg, clicking a lifecycle row). When null we display `currentTime`.
@ -90,7 +84,6 @@ export function TrackingDetails({
const containerRef = useRef<HTMLDivElement | null>(null);
const [_selectedZone, setSelectedZone] = useState("");
const [_lifecycleZones, setLifecycleZones] = useState<string[]>([]);
const [showZones, setShowZones] = useState(true);
const [seekToTimestamp, setSeekToTimestamp] = useState<number | null>(null);
const aspectRatio = useMemo(() => {
@ -464,22 +457,6 @@ export function TrackingDetails({
{t("trackingDetails.autoTrackingTips")}
</div>
)}
{showControls && (
<AnnotationSettingsPane
event={event}
showZones={showZones}
setShowZones={setShowZones}
annotationOffset={annotationOffset}
setAnnotationOffset={(value) => {
if (typeof value === "function") {
const newValue = value(annotationOffset);
setAnnotationOffset(newValue);
} else {
setAnnotationOffset(value);
}
}}
/>
)}
<div className="mt-4">
<div