mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-30 15:49:00 +03:00
UI tweaks (#20649)
* match face wizard with camera and classification wizards * remove review detail dialog and link chip to detail stream in history * remove footer on explore images and move to overlay * use consistent overlay button styles * spacing tweak * ensure selected ring stays on top of gradients * fix z-index * match object lifecycle with details
This commit is contained in:
@@ -811,7 +811,10 @@ function StateTrainGrid({
|
||||
image={data.filename}
|
||||
onRefresh={onRefresh}
|
||||
>
|
||||
<TbCategoryPlus className="size-7 cursor-pointer p-1 text-gray-200 hover:rounded-full hover:bg-primary-foreground/40" />
|
||||
<div className="group relative inline-flex items-center justify-center">
|
||||
<div className="pointer-events-none absolute inset-0 m-auto size-5 scale-95 rounded-full bg-black opacity-0 blur-sm transition-all duration-200 group-hover:scale-100 group-hover:opacity-100 group-hover:blur-xl" />
|
||||
<TbCategoryPlus className="relative z-10 size-5 cursor-pointer text-white/85 hover:text-white" />
|
||||
</div>
|
||||
</ClassificationSelectionDialog>
|
||||
</ClassificationCard>
|
||||
</div>
|
||||
@@ -958,7 +961,10 @@ function ObjectTrainGrid({
|
||||
image={data.filename}
|
||||
onRefresh={onRefresh}
|
||||
>
|
||||
<TbCategoryPlus className="size-7 cursor-pointer p-1 text-gray-200 hover:rounded-full hover:bg-primary-foreground/40" />
|
||||
<div className="group relative inline-flex items-center justify-center">
|
||||
<div className="pointer-events-none absolute inset-0 m-auto size-5 scale-95 rounded-full bg-black opacity-0 blur-sm transition-all duration-200 group-hover:scale-100 group-hover:opacity-100 group-hover:blur-xl" />
|
||||
<TbCategoryPlus className="relative z-10 size-5 cursor-pointer text-white/85 hover:text-white" />
|
||||
</div>
|
||||
</ClassificationSelectionDialog>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -53,8 +53,6 @@ import { cn } from "@/lib/utils";
|
||||
import { FilterList, LAST_24_HOURS_KEY } from "@/types/filter";
|
||||
import { GiSoundWaves } from "react-icons/gi";
|
||||
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
||||
import ReviewDetailDialog from "@/components/overlay/detail/ReviewDetailDialog";
|
||||
|
||||
import { useTimelineZoom } from "@/hooks/use-timeline-zoom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -398,6 +396,7 @@ export default function EventView({
|
||||
onSelectAllReviews={onSelectAllReviews}
|
||||
setSelectedReviews={setSelectedReviews}
|
||||
pullLatestData={pullLatestData}
|
||||
onOpenRecording={onOpenRecording}
|
||||
/>
|
||||
)}
|
||||
{severity == "significant_motion" && (
|
||||
@@ -441,6 +440,7 @@ type DetectionReviewProps = {
|
||||
onSelectAllReviews: () => void;
|
||||
setSelectedReviews: (reviews: ReviewSegment[]) => void;
|
||||
pullLatestData: () => void;
|
||||
onOpenRecording: (recordingInfo: RecordingStartingPoint) => void;
|
||||
};
|
||||
function DetectionReview({
|
||||
contentRef,
|
||||
@@ -460,15 +460,12 @@ function DetectionReview({
|
||||
onSelectAllReviews,
|
||||
setSelectedReviews,
|
||||
pullLatestData,
|
||||
onOpenRecording,
|
||||
}: DetectionReviewProps) {
|
||||
const { t } = useTranslation(["views/events"]);
|
||||
|
||||
const reviewTimelineRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// detail
|
||||
|
||||
const [reviewDetail, setReviewDetail] = useState<ReviewSegment>();
|
||||
|
||||
// preview
|
||||
|
||||
const [previewTime, setPreviewTime] = useState<number>();
|
||||
@@ -688,8 +685,6 @@ function DetectionReview({
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReviewDetailDialog review={reviewDetail} setReview={setReviewDetail} />
|
||||
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4"
|
||||
@@ -750,7 +745,12 @@ function DetectionReview({
|
||||
detail: boolean,
|
||||
) => {
|
||||
if (detail) {
|
||||
setReviewDetail(review);
|
||||
onOpenRecording({
|
||||
camera: review.camera,
|
||||
startTime: review.start_time - REVIEW_PADDING,
|
||||
severity: review.severity,
|
||||
timelineType: "detail",
|
||||
});
|
||||
} else {
|
||||
onSelectReview(review, ctrl);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
ASPECT_VERTICAL_LAYOUT,
|
||||
ASPECT_WIDE_LAYOUT,
|
||||
RecordingSegment,
|
||||
RecordingStartingPoint,
|
||||
} from "@/types/record";
|
||||
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -141,9 +142,15 @@ export function RecordingView({
|
||||
|
||||
// timeline
|
||||
|
||||
const [recording] = useOverlayState<RecordingStartingPoint>(
|
||||
"recording",
|
||||
undefined,
|
||||
false,
|
||||
);
|
||||
|
||||
const [timelineType, setTimelineType] = useOverlayState<TimelineType>(
|
||||
"timelineType",
|
||||
"timeline",
|
||||
recording?.timelineType ?? "timeline",
|
||||
);
|
||||
|
||||
const chunkedTimeRange = useMemo(
|
||||
|
||||
@@ -577,7 +577,7 @@ export default function SearchView({
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"aspect-square w-full overflow-hidden rounded-t-lg border",
|
||||
"relative aspect-square w-full overflow-hidden rounded-lg",
|
||||
)}
|
||||
>
|
||||
<SearchThumbnail
|
||||
@@ -634,38 +634,38 @@ export default function SearchView({
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute bottom-0 left-0 right-0 z-30 bg-gradient-to-t from-black/70 to-transparent p-2">
|
||||
<SearchThumbnailFooter
|
||||
searchResult={value}
|
||||
columns={columns}
|
||||
findSimilar={() => {
|
||||
if (config?.semantic_search.enabled) {
|
||||
setSimilaritySearch(value);
|
||||
}
|
||||
}}
|
||||
refreshResults={refresh}
|
||||
showObjectLifecycle={() =>
|
||||
onSelectSearch(value, false, "object_lifecycle")
|
||||
}
|
||||
showSnapshot={() =>
|
||||
onSelectSearch(value, false, "snapshot")
|
||||
}
|
||||
addTrigger={() => {
|
||||
if (
|
||||
config?.semantic_search.enabled &&
|
||||
value.data.type == "object"
|
||||
) {
|
||||
navigate(
|
||||
`/settings?page=triggers&camera=${value.camera}&event_id=${value.id}`,
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`review-item-ring pointer-events-none absolute inset-0 z-10 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${selected ? `shadow-selected outline-selected` : "outline-transparent duration-500"}`}
|
||||
className={`review-item-ring pointer-events-none absolute inset-0 z-30 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${selected ? `shadow-selected outline-selected` : "outline-transparent duration-500"}`}
|
||||
/>
|
||||
<div className="flex w-full grow items-center justify-between rounded-b-lg border border-t-0 bg-card p-3 text-card-foreground">
|
||||
<SearchThumbnailFooter
|
||||
searchResult={value}
|
||||
columns={columns}
|
||||
findSimilar={() => {
|
||||
if (config?.semantic_search.enabled) {
|
||||
setSimilaritySearch(value);
|
||||
}
|
||||
}}
|
||||
refreshResults={refresh}
|
||||
showObjectLifecycle={() =>
|
||||
onSelectSearch(value, false, "object_lifecycle")
|
||||
}
|
||||
showSnapshot={() =>
|
||||
onSelectSearch(value, false, "snapshot")
|
||||
}
|
||||
addTrigger={() => {
|
||||
if (
|
||||
config?.semantic_search.enabled &&
|
||||
value.data.type == "object"
|
||||
) {
|
||||
navigate(
|
||||
`/settings?page=triggers&camera=${value.camera}&event_id=${value.id}`,
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user