more mobile tweaks

This commit is contained in:
Josh Hawkins 2025-10-08 12:02:06 -05:00
parent 573b2b76e0
commit a34d7e04cc

View File

@ -56,11 +56,11 @@ import {
SidebarMenuSubItem, SidebarMenuSubItem,
SidebarProvider, SidebarProvider,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import { ChevronRight } from "lucide-react";
import { IoMdArrowRoundBack } from "react-icons/io"; import { IoMdArrowRoundBack } from "react-icons/io";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import { isPWA } from "@/utils/isPWA"; import { LuChevronRight } from "react-icons/lu";
import Logo from "@/components/Logo";
const allSettingsViews = [ const allSettingsViews = [
"ui", "ui",
@ -127,24 +127,26 @@ function MobileMenuItem({
item, item,
onSelect, onSelect,
onClose, onClose,
className,
}: { }: {
item: { key: string }; item: { key: string };
onSelect: (key: string) => void; onSelect: (key: string) => void;
onClose: () => void; onClose: () => void;
className?: string;
}) { }) {
const { t } = useTranslation(["views/settings"]); const { t } = useTranslation(["views/settings"]);
return ( return (
<Button <Button
variant="ghost" variant="ghost"
className="w-full justify-between" className={cn("w-full justify-between pr-2", className)}
onClick={() => { onClick={() => {
onSelect(item.key); onSelect(item.key);
onClose(); onClose();
}} }}
> >
<div className="smart-capitalize">{t("menu." + item.key)}</div> <div className="smart-capitalize">{t("menu." + item.key)}</div>
<ChevronRight className="h-4 w-4" /> <LuChevronRight className="size-4" />
</Button> </Button>
); );
} }
@ -268,96 +270,97 @@ export default function Settings() {
if (isMobile) { if (isMobile) {
return ( return (
<div className="flex size-full flex-col"> <>
<div className="sticky -top-2 z-50 mb-2 flex items-center bg-background p-4"> {mobileMenuOpen ? (
<Button <div className="flex size-full flex-col">
className="absolute left-4 z-10 rounded-lg" <div className="sticky -top-2 z-50 mb-2 bg-background p-4">
aria-label="Open menu" <div className="flex items-center justify-center">
size="sm" <Logo className="h-8" />
onClick={() => setMobileMenuOpen(true)}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
</Button>
<h2 className="flex-1 text-center text-lg font-semibold smart-capitalize">
{t("menu." + page)}
</h2>
{[
"debug",
"cameras",
"masksAndZones",
"motionTuner",
"triggers",
].includes(page) && (
<div className="absolute right-4 flex items-center gap-2">
{page == "masksAndZones" && (
<ZoneMaskFilterButton
selectedZoneMask={filterZoneMask}
updateZoneMaskFilter={setFilterZoneMask}
/>
)}
<CameraSelectButton
allCameras={cameras}
selectedCamera={selectedCamera}
setSelectedCamera={setSelectedCamera}
cameraEnabledStates={cameraEnabledStates}
currentPage={page}
/>
</div>
)}
</div>
{mobileMenuOpen && (
<div
className={cn(
"absolute top-0 z-50 mb-12 bg-background",
isPWA && "mb-16",
"landscape:mb-14 landscape:md:mb-16",
)}
>
<div className="px-4">
<div className="sticky -top-2 z-50 mb-2 flex items-center justify-center bg-background p-4">
<div className="flex flex-row text-center">
<h2 className="text-lg font-semibold">
{t("settings", { ns: "common" })}
</h2>
</div>
</div> </div>
<div className="flex flex-row text-center">
<h2 className="ml-2 text-lg font-semibold">
{t("settings", { ns: "common" })}
</h2>
</div>
</div>
<div className="scrollbar-container max-h-[calc(100vh-8rem)] overflow-y-auto"> <div className="scrollbar-container overflow-y-auto px-4">
{settingsGroups.map((group) => { {settingsGroups.map((group) => {
const filteredItems = group.items.filter((item) => const filteredItems = group.items.filter((item) =>
visibleSettingsViews.includes(item.key as SettingsType), visibleSettingsViews.includes(item.key as SettingsType),
); );
if (filteredItems.length === 0) return null; if (filteredItems.length === 0) return null;
return ( return (
<div key={group.label} className="mb-3"> <div key={group.label} className="mb-3">
<h3 className="mb-2 text-sm font-medium text-muted-foreground"> {filteredItems.length > 1 && (
<h3 className="mb-2 ml-2 text-sm font-medium text-secondary-foreground">
{group.label} {group.label}
</h3> </h3>
{filteredItems.map((item) => ( )}
<MobileMenuItem {filteredItems.map((item) => (
key={item.key} <MobileMenuItem
item={item} key={item.key}
onSelect={(key) => { item={item}
if ( className={cn(filteredItems.length == 1 && "pl-2")}
!isAdmin && onSelect={(key) => {
!allowedViewsForViewer.includes( if (
key as SettingsType, !isAdmin &&
) !allowedViewsForViewer.includes(key as SettingsType)
) { ) {
setPageToggle("ui"); setPageToggle("ui");
} else { } else {
setPageToggle(key as SettingsType); setPageToggle(key as SettingsType);
} }
}} }}
onClose={() => setMobileMenuOpen(false)} onClose={() => setMobileMenuOpen(false)}
/> />
))} ))}
</div> </div>
); );
})} })}
</div>
</div> </div>
</div> </div>
) : (
<div
className={cn(
"sticky -top-2 z-50 mb-2 flex items-center bg-background p-4",
)}
>
<Button
className="absolute left-4 z-10 rounded-lg"
aria-label="Open menu"
size="sm"
onClick={() => setMobileMenuOpen(true)}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
</Button>
<h2 className="flex-1 text-center text-lg font-semibold smart-capitalize">
{t("menu." + page)}
</h2>
{[
"debug",
"cameras",
"masksAndZones",
"motionTuner",
"triggers",
].includes(page) && (
<div className="absolute right-4 flex items-center gap-2">
{page == "masksAndZones" && (
<ZoneMaskFilterButton
selectedZoneMask={filterZoneMask}
updateZoneMaskFilter={setFilterZoneMask}
/>
)}
<CameraSelectButton
allCameras={cameras}
selectedCamera={selectedCamera}
setSelectedCamera={setSelectedCamera}
cameraEnabledStates={cameraEnabledStates}
currentPage={page}
/>
</div>
)}
</div>
)} )}
<div className="flex-1 overflow-auto p-2"> <div className="flex-1 overflow-auto p-2">
{(() => { {(() => {
@ -397,7 +400,7 @@ export default function Settings() {
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
)} )}
</div> </>
); );
} }