Merge branch 'main' into about-page

This commit is contained in:
Kalidou Diagne
2023-09-14 21:36:31 +02:00
29 changed files with 554 additions and 65 deletions

57
app/not-found.tsx Normal file
View File

@@ -0,0 +1,57 @@
import "@/styles/globals.css"
import React from "react"
import Image from "next/image"
import Link from "next/link"
import { fontDisplay, fontSans } from "@/lib/fonts"
import { Button } from "@/components/ui/button"
import { SiteHeader } from "@/components/site-header"
import { TailwindIndicator } from "@/components/tailwind-indicator"
export default function NotFound() {
return (
<html
lang="en"
className={`${fontSans.variable} ${fontDisplay.variable}`}
suppressHydrationWarning
>
<head />
<body className="min-h-screen">
<div className="relative flex flex-col h-screen bg-anakiwa-50">
<SiteHeader />
<div className="container m-auto">
<div className="flex flex-col -mt-16 gap-7">
<div className="flex flex-col items-center justify-center gap-3 text-center">
<div className="flex flex-col gap-2">
<Image
width={80}
height={80}
src="/icons/404-search.svg"
alt="emotion sad"
className="w-12 h-12 mx-auto md:w-24 md:h-24 text-anakiwa-400"
/>
<span className="text-5xl font-bold text-anakiwa-400 md:text-8xl font-display">
404
</span>
</div>
<div className="flex flex-col gap-5">
<span className="text-2xl font-bold md:text-6xl text-tuatara-950 font-display">
Oops! Page not found
</span>
<span className="font-sans text-base font-normal md:text-lg">
The page you are looking for might have been removed, had
its name changed or is temporarily unavailable.
</span>
</div>
</div>
<Link href="/" className="mx-auto">
<Button variant="black">Go to homepage</Button>
</Link>
</div>
</div>
</div>
<TailwindIndicator />
</body>
</html>
)
}

View File

@@ -143,6 +143,21 @@ 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>
),
plus: (props: LucideProps) => (
<svg
width="35"

View File

@@ -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>
)
}

View File

@@ -6,7 +6,7 @@ import { LucideIcon } from "lucide-react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center duration-100 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
"font-sans inline-flex items-center justify-center duration-100 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
{
variants: {
variant: {

View File

@@ -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 }

View File

@@ -7,7 +7,7 @@ Anon Aadhaar is a project that allows individuals to prove their citizenship ano
export const anonAadhaar: ProjectInterface = {
id: "anon-aadhaar",
projectStatus: "active",
image: "",
image: "anon-aadhaar.svg",
name: "Anon Aadhaar",
tldr: "Tools for building build privacy-preserving applications using government ID cards, specifically Aadhaar cards in India.",
description,
@@ -17,7 +17,7 @@ export const anonAadhaar: ProjectInterface = {
tags: {
keywords: ["Anonymity/privacy", "Social", "Identity", "Voting/governance"],
themes: ["build", "play"],
type: ["Legos/dev tools", "Lego sets/toolkits", "Proof of concept"],
types: ["Legos/dev tools", "Lego sets/toolkits", "Proof of concept"],
builtWith: ["circom", "rsa"],
},
}

View File

@@ -24,7 +24,7 @@ export const anonKlub: ProjectInterface = {
"Voting/governance",
],
themes: ["build", "play"],
type: ["Infrastructure/protocol", "Prototype", "Proof of concept"],
types: ["Infrastructure/protocol", "Prototype", "Proof of concept"],
builtWith: ["circom", "snarkjs", "halo2"],
},
}

View File

@@ -27,7 +27,7 @@ export const bandada: ProjectInterface = {
"Scaling",
"Key management",
],
type: [
types: [
"Legos/dev tools",
"Lego sets/toolkits",
"Prototype",

View File

@@ -19,7 +19,7 @@ export const channel4: ProjectInterface = {
tags: {
keywords: ["Scaling"],
themes: ["play"],
type: ["Application"],
types: ["Application"],
builtWith: ["state channel", "smart contract"],
},
}

View File

@@ -15,7 +15,7 @@ export const dslWorkingGroup: ProjectInterface = {
github: "https://github.com/privacy-scaling-explorations/chiquito/",
},
tags: {
type: ["Legos/dev tools", "Proof of concept", "Developer tooling"],
types: ["Legos/dev tools", "Proof of concept", "Developer tooling"],
keywords: [],
themes: ["research"],
builtWith: [],

View File

@@ -17,7 +17,7 @@ export const eigenTrust: ProjectInterface = {
tags: {
keywords: ["Reputation", "Identity"],
themes: ["build"],
type: ["Infrastructure/protocol"],
types: ["Infrastructure/protocol"],
builtWith: ["ethereum attestation service", "halo2", "ethers.rs"],
},
}

View File

@@ -18,7 +18,7 @@ export const pollenLabs: ProjectInterface = {
tags: {
keywords: ["Anonymity/privacy", "Scaling"],
themes: ["play"],
type: ["Application"],
types: ["Application"],
builtWith: [],
},
}

View File

@@ -7,7 +7,7 @@ Rate-Limiting Nullifier (RLN) is a protocol designed to combat spam and denial o
export const rln: ProjectInterface = {
id: "rln",
projectStatus: "active",
image: "rln.webp",
image: "rln.svg",
name: "Rate-Limiting Nullifier",
tldr: "A protocol for deterring spam and maintaining anonymity in communication systems.",
description,

View File

@@ -17,7 +17,7 @@ export const summa: ProjectInterface = {
tags: {
keywords: ["Anonymity/privacy", "Computational Integrity"],
themes: ["build", "play"],
type: ["Infrastructure/protocol", "Application"],
types: ["Infrastructure/protocol", "Application"],
builtWith: ["halo2"],
},
}

View File

@@ -18,7 +18,7 @@ export const tlsn: ProjectInterface = {
},
tags: {
themes: ["build"],
type: [],
types: [],
builtWith: [],
keywords: [],
},

View File

@@ -15,7 +15,7 @@ export const trustedSetups: ProjectInterface = {
},
tags: {
themes: ["play"],
type: ["Legos/dev tools", "Lego sets/toolkits"],
types: ["Legos/dev tools", "Lego sets/toolkits"],
builtWith: [],
keywords: ["Scaling", "Education"],
},

View File

@@ -20,7 +20,7 @@ export const unirepProtocol: ProjectInterface = {
tags: {
keywords: ["Anonymity/privacy", "Social", "Identity", "Reputation"],
themes: ["build"],
type: ["Legos/dev tools, Protocol"],
types: ["Legos/dev tools, Protocol"],
builtWith: ["semaphore", "circom"],
},
}

View File

@@ -18,7 +18,7 @@ export const zk3: ProjectInterface = {
},
tags: {
themes: ["play"],
type: [
types: [
"Legos/dev tools",
"Lego sets/toolkits",
"Infrastructure/protocol",

View File

@@ -17,7 +17,7 @@ export const zkml: ProjectInterface = {
tags: {
keywords: ["Anonymity/privacy", "Scaling"],
themes: ["research"],
type: ["Proof of concept", "Infrastructure/protocol"],
types: ["Proof of concept", "Infrastructure/protocol"],
builtWith: ["circom", "halo2", "nova"],
},
}

View File

@@ -18,8 +18,8 @@ export const zkp2p: ProjectInterface = {
},
tags: {
keywords: ["Private communications"],
theme: ["play"],
type: ["Proof of concept", "Application"],
themes: ["play"],
types: ["Proof of concept", "Application"],
builtWith: ["circom", "halo2"],
},
}

View File

@@ -19,6 +19,8 @@ export type ProjectLinkWebsite =
export type ProjectLinkType = Partial<Record<ProjectLinkWebsite, string>>
export type ProjectExtraLinkType = "buildWith" | "play" | "research" | "learn"
export type TagType = "types" | "themes" | "builtWith" | "keywords"
export type ProjectTags = Partial<Record<TagType, string[]>>
export type ActionLinkTypeLink = {
label: string
url: string
@@ -36,6 +38,6 @@ export interface ProjectInterface {
description: string
links?: ProjectLinkType
projectStatus: ProjectStatusType
tags?: Record<string, string[]>
tags?: ProjectTags
extraLinks?: ActionLinkType
}

View File

@@ -23,6 +23,7 @@
"@radix-ui/react-accordion": "^1.1.2",
"@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",

205
pnpm-lock.yaml generated
View File

@@ -14,6 +14,9 @@ dependencies:
'@radix-ui/react-dialog':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.5
version: 2.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.0.2(@types/react@18.2.7)(react@18.2.0)
@@ -360,6 +363,34 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@floating-ui/core@1.4.1:
resolution: {integrity: sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==}
dependencies:
'@floating-ui/utils': 0.1.1
dev: false
/@floating-ui/dom@1.5.1:
resolution: {integrity: sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==}
dependencies:
'@floating-ui/core': 1.4.1
'@floating-ui/utils': 0.1.1
dev: false
/@floating-ui/react-dom@2.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/dom': 1.5.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@floating-ui/utils@0.1.1:
resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==}
dev: false
/@humanwhocodes/config-array@0.11.8:
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
engines: {node: '>=10.10.0'}
@@ -574,6 +605,35 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@types/react': 18.2.7
'@types/react-dom': 18.2.4
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==}
peerDependencies:
@@ -755,6 +815,33 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-menu': 2.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@types/react': 18.2.7
'@types/react-dom': 18.2.4
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.7)(react@18.2.0):
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
peerDependencies:
@@ -807,6 +894,74 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-menu@2.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@types/react': 18.2.7
'@types/react-dom': 18.2.4
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.7
'@types/react-dom': 18.2.4
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==}
peerDependencies:
@@ -871,6 +1026,35 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0)
'@types/react': 18.2.7
'@types/react-dom': 18.2.4
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.2.7)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@@ -958,6 +1142,21 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-use-rect@1.0.1(@types/react@18.2.7)(react@18.2.0):
resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.7
react: 18.2.0
dev: false
/@radix-ui/react-use-size@1.0.1(@types/react@18.2.7)(react@18.2.0):
resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
peerDependencies:
@@ -973,6 +1172,12 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies:
'@babel/runtime': 7.22.3
dev: false
/@rushstack/eslint-patch@1.3.0:
resolution: {integrity: sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==}
dev: true

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 206 KiB

View File

@@ -5,9 +5,24 @@ import { create } from "zustand"
import { ProjectInterface } from "@/lib/types"
import { uniq } from "@/lib/utils"
export type ProjectSortBy = "random" | "asc" | "desc" | "relevance"
export type ProjectFilter = "keywords" | "builtWith" | "themes"
export type FiltersProps = Record<ProjectFilter, string[]>
interface ProjectInterfaceScore extends ProjectInterface {
score: number
}
export const SortByFnMapping: Record<
ProjectSortBy,
(a: ProjectInterfaceScore, b: ProjectInterfaceScore) => number
> = {
random: () => Math.random() - 0.5,
asc: (a, b) => a.name.localeCompare(b.name),
desc: (a, b) => b.name.localeCompare(a.name),
relevance: (a, b) => b?.score - a?.score, // sort from most relevant to least relevant
}
export const FilterLabelMapping: Record<ProjectFilter, string> = {
keywords: "Keywords",
builtWith: "Built with",
@@ -20,6 +35,7 @@ export const FilterTypeMapping: Record<ProjectFilter, "checkbox" | "button"> = {
themes: "button",
}
interface ProjectStateProps {
sortBy: ProjectSortBy
projects: ProjectInterface[]
filters: FiltersProps
activeFilters: Partial<FiltersProps>
@@ -43,6 +59,7 @@ interface ProjectActionsProps {
setFilterFromQueryString: (filters: Partial<FiltersProps>) => void
onFilterProject: (searchPattern: string) => void
onSelectTheme: (theme: string, searchPattern?: string) => void
sortProjectBy: (sortBy: ProjectSortBy) => void
}
const createURLQueryString = (params: Partial<FiltersProps>): string => {
@@ -141,17 +158,34 @@ export const filterProjects = ({
const fuse = new Fuse(projectList, {
threshold: 0.2,
useExtendedSearch: true,
includeScore: true,
keys,
})
const result = fuse.search(query)?.map(({ item }) => item)
const result = fuse.search(query)?.map(({ item, score }) => {
return {
...item,
score, // 0 indicates a perfect match, while a score of 1 indicates a complete mismatch.
}
})
return result ?? []
}
const sortProjectByFn = (
projects: ProjectInterface[],
sortBy: ProjectSortBy
) => {
const sortedProjectList: ProjectInterface[] = [
...(projects as ProjectInterfaceScore[]),
].sort(SortByFnMapping[sortBy])
return sortedProjectList
}
export const useProjectFiltersState = create<
ProjectStateProps & ProjectActionsProps
>()((set) => ({
sortBy: "random",
projects,
queryString: "",
filters: getProjectFilters(), // list of filters with all possible values from projects
@@ -183,7 +217,7 @@ export const useProjectFiltersState = create<
...state,
activeFilters,
queryString,
projects: filteredProjects,
projects: sortProjectByFn(filteredProjects, state.sortBy),
}
}),
onSelectTheme: (theme: string, searchQuery = "") => {
@@ -206,7 +240,7 @@ export const useProjectFiltersState = create<
return {
...state,
activeFilters,
projects: filteredProjects,
projects: sortProjectByFn(filteredProjects, state.sortBy),
}
})
},
@@ -219,7 +253,7 @@ export const useProjectFiltersState = create<
return {
...state,
projects: filteredProjects,
projects: sortProjectByFn(filteredProjects, state.sortBy),
}
})
},
@@ -232,4 +266,13 @@ export const useProjectFiltersState = create<
}
})
},
sortProjectBy(sortBy: ProjectSortBy) {
set((state: any) => {
return {
...state,
sortBy,
projects: sortProjectByFn(state.projects, sortBy),
}
})
},
}))

View File

@@ -30,10 +30,12 @@ module.exports = {
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
anakiwa: {
50: "#F2FAFD",
100: "hsl(var(--anakiwa))",
100: "#E4F3FA",
200: "#C2E8F5",
300: "#A3DFF0",
400: "#50C3E0",
500: "#29ACCE",
950: "#103241",
},