frigate/web/src/components/timeline/EventMenu.tsx
Josh Hawkins 530b69b877
Miscellaneous fixes (#20833)
* remove frigate+ icon from explore grid footer

* add margin

* pointer cursor on event menu items in detail stream

* don't show submit to plus for non-objects and if plus is disabled

* tweak spacing in annotation settings popover

* Fix deletion of classification images and library

* Ensure after creating a class that things are correct

* Fix dialog getting stuck

* Only show the genai summary popup on mobile when timeline is open

* fix audio transcription embedding

* spacing

* hide x icon on restart sheet to prevent closure issues

* prevent x overflow in detail stream on mobile safari

* ensure name is valid for search effect trigger

* add trigger to detail actions menu

* move find similar to actions menu

* Use a column layout for MobilePageContent in PlatformAwareSheet

 This is so the header is outside the scrolling area and the content can grow/scroll independently. This now matches the way it's done in classification

* Skip azure execution provider

* add optional ref to always scroll to top

the more filters in explore was not scrolled to the top on open due to the use of framer motion

* fix title classes on desktop

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-11-07 06:53:27 -07:00

123 lines
3.7 KiB
TypeScript

import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import { HiDotsHorizontal } from "react-icons/hi";
import { useApiHost } from "@/api";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Event } from "@/types/event";
import { FrigateConfig } from "@/types/frigateConfig";
import { useState } from "react";
type EventMenuProps = {
event: Event;
config?: FrigateConfig;
onOpenUpload?: (e: Event) => void;
onOpenSimilarity?: (e: Event) => void;
isSelected?: boolean;
onToggleSelection?: (event: Event | undefined) => void;
};
export default function EventMenu({
event,
config,
onOpenUpload,
onOpenSimilarity,
isSelected = false,
onToggleSelection,
}: EventMenuProps) {
const apiHost = useApiHost();
const navigate = useNavigate();
const { t } = useTranslation("views/explore");
const [isOpen, setIsOpen] = useState(false);
const handleObjectSelect = () => {
if (isSelected) {
onToggleSelection?.(undefined);
} else {
onToggleSelection?.(event);
}
};
return (
<>
<span tabIndex={0} className="sr-only" />
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenuTrigger>
<div className="rounded p-1 pr-2" role="button">
<HiDotsHorizontal className="size-4 text-muted-foreground" />
</div>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent>
<DropdownMenuItem
className="cursor-pointer"
onSelect={handleObjectSelect}
>
{isSelected
? t("itemMenu.hideObjectDetails.label")
: t("itemMenu.showObjectDetails.label")}
</DropdownMenuItem>
<DropdownMenuSeparator className="my-0.5" />
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => {
navigate(`/explore?event_id=${event.id}`);
}}
>
{t("details.item.button.viewInExplore")}
</DropdownMenuItem>
<DropdownMenuItem className="cursor-pointer" asChild>
<a
download
href={
event.has_snapshot
? `${apiHost}api/events/${event.id}/snapshot.jpg`
: `${apiHost}api/events/${event.id}/thumbnail.webp`
}
>
{t("itemMenu.downloadSnapshot.label")}
</a>
</DropdownMenuItem>
{event.has_snapshot &&
event.plus_id == undefined &&
event.data.type == "object" &&
config?.plus?.enabled && (
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => {
setIsOpen(false);
onOpenUpload?.(event);
}}
>
{t("itemMenu.submitToPlus.label")}
</DropdownMenuItem>
)}
{event.has_snapshot && config?.semantic_search?.enabled && (
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => {
if (onOpenSimilarity) onOpenSimilarity(event);
else
navigate(
`/explore?search_type=similarity&event_id=${event.id}`,
);
}}
>
{t("itemMenu.findSimilar.label")}
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
</>
);
}