Skip to content

Commit

Permalink
Merge pull request #8 from meme-party/feat/common-components-responsive
Browse files Browse the repository at this point in the history
디자인 시스템 적용 및 반응형처리
  • Loading branch information
KoJaem authored Feb 17, 2025
2 parents 9a3ab7e + d5d77af commit a234e8a
Show file tree
Hide file tree
Showing 22 changed files with 164 additions and 104 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"next": "15.1.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sonner": "^1.7.4",
"tailwind-merge": "^2.6.0",
"zustand": "^5.0.2"
},
Expand Down
20 changes: 12 additions & 8 deletions src/app/(routes)/test/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
"use client"

import Button from "@/components/Button"
import Footer from "@/components/Footer"
import Header from "@/components/Header"
import Input from "@/components/Input"
import MemeTypeButton from "@/components/MemeTypeButton"
import MoreViewButton from "@/components/MoreViewButton"
import SearchInput from "@/components/SearchInput"
import TagButton from "@/components/TagButton"
import TagSmallButton from "@/components/TagSmallButton"
import Textarea from "@/components/Textarea"
import ThumbnailCard from "@/components/ThumbnailCard"
import { useToast } from "@/store/toast"
import { COLORS } from "@/styles/colors"
import { Heart, Search, Type } from "lucide-react"
import { toast } from "sonner"

export default function TestPage() {
const { showToast } = useToast()

return (
<section className="mx-[20px] my-[80px] flex flex-col gap-[12px]">
<button onClick={() => showToast({ text: "테스트", type: "warning" })}>auto close Toast</button>
<button onClick={() => showToast({ text: "테스트", type: "success", autoClose: false })}>Toast</button>
<Header />
<Footer />
<button onClick={() => toast.success("삭제가 완료되었습니다!")}>토스트 메세지</button>
<MemeTypeButton
title="텍스트밈"
Icon={Type}
Expand All @@ -28,8 +30,10 @@ export default function TestPage() {
<article className="flex gap-[12px]">
<TagButton title="키워드" onClick={() => console.log("TagButton1 클릭")} />
<TagButton title="ㄱ키워드" variant="colored" onClick={() => console.log("TagButton2 클릭")} />
<TagSmallButton title="ㄱ키워드" onClick={() => console.log("TagSmallButton1 클릭")} />
<TagSmallButton title="ㄱ키워드" variant="colored" onClick={() => console.log("TagSmallButton2 클릭")} />
</article>
<Button className="w-[120px] text-white">컬렉션 1</Button>
<Button className="w-[120px] text-gray-scale-100">컬렉션 1</Button>
<article className="flex gap-[12px]">
<Input placeholder="컬렉션 이름을 검색해주세요" />
<Input placeholder="메모 내용을 입력해주세요" className="border-none" />
Expand All @@ -47,8 +51,8 @@ export default function TestPage() {
<SearchInput placeholder="아이콘 없는 검색 Input" onSearch={() => console.log("검색 이벤트!")} />
</article>
<article className="flex gap-[12px]">
<Textarea />
<Textarea className="border-none" />
<Textarea placeholder="메모를 입력해주세요" />
<Textarea placeholder="메모를 입력해주세요" className="border-none" />
</article>
<article className="flex gap-[12px]">
<ThumbnailCard
Expand Down
18 changes: 4 additions & 14 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import Toast from "@/components/Toast"
import { Pretendard } from "@/fonts"
import "@/styles/globals.css"
import "@/styles/reset.css"
import { cn } from "@/utils/cn"
import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google"

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"]
})

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"]
})

export const metadata: Metadata = {
title: "Create Next App",
Expand All @@ -26,10 +17,9 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} text-white antialiased`}>
<div id="toastPortal" />
<Toast />
<body className={cn(Pretendard.variable)}>
{children}
<Toast />
</body>
</html>
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function Button({ children, ...buttonProps }: Params) {
return (
<button
{...buttonProps}
className={cn("w-fit rounded-lg bg-primary-400 p-2 font-semibold text-dark", buttonProps.className)}
className={cn("w-fit rounded-[8px] bg-primary-400 p-[8px] text-gray-scale-900", buttonProps.className)}
>
{children}
</button>
Expand Down
11 changes: 11 additions & 0 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Image from "next/image"

export default function Footer() {
return (
<section>
<article className="relative h-[20px] w-[120px]">
<Image src={"/kakao.png"} alt="logo" fill />
</article>
</section>
)
}
26 changes: 26 additions & 0 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Search } from "lucide-react"
import Image from "next/image"
import Button from "@/components/Button"
import { COLORS } from "@/styles/colors"

export default function Header() {
// Todo 세션체크해서 닉네임, 버튼 조건부 렌더링
return (
// lg:px-[120px]
<section className="flex h-[94px] w-full items-end justify-between px-[16px] py-[12px] md:h-auto md:items-center md:px-[24px] md:py-[8px]">
<article className="relative h-[24px] w-[120px]">
<Image src={"/kakao.png"} alt="logo" fill />
</article>
<article className="flex flex-row-reverse items-center gap-[24px] md:flex md:flex-row">
<div className="flex items-center gap-[8px]">
<div className="relative h-[24px] w-[24px]">
<Image src={"/cat.png"} alt="profile-image" fill className="rounded-full" />
</div>
<p className="hidden text-h3-r md:flex">유저 닉네임이 들어가요</p>
</div>
<Search size={24} color={COLORS.PRIMARY} />
<Button className="hidden bg-primary-300 px-[24px] py-[4px] text-h2-sb md:flex">로그인</Button>
</article>
</section>
)
}
4 changes: 2 additions & 2 deletions src/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import type { InputHTMLAttributes } from "react"

export default function Input(props: InputHTMLAttributes<HTMLInputElement>) {
return (
<section className="relative flex w-fit items-center">
<section className="relative flex w-full items-center">
<input
{...props}
className={cn(
"w-fit rounded-lg border border-primary-400 bg-gray-scale-800 p-4 font-semibold outline-none placeholder:text-gray",
"w-full rounded-[8px] border border-primary-400 bg-gray-scale-800 px-[16px] py-[12px] text-gray-scale-100 outline-none placeholder:text-gray-scale-400",
props.className
)}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/components/MemeTypeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export default function MemeTypeButton({ title, Icon, iconProps, ...buttonProps
return (
<Button
{...buttonProps}
className={cn("flex w-[224px] items-center gap-[12px] rounded-lg bg-primary-400 p-2", buttonProps.className)}
className={cn("flex w-[224px] items-center gap-[12px] rounded-[8px] bg-primary-400 p-2", buttonProps.className)}
>
{!!Icon && <Icon {...iconProps} />}
<p className="font-bold text-dark">{title}</p>
<p className="text-h2-b text-gray-scale-900">{title}</p>
</Button>
)
}
4 changes: 2 additions & 2 deletions src/components/MoreViewButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export default function MoreViewButton({ title, Icon = ArrowRight, iconProps, ..
<Button
{...buttonProps}
className={cn(
"relative flex w-full cursor-pointer items-center justify-between rounded-lg border border-primary-400 bg-gray-scale-700 p-2 px-4 text-white",
"relative flex w-full cursor-pointer items-center justify-between rounded-[8px] border border-primary-400 bg-gray-scale-700 px-[16px] py-[8px] text-h2-sb text-gray-scale-100",
buttonProps.className
)}
>
<p className="font-semibold">{title}</p>
<p>{title}</p>
<Icon {...iconProps} />
</Button>
)
Expand Down
6 changes: 3 additions & 3 deletions src/components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ export default function SearchInput({ onSearch, Icon, iconProps, ...props }: Par
}

return (
<section className="relative flex w-fit items-center">
<section className="relative flex w-full items-center">
<input
{...props}
onKeyDown={handleKeyDown}
className={cn(
"w-full rounded-lg border border-primary-400 bg-gray-scale-800 p-4 font-semibold outline-none placeholder:text-gray",
"w-full rounded-[8px] border border-primary-400 bg-gray-scale-800 px-[16px] py-[12px] font-semibold outline-none placeholder:text-gray-scale-400",
Icon && "pr-10",
props.className
)}
/>
{Icon && <IconButton onClick={onSearch} className="absolute end-2" Icon={Icon} iconProps={iconProps} />}
{Icon && <IconButton onClick={onSearch} className="absolute end-[8px]" Icon={Icon} iconProps={iconProps} />}
</section>
)
}
6 changes: 4 additions & 2 deletions src/components/TagButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ export default function TagButton({ title, variant = "default", ...buttonProps }
<Button
{...buttonProps}
className={cn(
"w-fit rounded-lg bg-gray-scale-700 p-2 px-4 text-white",
variant === "colored" && "border border-primary-400 font-semibold text-primary-300",
"w-fit rounded-[8px] bg-gray-scale-700 px-[16px] py-[8px]",
variant === "colored"
? "border border-primary-400 text-h2-m text-primary-300"
: "text-h2-sb text-gray-scale-200",
buttonProps.className
)}
>
Expand Down
23 changes: 23 additions & 0 deletions src/components/TagSmallButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { cn } from "@/utils/cn"
import type { ButtonHTMLAttributes } from "react"
import Button from "./Button"

interface Params extends ButtonHTMLAttributes<HTMLButtonElement> {
title: string
variant?: "default" | "colored"
}

export default function TagSmallButton({ title, variant = "default", ...buttonProps }: Params) {
return (
<Button
{...buttonProps}
className={cn(
"w-fit rounded-[8px] bg-gray-scale-700 p-[8px] text-h3-m",
variant === "colored" ? "border border-primary-400 text-primary-300" : "text-gray-scale-200",
buttonProps.className
)}
>
<p>{title}</p>
</Button>
)
}
4 changes: 2 additions & 2 deletions src/components/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import type { TextareaHTMLAttributes } from "react"

export default function Textarea(props: TextareaHTMLAttributes<HTMLTextAreaElement>) {
return (
<section className="relative flex w-fit items-center">
<section className="relative flex w-full items-center">
<textarea
{...props}
className={cn(
"w-fit resize-none rounded-lg border border-primary-400 bg-gray-scale-800 p-4 font-semibold outline-none placeholder:text-gray",
"w-full resize-none rounded-[16px] border border-primary-400 bg-gray-scale-800 px-[16px] py-[12px] text-h3-r text-gray-scale-100 outline-none placeholder:text-gray-scale-400",
props.className
)}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/components/ThumbnailCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ export default function ThumbnailCard({
return (
<section
className={cn(
"relative flex h-fit w-[224px] flex-col gap-[24px] break-all rounded-lg bg-gray-scale-700 p-4",
"relative flex h-fit w-[224px] flex-col gap-[24px] break-all rounded-[8px] bg-gray-scale-700 p-[16px]",
like && "border border-primary drop-shadow-thumbnail"
)}
>
{Icon && <IconButton Icon={Icon} iconProps={iconProps} className="self-end" />}
{type === "text" && <p>{text}</p>}
{type === "image" && (
<Image src={url} alt={"meme-thumbnail"} width={224} height={0} className="rounded-md object-contain" />
<Image src={url} alt={"meme-thumbnail"} width={224} height={0} className="rounded-[8px] object-contain" />
)}
{type === "video" && <p>{url}</p>}
</section>
Expand Down
78 changes: 15 additions & 63 deletions src/components/Toast.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,20 @@
"use client"

import { useToast } from "@/store/toast"
import { cn } from "@/utils/cn"
import { AnimatePresence, motion } from "framer-motion"
import dynamic from "next/dynamic"
import ReactDOM from "react-dom"

const CircleCheck = dynamic(() => import("@/icons/CircleCheck"))
const TriangleAlert = dynamic(() => import("@/icons/TriangleAlert"))
const CircleAlert = dynamic(() => import("@/icons/CircleAlert"))
import { Toaster } from "sonner"

export default function Toast() {
const { toast, closeToast } = useToast()

const element = typeof window !== "undefined" && document.getElementById(`toastPortal`)

if (!element) return null

// Todo type 에 따라 텍스트색상 다르게 할건지 논의 필요
// const toastTypeClasses = {
// success: "text-primary-300",
// warning: "text-yellow-500",
// error: "text-red-500"
// }

const content = (
<AnimatePresence>
{toast.isOpened && (
<motion.section
className="fixed bottom-0 flex w-full justify-center bg-dark/60 px-6 py-10 drop-shadow-toast backdrop-blur-lg"
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 50, opacity: 0 }}
transition={{
duration: 0.5,
ease: "easeOut"
}}
>
<article className="flex w-full max-w-[1280px] items-center justify-between">
<div
className={cn(
"flex gap-[4px] text-primary-300"
// toastTypeClasses[toast.type]
)}
>
{toast.type === "success" && <CircleCheck pathColor="#17161799" />}
{toast.type === "warning" && <TriangleAlert pathColor="#17161799" />}
{toast.type === "error" && <CircleAlert pathColor="#17161799" />}

{toast.text}
</div>
{!toast.autoClose && (
<button
className="min-w-[148px] rounded-md bg-gray-scale-800 px-3 py-2 text-white hover:bg-gray-scale-700"
onClick={closeToast}
>
완료
</button>
)}
</article>
</motion.section>
)}
</AnimatePresence>
return (
<Toaster
duration={3000}
className="flex items-center justify-center font-pretendard"
position="bottom-center"
// offset={{ left: 0, right: 0 }}
mobileOffset={{ left: 0, right: 0, bottom: 20 }}
toastOptions={{
classNames: {
toast:
"bg-primary-300 drop-shadow-toast backdrop-blur-lg border-none text-h2-m max-w-[280px] sm:max-w-[340px] justify-self-center p-[8px]"
}
}}
/>
)

return ReactDOM.createPortal(content, element)
}
Binary file added src/fonts/PretendardVariable.woff2
Binary file not shown.
8 changes: 8 additions & 0 deletions src/fonts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import localFont from "next/font/local"

export const Pretendard = localFont({
src: "../fonts/PretendardVariable.woff2",
variable: "--font-pretendard",
weight: "400 500 600 700",
display: "swap"
})
Loading

0 comments on commit a234e8a

Please sign in to comment.