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 {
|
.revision {
|
||||||
padding: 30px 0 15px 35px;
|
padding: 30px 0 15px 35px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import styles from "./ItemDetail.module.css";
|
|||||||
|
|
||||||
import { RingBadge } from "@/components/Badge/Badge";
|
import { RingBadge } from "@/components/Badge/Badge";
|
||||||
import Attention from "@/components/Icons/Attention";
|
import Attention from "@/components/Icons/Attention";
|
||||||
|
import { Tag } from "@/components/Tags/Tags";
|
||||||
import { getReleases } from "@/lib/data";
|
import { getReleases } from "@/lib/data";
|
||||||
import { Item } from "@/lib/types";
|
import { Item } from "@/lib/types";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -19,7 +20,12 @@ interface ItemProps {
|
|||||||
export function ItemDetail({ item }: ItemProps) {
|
export function ItemDetail({ item }: ItemProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className={styles.title}>{item.title}</h1>
|
<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}>
|
<div className={styles.revisions}>
|
||||||
{isNotMaintained(item.release) && (
|
{isNotMaintained(item.release) && (
|
||||||
<div className={cn(styles.revision, styles.hint)}>
|
<div className={cn(styles.revision, styles.hint)}>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
.radar {
|
.radar {
|
||||||
padding: 0 15px;
|
padding: 0 15px 30px;
|
||||||
margin-bottom: 60px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: padding 200ms ease-in-out;
|
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;
|
return data.releases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTags(): string[] {
|
||||||
|
return data.tags;
|
||||||
|
}
|
||||||
|
|
||||||
export function getQuadrants(): Quadrant[] {
|
export function getQuadrants(): Quadrant[] {
|
||||||
return config.quadrants;
|
return config.quadrants;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import { QuadrantList } from "@/components/QuadrantList/QuadrantList";
|
import { QuadrantList } from "@/components/QuadrantList/QuadrantList";
|
||||||
import { Radar } from "@/components/Radar/Radar";
|
import { Radar } from "@/components/Radar/Radar";
|
||||||
|
import { Tags } from "@/components/Tags/Tags";
|
||||||
import {
|
import {
|
||||||
getAppName,
|
getAppName,
|
||||||
getItems,
|
getItems,
|
||||||
getQuadrants,
|
getQuadrants,
|
||||||
getReleases,
|
getReleases,
|
||||||
getRings,
|
getRings,
|
||||||
|
getTags,
|
||||||
} from "@/lib/data";
|
} from "@/lib/data";
|
||||||
import { CustomPage } from "@/pages/_app";
|
import { CustomPage } from "@/pages/_app";
|
||||||
|
|
||||||
const Home: CustomPage = () => {
|
const Home: CustomPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const tag = router.query.tag as string | undefined;
|
||||||
const appName = getAppName();
|
const appName = getAppName();
|
||||||
const version = getReleases().length;
|
const version = getReleases().length;
|
||||||
const rings = getRings();
|
const rings = getRings();
|
||||||
const quadrants = getQuadrants();
|
const quadrants = getQuadrants();
|
||||||
const items = getItems(undefined, true);
|
const tags = getTags();
|
||||||
|
const items = getItems(undefined, true).filter(
|
||||||
|
(item) => !tag || item.tags.includes(tag),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>
|
<h1>
|
||||||
@@ -24,6 +34,7 @@ const Home: CustomPage = () => {
|
|||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<Radar quadrants={quadrants} rings={rings} items={items} />
|
<Radar quadrants={quadrants} rings={rings} items={items} />
|
||||||
|
<Tags tags={tags} activeTag={tag} />
|
||||||
<QuadrantList items={items} />
|
<QuadrantList items={items} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
--text: #575757;
|
--text: #575757;
|
||||||
--highlight: #029df7;
|
--highlight: #029df7;
|
||||||
--border: rgba(255, 255, 255, 0.1);
|
--border: rgba(255, 255, 255, 0.1);
|
||||||
|
--tag: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
--overlay: #081a37b5;
|
--overlay: #081a37b5;
|
||||||
--dialog-bg: #173d7a;
|
--dialog-bg: #173d7a;
|
||||||
@@ -24,12 +25,9 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
scroll-behavior: smooth;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -62,6 +60,7 @@ img {
|
|||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: block;
|
display: block;
|
||||||
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
|
|||||||
Reference in New Issue
Block a user