Skip to content

Commit

Permalink
탭 컴포넌트 작업 (#27)
Browse files Browse the repository at this point in the history
* feat: NavLink 컴포넌트 작성

* feat: NavLink 컴포넌트 사용

* refactor: QuizLinkTab 컴포넌트로 분리

* refactor: QuizLinkTab 제거 후 레이아웃 활용하는 방식으로 변경

* feat: 로딩 화면 작성

* style: twMerge 대신 cn으로 변경

* style: loaddingSpinner에 twMerge 대신 cn 적용
  • Loading branch information
bbearcookie authored Dec 22, 2023
1 parent 5fa7d31 commit 2730c7e
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 43 deletions.
27 changes: 27 additions & 0 deletions app/quizzes/[id]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import NavLink from '@/components/common/link/nav-link';
import React from 'react';

interface LayoutProps {
children: React.ReactNode;
params: {
id: string;
};
}

export default function Layout({ children, params }: LayoutProps) {
const quizId = Number(params.id) ?? 0;

return (
<section className="p-4">
<article className="mb-4 flex w-full">
<NavLink className="grow" href={`/quizzes/${quizId}`}>
퀴즈
</NavLink>
<NavLink className="grow" href={`/quizzes/${quizId}/questions`}>
질문
</NavLink>
</article>
{children}
</section>
);
}
9 changes: 9 additions & 0 deletions app/quizzes/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import LoadingSpinner from '@/components/common/loading-spinner/loading-spinner';

export default function Loading() {
return (
<section className="flex min-h-[90vh] items-center justify-center overflow-hidden">
<LoadingSpinner size="3xl" className="text-blue-600" />
</section>
);
}
80 changes: 39 additions & 41 deletions app/quizzes/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,49 +22,47 @@ export default async function Page({ params }: { params: { id: string } }) {

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="p-4">
<section className="mb-10">
<h2 className="text-2xl font-bold">출제자</h2>
{/* TODO: 추후 상세 유저 페이지 라우팅 경로로 변경하기 */}
<Link className="underline" href={`/users/${quiz?.users?.id}`}>
{quiz?.users?.name}
</Link>
</section>
<section className="mb-10">
<h2 className="text-2xl font-bold">출제자</h2>
{/* TODO: 추후 상세 유저 페이지 라우팅 경로로 변경하기 */}
<Link className="underline" href={`/users/${quiz?.users?.id}`}>
{quiz?.users?.name}
</Link>
</section>

<section className="mb-10">
<h2 className="text-2xl font-bold">문제</h2>
<Markdown
className="prose"
remarkPlugins={[remarkGfm]}
components={{
code({ className, children, ...rest }) {
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter
{...rest}
PreTag="div"
language={match[1]}
style={dracula}
ref={null}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code {...rest} className={className}>
{children}
</code>
);
},
}}
>
{quiz?.description}
</Markdown>
</section>
<section className="mb-10">
<h2 className="text-2xl font-bold">문제</h2>
<Markdown
className="prose"
remarkPlugins={[remarkGfm]}
components={{
code({ className, children, ...rest }) {
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter
{...rest}
PreTag="div"
language={match[1]}
style={dracula}
ref={null}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code {...rest} className={className}>
{children}
</code>
);
},
}}
>
{quiz?.description}
</Markdown>
</section>

<section className="mb-10">
<ChoiceForm quizId={quizId} />
</section>
</div>
<section className="mb-10">
<ChoiceForm quizId={quizId} />
</section>
</HydrationBoundary>
);
}
5 changes: 5 additions & 0 deletions app/quizzes/[id]/questions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default async function Page({ params }: { params: { id: string } }) {
const quizId = Number(params.id) ?? 0;

return <section>{quizId}번 퀴즈에 대한 질문 페이지</section>;
}
45 changes: 45 additions & 0 deletions components/common/link/nav-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client';

import Link from 'next/link';
import { cva, type VariantProps } from 'class-variance-authority';
import { forwardRef, type Ref } from 'react';
import { usePathname } from 'next/navigation';
import { cn } from '@/libs/utils';

type NavLinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement> &
VariantProps<typeof navLink>;

const navLink = cva(
['border-b-2 border-b-neutral-300 pb-2 text-center text-neutral-300'],
{
variants: {
intent: {
primary: ['text-[#3077C6] ', 'border-b-[#3077C6]'],
secondary: ['text-red-600', 'border-b-red-600'],
},
},
defaultVariants: { intent: 'primary' },
}
);

export default forwardRef(function NavLink(
{ children, className, intent, href, ...props }: NavLinkProps,
forwardedRef: Ref<HTMLAnchorElement>
) {
const pathname = usePathname();
const isActive = pathname === href;

return (
<Link
ref={forwardedRef}
href={href ?? '#'}
className={cn(
navLink({ intent: isActive ? intent : null, className }),
isActive ? 'border-b-4 font-bold' : null
)}
{...props}
>
{children}
</Link>
);
});
4 changes: 2 additions & 2 deletions components/common/loading-spinner/loading-spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cn } from '@/libs/utils';
import { cva, type VariantProps } from 'class-variance-authority';
import { forwardRef, type Ref } from 'react';
import { twMerge } from 'tailwind-merge';

type LoadingSpinnerProps = React.HTMLAttributes<HTMLElement> &
VariantProps<typeof loadingSpinner>;
Expand Down Expand Up @@ -36,7 +36,7 @@ export default forwardRef(function LoadingSpinner(
return (
<div
ref={forwardedRef}
className={twMerge(loadingSpinner({ size, weight, className }))}
className={cn(loadingSpinner({ size, weight, className }))}
{...props}
/>
);
Expand Down

0 comments on commit 2730c7e

Please sign in to comment.