mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +00:00
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:
parent
7915af8acd
commit
ce8232a23c
5 changed files with 38 additions and 23 deletions
|
|
@ -25,10 +25,14 @@ export const Entry = ({
|
||||||
},
|
},
|
||||||
onClicked,
|
onClicked,
|
||||||
}: EntryProps) => {
|
}: EntryProps) => {
|
||||||
|
let countText = count.toString();
|
||||||
|
if (count > 999) {
|
||||||
|
countText = "999+";
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<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,
|
"hover:bg-gray-200 dark:hover:bg-gray-700": !isSelected,
|
||||||
"bg-blue-200 dark:bg-blue-400": isSelected,
|
"bg-blue-200 dark:bg-blue-400": isSelected,
|
||||||
|
|
@ -36,13 +40,15 @@ export const Entry = ({
|
||||||
)}
|
)}
|
||||||
onClick={() => onClicked()}
|
onClick={() => onClicked()}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center gap-1">
|
<div className="flex flex-row items-center gap-1 overflow-hidden">
|
||||||
{Icon ? Icon : (
|
{Icon ? Icon : (
|
||||||
<QuestionMarkCircledIcon className="w-4 h-4 flex-shrink-0" />
|
<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>
|
</div>
|
||||||
<p>{count}</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -53,4 +59,4 @@ export const compareEntries = (a: Entry, b: Entry) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.count - b.count;
|
return a.count - b.count;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ import { compareEntries, Entry } from "./entry";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
interface FilterProps {
|
interface FilterProps {
|
||||||
title: string,
|
title: string,
|
||||||
searchPlaceholder: string,
|
searchPlaceholder: string,
|
||||||
entries: Entry[],
|
entries: Entry[],
|
||||||
onEntryClicked: (key: string) => void,
|
onEntryClicked: (key: string) => void,
|
||||||
|
className?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Filter = ({
|
export const Filter = ({
|
||||||
|
|
@ -18,6 +20,7 @@ export const Filter = ({
|
||||||
searchPlaceholder,
|
searchPlaceholder,
|
||||||
entries,
|
entries,
|
||||||
onEntryClicked,
|
onEntryClicked,
|
||||||
|
className,
|
||||||
}: FilterProps) => {
|
}: FilterProps) => {
|
||||||
const [searchFilter, setSearchFilter] = useState<string>("");
|
const [searchFilter, setSearchFilter] = useState<string>("");
|
||||||
|
|
||||||
|
|
@ -36,7 +39,10 @@ export const Filter = ({
|
||||||
}, [entries, searchFilter]);
|
}, [entries, searchFilter]);
|
||||||
|
|
||||||
return (
|
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>
|
<h2 className="text-sm font-semibold">{title}</h2>
|
||||||
<Input
|
<Input
|
||||||
placeholder={searchPlaceholder}
|
placeholder={searchPlaceholder}
|
||||||
|
|
@ -44,11 +50,9 @@ export const Filter = ({
|
||||||
onChange={(event) => setSearchFilter(event.target.value)}
|
onChange={(event) => setSearchFilter(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScrollArea
|
<ScrollArea>
|
||||||
className="overflow-hidden"
|
|
||||||
>
|
|
||||||
<div
|
<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
|
{filteredEntries
|
||||||
.sort((entryA, entryB) => compareEntries(entryB, entryA))
|
.sort((entryA, entryB) => compareEntries(entryB, entryA))
|
||||||
|
|
@ -63,4 +67,4 @@ export const Filter = ({
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,22 +118,23 @@ export const FilterPanel = ({
|
||||||
onFilterChanged(filteredMatches);
|
onFilterChanged(filteredMatches);
|
||||||
}, [matches, repos, languages, onFilterChanged]);
|
}, [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 (
|
return (
|
||||||
<div className="p-3 flex flex-col gap-3">
|
<div className="p-3 flex flex-col gap-3 h-full">
|
||||||
<h1 className="text-lg font-semibold">Filter Results</h1>
|
|
||||||
|
|
||||||
<Filter
|
<Filter
|
||||||
title="By Repository"
|
title="Filter By Repository"
|
||||||
searchPlaceholder="Filter repositories"
|
searchPlaceholder={`Filter ${numRepos} repositories`}
|
||||||
entries={Object.values(repos)}
|
entries={Object.values(repos)}
|
||||||
onEntryClicked={(key) => onEntryClicked(key, setRepos)}
|
onEntryClicked={(key) => onEntryClicked(key, setRepos)}
|
||||||
|
className="max-h-[50%]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Filter
|
<Filter
|
||||||
title="By Language"
|
title="Filter By Language"
|
||||||
searchPlaceholder="Filter languages"
|
searchPlaceholder={`Filter ${numLanguages} languages`}
|
||||||
entries={Object.values(languages)}
|
entries={Object.values(languages)}
|
||||||
onEntryClicked={(key) => onEntryClicked(key, setLanguages)}
|
onEntryClicked={(key) => onEntryClicked(key, setLanguages)}
|
||||||
|
className="overflow-auto"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -167,4 +168,4 @@ const aggregateMatches = (
|
||||||
aggregation[key].count += 1;
|
aggregation[key].count += 1;
|
||||||
return aggregation;
|
return aggregation;
|
||||||
}, {} as Record<string, Entry>)
|
}, {} as Record<string, Entry>)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ const PanelGroup = ({
|
||||||
return (
|
return (
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
direction="horizontal"
|
direction="horizontal"
|
||||||
|
className="h-full"
|
||||||
>
|
>
|
||||||
{/* ~~ Filter panel ~~ */}
|
{/* ~~ Filter panel ~~ */}
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
|
|
@ -354,4 +355,4 @@ const PanelGroup = ({
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,10 @@ const ResizableHandle = ({
|
||||||
}) => (
|
}) => (
|
||||||
<ResizablePrimitive.PanelResizeHandle
|
<ResizablePrimitive.PanelResizeHandle
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue