UI tweaks and bugfixes (#11692)

* UI tweaks and bugfixes

* fix linter complaints in unmodified files
This commit is contained in:
Josh Hawkins 2024-06-02 12:00:59 -05:00 committed by GitHub
parent 7031c47fb2
commit 1e80342c41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 17 deletions

View File

@ -2,6 +2,7 @@ import { ReviewSegment } from "@/types/review";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { LuRefreshCcw } from "react-icons/lu"; import { LuRefreshCcw } from "react-icons/lu";
import { MutableRefObject, useMemo } from "react"; import { MutableRefObject, useMemo } from "react";
import { cn } from "@/lib/utils";
type NewReviewDataProps = { type NewReviewDataProps = {
className: string; className: string;
@ -29,11 +30,12 @@ export default function NewReviewData({
<div className={className}> <div className={className}>
<div className="pointer-events-auto mr-[65px] flex items-center justify-center md:mr-[115px]"> <div className="pointer-events-auto mr-[65px] flex items-center justify-center md:mr-[115px]">
<Button <Button
className={`${ className={cn(
hasUpdate hasUpdate
? "duration-500 animate-in slide-in-from-top" ? "duration-500 animate-in slide-in-from-top"
: "invisible" : "invisible",
} mx-auto mt-5 bg-gray-400 text-center text-white`} "mx-auto mt-5 bg-gray-400 text-center text-white",
)}
onClick={() => { onClick={() => {
pullLatestData(); pullLatestData();
if (contentRef.current) { if (contentRef.current) {

View File

@ -1,6 +1,7 @@
import { LuX } from "react-icons/lu"; import { LuX } from "react-icons/lu";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { FaCompactDisc } from "react-icons/fa"; import { FaCompactDisc } from "react-icons/fa";
import { cn } from "@/lib/utils";
type SaveExportOverlayProps = { type SaveExportOverlayProps = {
className: string; className: string;
@ -17,9 +18,11 @@ export default function SaveExportOverlay({
return ( return (
<div className={className}> <div className={className}>
<div <div
className={`pointer-events-auto flex items-center justify-center gap-2 rounded-lg px-2 ${ className={cn(
show ? "duration-500 animate-in slide-in-from-top" : "invisible" "pointer-events-auto flex items-center justify-center gap-2 rounded-lg px-2",
} mx-auto mt-5 text-center`} show ? "duration-500 animate-in slide-in-from-top" : "invisible",
"mx-auto mt-5 text-center",
)}
> >
<Button <Button
className="flex items-center gap-1" className="flex items-center gap-1"

View File

@ -1,3 +1,6 @@
import { FrigateConfig } from "@/types/frigateConfig";
import useSWR from "swr";
type MinimapSegmentProps = { type MinimapSegmentProps = {
isFirstSegmentInMinimap: boolean; isFirstSegmentInMinimap: boolean;
isLastSegmentInMinimap: boolean; isLastSegmentInMinimap: boolean;
@ -28,6 +31,8 @@ export function MinimapBounds({
firstMinimapSegmentRef, firstMinimapSegmentRef,
dense, dense,
}: MinimapSegmentProps) { }: MinimapSegmentProps) {
const { data: config } = useSWR<FrigateConfig>("config");
return ( return (
<> <>
{isFirstSegmentInMinimap && ( {isFirstSegmentInMinimap && (
@ -36,6 +41,7 @@ export function MinimapBounds({
ref={firstMinimapSegmentRef} ref={firstMinimapSegmentRef}
> >
{new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], { {new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
...(!dense && { month: "short", day: "2-digit" }), ...(!dense && { month: "short", day: "2-digit" }),
@ -46,6 +52,7 @@ export function MinimapBounds({
{isLastSegmentInMinimap && ( {isLastSegmentInMinimap && (
<div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary"> <div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary">
{new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], { {new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
...(!dense && { month: "short", day: "2-digit" }), ...(!dense && { month: "short", day: "2-digit" }),
@ -83,6 +90,8 @@ export function Timestamp({
timestampSpread, timestampSpread,
segmentKey, segmentKey,
}: TimestampSegmentProps) { }: TimestampSegmentProps) {
const { data: config } = useSWR<FrigateConfig>("config");
return ( return (
<div className="absolute left-[15px] z-10 h-[8px]"> <div className="absolute left-[15px] z-10 h-[8px]">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && ( {!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
@ -93,6 +102,7 @@ export function Timestamp({
{timestamp.getMinutes() % timestampSpread === 0 && {timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0 && timestamp.getSeconds() === 0 &&
timestamp.toLocaleTimeString([], { timestamp.toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
})} })}

View File

@ -1,6 +1,8 @@
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import scrollIntoView from "scroll-into-view-if-needed"; import scrollIntoView from "scroll-into-view-if-needed";
import { useTimelineUtils } from "./use-timeline-utils"; import { useTimelineUtils } from "./use-timeline-utils";
import { FrigateConfig } from "@/types/frigateConfig";
import useSWR from "swr";
type DraggableElementProps = { type DraggableElementProps = {
contentRef: React.RefObject<HTMLElement>; contentRef: React.RefObject<HTMLElement>;
@ -49,6 +51,8 @@ function useDraggableElement({
dense, dense,
timelineSegments, timelineSegments,
}: DraggableElementProps) { }: DraggableElementProps) {
const { data: config } = useSWR<FrigateConfig>("config");
const [clientYPosition, setClientYPosition] = useState<number | null>(null); const [clientYPosition, setClientYPosition] = useState<number | null>(null);
const [initialClickAdjustment, setInitialClickAdjustment] = useState(0); const [initialClickAdjustment, setInitialClickAdjustment] = useState(0);
const [elementScrollIntoView, setElementScrollIntoView] = useState(true); const [elementScrollIntoView, setElementScrollIntoView] = useState(true);
@ -170,6 +174,7 @@ function useDraggableElement({
draggableElementTimeRef.current.textContent = new Date( draggableElementTimeRef.current.textContent = new Date(
segmentStartTime * 1000, segmentStartTime * 1000,
).toLocaleTimeString([], { ).toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
...(segmentDuration < 60 && !dense && { second: "2-digit" }), ...(segmentDuration < 60 && !dense && { second: "2-digit" }),
@ -196,6 +201,7 @@ function useDraggableElement({
setDraggableElementTime, setDraggableElementTime,
setDraggableElementPosition, setDraggableElementPosition,
dense, dense,
config,
], ],
); );

View File

@ -29,7 +29,7 @@ import {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { isDesktop, isMobile } from "react-device-detect"; import { isDesktop, isMobile, isTablet } from "react-device-detect";
import { LuFolderCheck } from "react-icons/lu"; import { LuFolderCheck } from "react-icons/lu";
import { MdCircle } from "react-icons/md"; import { MdCircle } from "react-icons/md";
import useSWR from "swr"; import useSWR from "swr";
@ -48,6 +48,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import scrollIntoView from "scroll-into-view-if-needed"; import scrollIntoView from "scroll-into-view-if-needed";
import { Toaster } from "@/components/ui/sonner"; import { Toaster } from "@/components/ui/sonner";
import { toast } from "sonner"; import { toast } from "sonner";
import { cn } from "@/lib/utils";
type EventViewProps = { type EventViewProps = {
reviewItems?: SegmentedReviewData; reviewItems?: SegmentedReviewData;
@ -866,7 +867,12 @@ function MotionReview({
<div className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4"> <div className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4">
<div <div
ref={contentRef} ref={contentRef}
className="no-scrollbar grid w-full gap-2 overflow-auto px-1 sm:grid-cols-2 md:mx-2 md:gap-4 xl:grid-cols-3 3xl:grid-cols-4" className={cn(
"no-scrollbar grid w-full grid-cols-1",
(reviewCameras.length > 3 || isTablet || isDesktop) &&
"grid-cols-2",
"gap-2 overflow-auto px-1 md:mx-2 md:gap-4 xl:grid-cols-3 3xl:grid-cols-4",
)}
> >
{reviewCameras.map((camera) => { {reviewCameras.map((camera) => {
let grow; let grow;
@ -874,13 +880,12 @@ function MotionReview({
const aspectRatio = camera.detect.width / camera.detect.height; const aspectRatio = camera.detect.width / camera.detect.height;
if (aspectRatio > 2) { if (aspectRatio > 2) {
grow = "aspect-wide"; grow = "aspect-wide";
spans = "sm:col-span-2"; spans = reviewCameras.length > 3 && "col-span-2";
} else if (aspectRatio < 1) { } else if (aspectRatio < 1) {
grow = "md:h-full aspect-tall"; grow = "md:h-full aspect-tall";
spans = "md:row-span-2"; spans = "row-span-2";
} else { } else {
grow = "aspect-video"; grow = "aspect-video";
spans = "";
} }
const detectionType = getDetectionType(camera.name); const detectionType = getDetectionType(camera.name);
return ( return (

View File

@ -243,9 +243,17 @@ export default function LiveDashboardView({
> >
{includeBirdseye && birdseyeConfig?.enabled && ( {includeBirdseye && birdseyeConfig?.enabled && (
<div <div
style={{ className={(() => {
aspectRatio: birdseyeConfig.width / birdseyeConfig.height, const aspectRatio =
}} birdseyeConfig.width / birdseyeConfig.height;
if (aspectRatio > 2) {
return `${mobileLayout == "grid" && "col-span-2"} aspect-wide`;
} else if (aspectRatio < 1) {
return `${mobileLayout == "grid" && "row-span-2 md:h-full"} aspect-tall`;
} else {
return "aspect-video";
}
})()}
ref={birdseyeContainerRef} ref={birdseyeContainerRef}
> >
<BirdseyeLivePlayer <BirdseyeLivePlayer
@ -260,9 +268,9 @@ export default function LiveDashboardView({
let grow; let grow;
const aspectRatio = camera.detect.width / camera.detect.height; const aspectRatio = camera.detect.width / camera.detect.height;
if (aspectRatio > 2) { if (aspectRatio > 2) {
grow = `${mobileLayout == "grid" ? "col-span-2" : ""} aspect-wide`; grow = `${mobileLayout == "grid" && "col-span-2"} aspect-wide`;
} else if (aspectRatio < 1) { } else if (aspectRatio < 1) {
grow = `${mobileLayout == "grid" ? "row-span-2 aspect-tall md:h-full" : ""} aspect-tall`; grow = `${mobileLayout == "grid" && "row-span-2 md:h-full"} aspect-tall`;
} else { } else {
grow = "aspect-video"; grow = "aspect-video";
} }

View File

@ -445,7 +445,7 @@ export default function GeneralMetrics({
</Button> </Button>
)} )}
</div> </div>
<div className=" mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2"> <div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2">
{statsHistory.length != 0 ? ( {statsHistory.length != 0 ? (
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl"> <div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">GPU Usage</div> <div className="mb-5">GPU Usage</div>