feat: add basic overview page
This commit is contained in:
committed by
Mathias Schopmans
parent
38a59b029b
commit
1f3e1045c3
@@ -27,3 +27,24 @@
|
||||
color: var(--foreground);
|
||||
background-color: var(--badge);
|
||||
}
|
||||
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.selected) {
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--foreground);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:not(.colored) {
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--foreground);
|
||||
background: transparent;
|
||||
|
||||
&.selected {
|
||||
color: var(--background);
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { cn } from "@/lib/utils";
|
||||
interface BadgeProps extends ComponentPropsWithoutRef<"span"> {
|
||||
children?: ReactNode;
|
||||
color?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
size?: "small" | "medium" | "large";
|
||||
}
|
||||
|
||||
@@ -22,6 +24,8 @@ export function Badge({
|
||||
children,
|
||||
color,
|
||||
size = "medium",
|
||||
selectable,
|
||||
selected,
|
||||
...props
|
||||
}: BadgeProps) {
|
||||
const style = useMemo(
|
||||
@@ -40,6 +44,8 @@ export function Badge({
|
||||
styles.badge,
|
||||
styles[`size-${size}`],
|
||||
color && styles.colored,
|
||||
selectable && styles.selectable,
|
||||
selected && styles.selected,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
8
src/components/Filter/Filter.module.css
Normal file
8
src/components/Filter/Filter.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.filter {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
25
src/components/Filter/Filter.tsx
Normal file
25
src/components/Filter/Filter.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import styles from "./Filter.module.css";
|
||||
|
||||
import { QueryFilter } from "@/components/Filter/QueryFilter";
|
||||
import { RingFilter } from "@/components/Filter/RingFilter";
|
||||
|
||||
interface FilterProps {
|
||||
query?: string;
|
||||
onQueryChange: (query: string) => void;
|
||||
ring?: string;
|
||||
onRingChange: (ring: string) => void;
|
||||
}
|
||||
|
||||
export function Filter({
|
||||
query,
|
||||
onQueryChange,
|
||||
ring,
|
||||
onRingChange,
|
||||
}: FilterProps) {
|
||||
return (
|
||||
<div className={styles.filter}>
|
||||
<QueryFilter value={query} onChange={onQueryChange} />
|
||||
<RingFilter value={ring} onChange={onRingChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
25
src/components/Filter/QueryFilter.module.css
Normal file
25
src/components/Filter/QueryFilter.module.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.filter {
|
||||
flex: 1 1 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.filter {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
29
src/components/Filter/QueryFilter.tsx
Normal file
29
src/components/Filter/QueryFilter.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ChangeEvent } from "react";
|
||||
|
||||
import Search from "../Icons/Search";
|
||||
import styles from "./QueryFilter.module.css";
|
||||
|
||||
interface QueryFilterProps {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export function QueryFilter({ value, onChange }: QueryFilterProps) {
|
||||
const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.filter}>
|
||||
<input
|
||||
className={styles.input}
|
||||
type="search"
|
||||
value={value}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
<button className={styles.button} type="submit">
|
||||
<Search />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
src/components/Filter/RingFilter.module.css
Normal file
8
src/components/Filter/RingFilter.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.filter {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
43
src/components/Filter/RingFilter.tsx
Normal file
43
src/components/Filter/RingFilter.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import styles from "./RingFilter.module.css";
|
||||
|
||||
import { Badge, RingBadge } from "@/components/Badge/Badge";
|
||||
import { getRings } from "@/lib/data";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface RingFilterProps {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function RingFilter({ value, onChange, className }: RingFilterProps) {
|
||||
const rings = getRings();
|
||||
|
||||
return (
|
||||
<ul className={cn(styles.filter, className)}>
|
||||
<li>
|
||||
<Badge
|
||||
size="large"
|
||||
selectable
|
||||
selected={!value}
|
||||
onClick={() => {
|
||||
onChange("");
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Badge>
|
||||
</li>
|
||||
{rings.map((ring) => (
|
||||
<li key={ring.id}>
|
||||
<RingBadge
|
||||
ring={ring.id}
|
||||
size="large"
|
||||
selectable
|
||||
selected={value === ring.id}
|
||||
onClick={() => onChange(ring.id)}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { cn } from "@/lib/utils";
|
||||
export interface ItemListProps {
|
||||
items: Item[];
|
||||
activeId?: string;
|
||||
size?: "small" | "default";
|
||||
size?: "small" | "default" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ export function ItemList({
|
||||
<ul
|
||||
className={cn(styles.list, className, {
|
||||
[styles.isSmall]: size === "small",
|
||||
[styles.large]: size === "large",
|
||||
})}
|
||||
>
|
||||
{items.map((item) => (
|
||||
|
||||
@@ -3,16 +3,13 @@ import Link from "next/link";
|
||||
import styles from "./QuadrantList.module.css";
|
||||
|
||||
import { RingList } from "@/components/RingList/RingList";
|
||||
import {
|
||||
getQuadrant,
|
||||
groupItemsByQuadrant,
|
||||
groupItemsByRing,
|
||||
} from "@/lib/data";
|
||||
import { getQuadrant, groupItemsByQuadrant } from "@/lib/data";
|
||||
import { Item } from "@/lib/types";
|
||||
|
||||
interface RingListProps {
|
||||
items: Item[];
|
||||
}
|
||||
|
||||
export function QuadrantList({ items }: RingListProps) {
|
||||
const quadrants = groupItemsByQuadrant(items);
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user