mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-12-19 19:06:16 +01:00
Tweak camera group layout editor buttons (#11317)
* tweak layout editor buttons * remove bubble * spacing * button backgrounds
This commit is contained in:
parent
8b344cea81
commit
386ffbf5a6
@ -360,6 +360,65 @@ function NewGroupDialog({
|
||||
);
|
||||
}
|
||||
|
||||
type EditGroupDialogProps = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
currentGroups: [string, CameraGroupConfig][];
|
||||
activeGroup?: string;
|
||||
};
|
||||
export function EditGroupDialog({
|
||||
open,
|
||||
setOpen,
|
||||
currentGroups,
|
||||
activeGroup,
|
||||
}: EditGroupDialogProps) {
|
||||
// editing group and state
|
||||
|
||||
const editingGroup = useMemo(() => {
|
||||
if (currentGroups && activeGroup) {
|
||||
return currentGroups.find(([groupName]) => groupName === activeGroup);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}, [currentGroups, activeGroup]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toaster
|
||||
className="toaster group z-[100]"
|
||||
position="top-center"
|
||||
closeButton={true}
|
||||
/>
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(open) => {
|
||||
setOpen(open);
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
className={`min-w-0 ${isMobile ? "w-full p-3 rounded-t-2xl max-h-[90%]" : "w-6/12 max-h-dvh overflow-y-hidden"}`}
|
||||
>
|
||||
<div className="flex flex-col my-4 overflow-y-auto">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<DialogTitle>Edit Camera Group</DialogTitle>
|
||||
</div>
|
||||
<CameraGroupEdit
|
||||
currentGroups={currentGroups}
|
||||
editingGroup={editingGroup}
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
onSave={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type CameraGroupRowProps = {
|
||||
group: [string, CameraGroupConfig];
|
||||
onDeleteGroup: () => void;
|
||||
@ -572,7 +631,7 @@ export function CameraGroupEdit({
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="mt-2 space-y-6 overflow-y-auto"
|
||||
className="mt-2 space-y-6 overflow-y-hidden"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
@ -655,7 +714,7 @@ export function CameraGroupEdit({
|
||||
<Separator className="flex my-2 bg-secondary" />
|
||||
|
||||
<div className="flex flex-row gap-2 py-5 md:pb-0">
|
||||
<Button className="flex flex-1" onClick={onCancel}>
|
||||
<Button type="button" className="flex flex-1" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -24,15 +24,18 @@ import useSWR from "swr";
|
||||
import { isDesktop, isMobile, isSafari } from "react-device-detect";
|
||||
import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer";
|
||||
import LivePlayer from "@/components/player/LivePlayer";
|
||||
import { IoClose } from "react-icons/io5";
|
||||
import { LuLayoutDashboard, LuPencil } from "react-icons/lu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { EditGroupDialog } from "@/components/filter/CameraGroupSelector";
|
||||
import { usePersistedOverlayState } from "@/hooks/use-overlay-state";
|
||||
import { FaCompress, FaExpand } from "react-icons/fa";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { IoClose } from "react-icons/io5";
|
||||
import { LuMove } from "react-icons/lu";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type DraggableGridLayoutProps = {
|
||||
cameras: CameraConfig[];
|
||||
@ -67,6 +70,20 @@ export default function DraggableGridLayout({
|
||||
Layout[]
|
||||
>(`${cameraGroup}-draggable-layout`);
|
||||
|
||||
const [group] = usePersistedOverlayState("cameraGroup", "default" as string);
|
||||
|
||||
const groups = useMemo(() => {
|
||||
if (!config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(config.camera_groups).sort(
|
||||
(a, b) => a[1].order - b[1].order,
|
||||
);
|
||||
}, [config]);
|
||||
|
||||
const [editGroup, setEditGroup] = useState(false);
|
||||
|
||||
const [currentCameras, setCurrentCameras] = useState<CameraConfig[]>();
|
||||
const [currentIncludeBirdseye, setCurrentIncludeBirdseye] =
|
||||
useState<boolean>();
|
||||
@ -252,6 +269,25 @@ export default function DraggableGridLayout({
|
||||
);
|
||||
}, [containerRef, gridContainerRef, containerHeight]);
|
||||
|
||||
// fullscreen state
|
||||
|
||||
useEffect(() => {
|
||||
if (gridContainerRef.current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const listener = () => {
|
||||
setFullscreen(document.fullscreenElement != null);
|
||||
};
|
||||
document.addEventListener("fullscreenchange", listener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("fullscreenchange", listener);
|
||||
};
|
||||
}, [gridContainerRef]);
|
||||
|
||||
const [fullscreen, setFullscreen] = useState(false);
|
||||
|
||||
const cellHeight = useMemo(() => {
|
||||
const aspectRatio = 16 / 9;
|
||||
// subtract container margin, 1 camera takes up at least 4 rows
|
||||
@ -286,6 +322,12 @@ export default function DraggableGridLayout({
|
||||
className="my-2 px-2 pb-8 no-scrollbar overflow-x-hidden"
|
||||
ref={gridContainerRef}
|
||||
>
|
||||
<EditGroupDialog
|
||||
open={editGroup}
|
||||
setOpen={setEditGroup}
|
||||
currentGroups={groups}
|
||||
activeGroup={group}
|
||||
/>
|
||||
<ResponsiveGridLayout
|
||||
className="grid-layout"
|
||||
layouts={{
|
||||
@ -352,12 +394,86 @@ export default function DraggableGridLayout({
|
||||
);
|
||||
})}
|
||||
</ResponsiveGridLayout>
|
||||
{isDesktop && (
|
||||
<DesktopEditLayoutButton
|
||||
isEditMode={isEditMode}
|
||||
setIsEditMode={setIsEditMode}
|
||||
hasScrollbar={hasScrollbar}
|
||||
/>
|
||||
{isDesktop && !fullscreen && (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed",
|
||||
isDesktop && "bottom-12 lg:bottom-9",
|
||||
isMobile && "bottom-12 lg:bottom-16",
|
||||
hasScrollbar && isDesktop ? "right-6" : "right-3",
|
||||
"z-50 flex flex-row gap-2",
|
||||
)}
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
className="px-2 py-1 bg-secondary-foreground rounded-lg opacity-30 hover:opacity-100 transition-all duration-300"
|
||||
onClick={() =>
|
||||
setIsEditMode((prevIsEditMode) => !prevIsEditMode)
|
||||
}
|
||||
>
|
||||
{isEditMode ? (
|
||||
<>
|
||||
<IoClose className="size-5" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LuLayoutDashboard className="size-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{isEditMode ? "Exit Editing" : "Edit Layout"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{!isEditMode && (
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
className="px-2 py-1 bg-secondary-foreground rounded-lg opacity-30 hover:opacity-100 transition-all duration-300"
|
||||
onClick={() =>
|
||||
setEditGroup((prevEditGroup) => !prevEditGroup)
|
||||
}
|
||||
>
|
||||
<LuPencil className="size-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{isEditMode ? "Exit Editing" : "Edit Camera Group"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
className="px-2 py-1 bg-secondary-foreground rounded-lg opacity-30 hover:opacity-100 transition-all duration-300"
|
||||
onClick={() => {
|
||||
if (fullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
gridContainerRef.current?.requestFullscreen();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{fullscreen ? (
|
||||
<>
|
||||
<FaCompress className="size-5" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FaExpand className="size-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{fullscreen ? "Exit Fullscreen" : "Fullscreen"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@ -365,47 +481,6 @@ export default function DraggableGridLayout({
|
||||
);
|
||||
}
|
||||
|
||||
type DesktopEditLayoutButtonProps = {
|
||||
isEditMode?: boolean;
|
||||
setIsEditMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
hasScrollbar?: boolean | 0 | null;
|
||||
};
|
||||
|
||||
function DesktopEditLayoutButton({
|
||||
isEditMode,
|
||||
setIsEditMode,
|
||||
hasScrollbar,
|
||||
}: DesktopEditLayoutButtonProps) {
|
||||
return (
|
||||
<div className="flex flex-row gap-2 items-center text-primary">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="default"
|
||||
className={cn(
|
||||
"fixed",
|
||||
isDesktop && "bottom-12 lg:bottom-9",
|
||||
isMobile && "bottom-12 lg:bottom-16",
|
||||
hasScrollbar && isDesktop ? "right-6" : "right-1",
|
||||
"z-50 h-8 w-8 p-0 rounded-full opacity-30 hover:opacity-100 transition-all duration-300",
|
||||
)}
|
||||
onClick={() => setIsEditMode((prevIsEditMode) => !prevIsEditMode)}
|
||||
>
|
||||
{isEditMode ? (
|
||||
<IoClose className="size-5" />
|
||||
) : (
|
||||
<LuMove className="size-5" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left">
|
||||
{isEditMode ? "Exit Editing" : "Edit Layout"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CornerCircles() {
|
||||
return (
|
||||
<>
|
||||
|
@ -143,11 +143,11 @@
|
||||
--muted-foreground: hsl(0, 0%, 32%);
|
||||
--muted-foreground: 0 0% 32%;
|
||||
|
||||
--accent: hsl(0, 0%, 15%);
|
||||
--accent: 0 0% 15%;
|
||||
--accent: hsl(0, 0%, 19%);
|
||||
--accent: 0 0% 19%;
|
||||
|
||||
--accent-foreground: hsl(210, 40%, 98%);
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--accent-foreground: hsl(210, 40%, 95%);
|
||||
--accent-foreground: 210 40% 95%;
|
||||
|
||||
--destructive: hsl(0, 62.8%, 30.6%);
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
|
Loading…
Reference in New Issue
Block a user