diff --git a/.env.example b/.env.example index 4d0fb3d0..fde7d11b 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -NEXT_PUBLIC_API_MOCKING=enabled # or disabled \ No newline at end of file +NEXT_PUBLIC_API_MOCKING=enabled # or disabled +CHROMATIC_TOKEN=your-token diff --git a/package.json b/package.json index 3dcf635a..f83fc01d 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,14 @@ "chromatic": "bash ./scripts/chromatic_publish.sh" }, "dependencies": { + "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", "@tanstack/react-query": "^5.0.0", "autoprefixer": "^10.4.16", "class-variance-authority": "^0.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc8f4090..351df7af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@radix-ui/react-avatar': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) @@ -23,6 +26,9 @@ dependencies: '@radix-ui/react-switch': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) + '@radix-ui/react-tabs': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) '@tanstack/react-query': specifier: ^5.0.0 version: 5.0.0(react-dom@18.0.0)(react@18.0.0) @@ -2202,6 +2208,30 @@ packages: react: 18.0.0 react-dom: 18.0.0(react@18.0.0) + /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0): + resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/react-context': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@types/react': 18.0.0 + '@types/react-dom': 18.0.0 + react: 18.0.0 + react-dom: 18.0.0(react@18.0.0) + dev: false + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -2790,6 +2820,34 @@ packages: react-dom: 18.0.0(react@18.0.0) dev: false + /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0): + resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-context': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.0)(react@18.0.0) + '@types/react': 18.0.0 + '@types/react-dom': 18.0.0 + react: 18.0.0 + react-dom: 18.0.0(react@18.0.0) + dev: false + /@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0): resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} peerDependencies: diff --git a/public/images/google.png b/public/images/google.png new file mode 100644 index 00000000..bda6281c Binary files /dev/null and b/public/images/google.png differ diff --git a/public/images/kakao.png b/public/images/kakao.png new file mode 100644 index 00000000..8b92976d Binary files /dev/null and b/public/images/kakao.png differ diff --git a/public/images/logo.svg b/public/images/logo.svg new file mode 100644 index 00000000..e1a6deea --- /dev/null +++ b/public/images/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/x-icon.svg b/public/images/x-icon.svg new file mode 100644 index 00000000..8f4a1cbf --- /dev/null +++ b/public/images/x-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/app/(root)/(routes)/(auth)/login/page.tsx b/src/app/(root)/(routes)/(auth)/login/page.tsx index 712ff4ec..663bbf6f 100644 --- a/src/app/(root)/(routes)/(auth)/login/page.tsx +++ b/src/app/(root)/(routes)/(auth)/login/page.tsx @@ -1,5 +1,16 @@ -import React from 'react' +import { FunctionComponent } from 'react' +import LoginForm from '@/components/domain/LoginForm' -export default function LoginPage() { - return
LoginPage
+interface LoginPageProps {} + +const LoginPage: FunctionComponent = ({}) => { + return ( +
+
+ +
+
+ ) } + +export default LoginPage diff --git a/src/app/(root)/(routes)/(home)/page.tsx b/src/app/(root)/(routes)/(home)/page.tsx index 0f000334..0fe1bf9f 100644 --- a/src/app/(root)/(routes)/(home)/page.tsx +++ b/src/app/(root)/(routes)/(home)/page.tsx @@ -1,5 +1,3 @@ -import Link from 'next/link' -import Button from '@/components/ui/Button' import { DarkModeButton } from '@/components/ui/DarkModeButton' export default function HomePage() { @@ -7,8 +5,6 @@ export default function HomePage() {
hi - 로긴 -
) } diff --git a/src/app/@authModal/(...)login/page.tsx b/src/app/@authModal/(...)login/page.tsx index 5bbc7b70..b37b2f02 100644 --- a/src/app/@authModal/(...)login/page.tsx +++ b/src/app/@authModal/(...)login/page.tsx @@ -1,5 +1,30 @@ -import React from 'react' +'use client' -export default function LoginModalPage() { - return
LoginModalPage
+import { FunctionComponent } from 'react' +import { useRouter } from 'next/navigation' +import LoginForm from '@/components/domain/LoginForm' +import { Dialog, DialogContent } from '@/components/ui/Dialog' + +interface LoginModalPageProps {} + +const LoginModalPage: FunctionComponent = () => { + const router = useRouter() + return ( + { + if (!open) { + router.back() + } + }} + > + +
+ +
+
+
+ ) } + +export default LoginModalPage diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d64b5807..95f6b0cc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,7 @@ import { Suspense } from 'react' import type { Metadata } from 'next' +import Head from 'next/head' +import Header from '@/components/domain/Header' import { Environment } from '@/config/environment' import TanstackQueryContext from '@/contexts/TanstackQueryContext' import ThemeProviderContext from '@/contexts/ThemeProviderContext' @@ -8,7 +10,7 @@ import '@/styles/globals.css' export const metadata: Metadata = { title: '나비장터', - description: 'Generated by create next app', + description: '물물교환 플랫폼 나비장터입니다.', } if (Environment.apiMocking() === 'enabled') { @@ -25,12 +27,18 @@ export default function RootLayout({ }) { return ( + + + - {children} - {authModal} +
+
+ {children} + {authModal} +
diff --git a/src/components/domain/Header/Header.tsx b/src/components/domain/Header/Header.tsx index c861ac81..e0848a85 100644 --- a/src/components/domain/Header/Header.tsx +++ b/src/components/domain/Header/Header.tsx @@ -19,7 +19,7 @@ type HeaderProps = { const Header = ({ isLogin = false }: HeaderProps) => { return ( -
+
diff --git a/src/components/domain/LoginForm/LoginForm.stories.tsx b/src/components/domain/LoginForm/LoginForm.stories.tsx new file mode 100644 index 00000000..4a67a72d --- /dev/null +++ b/src/components/domain/LoginForm/LoginForm.stories.tsx @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from '@storybook/react' +import LoginForm from './LoginForm' + +const meta = { + title: 'DOMAIN/LoginForm', + component: LoginForm, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Normal: Story = { + args: {}, +} diff --git a/src/components/domain/LoginForm/LoginForm.tsx b/src/components/domain/LoginForm/LoginForm.tsx new file mode 100644 index 00000000..4e47338c --- /dev/null +++ b/src/components/domain/LoginForm/LoginForm.tsx @@ -0,0 +1,14 @@ +import Image from 'next/image' +import Assets from '@/config/assets' +import LoginButtons from './section/LoginButtons' + +const LoginForm = () => { + return ( +
+ nabi-logo + +
+ ) +} + +export default LoginForm diff --git a/src/components/domain/LoginForm/index.tsx b/src/components/domain/LoginForm/index.tsx new file mode 100644 index 00000000..608996c9 --- /dev/null +++ b/src/components/domain/LoginForm/index.tsx @@ -0,0 +1,3 @@ +import LoginForm from './LoginForm' + +export default LoginForm diff --git a/src/components/domain/LoginForm/section/LoginButtons.tsx b/src/components/domain/LoginForm/section/LoginButtons.tsx new file mode 100644 index 00000000..5b80d764 --- /dev/null +++ b/src/components/domain/LoginForm/section/LoginButtons.tsx @@ -0,0 +1,15 @@ +'use client' + +import React from 'react' +import { KakaoLoginButton, GoogleLoginButton } from '../../buttons/LoginButtons' + +const LoginButtons = () => { + return ( +
+ alert('k')} /> + alert('g')} /> +
+ ) +} + +export default LoginButtons diff --git a/src/components/domain/buttons/LoginButtons/LoginButtons.stories.tsx b/src/components/domain/buttons/LoginButtons/LoginButtons.stories.tsx new file mode 100644 index 00000000..13380b8e --- /dev/null +++ b/src/components/domain/buttons/LoginButtons/LoginButtons.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { KakaoLoginButton, GoogleLoginButton } from './LoginButtons' + +const meta = { + title: 'DOMAIN/LoginButtons', + component: KakaoLoginButton, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Normal: Story = { + args: {}, + render: () => ( +
+ alert('카카오 버튼')} /> + alert('구글 버튼')} /> +
+ ), +} + +export const Kakao: Story = { + render: () => alert('카카오 버튼')} />, +} + +export const Google: Story = { + render: () => alert('구글 버튼')} />, +} diff --git a/src/components/domain/buttons/LoginButtons/LoginButtons.tsx b/src/components/domain/buttons/LoginButtons/LoginButtons.tsx new file mode 100644 index 00000000..fc0f6fb8 --- /dev/null +++ b/src/components/domain/buttons/LoginButtons/LoginButtons.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import Image from 'next/image' +import Button from '@/components/ui/Button' +import Assets from '@/config/assets' + +const KakaoLoginButton = ({ onClickButton }: { onClickButton: () => void }) => { + return ( + + ) +} + +const GoogleLoginButton = ({ + onClickButton, +}: { + onClickButton: () => void +}) => { + return ( + + ) +} + +export { KakaoLoginButton, GoogleLoginButton } diff --git a/src/components/domain/buttons/LoginButtons/index.tsx b/src/components/domain/buttons/LoginButtons/index.tsx new file mode 100644 index 00000000..3ac89a00 --- /dev/null +++ b/src/components/domain/buttons/LoginButtons/index.tsx @@ -0,0 +1,3 @@ +import { KakaoLoginButton, GoogleLoginButton } from './LoginButtons' + +export { KakaoLoginButton, GoogleLoginButton } diff --git a/src/components/ui/Avatar/Avatar.stories.tsx b/src/components/ui/Avatar/Avatar.stories.tsx new file mode 100644 index 00000000..10fc1ab3 --- /dev/null +++ b/src/components/ui/Avatar/Avatar.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { Avatar, AvatarImage, AvatarFallback } from './Avatar' + +const meta = { + title: 'UI/Avatar', + component: Avatar, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Normal: Story = { + args: {}, + render: () => { + return ( + <> + + + NB + + + + NB + + + + NB + + + ) + }, +} diff --git a/src/components/ui/Avatar/Avatar.tsx b/src/components/ui/Avatar/Avatar.tsx new file mode 100644 index 00000000..bf9ed9e7 --- /dev/null +++ b/src/components/ui/Avatar/Avatar.tsx @@ -0,0 +1,67 @@ +'use client' + +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' +import { cva, type VariantProps } from 'class-variance-authority' +import { cn } from '@/utils' + +const avatarVariants = cva( + 'relative flex shrink-0 overflow-hidden rounded-full', + { + variants: { + size: { + sm: 'w-6 h-6', + md: 'w-10 h-10', + lg: 'w-[100px] h-[100px]', + }, + }, + }, +) + +export interface AvatarProps + extends React.HTMLAttributes, + VariantProps { + ref?: React.Ref +} + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, size, ...props }: AvatarProps, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/Avatar/index.tsx b/src/components/ui/Avatar/index.tsx new file mode 100644 index 00000000..6fe2f610 --- /dev/null +++ b/src/components/ui/Avatar/index.tsx @@ -0,0 +1,3 @@ +import { Avatar, AvatarImage, AvatarFallback } from './Avatar' + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/Badge/Badge.stories.tsx b/src/components/ui/Badge/Badge.stories.tsx new file mode 100644 index 00000000..ed875344 --- /dev/null +++ b/src/components/ui/Badge/Badge.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { Badge } from './Badge' + +const meta = { + title: 'UI/Badge', + component: Badge, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Normal: Story = { + args: { + variant: 'gradation', + size: 'sm', + children: '거래성사', + }, +} diff --git a/src/components/ui/Badge/Badge.tsx b/src/components/ui/Badge/Badge.tsx new file mode 100644 index 00000000..cce0aead --- /dev/null +++ b/src/components/ui/Badge/Badge.tsx @@ -0,0 +1,42 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' +import { cn } from '@/utils' + +const badgeVariants = cva( + 'inline-flex items-center rounded-full px-2 font-medium', + { + variants: { + variant: { + primary: 'bg-primary-color text-white', + secondary: 'bg-secondary-color text-white', + gradation: 'bg-gradient-primary text-white', + information: 'bg-background-secondary-color text-black', + }, + size: { + sm: 'h-[18px] text-xs', + lg: 'h-6 text-sm', + }, + }, + defaultVariants: { + variant: 'information', + size: 'sm', + }, + }, +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +const Badge = ({ className, variant, size, ...props }: BadgeProps) => { + return ( +
+ ) +} + +Badge.displayName = 'Badge' + +export { Badge, badgeVariants } diff --git a/src/components/ui/Badge/index.tsx b/src/components/ui/Badge/index.tsx new file mode 100644 index 00000000..97ca7067 --- /dev/null +++ b/src/components/ui/Badge/index.tsx @@ -0,0 +1,3 @@ +import { Badge } from './Badge' + +export default Badge diff --git a/src/components/ui/Card/Card.stories.tsx b/src/components/ui/Card/Card.stories.tsx new file mode 100644 index 00000000..0d81bf00 --- /dev/null +++ b/src/components/ui/Card/Card.stories.tsx @@ -0,0 +1,82 @@ +import type { Meta, StoryObj } from '@storybook/react' +import Button from '../Button' +import { Card, CardFlex, CardImage, CardText } from './Card' + +const meta = { + title: 'UI/Card', + component: Card, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Small: Story = { + args: {}, + render: () => { + return ( + + +
+ +
+ + + 스위치 팜 + 스위치 + 10만원대 + 25분전 + +
+
+ ) + }, +} + +export const Large: Story = { + args: {}, + render: () => { + return ( + + +
+ +
+ + + 스위치 팜 + 스위치 + 10만원대 + + 25분전 + +
+
+ ) + }, +} diff --git a/src/components/ui/Card/Card.tsx b/src/components/ui/Card/Card.tsx new file mode 100644 index 00000000..82bbc8e4 --- /dev/null +++ b/src/components/ui/Card/Card.tsx @@ -0,0 +1,125 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' +import Image from 'next/image' +import COLORS from '@/styles/colors' +import { TYPHOGRAPHY } from '@/styles/sizes' +import { cn } from '@/utils' + +const cardVariants = cva( + 'rounded-card border border-background-secondary-color p-1.5', + { + variants: { + size: { + lg: 'h-card-lg', + md: 'h-card-md', + sm: 'h-card-sm', + }, + }, + defaultVariants: { + size: 'lg', + }, + }, +) + +export type CardProps = React.HTMLAttributes & + VariantProps & { + asChild?: boolean + } + +const Card = React.forwardRef( + ({ size, className, ...props }, ref) => ( +
+ ), +) +Card.displayName = 'Card' + +const cardFlexVariants = cva('flex h-full', { + variants: { + direction: { + row: 'flex-row', + col: 'flex-col', + }, + justify: { + start: 'justify-start', + center: 'justify-center', + end: 'justify-end', + between: 'justify-between', + around: 'justify-around', + }, + align: { + start: 'items-start', + center: 'items-center', + end: 'items-end', + }, + gap: { + space: 'gap-2', + none: 'gap-0', + }, + }, + defaultVariants: { + direction: 'row', + gap: 'none', + }, +}) + +export type CardFlexProps = React.HTMLAttributes & + VariantProps & { + asChild?: boolean + } + +const CardFlex = React.forwardRef( + ({ direction, justify, align, gap, className, ...props }, ref) => ( +
+ ), +) +CardFlex.displayName = 'CardFlex' + +const CardImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, alt, ...props }, ref) => ( + {alt} +)) +CardImage.displayName = 'CardImage' + +const cardTextVariants = cva('', { + variants: { + type: { + title: TYPHOGRAPHY.title, + description: TYPHOGRAPHY.description, + date: TYPHOGRAPHY.date, + icon: TYPHOGRAPHY.icon, + }, + }, + defaultVariants: { + type: 'description', + }, +}) + +export type CardTextProps = React.HTMLAttributes & + VariantProps & { + asChild?: boolean + } + +const CardText = React.forwardRef( + ({ type, className, ...props }, ref) => ( +
+ ), +) +CardText.displayName = 'CardText' + +export { Card, CardFlex, CardImage, CardText } diff --git a/src/components/ui/Card/index.tsx b/src/components/ui/Card/index.tsx new file mode 100644 index 00000000..6183f4d6 --- /dev/null +++ b/src/components/ui/Card/index.tsx @@ -0,0 +1,3 @@ +import { Card } from './Card' + +export default Card diff --git a/src/components/ui/DropdownMenu/DropdownMenu.tsx b/src/components/ui/DropdownMenu/DropdownMenu.tsx index 909e4c98..8fd4f40b 100644 --- a/src/components/ui/DropdownMenu/DropdownMenu.tsx +++ b/src/components/ui/DropdownMenu/DropdownMenu.tsx @@ -68,7 +68,7 @@ const DropdownMenuContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-background-color p-1 text-text-color shadow-md', + 'z-50 absolute min-w-[8rem] overflow-hidden rounded-md border bg-background-color p-1 text-text-color shadow-md', 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', className, )} diff --git a/src/components/ui/Tabs/Tabs.stories.tsx b/src/components/ui/Tabs/Tabs.stories.tsx new file mode 100644 index 00000000..c2859925 --- /dev/null +++ b/src/components/ui/Tabs/Tabs.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs' + +const meta = { + title: 'UI/Tabs', + component: Tabs, + tags: ['autodocs'], + argTypes: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Normal: Story = { + args: {}, + render: () => { + return ( + + + 오퍼하기 + 찔러보기 + + +
카드1
+
카드2
+
카드3
+
+ +
카드4
+
카드5
+
카드6
+
+
+ ) + }, +} diff --git a/src/components/ui/Tabs/Tabs.tsx b/src/components/ui/Tabs/Tabs.tsx new file mode 100644 index 00000000..51d0343d --- /dev/null +++ b/src/components/ui/Tabs/Tabs.tsx @@ -0,0 +1,51 @@ +'use client' + +import * as React from 'react' +import * as TabsPrimitive from '@radix-ui/react-tabs' +import { cn } from '@/utils/cn' + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/components/ui/Tabs/index.tsx b/src/components/ui/Tabs/index.tsx new file mode 100644 index 00000000..010f6d3a --- /dev/null +++ b/src/components/ui/Tabs/index.tsx @@ -0,0 +1,3 @@ +import { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs' + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/config/assets.ts b/src/config/assets.ts index c80e7d0c..8fd81228 100644 --- a/src/config/assets.ts +++ b/src/config/assets.ts @@ -1,14 +1,21 @@ import AlarmIcon from '/public/images/bell.svg' +import GoogleIcon from '/public/images/google.png' +import KakaoIcon from '/public/images/kakao.png' +import Logo from '/public/images/logo.svg' import MenuIcon from '/public/images/menu-icon.svg' import LeftIcon from '/public/images/icon-left.svg' import RightIcon from '/public/images/icon-right.svg' - +import XIcon from '/public/images/x-icon.svg' const Assets = { menuIcon: MenuIcon, alarmIcon: AlarmIcon, leftIcon: LeftIcon, rightIcon: RightIcon + googleIcon: GoogleIcon, + kakaoIcon: KakaoIcon, + logo: Logo, + xIcon: XIcon, } as const export default Assets diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 73613926..cd89cd05 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -8,6 +8,7 @@ const COLORS = { gray: '#BFBFBF', black: '#000000', white: '#FFFFFF', + kakao: '#FEE103', // dark: { // primary_300: '#534CD0', @@ -30,6 +31,7 @@ const LIGHT_THEMES = { 'text-color': COLORS.black, 'background-secondary-color': COLORS.gray, 'dialog-background-color': COLORS.black, + 'kakao-color': COLORS.kakao, } as const const DARK_THEMES = { @@ -41,6 +43,7 @@ const DARK_THEMES = { 'text-color': COLORS.white, 'background-secondary-color': COLORS.gray, 'dialog-background-color': COLORS.black, + 'kakao-color': COLORS.kakao, } as const export default COLORS diff --git a/src/styles/globals.css b/src/styles/globals.css index b5c61c95..63103c99 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,3 +1,23 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + :root { + --nav-height: 3.5rem; + --page-min-width: 320px; + --page-max-width: 640px; + } +} + +@layer base { + .centered-content { + @apply max-w-[640px] min-w-[320px] shadow-2xl bg-background-color text-text-color relative overflow-hidden mx-auto p-2 pt-16; + } + + :root, + html, + body { + @apply h-full; + } +} diff --git a/src/styles/sizes.ts b/src/styles/sizes.ts new file mode 100644 index 00000000..e79f92f7 --- /dev/null +++ b/src/styles/sizes.ts @@ -0,0 +1,16 @@ +const TYPHOGRAPHY = { + title: 'text-[14px] font-bold', + description: 'text-[12px]', + date: 'text-[10px] text-[#BFBFBF]', + icon: 'text-[10px]', +} +const HEIGHT = { + 'card-lg': '161px', + 'card-md': '118px', + 'card-sm': '106px', +} +const BORDER_RADIUS = { + card: '6px', +} + +export { TYPHOGRAPHY, HEIGHT, BORDER_RADIUS } diff --git a/tailwind.config.js b/tailwind.config.js index 5db486eb..7e7e7b94 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,6 @@ const { createThemes } = require('tw-colors') const { LIGHT_THEMES, DARK_THEMES } = require('./src/styles/colors') +const { HEIGHT, BORDER_RADIUS } = require('./src/styles/sizes') /** @type {import('tailwindcss').Config} */ module.exports = { @@ -20,6 +21,18 @@ module.exports = { 'gradient-secondary': 'linear-gradient(to right, #7C54D1 0%, #534CD0 100%)', }), + height: { + ...HEIGHT, + nav: 'var(--nav-height)', + }, + borderRadius: { + ...BORDER_RADIUS, + nav: 'var(--nav-height)', + }, + width: { + page_min: 'var(--page-min-width)', + page_max: 'var(--page-max-width)', + }, }, }, plugins: [