mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
UI tweaks (#11036)
* spacing, mobile navbar, and minor color updates * tab scrolling behavior
This commit is contained in:
parent
d6dfa596de
commit
3b0f9988df
@ -154,7 +154,7 @@ export default function MotionTuner({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col md:flex-row size-full">
|
<div className="flex flex-col md:flex-row size-full">
|
||||||
<Toaster position="top-center" />
|
<Toaster position="top-center" />
|
||||||
<div className="flex flex-col w-full overflow-y-auto mt-2 md:mt-0 md:w-3/12 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
|
<div className="flex flex-col h-full w-full overflow-y-auto mt-2 md:mt-0 mb-10 md:mb-0 md:w-3/12 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
|
||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
Motion Detection Tuner
|
Motion Detection Tuner
|
||||||
</Heading>
|
</Heading>
|
||||||
|
@ -352,7 +352,7 @@ function Logs() {
|
|||||||
{Object.values(logTypes).map((item) => (
|
{Object.values(logTypes).map((item) => (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
key={item}
|
key={item}
|
||||||
className={`flex items-center justify-between gap-2 ${logService == item ? "" : "text-gray-500"}`}
|
className={`flex items-center justify-between gap-2 ${logService == item ? "" : "text-muted-foreground"}`}
|
||||||
value={item}
|
value={item}
|
||||||
aria-label={`Select ${item}`}
|
aria-label={`Select ${item}`}
|
||||||
>
|
>
|
||||||
|
@ -20,7 +20,7 @@ import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
|
|||||||
import MotionTuner from "@/components/settings/MotionTuner";
|
import MotionTuner from "@/components/settings/MotionTuner";
|
||||||
import MasksAndZones from "@/components/settings/MasksAndZones";
|
import MasksAndZones from "@/components/settings/MasksAndZones";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import useOptimisticState from "@/hooks/use-optimistic-state";
|
import useOptimisticState from "@/hooks/use-optimistic-state";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import { FaVideo } from "react-icons/fa";
|
import { FaVideo } from "react-icons/fa";
|
||||||
@ -31,6 +31,8 @@ import FilterSwitch from "@/components/filter/FilterSwitch";
|
|||||||
import { ZoneMaskFilterButton } from "@/components/filter/ZoneMaskFilter";
|
import { ZoneMaskFilterButton } from "@/components/filter/ZoneMaskFilter";
|
||||||
import { PolygonType } from "@/types/canvas";
|
import { PolygonType } from "@/types/canvas";
|
||||||
import ObjectSettings from "@/components/settings/ObjectSettings";
|
import ObjectSettings from "@/components/settings/ObjectSettings";
|
||||||
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
|
import scrollIntoView from "scroll-into-view-if-needed";
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const settingsViews = [
|
const settingsViews = [
|
||||||
@ -43,6 +45,7 @@ export default function Settings() {
|
|||||||
type SettingsType = (typeof settingsViews)[number];
|
type SettingsType = (typeof settingsViews)[number];
|
||||||
const [page, setPage] = useState<SettingsType>("general");
|
const [page, setPage] = useState<SettingsType>("general");
|
||||||
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
||||||
|
const tabsRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
@ -83,33 +86,51 @@ export default function Settings() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tabsRef.current) {
|
||||||
|
const element = tabsRef.current.querySelector(
|
||||||
|
`[data-nav-item="${pageToggle}"]`,
|
||||||
|
);
|
||||||
|
if (element instanceof HTMLElement) {
|
||||||
|
scrollIntoView(element, {
|
||||||
|
behavior: "smooth",
|
||||||
|
inline: "start",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [tabsRef, pageToggle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="size-full p-2 flex flex-col">
|
<div className="size-full p-2 flex flex-col">
|
||||||
<div className="w-full h-11 relative flex justify-between items-center">
|
<div className="w-full h-11 relative flex justify-between items-center">
|
||||||
<div className="flex flex-row overflow-x-auto">
|
<ScrollArea className="w-full whitespace-nowrap">
|
||||||
<ToggleGroup
|
<div ref={tabsRef} className="flex flex-row">
|
||||||
className="*:px-3 *:py-4 *:rounded-md flex-shrink-0"
|
<ToggleGroup
|
||||||
type="single"
|
className="*:px-3 *:py-4 *:rounded-md"
|
||||||
size="sm"
|
type="single"
|
||||||
value={pageToggle}
|
size="sm"
|
||||||
onValueChange={(value: SettingsType) => {
|
value={pageToggle}
|
||||||
if (value) {
|
onValueChange={(value: SettingsType) => {
|
||||||
setPageToggle(value);
|
if (value) {
|
||||||
}
|
setPageToggle(value);
|
||||||
}}
|
}
|
||||||
>
|
}}
|
||||||
{Object.values(settingsViews).map((item) => (
|
>
|
||||||
<ToggleGroupItem
|
{Object.values(settingsViews).map((item) => (
|
||||||
key={item}
|
<ToggleGroupItem
|
||||||
className={`flex items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
key={item}
|
||||||
value={item}
|
className={`flex items-center justify-between gap-2 scroll-mx-10 ${page == "general" ? "last:mr-20" : ""} ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
||||||
aria-label={`Select ${item}`}
|
value={item}
|
||||||
>
|
data-nav-item={item}
|
||||||
<div className="capitalize">{item}</div>
|
aria-label={`Select ${item}`}
|
||||||
</ToggleGroupItem>
|
>
|
||||||
))}
|
<div className="capitalize">{item}</div>
|
||||||
</ToggleGroup>
|
</ToggleGroupItem>
|
||||||
</div>
|
))}
|
||||||
|
</ToggleGroup>
|
||||||
|
<ScrollBar orientation="horizontal" className="h-0" />
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
{(page == "objects" ||
|
{(page == "objects" ||
|
||||||
page == "masks / zones" ||
|
page == "masks / zones" ||
|
||||||
page == "motion tuner") && (
|
page == "motion tuner") && (
|
||||||
|
@ -52,7 +52,7 @@ function System() {
|
|||||||
{Object.values(metrics).map((item) => (
|
{Object.values(metrics).map((item) => (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
key={item}
|
key={item}
|
||||||
className={`flex items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-gray-500"}`}
|
className={`flex items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
||||||
value={item}
|
value={item}
|
||||||
aria-label={`Select ${item}`}
|
aria-label={`Select ${item}`}
|
||||||
>
|
>
|
||||||
|
@ -228,7 +228,7 @@ export default function EventView({
|
|||||||
} // don't allow the severity to be unselected
|
} // don't allow the severity to be unselected
|
||||||
>
|
>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`${severityToggle == "alert" ? "" : "text-gray-500"}`}
|
className={`${severityToggle == "alert" ? "" : "text-muted-foreground"}`}
|
||||||
value="alert"
|
value="alert"
|
||||||
aria-label="Select alerts"
|
aria-label="Select alerts"
|
||||||
>
|
>
|
||||||
@ -238,7 +238,7 @@ export default function EventView({
|
|||||||
</div>
|
</div>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`${severityToggle == "detection" ? "" : "text-gray-500"}`}
|
className={`${severityToggle == "detection" ? "" : "text-muted-foreground"}`}
|
||||||
value="detection"
|
value="detection"
|
||||||
aria-label="Select detections"
|
aria-label="Select detections"
|
||||||
>
|
>
|
||||||
@ -250,7 +250,9 @@ export default function EventView({
|
|||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`px-3 py-4 rounded-2xl ${
|
className={`px-3 py-4 rounded-2xl ${
|
||||||
severityToggle == "significant_motion" ? "" : "text-gray-500"
|
severityToggle == "significant_motion"
|
||||||
|
? ""
|
||||||
|
: "text-muted-foreground"
|
||||||
}`}
|
}`}
|
||||||
value="significant_motion"
|
value="significant_motion"
|
||||||
aria-label="Select motion"
|
aria-label="Select motion"
|
||||||
|
@ -321,14 +321,14 @@ export function RecordingView({
|
|||||||
} // don't allow the severity to be unselected
|
} // don't allow the severity to be unselected
|
||||||
>
|
>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`${timelineType == "timeline" ? "" : "text-gray-500"}`}
|
className={`${timelineType == "timeline" ? "" : "text-muted-foreground"}`}
|
||||||
value="timeline"
|
value="timeline"
|
||||||
aria-label="Select timeline"
|
aria-label="Select timeline"
|
||||||
>
|
>
|
||||||
<div className="">Timeline</div>
|
<div className="">Timeline</div>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`${timelineType == "events" ? "" : "text-gray-500"}`}
|
className={`${timelineType == "events" ? "" : "text-muted-foreground"}`}
|
||||||
value="events"
|
value="events"
|
||||||
aria-label="Select events"
|
aria-label="Select events"
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user