Filter panel visual fixes (#105)

* Make filter panel full page height

* Fix filter items text

when the filter items were cutoff, the text would break onto multiple
lines and the count would overlap with the text

---------

Co-authored-by: Brendan Kellam <bshizzle1234@gmail.com>
This commit is contained in:
Konrad Staniszewski 2024-12-03 12:46:42 -08:00 committed by GitHub
parent 7915af8acd
commit ce8232a23c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 23 deletions

View file

@ -25,10 +25,14 @@ export const Entry = ({
},
onClicked,
}: EntryProps) => {
let countText = count.toString();
if (count > 999) {
countText = "999+";
}
return (
<div
className={clsx("flex flex-row items-center justify-between py-0.5 px-2 cursor-pointer rounded-md gap-2 select-none",
className={clsx(
"flex flex-row items-center justify-between py-0.5 px-2 cursor-pointer rounded-md gap-2 select-none",
{
"hover:bg-gray-200 dark:hover:bg-gray-700": !isSelected,
"bg-blue-200 dark:bg-blue-400": isSelected,
@ -36,13 +40,15 @@ export const Entry = ({
)}
onClick={() => onClicked()}
>
<div className="flex flex-row items-center gap-1">
<div className="flex flex-row items-center gap-1 overflow-hidden">
{Icon ? Icon : (
<QuestionMarkCircledIcon className="w-4 h-4 flex-shrink-0" />
)}
<p className="text-wrap">{displayName}</p>
<p className="overflow-hidden text-ellipsis whitespace-nowrap">{displayName}</p>
</div>
<div className="px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-sm rounded-md">
{countText}
</div>
<p>{count}</p>
</div>
);
}
@ -53,4 +59,4 @@ export const compareEntries = (a: Entry, b: Entry) => {
}
return a.count - b.count;
}
}

View file

@ -5,12 +5,14 @@ import { compareEntries, Entry } from "./entry";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import Fuse from "fuse.js";
import { cn } from "@/lib/utils"
interface FilterProps {
title: string,
searchPlaceholder: string,
entries: Entry[],
onEntryClicked: (key: string) => void,
className?: string,
}
export const Filter = ({
@ -18,6 +20,7 @@ export const Filter = ({
searchPlaceholder,
entries,
onEntryClicked,
className,
}: FilterProps) => {
const [searchFilter, setSearchFilter] = useState<string>("");
@ -36,7 +39,10 @@ export const Filter = ({
}, [entries, searchFilter]);
return (
<div className="flex flex-col gap-2 p-1">
<div className={cn(
"flex flex-col gap-2 p-1",
className
)}>
<h2 className="text-sm font-semibold">{title}</h2>
<Input
placeholder={searchPlaceholder}
@ -44,11 +50,9 @@ export const Filter = ({
onChange={(event) => setSearchFilter(event.target.value)}
/>
<ScrollArea
className="overflow-hidden"
>
<ScrollArea>
<div
className="flex flex-col gap-0.5 text-sm h-full max-h-80 px-0.5"
className="flex flex-col gap-0.5 text-sm px-0.5"
>
{filteredEntries
.sort((entryA, entryB) => compareEntries(entryB, entryA))
@ -63,4 +67,4 @@ export const Filter = ({
</ScrollArea>
</div>
)
}
}

View file

@ -118,22 +118,23 @@ export const FilterPanel = ({
onFilterChanged(filteredMatches);
}, [matches, repos, languages, onFilterChanged]);
const numRepos = Object.keys(repos).length > 100 ? '100+' : Object.keys(repos).length;
const numLanguages = Object.keys(languages).length > 100 ? '100+' : Object.keys(languages).length;
return (
<div className="p-3 flex flex-col gap-3">
<h1 className="text-lg font-semibold">Filter Results</h1>
<div className="p-3 flex flex-col gap-3 h-full">
<Filter
title="By Repository"
searchPlaceholder="Filter repositories"
title="Filter By Repository"
searchPlaceholder={`Filter ${numRepos} repositories`}
entries={Object.values(repos)}
onEntryClicked={(key) => onEntryClicked(key, setRepos)}
className="max-h-[50%]"
/>
<Filter
title="By Language"
searchPlaceholder="Filter languages"
title="Filter By Language"
searchPlaceholder={`Filter ${numLanguages} languages`}
entries={Object.values(languages)}
onEntryClicked={(key) => onEntryClicked(key, setLanguages)}
className="overflow-auto"
/>
</div>
)
@ -167,4 +168,4 @@ const aggregateMatches = (
aggregation[key].count += 1;
return aggregation;
}, {} as Record<string, Entry>)
}
}

View file

@ -286,6 +286,7 @@ const PanelGroup = ({
return (
<ResizablePanelGroup
direction="horizontal"
className="h-full"
>
{/* ~~ Filter panel ~~ */}
<ResizablePanel
@ -354,4 +355,4 @@ const PanelGroup = ({
</ResizablePanel>
</ResizablePanelGroup>
)
}
}

View file

@ -29,7 +29,10 @@ const ResizableHandle = ({
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
"relative flex w-px items-center justify-center bg-border",
"after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1",
"data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
{...props}