mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-07 22:05:44 +03:00
turn annotation settings into a popover
This commit is contained in:
parent
02c5b9a4f5
commit
78e278d5e8
@ -140,7 +140,7 @@ export function AnnotationSettingsPane({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<Heading as="h4" className="my-2">
|
||||||
{t("trackingDetails.annotationSettings.title")}
|
{t("trackingDetails.annotationSettings.title")}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import {
|
|||||||
FaChevronRight,
|
FaChevronRight,
|
||||||
} from "react-icons/fa";
|
} from "react-icons/fa";
|
||||||
import { TrackingDetails } from "./TrackingDetails";
|
import { TrackingDetails } from "./TrackingDetails";
|
||||||
|
import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
|
||||||
import { LuSettings } from "react-icons/lu";
|
import { LuSettings } from "react-icons/lu";
|
||||||
import { DetailStreamProvider } from "@/context/detail-stream-context";
|
import { DetailStreamProvider } from "@/context/detail-stream-context";
|
||||||
import {
|
import {
|
||||||
@ -74,6 +75,7 @@ import { useIsAdmin } from "@/hooks/use-is-admin";
|
|||||||
import { getTranslatedLabel } from "@/utils/i18n";
|
import { getTranslatedLabel } from "@/utils/i18n";
|
||||||
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
||||||
import { DialogPortal } from "@radix-ui/react-dialog";
|
import { DialogPortal } from "@radix-ui/react-dialog";
|
||||||
|
import { useDetailStream } from "@/context/detail-stream-context";
|
||||||
|
|
||||||
const SEARCH_TABS = ["snapshot", "tracking_details"] as const;
|
const SEARCH_TABS = ["snapshot", "tracking_details"] as const;
|
||||||
export type SearchTab = (typeof SEARCH_TABS)[number];
|
export type SearchTab = (typeof SEARCH_TABS)[number];
|
||||||
@ -147,17 +149,46 @@ function TabsWithActions({
|
|||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setSimilarity={setSimilarity}
|
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">
|
<div className="ml-2">
|
||||||
|
<Popover modal={true} open={showControls} onOpenChange={setShowControls}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant={showControls ? "select" : "default"}
|
variant={showControls ? "select" : "default"}
|
||||||
className="size-7 p-1.5"
|
className="size-7 p-1.5"
|
||||||
aria-label={t("trackingDetails.adjustAnnotationSettings")}
|
aria-label={t("trackingDetails.adjustAnnotationSettings")}
|
||||||
onClick={() => setShowControls?.(!showControls)}
|
|
||||||
>
|
>
|
||||||
<LuSettings className="size-5" />
|
<LuSettings className="size-5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipPortal>
|
<TooltipPortal>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@ -165,7 +196,23 @@ function TabsWithActions({
|
|||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</TooltipPortal>
|
</TooltipPortal>
|
||||||
</Tooltip>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -221,8 +268,6 @@ function DialogContentComponent({
|
|||||||
/>
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
showControls={showControls}
|
|
||||||
setShowControls={setShowControls}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
|||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
import { LuCircle, LuFolderX } from "react-icons/lu";
|
import { LuCircle, LuFolderX } from "react-icons/lu";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
|
|
||||||
|
|
||||||
import HlsVideoPlayer from "@/components/player/HlsVideoPlayer";
|
import HlsVideoPlayer from "@/components/player/HlsVideoPlayer";
|
||||||
import { baseUrl } from "@/api/baseUrl";
|
import { baseUrl } from "@/api/baseUrl";
|
||||||
import { REVIEW_PADDING } from "@/types/review";
|
import { REVIEW_PADDING } from "@/types/review";
|
||||||
@ -40,15 +38,12 @@ type TrackingDetailsProps = {
|
|||||||
event: Event;
|
event: Event;
|
||||||
fullscreen?: boolean;
|
fullscreen?: boolean;
|
||||||
tabs?: React.ReactNode;
|
tabs?: React.ReactNode;
|
||||||
showControls?: boolean;
|
|
||||||
setShowControls?: (v: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TrackingDetails({
|
export function TrackingDetails({
|
||||||
className,
|
className,
|
||||||
event,
|
event,
|
||||||
tabs,
|
tabs,
|
||||||
showControls = false,
|
|
||||||
}: TrackingDetailsProps) {
|
}: TrackingDetailsProps) {
|
||||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
const { t } = useTranslation(["views/explore"]);
|
const { t } = useTranslation(["views/explore"]);
|
||||||
@ -58,8 +53,7 @@ export function TrackingDetails({
|
|||||||
const [displaySource, _setDisplaySource] = useState<"video" | "image">(
|
const [displaySource, _setDisplaySource] = useState<"video" | "image">(
|
||||||
"video",
|
"video",
|
||||||
);
|
);
|
||||||
const { setSelectedObjectIds, annotationOffset, setAnnotationOffset } =
|
const { setSelectedObjectIds, annotationOffset } = useDetailStream();
|
||||||
useDetailStream();
|
|
||||||
|
|
||||||
// manualOverride holds a record-stream timestamp explicitly chosen by the
|
// manualOverride holds a record-stream timestamp explicitly chosen by the
|
||||||
// user (eg, clicking a lifecycle row). When null we display `currentTime`.
|
// 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 containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [_selectedZone, setSelectedZone] = useState("");
|
const [_selectedZone, setSelectedZone] = useState("");
|
||||||
const [_lifecycleZones, setLifecycleZones] = useState<string[]>([]);
|
const [_lifecycleZones, setLifecycleZones] = useState<string[]>([]);
|
||||||
const [showZones, setShowZones] = useState(true);
|
|
||||||
const [seekToTimestamp, setSeekToTimestamp] = useState<number | null>(null);
|
const [seekToTimestamp, setSeekToTimestamp] = useState<number | null>(null);
|
||||||
|
|
||||||
const aspectRatio = useMemo(() => {
|
const aspectRatio = useMemo(() => {
|
||||||
@ -464,22 +457,6 @@ export function TrackingDetails({
|
|||||||
{t("trackingDetails.autoTrackingTips")}
|
{t("trackingDetails.autoTrackingTips")}
|
||||||
</div>
|
</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 className="mt-4">
|
||||||
<div
|
<div
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user