Skip to content

Commit

Permalink
Merge pull request #81 from privacy-scaling-explorations/45-add-filte…
Browse files Browse the repository at this point in the history
…r-functionality-sorting-to-project-lib

45 add filter functionality sorting to project lib
  • Loading branch information
kalidiagne authored Sep 14, 2023
2 parents d0575a8 + 46d3a05 commit f19a2db
Show file tree
Hide file tree
Showing 6 changed files with 442 additions and 43 deletions.
15 changes: 15 additions & 0 deletions components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,19 @@ export const Icons = {
/>
</svg>
),
arrowDown: (props: LucideProps) => (
<svg
width="25"
height="25"
viewBox="0 0 25 25"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M12.4997 13.6719L17.4497 8.72192L18.8637 10.1359L12.4997 16.4999L6.13574 10.1359L7.54974 8.72192L12.4997 13.6719Z"
fill="currentColor"
/>
</svg>
),
}
94 changes: 56 additions & 38 deletions components/project/project-result-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,79 @@

import {
ProjectFilter,
ProjectSortBy,
useProjectFiltersState,
} from "@/state/useProjectFiltersState"

import { CategoryTag } from "../ui/categoryTag"
import { Dropdown } from "../ui/dropdown"

const labelClass = "h-5 text-xs text-base md:h-6 text-slate-900/70 md:text-lg"

const projectSortItems: { label: string; value: ProjectSortBy }[] = [
{ label: "Random", value: "random" },
{ label: "Title: A-Z", value: "asc" },
{ label: "Title: Z-A", value: "desc" },
{ label: "Relevance", value: "relevance" },
]

const getSortLabel = (sortBy: ProjectSortBy) => {
return projectSortItems.find((item) => item.value === sortBy)?.label || sortBy
}

export const ProjectResultBar = () => {
const { activeFilters, toggleFilter, projects } = useProjectFiltersState(
(state) => state
)
const { activeFilters, toggleFilter, projects, sortProjectBy, sortBy } =
useProjectFiltersState((state) => state)

const haveActiveFilters = Object.entries(activeFilters).some(
([_key, values]) => values?.length > 0
)

if (!haveActiveFilters)
return (
<span className={labelClass}>
{`Showing ${projects.length} projects`}{" "}
</span>
)
const resultLabel = haveActiveFilters
? `Showing ${projects?.length} projects with:`
: `Showing ${projects.length} projects`

return (
<div className="flex flex-col gap-2">
<span className={labelClass}>
{`Showing ${projects?.length} projects with:`}{" "}
</span>
<div className="inline-flex flex-wrap gap-1 md:gap-4">
{Object.entries(activeFilters).map(([key, filters], index) => {
return (
<>
{filters?.map((filter) => {
if (filter?.length === 0) return null

return (
<CategoryTag
closable
variant="gray"
onClose={() =>
toggleFilter({
tag: key as ProjectFilter,
value: filter,
})
}
key={`${index}-${filter}`}
>
{filter}
</CategoryTag>
)
})}
</>
)
})}
<div className="flex items-center justify-between">
<span className={labelClass}>{resultLabel}</span>
<Dropdown
label={`Sort by: ${getSortLabel(sortBy)}`}
defaultItem="random"
items={projectSortItems}
onChange={(sortBy) => sortProjectBy(sortBy as ProjectSortBy)}
disabled={!projects?.length}
/>
</div>
{haveActiveFilters && (
<div className="inline-flex flex-wrap gap-1 md:gap-4">
{Object.entries(activeFilters).map(([key, filters], index) => {
return (
<>
{filters?.map((filter) => {
if (filter?.length === 0) return null

return (
<CategoryTag
closable
variant="gray"
onClose={() =>
toggleFilter({
tag: key as ProjectFilter,
value: filter,
})
}
key={`${index}-${filter}`}
>
{filter}
</CategoryTag>
)
})}
</>
)
})}
</div>
)}
</div>
)
}
87 changes: 87 additions & 0 deletions components/ui/dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState } from "react"
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"

import { cn } from "@/lib/utils"

import { Icons } from "../icons"

interface DropdownItemProps {
label: string
value?: string | number
}

interface DropdownProps {
label: string
items?: DropdownItemProps[]
defaultItem?: string | number
onChange?: (value: DropdownItemProps["value"]) => void
disabled?: boolean
}

const Dropdown = ({
label,
onChange,
defaultItem,
disabled,
items,
}: DropdownProps) => {
const [selected, setSelected] =
useState<DropdownItemProps["value"]>(defaultItem)

const onSelectCallback = ({ value }: DropdownItemProps) => {
setSelected(value)
if (typeof onChange === "function") onChange(value)
}

return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild disabled={disabled}>
<button
className={cn("focus:outline-none ring-0", {
"opacity-70 cursor-not-allowed": disabled,
})}
aria-label="dropdown menu"
>
<div className="flex items-center gap-1">
<span className="text-sm font-medium break-words text-tuatara-950">
{label}
</span>
<Icons.arrowDown />
</div>
</button>
</DropdownMenu.Trigger>

<DropdownMenu.Portal>
<DropdownMenu.Content
className="min-w-[136px] max-h-[250px] overflow-scroll border border-tuatara-200 rounded-md bg-white py-2"
sideOffset={5}
>
{items?.map((item, index) => {
const active = selected === item.value
return (
<DropdownMenu.Item
key={index}
className={cn(
"relative py-3 px-5 w-full font-sans text-sm cursor-pointer hover:font-medium focus:outline-none ring-0 hover:text-anakiwa-500 text-duration-200",
{
"text-tuatara-950 font-normal": !active,
"text-anakiwa-500 font-medium": active,
}
)}
onSelect={() => onSelectCallback(item)}
>
{active && (
<div className="bg-anakiwa-500 w-[3px] absolute left-0 top-0 bottom-0"></div>
)}
{item.label}
</DropdownMenu.Item>
)
})}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
}

Dropdown.displayName = "Dropdown"
export { Dropdown }
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.4.0",
"clsx": "^1.2.1",
Expand Down
Loading

1 comment on commit f19a2db

@vercel
Copy link

@vercel vercel bot commented on f19a2db Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.