Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: separate UI elements into components. #62

Merged
merged 8 commits into from
Sep 16, 2024
3 changes: 1 addition & 2 deletions src/app/docs/[...categories]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import parse from 'html-react-parser';

import { REPOSITORY } from '@/constants/github';
import { DOCS } from '@/constants/path';
import Article from '@/layouts/Article';

export async function generateStaticParams() {
const paths = await fs.readdir(DOCS, {
Expand All @@ -24,7 +23,7 @@ export default async function Page({ params }) {
'utf-8',
);

return <Article>{await markdownToJsx(markdown)}</Article>;
return <>{await markdownToJsx(markdown)}</>;
}

async function markdownToHtml(markdown) {
Expand Down
37 changes: 29 additions & 8 deletions src/app/layout.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
import { SpeedInsights } from '@vercel/speed-insights/next';

import Aside from '@/layouts/Aside';
import Body from '@/layouts/Body';
import Header from '@/layouts/Header';
import Main from '@/layouts/Main';
import Article from '@/components/layouts/Article';
import Aside from '@/components/layouts/Aside';
import Body from '@/components/layouts/Body';
import Header from '@/components/layouts/Header';
import Main from '@/components/layouts/Main';

export default function Layout({ children }) {
import Categories from '@/components/aside/Categories';
import Home from '@/components/aside/Home';

import Title from '@/components/header/Title';

// TODO: import '@/styles/global.scss';

// TODO: export const metadata = {
// title: 'Next.js',
// description: 'Generated by Next.js',
// };

export default function RootLayout({ children }) {
return (
<html lang="ko">
<Body>
<Header />
<Aside />
<Main>{children}</Main>
<Header>
<Title />
</Header>
<Aside>
<Home />
<Categories />
</Aside>
<Main>
<Article>{children}</Article>
</Main>

<SpeedInsights />
</Body>
</html>
Expand Down
4 changes: 1 addition & 3 deletions src/app/page.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { USER } from '@/constants/github';

import Article from '@/layouts/Article';

export default function Page() {
return <Article>{`Hello, It's ${USER.name}'s blog!`}</Article>;
return <>{`Hello, It's ${USER.name}'s blog!`}</>;
}
37 changes: 37 additions & 0 deletions src/components/aside/Categories/Categories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { join } from 'path';

import Link from 'next/link';

import { DOCS } from '@/constants/path';
import { getDirTree } from '@/utils/dirTree';

export default async function Categories() {
const dirTree = await getDirTree(DOCS);

return <>{renderDirTree(dirTree)}</>;
}

function renderDirTree(dirTree, basePath = '') {
return (
<ul>
{dirTree.map(dirTreeNode => {
const currPath = join(basePath, dirTreeNode.name);

return (
<li key={currPath}>
{dirTreeNode.name.endsWith('.md') ? (
<Link href={`/docs/${currPath.replace('.md', '')}`}>
{dirTreeNode.name.replace('.md', '')}
</Link>
) : (
<>
<span>{dirTreeNode.name}</span>
{renderDirTree(dirTreeNode.children, currPath)}
</>
)}
</li>
);
})}
</ul>
);
}
3 changes: 3 additions & 0 deletions src/components/aside/Categories/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Categories from './Categories';

export default Categories;
11 changes: 11 additions & 0 deletions src/components/aside/Home/Home.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Link from 'next/link';

export default async function Home() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
</ul>
);
}
3 changes: 3 additions & 0 deletions src/components/aside/Home/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Home from './Home';

export default Home;
7 changes: 7 additions & 0 deletions src/components/header/Title/Title.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Link from 'next/link';

import { USER } from '@/constants/github';

export default function Title() {
return <Link href={USER.htmlUrl}>{USER.name}</Link>;
}
3 changes: 3 additions & 0 deletions src/components/header/Title/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Title from './Title';

export default Title;
5 changes: 5 additions & 0 deletions src/components/layouts/Aside/Aside.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import styles from './Aside.module.scss';

export default function Aside({ children }) {
return <aside className={styles.aside}>{children}</aside>;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.aside {
position: sticky;
top: 0;
grid-area: 1 / 1 / 3 / 2;
width: 300px;
height: 100vh;
overflow-y: scroll;
Expand Down
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions src/components/layouts/Body/Body.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.body {
display: grid;
grid-template-rows: 80px 1fr;
grid-template-columns: 300px 1fr;

> header {
grid-area: 1 / 2 / 2 / 3;
}

> aside {
grid-area: 1 / 1 / 3 / 2;
}

> main {
grid-area: 2 / 2 / 3 / 3;
}
}
File renamed without changes.
5 changes: 5 additions & 0 deletions src/components/layouts/Header/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import styles from './Header.module.scss';

export default function Header({ children }) {
return <header className={styles.header}>{children}</header>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
position: sticky;
top: 0;
z-index: 1;
grid-area: 1 / 2 / 2 / 3;
font-size: 32px;
background-color: inherit;
}
File renamed without changes.
3 changes: 3 additions & 0 deletions src/components/layouts/Main/Main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Main({ children }) {
return <main>{children}</main>;
}
File renamed without changes.
70 changes: 0 additions & 70 deletions src/layouts/Aside/Aside.jsx

This file was deleted.

5 changes: 0 additions & 5 deletions src/layouts/Body/Body.module.scss

This file was deleted.

11 changes: 0 additions & 11 deletions src/layouts/Header/Header.jsx

This file was deleted.

5 changes: 0 additions & 5 deletions src/layouts/Main/Main.jsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/layouts/Main/Main.module.scss

This file was deleted.

50 changes: 50 additions & 0 deletions src/utils/dirTree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { promises as fs } from 'fs';
import { join } from 'path';

/**
* @typedef {object} DirTreeNode
*
* @property {string} name The name of the node.
* @property {Array<DirTreeNode>} [children] The array of child nodes if the node is a directory. This is a recursive structure.
*/

/**
* Asynchronously retrieves the directory tree structure.
*
* @async
* @param {string} dirPath The path of the directory.
* @returns {Promise<Array<DirTreeNode>>} A promise that resolves to an array of `DirTreeNode`.
*
* @example
* // Get the directory tree structure
* const dirTree = await getDirTree('/path/to/dir');
* console.log(dirTree);
*/
export async function getDirTree(dirPath) {
// `readdir` automatically throws an error when `dirPath` is not a directory.
const dirents = await fs.readdir(dirPath, { withFileTypes: true });

return await Promise.all(
dirents.map(async dirent => ({
name: dirent.name,
...(dirent.isDirectory()
? { children: await getDirTree(join(dirPath, dirent.name)) }
: {}),
})),
);
}

/**
* Checks if a `DirTreeNode` is a directory.
*
* @param {DirTreeNode} dirTreeNode The `DirTreeNode` object.
* @returns {boolean} `true` if the node is a directory. otherwise, `false`.
*
* @example
* // Check if a node is a directory
* const isDir = isDirectory({ name: 'folder', children: [...] });
* console.log(isDir); // true
*/
export function isDirectory(dirTreeNode) {
return Boolean(dirTreeNode.children);
}