@@ -27,6 +27,13 @@ | |||
"createdAt": "2024-01-03T10:10:00.000Z", | |||
"slug": "yet-another-article-title", | |||
"content": "Excerpt lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam." | |||
}, | |||
{ | |||
"id": 5, | |||
"title": "Yet Another Article Title", | |||
"createdAt": "2023-12-03T10:10:00.000Z", | |||
"slug": "yet-another-article-title-2", | |||
"content": "Excerpt lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam. Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod aliquam." | |||
} | |||
], | |||
"make": [ | |||
@@ -49,7 +49,7 @@ export const BlogItem: React.FC<BlogItemProps> = ({ | |||
<header className="flex flex-col gap-4"> | |||
<h1 className="m-0 pt-12 sm:h-32 text-[2.5em] sm:text-5xl flex items-end px-8"> | |||
<Link | |||
className="no-underline" | |||
className="no-underline p-1 -m-1" | |||
href={{ | |||
pathname: '/blog/articles/[slug]', | |||
query: { | |||
@@ -1,7 +1,35 @@ | |||
import * as React from 'react'; | |||
export const Brand = () => ( | |||
<span className="rounded-[0.25em] lowercase align-middle font-headings bg-positive text-negative px-[0.125em] pb-[0.2em] inline-block leading-none font-semibold -mx-2 before:content-['@']"> | |||
TheoryOfNekomata | |||
</span> | |||
export interface BrandProps { | |||
short?: boolean; | |||
} | |||
export const Brand: React.FC<BrandProps> = ({ | |||
short = false, | |||
}) => ( | |||
short | |||
? ( | |||
<span | |||
className="box-border rounded-[0.25em] lowercase align-middle font-headings bg-current px-[0.125em] pb-[0.2em] inline-block leading-none font-semibold" | |||
> | |||
<span className="text-negative before:content-['@']"> | |||
<span className="sr-only"> | |||
TheoryOf | |||
</span> | |||
Neko | |||
<span className="sr-only"> | |||
mata | |||
</span> | |||
</span> | |||
</span> | |||
) | |||
: ( | |||
<span | |||
className="box-border rounded-[0.25em] lowercase align-middle font-headings bg-current px-[0.125em] pb-[0.2em] inline-block leading-none font-semibold" | |||
> | |||
<span className="text-negative before:content-['@']"> | |||
TheoryOfNekomata | |||
</span> | |||
</span> | |||
) | |||
); |
@@ -1,6 +1,8 @@ | |||
import * as React from 'react'; | |||
import {Layouts, Widgets} from '@tesseract-design/viewfinder-react'; | |||
import {BlogItem, BlogItemProps} from '@/components/molecules/BlogItem'; | |||
import {Brand} from '@/components/molecules/Brand'; | |||
import Link from 'next/link'; | |||
interface SingleBlogItem extends BlogItemProps { | |||
id: string; | |||
@@ -14,16 +16,75 @@ export const BlogLayout: React.FC<BlogLayoutProps> = ({ | |||
blogItems, | |||
}) => ( | |||
<Layouts.LeftSidebar.Root | |||
topBarWidget={ | |||
<Widgets.TopBar | |||
brand={ | |||
<span className="text-xl w-16 flex items-center justify-center"> | |||
<Link href="/" className="flex items-center justify-center leading-none w-full h-8"> | |||
<Brand short /> | |||
</Link> | |||
</span> | |||
} | |||
span="wide" | |||
/> | |||
} | |||
sidebarBaseWidget={ | |||
<Widgets.LeftSidebarBase> | |||
<Layouts.LeftSidebar.SidebarContentContainer> | |||
<aside className="my-16"> | |||
<h1 className="m-0"> | |||
Sidebar | |||
</h1> | |||
<div> | |||
afasdfs | |||
</div> | |||
<nav className="flex flex-col gap-8"> | |||
<h1 className="m-0"> | |||
Archive | |||
</h1> | |||
{Object.entries( | |||
blogItems | |||
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) | |||
.reduce( | |||
(groupedBlogItems, item) => { | |||
const year = new Date(item.createdAt).getFullYear(); | |||
return { | |||
...groupedBlogItems, | |||
[year]: [ | |||
...(groupedBlogItems[year] ?? []), | |||
item, | |||
], | |||
} | |||
}, | |||
{} as Record<string, SingleBlogItem[]>, | |||
) | |||
) | |||
.sort(([yearA], [yearB]) => yearB.localeCompare(yearA)) | |||
.map(([year, entries]) => ( | |||
<div | |||
key={year} | |||
className="flex flex-col gap-6" | |||
> | |||
<h2 className="m-0"> | |||
{year} | |||
</h2> | |||
<div className="flex flex-col gap-4"> | |||
{entries.map((e) => ( | |||
<div | |||
key={e.id} | |||
> | |||
<Link | |||
href={{ | |||
pathname: '/blog/articles/[slug]', | |||
query: { | |||
slug: e.slug, | |||
} | |||
} as unknown as string} | |||
className="no-underline p-1 -m-1" | |||
> | |||
{e.title} | |||
</Link> | |||
</div> | |||
))} | |||
</div> | |||
</div> | |||
)) | |||
} | |||
</nav> | |||
</aside> | |||
</Layouts.LeftSidebar.SidebarContentContainer> | |||
</Widgets.LeftSidebarBase> | |||