feat(guide): sidebar

This commit is contained in:
iCrawl 2023-04-12 00:39:35 +02:00
parent 731ea5f3cb
commit ee907f32f3
No known key found for this signature in database
GPG key ID: 1AB888B16355FBB2
12 changed files with 82 additions and 12 deletions

View file

@ -17,21 +17,21 @@ export const Content = defineDocumentType(() => ({
type: 'string',
required: true,
},
summary: {
type: 'string',
},
image: {
category: {
type: 'string',
required: true,
},
},
computedFields: {
slug: {
type: 'string',
resolve: (doc) => doc._raw.flattenedPath,
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => doc._raw.flattenedPath.replace(/\d+-/g, ''),
},
url: {
type: 'string',
resolve: (post) => `/guide/${post._raw.flattenedPath}`,
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => `/guide/${doc._raw.flattenedPath.replace(/\d+-/g, '')}`,
},
},
}));

View file

@ -1,5 +1,5 @@
import { allContents } from 'contentlayer/generated';
import { notFound } from 'next/navigation';
import { redirect } from 'next/navigation';
import { Mdx } from '~/components/Mdx';
export async function generateStaticParams() {
@ -10,7 +10,7 @@ export default function Page({ params }: { params: { slug: string[] } }) {
const content = allContents.find((content) => content.slug === params.slug?.join('/'));
if (!content) {
notFound();
redirect('/guide/introduction');
}
return (

View file

@ -0,0 +1,16 @@
'use client';
import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
import type { PropsWithChildren } from 'react';
import { useMedia } from 'react-use';
// This is wrapper around the Section component from @discordjs/ui,
// it simply automatically sets the dense prop to true if the screen
// width is less than 768px. This is done to separate client-side logic
// from server-side rendering.
export function Section(options: PropsWithChildren<SectionOptions>) {
const matches = useMedia('(max-width: 768px)', true);
const modifiedOptions = { ...options, dense: matches };
return <DJSSection {...modifiedOptions} />;
}

View file

@ -1,8 +1,62 @@
'use client';
export function Sidebar() {
// const pathname = usePathname();
// const { setOpened } = useNav();
import { allContents } from 'contentlayer/generated';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Section } from './Section';
import { useNav } from '~/contexts/nav';
return null;
function transformItemsByCategory(allContents: any[]) {
return allContents.reduce((accumulator: any, content) => {
if (!accumulator[content.category]) {
accumulator[content.category] = [];
}
accumulator[content.category].push(content);
return accumulator;
}, {});
}
const items = allContents.map((content) => ({
title: content.title,
category: content.category,
slug: content.slug,
href: content.url,
}));
const itemsByCategory = transformItemsByCategory(items);
export function Sidebar() {
const pathname = usePathname();
const { setOpened } = useNav();
return (
<div className="flex flex-col gap-3 p-3">
{Object.keys(itemsByCategory).map((category, idx) => (
<Section
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
key={`${category}-${idx}`}
title={category}
>
{itemsByCategory[category].map((member, index) => (
<Link
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
decodeURIComponent(pathname ?? '') === member.href
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}
href={member.href}
key={`${member.title}-${index}`}
onClick={() => setOpened(false)}
title={member.title}
>
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
<span className="truncate">{member.title}</span>
</div>
</Link>
))}
</Section>
))}
</div>
);
}