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

feat(design-system): breadcrumb 컴포넌트 추가 #48

Merged
merged 5 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions apps/community/src/components/page-header.css.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { themeVars } from '@aics-client/design-system/styles';
import { style } from '@vanilla-extract/css';
import { PageHeader } from '~/components/page-header';

const pageHeaderWrapper = style([
const pageHeaderWrapper = style({
marginBottom: '3rem',
display: themeVars.display.flex,
flexDirection: themeVars.flexDirection.column,
gap: themeVars.spacing.xl,
});

const pageHeaderTitle = style([
themeVars.flexColumn,
{
gap: '0.75rem',
Expand All @@ -21,4 +29,4 @@ const description = style({
color: '#4E5968',
});

export { pageHeaderWrapper, title, description };
export { pageHeaderWrapper, pageHeaderTitle, title, description };
52 changes: 50 additions & 2 deletions apps/community/src/components/page-header.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
'use client';

import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Fragment } from 'react';

import { Breadcrumb } from '@aics-client/design-system';

import { PATHMAP, type pathmapKey } from '~/constants/path';

import * as styles from '~/components/page-header.css';

interface Props {
Expand All @@ -6,10 +16,48 @@ interface Props {
}

function PageHeader({ title, description }: Props) {
const pathname = usePathname();
const paths = pathname.split('/').filter((path) => path !== '');

return (
<div className={styles.pageHeaderWrapper}>
<h1 className={styles.title}>{title}</h1>
<p className={styles.description}>{description}</p>
<Breadcrumb>
<Breadcrumb.List>
<Breadcrumb.Item>
<Breadcrumb.Link>
<Link href="/">홈</Link>
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
{paths.map((path, index) =>
index !== paths.length - 1 ? (
<Fragment key={`subpath-${path}`}>
<Breadcrumb.Item>
<Breadcrumb.Link>
<Link href={`/${paths.slice(0, index + 1).join('/')}`}>
{PATHMAP[path as pathmapKey]}
</Link>
</Breadcrumb.Link>
</Breadcrumb.Item>
</Fragment>
) : (
<Fragment key={`subpath-${path}`}>
<Breadcrumb.Separator />
<Breadcrumb.Item>
<Breadcrumb.Page>
{PATHMAP[path as pathmapKey]}
</Breadcrumb.Page>
</Breadcrumb.Item>
</Fragment>
),
)}
</Breadcrumb.List>
</Breadcrumb>

<div className={styles.pageHeaderTitle}>
<h1 className={styles.title}>{title}</h1>
<p className={styles.description}>{description}</p>
</div>
</div>
);
}
Expand Down
20 changes: 19 additions & 1 deletion apps/community/src/constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,22 @@ const PATH = {
NOTICE_DETAIL: (id: number) => `/board/notice/${id}`,
};

export { PATH };
type pathmapKey = keyof typeof PATHMAP;

const PATHMAP = {
about: '소개',
dept: '학부 소개',
club: '동아리',
contact: '찾아오시는 길',
curriculum: '교육과정',
history: '연혁',
member: '구성원',
professor: '교수진 소개',
research: '연구',
lab: '연구실 소개',
board: '게시판',
notice: '공지 사항',
news: '학부 소식',
} as const;

export { PATH, PATHMAP, type pathmapKey };
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { style } from '@vanilla-extract/css';
import { screen, themeVars } from '../../styles';

const breadcrumbList = style({
display: themeVars.display.flex,
flexWrap: themeVars.flexWrap.wrap,
alignItems: themeVars.alignItems.center,
gap: themeVars.spacing.sm,
wordBreak: 'break-word',
fontSize: themeVars.fontSize.sm,
...screen.sm({
gap: '0.625rem',
}),
});

const breadcrumbItem = style({
display: themeVars.display.inlineFlex,
alignItems: themeVars.alignItems.center,
gap: '0.375rem',
});

const breadcrumbLink = style({
transition: 'color 0.2s ease-in-out',
color: themeVars.color.gray500,
selectors: {
'&:hover': {
color: themeVars.color.black,
},
},
});

const breadcrumbPage = style({
color: 'var(--foreground-color)',
fontWeight: themeVars.fontWeight.regular,
});

const breadcrumbSeperator = style({
color: themeVars.color.gray500,
});

export {
breadcrumbList,
breadcrumbItem,
breadcrumbLink,
breadcrumbPage,
breadcrumbSeperator,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Meta, StoryObj } from '@storybook/react';
import Breadcrumb from './breadcrumb';

const meta: Meta<typeof Breadcrumb> = {
title: 'Components/Spacing',
component: Breadcrumb,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
args: {},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Example: Story = {
args: {
children: (
<>
<Breadcrumb.List>
<Breadcrumb.Item>
<Breadcrumb.Link>홈</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Item>
<Breadcrumb.Link>
<Breadcrumb.Page>소개</Breadcrumb.Page>
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Item>
<Breadcrumb.Page>동아리</Breadcrumb.Page>
</Breadcrumb.Item>
</Breadcrumb.List>
</>
),
},
};
58 changes: 58 additions & 0 deletions packages/design-system/src/components/breadcrumb/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ChevronRight } from 'lucide-react';

import { cn } from '../../utils';

import * as styles from './breadcrumb.css';

export default function Breadcrumb({
...props
}: React.ComponentPropsWithoutRef<'nav'>) {
return <nav aria-label="breadcrumb" {...props} />;
}

function List({ className, ...props }: React.ComponentPropsWithoutRef<'ol'>) {
return <ol className={cn(styles.breadcrumbList, className)} {...props} />;
}

function Item({ className, ...props }: React.ComponentPropsWithoutRef<'li'>) {
return <li className={cn(styles.breadcrumbItem, className)} {...props} />;
}

function Link({
className,
...props
}: React.ComponentPropsWithoutRef<'a'> & {
isLast?: boolean;
}) {
return <a className={cn(styles.breadcrumbLink, className)} {...props} />;
}

function Page({
className,
...props
}: React.ComponentPropsWithoutRef<'span'> & {}) {
return <span className={cn(styles.breadcrumbPage, className)} {...props} />;
}

function Separator({
children,
className,
...props
}: React.ComponentPropsWithoutRef<'li'>) {
return (
<li
role="presentation"
aria-hidden="true"
className={cn(styles.breadcrumbSeperator, className)}
{...props}
>
{children ?? <ChevronRight size={'0.875rem'} />}
</li>
);
}

Breadcrumb.Item = Item;
Breadcrumb.Link = Link;
Breadcrumb.List = List;
Breadcrumb.Page = Page;
Breadcrumb.Separator = Separator;
1 change: 1 addition & 0 deletions packages/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export { default as ListRow } from './list-row/list-row';
export { default as Card } from './card/card';
export { default as Badge } from './badge/badge';
export { default as Input } from './input/input';
export { default as Breadcrumb } from './breadcrumb/breadcrumb';
export { default as ThemeProvider } from './theme-provider';
Loading