mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Use MobilePage for camera group editor (#13710)
* Use MobilePage for camera group editor * alignment * clear editing group name
This commit is contained in:
parent
644ea7be4a
commit
1f9ba1d625
@ -7,8 +7,13 @@ import { Button } from "../ui/button";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||
import { LuPencil, LuPlus } from "react-icons/lu";
|
||||
import { Dialog, DialogContent, DialogTitle } from "../ui/dialog";
|
||||
import { Drawer, DrawerContent } from "../ui/drawer";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../ui/dialog";
|
||||
import { Input } from "../ui/input";
|
||||
import { Separator } from "../ui/separator";
|
||||
import {
|
||||
@ -24,6 +29,7 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu";
|
||||
import {
|
||||
@ -53,6 +59,13 @@ import { cn } from "@/lib/utils";
|
||||
import * as LuIcons from "react-icons/lu";
|
||||
import IconPicker, { IconName, IconRenderer } from "../icons/IconPicker";
|
||||
import { isValidIconName } from "@/utils/iconUtil";
|
||||
import {
|
||||
MobilePage,
|
||||
MobilePageContent,
|
||||
MobilePageDescription,
|
||||
MobilePageHeader,
|
||||
MobilePageTitle,
|
||||
} from "../mobile/MobilePage";
|
||||
|
||||
type CameraGroupSelectorProps = {
|
||||
className?: string;
|
||||
@ -278,6 +291,7 @@ function NewGroupDialog({
|
||||
const onSave = () => {
|
||||
setOpen(false);
|
||||
setEditState("none");
|
||||
setEditingGroupName("");
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
@ -290,8 +304,11 @@ function NewGroupDialog({
|
||||
setEditState("edit");
|
||||
}, []);
|
||||
|
||||
const Overlay = isDesktop ? Dialog : Drawer;
|
||||
const Content = isDesktop ? DialogContent : DrawerContent;
|
||||
const Overlay = isDesktop ? Dialog : MobilePage;
|
||||
const Content = isDesktop ? DialogContent : MobilePageContent;
|
||||
const Header = isDesktop ? DialogHeader : MobilePageHeader;
|
||||
const Description = isDesktop ? DialogDescription : MobilePageDescription;
|
||||
const Title = isDesktop ? DialogTitle : MobilePageTitle;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -308,16 +325,36 @@ function NewGroupDialog({
|
||||
}}
|
||||
>
|
||||
<Content
|
||||
className={`min-w-0 ${isMobile ? "max-h-[90%] w-full rounded-t-2xl p-3" : "max-h-dvh w-6/12 overflow-y-hidden"}`}
|
||||
className={cn(
|
||||
"scrollbar-container overflow-y-auto",
|
||||
isDesktop && "my-4 flex max-h-dvh w-6/12 flex-col",
|
||||
isMobile && "px-4",
|
||||
)}
|
||||
>
|
||||
<div className="scrollbar-container my-4 flex flex-col overflow-y-auto">
|
||||
{editState === "none" && (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-between py-2">
|
||||
<DialogTitle>Camera Groups</DialogTitle>
|
||||
{editState === "none" && (
|
||||
<>
|
||||
<Header
|
||||
className={cn(isDesktop && "mt-5", "justify-center")}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<Title>Camera Groups</Title>
|
||||
<Description className="sr-only">
|
||||
Edit camera groups
|
||||
</Description>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute",
|
||||
isDesktop && "right-6 top-10",
|
||||
isMobile && "absolute right-0 top-4",
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
|
||||
size="sm"
|
||||
className={cn(
|
||||
isDesktop &&
|
||||
"size-6 rounded-md bg-secondary-foreground p-1 text-background",
|
||||
isMobile && "text-secondary-foreground",
|
||||
)}
|
||||
onClick={() => {
|
||||
setEditState("add");
|
||||
}}
|
||||
@ -325,6 +362,8 @@ function NewGroupDialog({
|
||||
<LuPlus />
|
||||
</Button>
|
||||
</div>
|
||||
</Header>
|
||||
<div className="flex flex-col gap-4 md:gap-3">
|
||||
{currentGroups.map((group) => (
|
||||
<CameraGroupRow
|
||||
key={group[0]}
|
||||
@ -333,27 +372,36 @@ function NewGroupDialog({
|
||||
onEditGroup={() => onEditGroup(group)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{editState != "none" && (
|
||||
<>
|
||||
<div className="mb-3 flex flex-row items-center justify-between">
|
||||
<DialogTitle>
|
||||
{editState == "add" ? "Add" : "Edit"} Camera Group
|
||||
</DialogTitle>
|
||||
</div>
|
||||
<CameraGroupEdit
|
||||
currentGroups={currentGroups}
|
||||
editingGroup={editingGroup}
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
onSave={onSave}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{editState != "none" && (
|
||||
<>
|
||||
<Header
|
||||
className="mt-2"
|
||||
onClose={() => {
|
||||
setEditState("none");
|
||||
setEditingGroupName("");
|
||||
}}
|
||||
>
|
||||
<Title>
|
||||
{editState == "add" ? "Add" : "Edit"} Camera Group
|
||||
</Title>
|
||||
<Description className="sr-only">
|
||||
Edit camera groups
|
||||
</Description>
|
||||
</Header>
|
||||
<CameraGroupEdit
|
||||
currentGroups={currentGroups}
|
||||
editingGroup={editingGroup}
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
onSave={onSave}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Content>
|
||||
</Overlay>
|
||||
</>
|
||||
@ -372,6 +420,12 @@ export function EditGroupDialog({
|
||||
currentGroups,
|
||||
activeGroup,
|
||||
}: EditGroupDialogProps) {
|
||||
const Overlay = isDesktop ? Dialog : MobilePage;
|
||||
const Content = isDesktop ? DialogContent : MobilePageContent;
|
||||
const Header = isDesktop ? DialogHeader : MobilePageHeader;
|
||||
const Description = isDesktop ? DialogDescription : MobilePageDescription;
|
||||
const Title = isDesktop ? DialogTitle : MobilePageTitle;
|
||||
|
||||
// editing group and state
|
||||
|
||||
const editingGroup = useMemo(() => {
|
||||
@ -391,19 +445,24 @@ export function EditGroupDialog({
|
||||
position="top-center"
|
||||
closeButton={true}
|
||||
/>
|
||||
<Dialog
|
||||
<Overlay
|
||||
open={open}
|
||||
onOpenChange={(open) => {
|
||||
setOpen(open);
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
className={`min-w-0 ${isMobile ? "max-h-[90%] w-full rounded-t-2xl p-3" : "max-h-dvh w-6/12 overflow-y-hidden"}`}
|
||||
<Content
|
||||
className={cn(
|
||||
"min-w-0",
|
||||
isDesktop && "max-h-dvh w-6/12 overflow-y-hidden",
|
||||
)}
|
||||
>
|
||||
<div className="scrollbar-container my-4 flex flex-col overflow-y-auto">
|
||||
<div className="mb-3 flex flex-row items-center justify-between">
|
||||
<DialogTitle>Edit Camera Group</DialogTitle>
|
||||
</div>
|
||||
<div className="scrollbar-container flex flex-col overflow-y-auto md:my-4">
|
||||
<Header className="mt-2" onClose={() => setOpen(false)}>
|
||||
<Title>Edit Camera Group</Title>
|
||||
<Description className="sr-only">Edit camera group</Description>
|
||||
</Header>
|
||||
|
||||
<CameraGroupEdit
|
||||
currentGroups={currentGroups}
|
||||
editingGroup={editingGroup}
|
||||
@ -413,8 +472,8 @@ export function EditGroupDialog({
|
||||
onCancel={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Content>
|
||||
</Overlay>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -440,7 +499,7 @@ export function CameraGroupRow({
|
||||
<>
|
||||
<div
|
||||
key={group[0]}
|
||||
className="transition-background my-1.5 flex flex-row items-center justify-between rounded-lg duration-100 md:p-1"
|
||||
className="transition-background flex flex-row items-center justify-between rounded-lg duration-100 md:p-1"
|
||||
>
|
||||
<div className={`flex items-center`}>
|
||||
<p className="cursor-default">{group[0]}</p>
|
||||
@ -472,12 +531,16 @@ export function CameraGroupRow({
|
||||
<DropdownMenuTrigger>
|
||||
<HiOutlineDotsVertical className="size-5" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={onEditGroup}>Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setDeleteDialogOpen(true)}>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={onEditGroup}>
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setDeleteDialogOpen(true)}>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
@ -659,7 +722,7 @@ export function CameraGroupEdit({
|
||||
/>
|
||||
|
||||
<Separator className="my-2 flex bg-secondary" />
|
||||
<div className="scrollbar-container max-h-[25dvh] overflow-y-auto md:max-h-[40dvh]">
|
||||
<div className="scrollbar-container max-h-[40dvh] overflow-y-auto">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="cameras"
|
||||
|
@ -32,7 +32,7 @@ export function MobilePage({ children, open, onOpenChange }: MobilePageProps) {
|
||||
{isVisible && (
|
||||
<motion.div
|
||||
className={cn(
|
||||
"fixed inset-0 z-[100] mb-12 bg-background",
|
||||
"fixed inset-0 z-50 mb-12 bg-background",
|
||||
isPWA && "mb-16",
|
||||
"landscape:mb-14 landscape:md:mb-16",
|
||||
)}
|
||||
|
@ -641,7 +641,7 @@ function DetectionReview({
|
||||
>
|
||||
{filter?.before == undefined && (
|
||||
<NewReviewData
|
||||
className="pointer-events-none absolute left-1/2 z-50 -translate-x-1/2"
|
||||
className="pointer-events-none absolute left-1/2 z-[49] -translate-x-1/2"
|
||||
contentRef={contentRef}
|
||||
reviewItems={currentItems}
|
||||
itemsToReview={loading ? 0 : itemsToReview}
|
||||
|
Loading…
Reference in New Issue
Block a user