From 152f1c377d0f5808dd76a302b6988d18d5746dc6 Mon Sep 17 00:00:00 2001 From: Vitalii Budnik Date: Thu, 12 Sep 2024 17:43:11 +0300 Subject: [PATCH] feat: allow selecting multiple tags --- src/components/Tags/Tags.tsx | 51 +++++++++++++++++++++++++++++++++--- src/pages/index.tsx | 11 +++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/components/Tags/Tags.tsx b/src/components/Tags/Tags.tsx index b151a811..1974bda7 100644 --- a/src/components/Tags/Tags.tsx +++ b/src/components/Tags/Tags.tsx @@ -1,4 +1,5 @@ import Link, { LinkProps } from "next/link"; +import { useRouter } from "next/router"; import { ComponentPropsWithoutRef } from "react"; import styles from "./Tags.module.css"; @@ -14,13 +15,49 @@ type TagProps = { } & Omit & ComponentPropsWithoutRef<"a">; +function QueryString(tag: string, isActive: boolean | undefined) { + const router = useRouter(); + + // Convert router.query dict to URLSearchParams. + const queryParams = new URLSearchParams(); + for (const key in router.query) { + const value = router.query[key]; + if (Array.isArray(value)) { + value.forEach((v) => queryParams.append(key, v)); + } else { + queryParams.append(key, router.query[key] as string); + } + } + + // Convert tag query parameter to tag[]. + if (queryParams.has("tag")) { + queryParams.append("tag[]", queryParams.get("tag") as string); + queryParams.delete("tag"); + } + + // Update the tag query parameter. + // If tag is active, remove it from the query string. + if (isActive) { + queryParams.delete("tag[]", tag); + } else { + queryParams.append("tag[]", tag); + } + + // Convert URLSearchParams to query string. + // Replace %5B%5D= with []= to be more readable. + const queryString = queryParams.toString().replaceAll("%5B%5D=", "[]="); + + // Return the query string. + return queryString.length ? `/?${queryString}` : "/"; +} + export function Tag({ tag, isActive, className, ...props }: TagProps) { const Icon = isActive ? IconRemove : IconTag; return ( {tag} @@ -30,17 +67,23 @@ export function Tag({ tag, isActive, className, ...props }: TagProps) { interface TagsProps { tags: string[]; - activeTag?: string; + activeTags?: string[]; className?: string; } -export function Tags({ tags, activeTag, className }: TagsProps) { +export function Tags({ tags, activeTags, className }: TagsProps) { const label = getLabel("filterByTag"); + const activeTagsList = activeTags || []; return (
{!!label &&

{label}

} {tags.map((tag) => ( - + ))}
); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index af89970a..f18aedbb 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -20,7 +20,9 @@ import { CustomPage } from "@/pages/_app"; const Home: CustomPage = () => { const router = useRouter(); - const tag = router.query.tag as string | undefined; + const tag = router.query.tag ? router.query.tag : router.query["tag[]"]; + const activeTags = Array.isArray(tag) ? tag : tag ? [tag] : []; + const appName = getAppName(); const metaDescription = getLabel("metaDescription"); const chartConfig = getChartConfig(); @@ -30,7 +32,10 @@ const Home: CustomPage = () => { const quadrants = getQuadrants(); const tags = getTags(); const items = getItems(undefined, true).filter( - (item) => !tag || item.tags?.includes(tag), + (item) => + !tag || + item.tags?.filter((itemTag) => activeTags.includes(itemTag)).length === + activeTags.length, ); return ( @@ -65,7 +70,7 @@ const Home: CustomPage = () => { return ( getToggle("showTagFilter") && tags.length > 0 && ( - + ) ); case "list":