feat: add basic categories

This commit is contained in:
MAZE 2023-10-05 19:42:00 +03:30
parent 5791346a88
commit 8d7e4d26fd
13 changed files with 180 additions and 84 deletions

View file

@ -1,6 +1,5 @@
{
"*.{ts,tsx}": ["eslint --fix", "tsc-files --noEmit"],
"*.{js,jsx}": "eslint --fix",
"*.{ts,tsx,js,jsx}": "eslint --fix",
"*.{json,md}": "prettier --write",
"*.css": "stylelint --fix",
"*.astro": ["eslint --fix", "stylelint --fix"],

26
package-lock.json generated
View file

@ -13,7 +13,8 @@
"@types/react-dom": "^18.2.10",
"astro": "^3.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-icons": "4.11.0"
},
"devDependencies": {
"@commitlint/cli": "17.7.2",
@ -47,8 +48,7 @@
"stylelint-config-html": "1.1.0",
"stylelint-config-idiomatic-order": "9.0.0",
"stylelint-config-standard": "34.0.0",
"stylelint-prettier": "4.0.2",
"tsc-files": "1.1.4"
"stylelint-prettier": "4.0.2"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@ -12522,6 +12522,14 @@
"react": "^18.2.0"
}
},
"node_modules/react-icons": {
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz",
"integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -14674,18 +14682,6 @@
"node": ">=0.3.1"
}
},
"node_modules/tsc-files": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/tsc-files/-/tsc-files-1.1.4.tgz",
"integrity": "sha512-RePsRsOLru3BPpnf237y1Xe1oCGta8rmSYzM76kYo5tLGsv5R2r3s64yapYorGTPuuLyfS9NVbh9ydzmvNie2w==",
"dev": true,
"bin": {
"tsc-files": "cli.js"
},
"peerDependencies": {
"typescript": ">=3"
}
},
"node_modules/tsconfig-paths": {
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",

View file

@ -26,7 +26,8 @@
"@types/react-dom": "^18.2.10",
"astro": "^3.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-icons": "4.11.0"
},
"devDependencies": {
"@commitlint/cli": "17.7.2",
@ -60,7 +61,6 @@
"stylelint-config-html": "1.1.0",
"stylelint-config-idiomatic-order": "9.0.0",
"stylelint-config-standard": "34.0.0",
"stylelint-prettier": "4.0.2",
"tsc-files": "1.1.4"
"stylelint-prettier": "4.0.2"
}
}

View file

@ -1,61 +0,0 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View file

@ -0,0 +1,64 @@
import { useMemo } from 'react';
import { BiSolidTree } from 'react-icons/bi';
import { FaCity } from 'react-icons/fa';
import { Container } from '@/components/container';
import { Category } from '@/components/category';
interface CategoriesProps {
sounds: {
[id: string]: {
title: string;
sounds: Array<{
label: string;
src: string;
}>;
};
};
}
export function Categories({ sounds }: CategoriesProps) {
const categories = useMemo(() => {
const idToColor: { [id: string]: string } = {
nature: 'green',
urban: 'indigo',
};
const idToIcon: { [id: string]: React.ReactNode } = {
nature: <BiSolidTree />,
urban: <FaCity />,
};
const ids = Object.keys(sounds);
const categories: Array<{
color: string;
icon: React.ReactNode;
title: string;
id: string;
sounds: Array<{ label: string; src: string }>;
}> = [];
ids.forEach(id => {
const category = sounds[id];
categories.push({
color: idToColor[id] || 'green',
icon: idToIcon[id] || '-',
id: id,
...category,
});
});
return categories;
}, [sounds]);
return (
<Container>
<div>
{categories.map(category => (
<Category {...category} key={category.id} />
))}
</div>
</Container>
);
}

View file

@ -0,0 +1 @@
export { Categories } from './categories';

View file

@ -0,0 +1,50 @@
.category {
& .iconContainer {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 15px;
& .tail {
width: 1px;
height: 75px;
background: linear-gradient(transparent, var(--color-neutral-300));
&.green {
background: linear-gradient(transparent, #16a34a);
}
&.indigo {
background: linear-gradient(transparent, #4f46e5);
}
}
& .icon {
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
font-size: var(--font-md);
&.green {
border-color: #16a34a;
background-color: rgb(22 163 74 / 20%);
}
&.indigo {
border-color: #4f46e5;
background-color: rgb(79 70 229 / 20%);
}
}
}
& .title {
font-family: var(--font-display);
font-size: var(--font-lg);
font-weight: 600;
text-align: center;
}
}

View file

@ -0,0 +1,35 @@
import { useMemo } from 'react';
import { cn } from '@/helpers/styles';
import styles from './category.module.css';
interface CategoryProps {
color: string;
icon: React.ReactNode;
title: string;
id: string;
sounds: Array<{ label: string; src: string }>;
}
export function Category({ color, icon, title }: CategoryProps) {
const colorStyle = useMemo(() => {
const colorToStyle: { [color: string]: string } = {
green: styles.green,
indigo: styles.indigo,
};
return colorToStyle[color];
}, [color]);
return (
<div className={styles.category}>
<div className={styles.iconContainer}>
<div className={cn(styles.tail, colorStyle)} />
<div className={cn(styles.icon, colorStyle)}>{icon}</div>
</div>
<h2 className={styles.title}>{title}</h2>
</div>
);
}

View file

@ -0,0 +1 @@
export { Category } from './category';

View file

@ -1,5 +1,5 @@
.hero {
padding: 100px 0;
padding: 100px 0 10px;
text-align: center;
& .logo {

View file

@ -1,7 +1,7 @@
{
"sounds": {
"nature": {
"label": "Nature",
"title": "Nature",
"sounds": [
{
"label": "Rain",
@ -38,7 +38,7 @@
]
},
"urban": {
"label": "Urban",
"title": "Urban",
"sounds": [
{
"label": "Airport",

7
src/helpers/styles.ts Normal file
View file

@ -0,0 +1,7 @@
type className = undefined | null | false | string;
export function cn(...classNames: Array<className>): string {
const className = classNames.filter(className => !!className).join(' ');
return className;
}

View file

@ -3,9 +3,13 @@ import Layout from '@/layouts/layout.astro';
import { Gradient } from '@/components/gradient';
import { Hero } from '@/components/hero';
import { Categories } from '@/components/categories';
import sounds from '@/data/sounds.json';
---
<Layout title="Welcome to Astro.">
<Gradient />
<Hero />
<Categories client:load sounds={sounds.sounds} />
</Layout>