Search and search filter UI tweaks (#14381)

* fix search type switches

* select/unselect style for more filters button

* fix reset button

* fix labels scrollbar

* set min width and remove modal to allow scrolling with filters open

* hover colors

* better match of font size

* stop sheet from displaying console errors

* fix detail dialog behavior
This commit is contained in:
Josh Hawkins 2024-10-16 07:15:25 -05:00 committed by GitHub
parent 3f1ab66899
commit eda52a3b82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 86 additions and 45 deletions

View File

@ -113,7 +113,7 @@ export default function SearchThumbnailFooter({
}} }}
/> />
<div className="flex flex-col items-start text-xs"> <div className="flex flex-col items-start text-xs text-primary-variant">
{searchResult.end_time ? ( {searchResult.end_time ? (
<TimeAgo time={searchResult.start_time * 1000} dense /> <TimeAgo time={searchResult.start_time * 1000} dense />
) : ( ) : (
@ -132,7 +132,7 @@ export default function SearchThumbnailFooter({
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<FrigatePlusIcon <FrigatePlusIcon
className="size-5 cursor-pointer text-primary" className="size-5 cursor-pointer text-primary-variant hover:text-primary"
onClick={() => setShowFrigatePlus(true)} onClick={() => setShowFrigatePlus(true)}
/> />
</TooltipTrigger> </TooltipTrigger>
@ -144,7 +144,7 @@ export default function SearchThumbnailFooter({
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<MdImageSearch <MdImageSearch
className="size-5 cursor-pointer text-primary" className="size-5 cursor-pointer text-primary-variant hover:text-primary"
onClick={findSimilar} onClick={findSimilar}
/> />
</TooltipTrigger> </TooltipTrigger>
@ -154,7 +154,7 @@ export default function SearchThumbnailFooter({
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger> <DropdownMenuTrigger>
<LuMoreVertical className="size-5 cursor-pointer text-primary" /> <LuMoreVertical className="size-5 cursor-pointer text-primary-variant hover:text-primary" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align={"end"}> <DropdownMenuContent align={"end"}>
{searchResult.has_clip && ( {searchResult.has_clip && (

View File

@ -253,7 +253,11 @@ function GeneralFilterButton({
<PlatformAwareDialog <PlatformAwareDialog
trigger={trigger} trigger={trigger}
content={content} content={content}
contentClassName={isDesktop ? "" : "max-h-[75dvh] overflow-hidden p-4"} contentClassName={
isDesktop
? "scrollbar-container h-auto max-h-[80dvh] overflow-y-auto"
: "max-h-[75dvh] overflow-hidden p-4"
}
open={open} open={open}
onOpenChange={(open) => { onOpenChange={(open) => {
if (!open) { if (!open) {
@ -284,7 +288,7 @@ export function GeneralFilterContent({
}: GeneralFilterContentProps) { }: GeneralFilterContentProps) {
return ( return (
<> <>
<div className="scrollbar-container h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden"> <div className="overflow-x-hidden">
<div className="mb-5 mt-2.5 flex items-center justify-between"> <div className="mb-5 mt-2.5 flex items-center justify-between">
<Label <Label
className="mx-2 cursor-pointer text-primary" className="mx-2 cursor-pointer text-primary"

View File

@ -102,7 +102,9 @@ export default function SearchDetailDialog({
const [isOpen, setIsOpen] = useState(search != undefined); const [isOpen, setIsOpen] = useState(search != undefined);
useEffect(() => { useEffect(() => {
setIsOpen(search != undefined); if (search) {
setIsOpen(search != undefined);
}
}, [search]); }, [search]);
const searchTabs = useMemo(() => { const searchTabs = useMemo(() => {
@ -122,12 +124,6 @@ export default function SearchDetailDialog({
views.splice(index, 1); views.splice(index, 1);
} }
// TODO implement
//if (!config.semantic_search.enabled) {
// const index = views.indexOf("similar-calendar");
// views.splice(index, 1);
// }
return views; return views;
}, [config, search]); }, [config, search]);
@ -154,14 +150,7 @@ export default function SearchDetailDialog({
const Description = isDesktop ? DialogDescription : MobilePageDescription; const Description = isDesktop ? DialogDescription : MobilePageDescription;
return ( return (
<Overlay <Overlay open={isOpen} onOpenChange={() => setIsOpen(!isOpen)}>
open={isOpen}
onOpenChange={(open) => {
if (!open) {
setSearch(undefined);
}
}}
>
<Content <Content
className={cn( className={cn(
"scrollbar-container overflow-y-auto", "scrollbar-container overflow-y-auto",

View File

@ -10,7 +10,14 @@ import {
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
type PlatformAwareDialogProps = { type PlatformAwareDialogProps = {
@ -52,16 +59,20 @@ export default function PlatformAwareDialog({
type PlatformAwareSheetProps = { type PlatformAwareSheetProps = {
trigger: JSX.Element; trigger: JSX.Element;
title?: string | JSX.Element;
content: JSX.Element; content: JSX.Element;
triggerClassName?: string; triggerClassName?: string;
titleClassName?: string;
contentClassName?: string; contentClassName?: string;
open: boolean; open: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
}; };
export function PlatformAwareSheet({ export function PlatformAwareSheet({
trigger, trigger,
title,
content, content,
triggerClassName = "", triggerClassName = "",
titleClassName = "",
contentClassName = "", contentClassName = "",
open, open,
onOpenChange, onOpenChange,
@ -86,11 +97,19 @@ export function PlatformAwareSheet({
} }
return ( return (
<Sheet open={open} onOpenChange={onOpenChange}> <Sheet open={open} onOpenChange={onOpenChange} modal={false}>
<SheetTrigger asChild className={triggerClassName}> <SheetTrigger asChild className={triggerClassName}>
{trigger} {trigger}
</SheetTrigger> </SheetTrigger>
<SheetContent className={contentClassName}>{content}</SheetContent> <SheetContent className={contentClassName}>
<SheetHeader>
<SheetTitle className={title ? titleClassName : "sr-only"}>
{title ?? ""}
</SheetTitle>
<SheetDescription className="sr-only">Information</SheetDescription>
</SheetHeader>
{content}
</SheetContent>
</Sheet> </Sheet>
); );
} }

View File

@ -18,7 +18,6 @@ import {
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { isDesktop, isMobileOnly } from "react-device-detect"; import { isDesktop, isMobileOnly } from "react-device-detect";
import { useFormattedHour } from "@/hooks/use-date-utils"; import { useFormattedHour } from "@/hooks/use-date-utils";
import Heading from "@/components/ui/heading";
import FilterSwitch from "@/components/filter/FilterSwitch"; import FilterSwitch from "@/components/filter/FilterSwitch";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@ -51,9 +50,27 @@ export default function SearchFilterDialog({
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const moreFiltersSelected = useMemo(
() =>
currentFilter &&
(currentFilter.time_range ||
(currentFilter.zones?.length ?? 0) > 0 ||
(currentFilter.sub_labels?.length ?? 0) > 0 ||
(currentFilter.search_type?.length ?? 2) !== 2),
[currentFilter],
);
const trigger = ( const trigger = (
<Button className="flex items-center gap-2" size="sm"> <Button
<FaCog className={"text-secondary-foreground"} /> className="flex items-center gap-2"
size="sm"
variant={moreFiltersSelected ? "select" : "default"}
>
<FaCog
className={cn(
moreFiltersSelected ? "text-white" : "text-secondary-foreground",
)}
/>
More Filters More Filters
</Button> </Button>
); );
@ -80,14 +97,20 @@ export default function SearchFilterDialog({
setCurrentFilter({ ...currentFilter, sub_labels: newSubLabels }) setCurrentFilter({ ...currentFilter, sub_labels: newSubLabels })
} }
/> />
<SearchTypeContent {config?.semantic_search?.enabled &&
searchSources={ !currentFilter?.search_type?.includes("similarity") && (
currentFilter?.search_type ?? ["thumbnail", "description"] <SearchTypeContent
} searchSources={
setSearchSources={(newSearchSource) => currentFilter?.search_type ?? ["thumbnail", "description"]
onUpdateFilter({ ...currentFilter, search_type: newSearchSource }) }
} setSearchSources={(newSearchSource) =>
/> setCurrentFilter({
...currentFilter,
search_type: newSearchSource,
})
}
/>
)}
{isDesktop && <DropdownMenuSeparator />} {isDesktop && <DropdownMenuSeparator />}
<div className="flex items-center justify-evenly p-2"> <div className="flex items-center justify-evenly p-2">
<Button <Button
@ -104,7 +127,13 @@ export default function SearchFilterDialog({
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
setCurrentFilter(filter ?? {}); setCurrentFilter((prevFilter) => ({
...prevFilter,
time_range: undefined,
zones: undefined,
sub_labels: undefined,
search_type: ["thumbnail", "description"],
}));
}} }}
> >
Reset Reset
@ -118,7 +147,7 @@ export default function SearchFilterDialog({
trigger={trigger} trigger={trigger}
content={content} content={content}
contentClassName={cn( contentClassName={cn(
"w-auto lg:w-[300px] scrollbar-container h-full overflow-auto px-4", "w-auto lg:min-w-[275px] scrollbar-container h-full overflow-auto px-4",
isMobileOnly && "pb-20", isMobileOnly && "pb-20",
)} )}
open={open} open={open}
@ -184,8 +213,8 @@ function TimeRangeFilterContent({
return ( return (
<div className="overflow-x-hidden"> <div className="overflow-x-hidden">
<Heading as="h4">Time Range</Heading> <div className="text-lg">Time Range</div>
<div className="my-3 flex flex-row items-center justify-center gap-2"> <div className="mt-3 flex flex-row items-center justify-center gap-2">
<Popover <Popover
open={startOpen} open={startOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
@ -280,7 +309,7 @@ export function ZoneFilterContent({
<> <>
<div className="overflow-x-hidden"> <div className="overflow-x-hidden">
<DropdownMenuSeparator className="mb-3" /> <DropdownMenuSeparator className="mb-3" />
<Heading as="h4">Zones</Heading> <div className="text-lg">Zones</div>
{allZones && ( {allZones && (
<> <>
<div className="mb-5 mt-2.5 flex items-center justify-between"> <div className="mb-5 mt-2.5 flex items-center justify-between">
@ -301,7 +330,7 @@ export function ZoneFilterContent({
}} }}
/> />
</div> </div>
<div className="my-2.5 flex flex-col gap-2.5"> <div className="mt-2.5 flex flex-col gap-2.5">
{allZones.map((item) => ( {allZones.map((item) => (
<FilterSwitch <FilterSwitch
key={item} key={item}
@ -346,7 +375,7 @@ export function SubFilterContent({
return ( return (
<div className="overflow-x-hidden"> <div className="overflow-x-hidden">
<DropdownMenuSeparator className="mb-3" /> <DropdownMenuSeparator className="mb-3" />
<Heading as="h4">Sub Labels</Heading> <div className="text-lg">Sub Labels</div>
<div className="mb-5 mt-2.5 flex items-center justify-between"> <div className="mb-5 mt-2.5 flex items-center justify-between">
<Label className="mx-2 cursor-pointer text-primary" htmlFor="allLabels"> <Label className="mx-2 cursor-pointer text-primary" htmlFor="allLabels">
All Sub Labels All Sub Labels
@ -362,7 +391,7 @@ export function SubFilterContent({
}} }}
/> />
</div> </div>
<div className="my-2.5 flex flex-col gap-2.5"> <div className="mt-2.5 flex flex-col gap-2.5">
{allSubLabels.map((item) => ( {allSubLabels.map((item) => (
<FilterSwitch <FilterSwitch
key={item} key={item}
@ -403,8 +432,8 @@ export function SearchTypeContent({
<> <>
<div className="overflow-x-hidden"> <div className="overflow-x-hidden">
<DropdownMenuSeparator className="mb-3" /> <DropdownMenuSeparator className="mb-3" />
<Heading as="h4">Search Sources</Heading> <div className="text-lg">Search Sources</div>
<div className="my-2.5 flex flex-col gap-2.5"> <div className="mt-2.5 flex flex-col gap-2.5">
<FilterSwitch <FilterSwitch
label="Thumbnail Image" label="Thumbnail Image"
isChecked={searchSources?.includes("thumbnail") ?? false} isChecked={searchSources?.includes("thumbnail") ?? false}