들어가기
apple-market의 여러page에서 사용되는 components들을
정리해 보자
총 8개의 component를 만듬.
보면서 tailwindcss에 대해서도 정리가 됨.
import { cls } from '../libs/utils' ///? : 에 따라서 다르게 적용될 css를 설정하기 위한 hook!!
interface ButtonProps {
large?: boolean
text: string
[key: string]: any
}
///button component를 사용하기 위해 보내주어야 할 props. [key:string]: any를 유심히본다.
export default function Button({
large = false,
onClick, ///button이기 때문에 onClick도 받는다.
text,
...rest ///ButtonProps 외에 받는것은 ...rest로 처리함.
}: ButtonProps) {
return (
<button
{...rest} ///ButtonProps 외에 받는것은 {...rest}로 처리함.
className={cls(
'w-full bg-orange-400 hover:bg-orange-600 text-white px-4 border text-lg border-transparent rounded-md shadow-lg font-medium foucs:ring-offset-2 focus:ring-2 focus:ring-orange-500 focus:outline-none',
large ? 'py-3 text-base' : 'py-2 text-sm'
)}
>
{text}
</button>
)
}
사진올리기, 아이템 올리기, 라이브방송올리기 등, 고정되어 있는 버튼만들기
위의 버튼은 submit
import Link from 'next/link'
import React from 'react'
interface FloatingButton {
children: React.ReactNode ///floating Button에 들어갈 것을 children으로 받음.
href: string ///클릭했을떄 이동 될, page를 입력.
}
export default function FloatingButton({ children, href }: FloatingButton) {
return (
<Link href={href}> ///Link는 import되어야함, a와 같이 쓰여야 함.
<a className="fixed hover:bg-orange-600 border-o aspect-square border-transparent transition-colors cursor-pointer bottom-24 opacity-70 right-5 shadow-lg bg-orange-400 rounded-full w-14 flex items-center justify-center text-white">
{children}
</a>
</Link>
)
}
이메일, password등을 입력하는 칸.
label: string
name: string
kind?: 'text' | 'phone' | 'price'
[key: string]: any
}
///Props로 label, name, kind?, [key:string]:any 를 받는다.
export default function Input({
label,
name,
kind = 'text', ///default값은 'text'로~
...rest ///Props외에것은 ...rest로~
}: InputProps) {
return (
<div>
<label
className="mb-1 block text-sm font-medium text-gray-700"
htmlFor={name}
>
{label}
</label> ///Props에서 받은 label과 name을 먼저 처리한다.
{kind === 'text' ? (
<div className="rounded-md relative flex items-center shadow-lg">
<input
id={name}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null} ///kindrk text일 경우 처리하는 로직, id={name}으로 한다!!
{kind === 'price' ? (
<div className="rounded-md relative flex items-center shadow-lg">
<div className="absolute left-0 pointer-events-none pl-3 flex items-center justify-center">
<span className="text-gray-500 absolute left-1 text-sm">$</span>
</div>
<input
id={name}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
<div className="absolute right-0 pointer-events-none pr-3 flex items-center">
<span className="text-gray-500">KRW</span>
</div>
</div>
) : null}
///kind가 price일 경우 처리하는 방법, $나 KRW같은 absolute가 들어가는것을
///집중해서볼것!!
{kind === 'phone' ? (
<div className="flex rounded-md shadow-lg">
<span className="flex items-center justify-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text0gray-500 select-none text-sm">
+82
</span>
<input
id={name}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null}
///kind가 phone일 경우 로직. 역시 id={name}으로 받고, +82가 처리되는 부분도 잘 본다.
</div>
)
}
하나의 item이 layout되는것.
클릭시 localhost:3000/items/[id]로 page이동됨.
heroicons 사용떄문에 코드가 길어졌을뿐.
import Link from 'next/link' ///item을 클릭시 items/[id]로 보내줘야함.
import React from 'react'
interface ItemProps {
title: string
id: number
price: number
comments: number
hearts: number
}
///하나의 item이 받는 props
export default function Item({
title,
id,
price,
comments,
hearts,
}: ItemProps) {
return (
<Link href={`/items/${id}`}> ///아이템 클릭시 위의 주소로 이동함.
<a className="flex px-4 py-3 cursor-pointer justify-between shadow-lg">
<div className="flex space-x-4">
<div className="w-20 h-20 bg-gray-400 rounded-md" />
<div className="pt-2 flex flex-col">
<h3 className="text-sm font-medium text-gray-900">{title}</h3>
<span className="font-medium mt-1 text-gray-900">${price}</span>
</div>
</div>
<div className="flex space-x-2 items-end justify-end">
<div className='flex space-x-0.5 items-center text-sm text-gray-600'>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<span>{hearts}</span>
</div>
<div className='flex space-x-0.5 items-center text-sm text-gray-600'>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
<span>{comments}</span> ///댓글 개수임..
</div>
</div>
</a>
</Link>
)
}
위의 header부분과
밑의 nav부분등
모든 page에 적용되는 component
import Link from 'next/link'
import { useRouter } from 'next/router' ///goBack을 사용하기 위해서 import함.
import React from 'react'
import { cls } from '../libs/utils'
interface LayoutProps {
title?: string
canGoBack?: boolean
hasTabBar?: boolean
children: React.ReactNode ///children의 속성을 잘 봐 놓는다.
}
export default function Layout({
title,
canGoBack,
hasTabBar,
children,
}: LayoutProps) {
const router = useRouter()
const onClick = () => {
router.back()
} ///back 화살표 누르면 한단계 back시킴.
return (
<div>
<div className="bg-white w-full h-12 max-w-xl justify-center text-xl px-10 font-medium fixed text-gray-800 border-b top-o flex items-center shadow-lg">
{canGoBack ? (
<button onClick={onClick} className="absolute left-4">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10 19l-7-7m0 0l7-7m-7 7h18"
/>
</svg>
</button>
) : null}
///canGoBack이 있을때, header 코딩!!!
{title ? (
<span className={cls(canGoBack ? 'mx-auto' : '', '')}>{title}</span>
) : null}
////title이 있을때, header 부분.
</div>
///header부분 끝
<div className={cls('pt-16', hasTabBar ? 'pb-16' : '')}>{children}</div>
///가운데 children부분 받는다.
{hasTabBar ? (
///아랫부분 Nav 부분, 클릭시 이동 가능하게 Link를 5개에 다 걸어줌.
///Link걸떄는, Link와 a 로~
<nav className="bg-white w-full max-w-lg text-gray-800 border-t opacity-70 fixed bottom-0 px-10 pb-3 pt-3 flex justify-between items-center">
<Link href="/">
<a
className={cls(
'flex flex-col items-center space-y-2',
router.pathname === '/'
? 'text-orange-500'
: 'hover:text-orange-500 transition-colors'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
<span>홈</span>
</a>
</Link>
<Link href="/community">
<a
className={cls(
'flex flex-col items-center space-y-2',
router.pathname === '/community'
? 'text-orange-500'
: 'hover:text-orange-500 transition-colors'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
/>
</svg>
<span>동네생활</span>
</a>
</Link>
<Link href="/chats">
<a
className={cls(
'flex flex-col items-center space-y-2',
router.pathname === '/chats'
? 'text-orange-500'
: 'hover:text-orange-500 transition-colors'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<span>채팅</span>
</a>
</Link>
<Link href="/live">
<a
className={cls(
'flex flex-col items-center space-y-2',
router.pathname === '/live'
? 'text-orange-600'
: 'hover:text-orange-500 transition-colors'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
<span>라이브</span>
</a>
</Link>
<Link href="/profile">
<a
className={cls(
'flex flex-col items-center space-y-2',
router.pathname === '/profile'
? 'text-orange-600'
: 'hover:text-orange-500 transition-colors'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/>
</svg>
<span>나의 애플</span>
</a>
</Link>
</nav>
) : null}
</div>
)
}
chating에서 메세지 하나하나 나타내지는 component
reverse되는 부분을 유심히 잘 봐 놓는다.
import { cls } from '../libs/utils'
interface MessageProps {
message: string
reversed?: boolean
avatarUrl?: string
}
export default function Message({
message,
reversed,
avatarUrl,
}: MessageProps) {
return (
<div
className={cls(
'flex items-start ',
reversed ? 'flex-row-reverse space-x-2 space-x-reverse' : 'space-x-2'
)}
>
<div className="w-8 h-8 rounded-full bg-slate-400" />
<div className="w-1/2 text-sm text-gray-700 p-2 border border-gray-300 rounded-md">
<p>{message}</p>
</div>
</div>
)
}
동네질문 등, 글쓰기 하는 부분!!!
label, name, [key:string]:any 부분을 잘 봐놓을 것!!!!
interface TextAreaProps {
label?: string
name?: string
[key: string]: any
}
export default function TextArea({ label, name, ...rest }: TextAreaProps) {
return (
<div>
{label ? (
<label
htmlFor={name}
className="mb-1 block text-sm font-medium text-gray-700"
>
{label}
</label>
) : null}
<textarea
id={name}
className="mt-3 shadow-lg w-full focus:ring-orange-500 rounded-md border-gray-300 focus:border-orange-500"
rows={4}
{...rest}
/>
</div>
)
}
label의 htmlFor={name}부분과 <textarea id={name} 부분을 잘 봐 놓는다~~ {...rest}부분도