feat: add tag filter on homepage
This commit is contained in:
committed by
Mathias Schopmans
parent
e42b175ab7
commit
13591b9672
@@ -1,3 +1,14 @@
|
||||
.header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 30px 0 0;
|
||||
}
|
||||
|
||||
.revision {
|
||||
padding: 30px 0 15px 35px;
|
||||
margin-left: 5px;
|
||||
|
||||
@@ -2,6 +2,7 @@ import styles from "./ItemDetail.module.css";
|
||||
|
||||
import { RingBadge } from "@/components/Badge/Badge";
|
||||
import Attention from "@/components/Icons/Attention";
|
||||
import { Tag } from "@/components/Tags/Tags";
|
||||
import { getReleases } from "@/lib/data";
|
||||
import { Item } from "@/lib/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -19,7 +20,12 @@ interface ItemProps {
|
||||
export function ItemDetail({ item }: ItemProps) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.header}>
|
||||
<h1 className={styles.title}>{item.title}</h1>
|
||||
{item.tags.map((tag) => (
|
||||
<Tag key={tag} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.revisions}>
|
||||
{isNotMaintained(item.release) && (
|
||||
<div className={cn(styles.revision, styles.hint)}>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.radar {
|
||||
padding: 0 15px;
|
||||
margin-bottom: 60px;
|
||||
padding: 0 15px 30px;
|
||||
position: relative;
|
||||
transition: padding 200ms ease-in-out;
|
||||
}
|
||||
|
||||
43
src/components/Tags/Tags.module.css
Normal file
43
src/components/Tags/Tags.module.css
Normal file
@@ -0,0 +1,43 @@
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: -2px 6px 0 -5px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 6px 15px 5px;
|
||||
margin: 6px;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid var(--tag);
|
||||
border-radius: 13px;
|
||||
background: var(--tag);
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.active {
|
||||
background: var(--foreground);
|
||||
color: var(--background);
|
||||
}
|
||||
|
||||
&.active {
|
||||
.icon {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
text-align: center;
|
||||
margin: 0 auto 60px;
|
||||
max-width: 600px;
|
||||
}
|
||||
45
src/components/Tags/Tags.tsx
Normal file
45
src/components/Tags/Tags.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import Link, { LinkProps } from "next/link";
|
||||
import { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./Tags.module.css";
|
||||
|
||||
import IconRemove from "@/components/Icons/Close";
|
||||
import IconTag from "@/components/Icons/Tag";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type TagProps = {
|
||||
tag: string;
|
||||
isActive?: boolean;
|
||||
} & Omit<LinkProps, "href"> &
|
||||
ComponentPropsWithoutRef<"a">;
|
||||
|
||||
export function Tag({ tag, isActive, className, ...props }: TagProps) {
|
||||
const Icon = isActive ? IconRemove : IconTag;
|
||||
return (
|
||||
<Link
|
||||
{...props}
|
||||
className={cn(styles.tag, className, isActive && styles.active)}
|
||||
href={isActive ? "/" : `/?tag=${tag}`}
|
||||
>
|
||||
<Icon className={cn(styles.icon)} />
|
||||
<span className={styles.label}>{tag}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
interface TagsProps {
|
||||
tags: string[];
|
||||
activeTag?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Tags({ tags, activeTag, className }: TagsProps) {
|
||||
return (
|
||||
<div className={cn(styles.tags, className)}>
|
||||
<h3>Filter by Tag</h3>
|
||||
{tags.map((tag) => (
|
||||
<Tag key={tag} tag={tag} isActive={activeTag == tag} scroll={false} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1
src/icons/tag.svg
Normal file
1
src/icons/tag.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg version="1.1" viewBox="0 0 30 30" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M23,3h-6c-0.512,0-1.024,0.195-1.414,0.586l-12,12c-0.781,0.781-0.781,2.047,0,2.828l8,8c0.781,0.781,2.047,0.781,2.828,0 c0.391-0.391,11.609-11.609,12-12C26.805,14.024,27,13.512,27,13V7C27,4.791,25.209,3,23,3z M23,9c-1.105,0-2-0.895-2-2 c0-1.105,0.895-2,2-2s2,0.895,2,2C25,8.105,24.105,9,23,9z"/></svg>
|
||||
|
After Width: | Height: | Size: 470 B |
@@ -36,6 +36,10 @@ export function getReleases(): string[] {
|
||||
return data.releases;
|
||||
}
|
||||
|
||||
export function getTags(): string[] {
|
||||
return data.tags;
|
||||
}
|
||||
|
||||
export function getQuadrants(): Quadrant[] {
|
||||
return config.quadrants;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { QuadrantList } from "@/components/QuadrantList/QuadrantList";
|
||||
import { Radar } from "@/components/Radar/Radar";
|
||||
import { Tags } from "@/components/Tags/Tags";
|
||||
import {
|
||||
getAppName,
|
||||
getItems,
|
||||
getQuadrants,
|
||||
getReleases,
|
||||
getRings,
|
||||
getTags,
|
||||
} from "@/lib/data";
|
||||
import { CustomPage } from "@/pages/_app";
|
||||
|
||||
const Home: CustomPage = () => {
|
||||
const router = useRouter();
|
||||
const tag = router.query.tag as string | undefined;
|
||||
const appName = getAppName();
|
||||
const version = getReleases().length;
|
||||
const rings = getRings();
|
||||
const quadrants = getQuadrants();
|
||||
const items = getItems(undefined, true);
|
||||
const tags = getTags();
|
||||
const items = getItems(undefined, true).filter(
|
||||
(item) => !tag || item.tags.includes(tag),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>
|
||||
@@ -24,6 +34,7 @@ const Home: CustomPage = () => {
|
||||
</span>
|
||||
</h1>
|
||||
<Radar quadrants={quadrants} rings={rings} items={items} />
|
||||
<Tags tags={tags} activeTag={tag} />
|
||||
<QuadrantList items={items} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
--text: #575757;
|
||||
--highlight: #029df7;
|
||||
--border: rgba(255, 255, 255, 0.1);
|
||||
--tag: rgba(255, 255, 255, 0.1);
|
||||
|
||||
--overlay: #081a37b5;
|
||||
--dialog-bg: #173d7a;
|
||||
@@ -24,12 +25,9 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
scroll-behavior: smooth;
|
||||
max-width: 100vw;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -62,6 +60,7 @@ img {
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
||||
Reference in New Issue
Block a user