forked from Github/frigate
Implement score filtering on Frigate+ Page (#10968)
* Fix portrait layout disappearing * Refactor sliders * Reuse camera filter * Reuse label filter content * Implement score slider including keyboard input * Implement ability to sort frigate plus submissions
This commit is contained in:
@@ -209,7 +209,7 @@ type CameraFilterButtonProps = {
|
||||
selectedCameras: string[] | undefined;
|
||||
updateCameraFilter: (cameras: string[] | undefined) => void;
|
||||
};
|
||||
function CamerasFilterButton({
|
||||
export function CamerasFilterButton({
|
||||
allCameras,
|
||||
groups,
|
||||
selectedCameras,
|
||||
@@ -227,7 +227,7 @@ function CamerasFilterButton({
|
||||
size="sm"
|
||||
>
|
||||
<FaVideo
|
||||
className={`${selectedCameras?.length == 1 ? "text-selected-foreground" : "text-secondary-foreground"}`}
|
||||
className={`${(selectedCameras?.length ?? 0) >= 1 ? "text-selected-foreground" : "text-secondary-foreground"}`}
|
||||
/>
|
||||
<div
|
||||
className={`hidden md:block ${selectedCameras?.length ? "text-selected-foreground" : "text-primary"}`}
|
||||
|
||||
@@ -8,7 +8,6 @@ import React, {
|
||||
import { useApiHost } from "@/api";
|
||||
import { isCurrentHour } from "@/utils/dateUtil";
|
||||
import { ReviewSegment } from "@/types/review";
|
||||
import { Slider } from "../ui/slider-no-thumb";
|
||||
import { getIconForLabel } from "@/utils/iconUtil";
|
||||
import TimeAgo from "../dynamic/TimeAgo";
|
||||
import useSWR from "swr";
|
||||
@@ -23,6 +22,7 @@ import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
|
||||
import useContextMenu from "@/hooks/use-contextmenu";
|
||||
import ActivityIndicator from "../indicators/activity-indicator";
|
||||
import { TimeRange } from "@/types/timeline";
|
||||
import { NoThumbSlider } from "../ui/slider";
|
||||
|
||||
type PreviewPlayerProps = {
|
||||
review: ReviewSegment;
|
||||
@@ -543,7 +543,7 @@ function VideoPreview({
|
||||
>
|
||||
<source src={relevantPreview.src} type={relevantPreview.type} />
|
||||
</video>
|
||||
<Slider
|
||||
<NoThumbSlider
|
||||
ref={sliderRef}
|
||||
className="absolute inset-x-0 bottom-0 z-30"
|
||||
value={[progress]}
|
||||
@@ -707,7 +707,7 @@ function InProgressPreview({
|
||||
src={`${apiHost}api/preview/${previewFrames[key]}/thumbnail.webp`}
|
||||
onLoad={handleLoad}
|
||||
/>
|
||||
<Slider
|
||||
<NoThumbSlider
|
||||
ref={sliderRef}
|
||||
className="absolute inset-x-0 bottom-0 z-30"
|
||||
value={[key]}
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
MdVolumeOff,
|
||||
MdVolumeUp,
|
||||
} from "react-icons/md";
|
||||
import { Slider } from "../ui/slider-volume";
|
||||
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
||||
import { VolumeSlider } from "../ui/slider";
|
||||
|
||||
type VideoControls = {
|
||||
volume?: boolean;
|
||||
@@ -154,7 +154,7 @@ export default function VideoControls({
|
||||
}}
|
||||
/>
|
||||
{video.muted == false && (
|
||||
<Slider
|
||||
<VolumeSlider
|
||||
className="w-20"
|
||||
value={[video.volume]}
|
||||
min={0}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-blue-500" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-4 w-16 rounded-full bg-transparent -translate-y-[50%] ring-offset-transparent focus-visible:outline-none focus-visible:ring-transparent disabled:pointer-events-none disabled:opacity-50 cursor-col-resize" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-muted">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-white" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-3 w-3 rounded-full bg-white ring-white focus:ring-white disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react"
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||
import * as React from "react";
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
@@ -11,7 +11,7 @@ const Slider = React.forwardRef<
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
@@ -20,7 +20,68 @@ const Slider = React.forwardRef<
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
))
|
||||
Slider.displayName = SliderPrimitive.Root.displayName
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider }
|
||||
const VolumeSlider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-muted">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-white" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-3 w-3 rounded-full bg-white ring-white focus:ring-white disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
VolumeSlider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
const NoThumbSlider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-selected" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-4 w-16 rounded-full bg-transparent -translate-y-[50%] ring-offset-transparent focus-visible:outline-none focus-visible:ring-transparent disabled:pointer-events-none disabled:opacity-50 cursor-col-resize" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
NoThumbSlider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
const DualThumbSlider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-selected/60">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-selected" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block size-3 rounded-full bg-selected transition-colors cursor-col-resize disabled:pointer-events-none disabled:opacity-50" />
|
||||
<SliderPrimitive.Thumb className="block size-3 rounded-full bg-selected transition-colors cursor-col-resize disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
DualThumbSlider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { DualThumbSlider, Slider, NoThumbSlider, VolumeSlider };
|
||||
|
||||
Reference in New Issue
Block a user