diff --git a/apps/storybook/.storybook/main.ts b/apps/storybook/.storybook/main.ts index 153ec5a..fa505d3 100644 --- a/apps/storybook/.storybook/main.ts +++ b/apps/storybook/.storybook/main.ts @@ -1,24 +1,44 @@ import type { StorybookConfig } from '@storybook/nextjs' const config: StorybookConfig = { - stories: [ - '../stories/**/*.mdx', - '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', - // '../../../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)', - ], + stories: ['../../../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ '@chromatic-com/storybook', '@storybook/addon-docs', '@storybook/addon-a11y', + { + name: '@storybook/addon-styling-webpack', + options: { + rules: [ + { + test: /\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + }, + }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [['@tailwindcss/postcss', {}]], + }, + }, + }, + ], + }, + ], + }, + }, ], framework: { name: '@storybook/nextjs', options: {}, }, - staticDirs: [ - '../../../packages/ui/public', // UI 컴포넌트용 리소스 - '../public', // Storybook 전용 리소스 - ], + staticDirs: ['../../../packages/ui/public'], } export default config diff --git a/apps/storybook/.storybook/preview.ts b/apps/storybook/.storybook/preview.ts index 73e6da9..54a7fb5 100644 --- a/apps/storybook/.storybook/preview.ts +++ b/apps/storybook/.storybook/preview.ts @@ -1,14 +1,15 @@ import type { Preview } from '@storybook/nextjs' +import '@repo/styles' const preview: Preview = { parameters: { controls: { matchers: { - color: /(background|color)$/i, - date: /Date$/i, + color: /(background|color)$/i, + date: /Date$/i, }, }, }, -}; +} -export default preview; \ No newline at end of file +export default preview diff --git a/apps/storybook/package.json b/apps/storybook/package.json index 7063de3..3e8c857 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -11,27 +11,31 @@ "@repo/ui": "workspace:^" }, "devDependencies": { + "@chromatic-com/storybook": "^4.1.0", + "@eslint/eslintrc": "^3", "@repo/eslint-config": "workspace:*", "@repo/tailwind-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@chromatic-com/storybook": "^4.1.0", - "@eslint/eslintrc": "^3", "@storybook/addon-a11y": "^9.1.0", "@storybook/addon-docs": "^9.1.0", + "@storybook/addon-styling-webpack": "^2.0.0", "@storybook/nextjs": "^9.1.0", - "@tailwindcss/postcss": "^4", + "@tailwindcss/postcss": "^4.1.5", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", "chromatic": "^13.1.3", + "css-loader": "^7.1.2", "eslint": "^9.32.0", "eslint-config-next": "15.4.5", "eslint-plugin-react": "^7.37.4", "eslint-plugin-storybook": "^9.1.0", + "postcss-loader": "^8.1.1", "storybook": "^9.1.0", - "tailwindcss": "^4", + "style-loader": "^4.0.0", + "tailwindcss": "^4.1.5", "typescript": "^5.8.2", "typescript-eslint": "^8.37.0" } diff --git a/apps/storybook/postcss.config.mjs b/apps/storybook/postcss.config.mjs deleted file mode 100644 index c7bcb4b..0000000 --- a/apps/storybook/postcss.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -const config = { - plugins: ["@tailwindcss/postcss"], -}; - -export default config; diff --git a/apps/storybook/public/favicon.ico b/apps/storybook/public/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/apps/storybook/public/favicon.ico and /dev/null differ diff --git a/apps/storybook/public/file.svg b/apps/storybook/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/apps/storybook/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/public/globe.svg b/apps/storybook/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/apps/storybook/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/public/next.svg b/apps/storybook/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/apps/storybook/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/public/vercel.svg b/apps/storybook/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/apps/storybook/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/public/window.svg b/apps/storybook/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/apps/storybook/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/stories/Button.stories.ts b/apps/storybook/stories/Button.stories.ts deleted file mode 100644 index 747e7c8..0000000 --- a/apps/storybook/stories/Button.stories.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/nextjs'; - -import { fn } from 'storybook/test'; - -import { Button } from './Button'; - -// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -const meta = { - title: 'Example/Button', - component: Button, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: ['autodocs'], - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - backgroundColor: { control: 'color' }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -export const Primary: Story = { - args: { - primary: true, - label: 'Button', - }, -}; - -export const Secondary: Story = { - args: { - label: 'Button', - }, -}; - -export const Large: Story = { - args: { - size: 'large', - label: 'Button', - }, -}; - -export const Small: Story = { - args: { - size: 'small', - label: 'Button', - }, -}; diff --git a/apps/storybook/stories/Button.tsx b/apps/storybook/stories/Button.tsx deleted file mode 100644 index d96916c..0000000 --- a/apps/storybook/stories/Button.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import './button.css'; - -export interface ButtonProps { - /** Is this the principal call to action on the page? */ - primary?: boolean; - /** What background color to use */ - backgroundColor?: string; - /** How large should the button be? */ - size?: 'small' | 'medium' | 'large'; - /** Button contents */ - label: string; - /** Optional click handler */ - onClick?: () => void; -} - -/** Primary UI component for user interaction */ -export const Button = ({ - primary = false, - size = 'medium', - backgroundColor, - label, - ...props -}: ButtonProps) => { - const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - return ( - - ); -}; diff --git a/apps/storybook/stories/Configure.mdx b/apps/storybook/stories/Configure.mdx deleted file mode 100644 index 8734a26..0000000 --- a/apps/storybook/stories/Configure.mdx +++ /dev/null @@ -1,446 +0,0 @@ -import { Meta } from "@storybook/addon-docs/blocks"; -import Image from "next/image"; - -import Github from "./assets/github.svg"; -import Discord from "./assets/discord.svg"; -import Youtube from "./assets/youtube.svg"; -import Tutorials from "./assets/tutorials.svg"; -import Styling from "./assets/styling.png"; -import Context from "./assets/context.png"; -import Assets from "./assets/assets.png"; -import Docs from "./assets/docs.png"; -import Share from "./assets/share.png"; -import FigmaPlugin from "./assets/figma-plugin.png"; -import Testing from "./assets/testing.png"; -import Accessibility from "./assets/accessibility.png"; -import Theming from "./assets/theming.png"; -import AddonLibrary from "./assets/addon-library.png"; - -export const RightArrow = () => - - - - - -
-
- # Configure your project - - Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community. -
-
-
- A wall of logos representing different styling technologies -

Add styling and CSS

-

Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.

- Learn more -
-
- An abstraction representing the composition of data for a component -

Provide context and mocking

-

Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.

- Learn more -
-
- A representation of typography and image assets -
-

Load assets and resources

-

To link static files (like fonts) to your projects and stories, use the - `staticDirs` configuration option to specify folders to load when - starting Storybook.

- Learn more -
-
-
-
-
-
- # Do more with Storybook - - Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs. -
- -
-
-
- A screenshot showing the autodocs tag being set, pointing a docs page being generated -

Autodocs

-

Auto-generate living, - interactive reference documentation from your components and stories.

- Learn more -
-
- A browser window showing a Storybook being published to a chromatic.com URL -

Publish to Chromatic

-

Publish your Storybook to review and collaborate with your entire team.

- Learn more -
-
- Windows showing the Storybook plugin in Figma -

Figma Plugin

-

Embed your stories into Figma to cross-reference the design and live - implementation in one place.

- Learn more -
-
- Screenshot of tests passing and failing -

Testing

-

Use stories to test a component in all its variations, no matter how - complex.

- Learn more -
-
- Screenshot of accessibility tests passing and failing -

Accessibility

-

Automatically test your components for a11y issues as you develop.

- Learn more -
-
- Screenshot of Storybook in light and dark mode -

Theming

-

Theme Storybook's UI to personalize it to your project.

- Learn more -
-
-
-
-
-
-

Addons

-

Integrate your tools with Storybook to connect workflows.

- Discover all addons -
-
- Integrate your tools with Storybook to connect workflows. -
-
- -
-
- Github logo - Join our contributors building the future of UI development. - - Star on GitHub -
-
- Discord logo -
- Get support and chat with frontend developers. - - Join Discord server -
-
-
- Youtube logo -
- Watch tutorials, feature previews and interviews. - - Watch on YouTube -
-
-
- A book -

Follow guided walkthroughs on for key workflows.

- - Discover tutorials -
-
- - diff --git a/apps/storybook/stories/Header.stories.ts b/apps/storybook/stories/Header.stories.ts deleted file mode 100644 index 56ed5e0..0000000 --- a/apps/storybook/stories/Header.stories.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/nextjs'; - -import { fn } from 'storybook/test'; - -import { Header } from './Header'; - -const meta = { - title: 'Example/Header', - component: Header, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: ['autodocs'], - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'fullscreen', - }, - args: { - onLogin: fn(), - onLogout: fn(), - onCreateAccount: fn(), - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const LoggedIn: Story = { - args: { - user: { - name: 'Jane Doe', - }, - }, -}; - -export const LoggedOut: Story = {}; diff --git a/apps/storybook/stories/Header.tsx b/apps/storybook/stories/Header.tsx deleted file mode 100644 index d05ed4f..0000000 --- a/apps/storybook/stories/Header.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Button } from './Button'; -import './header.css'; - -type User = { - name: string; -}; - -export interface HeaderProps { - user?: User; - onLogin?: () => void; - onLogout?: () => void; - onCreateAccount?: () => void; -} - -export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( -
-
-
- - - - - - - -

Acme

-
-
- {user ? ( - <> - - Welcome, {user.name}! - -
-
-
-); diff --git a/apps/storybook/stories/Page.stories.ts b/apps/storybook/stories/Page.stories.ts deleted file mode 100644 index 46f09c0..0000000 --- a/apps/storybook/stories/Page.stories.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/nextjs'; - -import { expect, userEvent, within } from 'storybook/test'; - -import { Page } from './Page'; - -const meta = { - title: 'Example/Page', - component: Page, - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'fullscreen', - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const LoggedOut: Story = {}; - -// More on component testing: https://storybook.js.org/docs/writing-tests/interaction-testing -export const LoggedIn: Story = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const loginButton = canvas.getByRole('button', { name: /Log in/i }); - await expect(loginButton).toBeInTheDocument(); - await userEvent.click(loginButton); - await expect(loginButton).not.toBeInTheDocument(); - - const logoutButton = canvas.getByRole('button', { name: /Log out/i }); - await expect(logoutButton).toBeInTheDocument(); - }, -}; diff --git a/apps/storybook/stories/Page.tsx b/apps/storybook/stories/Page.tsx deleted file mode 100644 index 79e43f1..0000000 --- a/apps/storybook/stories/Page.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react' - -import { Header } from './Header' -import './page.css' - -type User = { - name: string -} - -export const Page: React.FC = () => { - const [user, setUser] = React.useState() - - return ( -
-
setUser({ name: 'Jane Doe' })} - onLogout={() => setUser(undefined)} - onCreateAccount={() => setUser({ name: 'Jane Doe' })} - /> - -
-

Pages in Storybook

-

- We recommend building UIs with a{' '} - - component-driven - {' '} - process starting with atomic components and ending with pages. -

-

- Render pages with mock data. This makes it easy to build and review - page states without needing to navigate to them in your app. Here are - some handy patterns for managing page data in Storybook: -

-
    -
  • - Use a higher-level connected component. Storybook helps you compose -
  • -
  • - Assemble data in the page component from your services. You can mock - these services out using Storybook. -
  • -
-

- Get a guided tutorial on component-driven development at{' '} - - Storybook tutorials - - . Read more in the{' '} - - docs - - . -

-
- Tip Adjust the width of the canvas with - the{' '} - - - - - - Viewports addon in the toolbar -
-
-
- ) -} diff --git a/apps/storybook/stories/assets/accessibility.png b/apps/storybook/stories/assets/accessibility.png deleted file mode 100644 index 6ffe6fe..0000000 Binary files a/apps/storybook/stories/assets/accessibility.png and /dev/null differ diff --git a/apps/storybook/stories/assets/accessibility.svg b/apps/storybook/stories/assets/accessibility.svg deleted file mode 100644 index 107e93f..0000000 --- a/apps/storybook/stories/assets/accessibility.svg +++ /dev/null @@ -1 +0,0 @@ -Accessibility \ No newline at end of file diff --git a/apps/storybook/stories/assets/addon-library.png b/apps/storybook/stories/assets/addon-library.png deleted file mode 100644 index 95deb38..0000000 Binary files a/apps/storybook/stories/assets/addon-library.png and /dev/null differ diff --git a/apps/storybook/stories/assets/assets.png b/apps/storybook/stories/assets/assets.png deleted file mode 100644 index cfba681..0000000 Binary files a/apps/storybook/stories/assets/assets.png and /dev/null differ diff --git a/apps/storybook/stories/assets/avif-test-image.avif b/apps/storybook/stories/assets/avif-test-image.avif deleted file mode 100644 index 530709b..0000000 Binary files a/apps/storybook/stories/assets/avif-test-image.avif and /dev/null differ diff --git a/apps/storybook/stories/assets/context.png b/apps/storybook/stories/assets/context.png deleted file mode 100644 index e5cd249..0000000 Binary files a/apps/storybook/stories/assets/context.png and /dev/null differ diff --git a/apps/storybook/stories/assets/discord.svg b/apps/storybook/stories/assets/discord.svg deleted file mode 100644 index d638958..0000000 --- a/apps/storybook/stories/assets/discord.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/stories/assets/docs.png b/apps/storybook/stories/assets/docs.png deleted file mode 100644 index a749629..0000000 Binary files a/apps/storybook/stories/assets/docs.png and /dev/null differ diff --git a/apps/storybook/stories/assets/figma-plugin.png b/apps/storybook/stories/assets/figma-plugin.png deleted file mode 100644 index 8f79b08..0000000 Binary files a/apps/storybook/stories/assets/figma-plugin.png and /dev/null differ diff --git a/apps/storybook/stories/assets/github.svg b/apps/storybook/stories/assets/github.svg deleted file mode 100644 index dc51352..0000000 --- a/apps/storybook/stories/assets/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/stories/assets/share.png b/apps/storybook/stories/assets/share.png deleted file mode 100644 index 8097a37..0000000 Binary files a/apps/storybook/stories/assets/share.png and /dev/null differ diff --git a/apps/storybook/stories/assets/styling.png b/apps/storybook/stories/assets/styling.png deleted file mode 100644 index d341e82..0000000 Binary files a/apps/storybook/stories/assets/styling.png and /dev/null differ diff --git a/apps/storybook/stories/assets/testing.png b/apps/storybook/stories/assets/testing.png deleted file mode 100644 index d4ac39a..0000000 Binary files a/apps/storybook/stories/assets/testing.png and /dev/null differ diff --git a/apps/storybook/stories/assets/theming.png b/apps/storybook/stories/assets/theming.png deleted file mode 100644 index 1535eb9..0000000 Binary files a/apps/storybook/stories/assets/theming.png and /dev/null differ diff --git a/apps/storybook/stories/assets/tutorials.svg b/apps/storybook/stories/assets/tutorials.svg deleted file mode 100644 index b492a9c..0000000 --- a/apps/storybook/stories/assets/tutorials.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/stories/assets/youtube.svg b/apps/storybook/stories/assets/youtube.svg deleted file mode 100644 index a7515d7..0000000 --- a/apps/storybook/stories/assets/youtube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/storybook/stories/button.css b/apps/storybook/stories/button.css deleted file mode 100644 index 4e3620b..0000000 --- a/apps/storybook/stories/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - display: inline-block; - cursor: pointer; - border: 0; - border-radius: 3em; - font-weight: 700; - line-height: 1; - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} -.storybook-button--primary { - background-color: #555ab9; - color: white; -} -.storybook-button--secondary { - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; - background-color: transparent; - color: #333; -} -.storybook-button--small { - padding: 10px 16px; - font-size: 12px; -} -.storybook-button--medium { - padding: 11px 20px; - font-size: 14px; -} -.storybook-button--large { - padding: 12px 24px; - font-size: 16px; -} diff --git a/apps/storybook/stories/header.css b/apps/storybook/stories/header.css deleted file mode 100644 index 5efd46c..0000000 --- a/apps/storybook/stories/header.css +++ /dev/null @@ -1,32 +0,0 @@ -.storybook-header { - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 15px 20px; - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -.storybook-header svg { - display: inline-block; - vertical-align: top; -} - -.storybook-header h1 { - display: inline-block; - vertical-align: top; - margin: 6px 0 6px 10px; - font-weight: 700; - font-size: 20px; - line-height: 1; -} - -.storybook-header button + button { - margin-left: 10px; -} - -.storybook-header .welcome { - margin-right: 10px; - color: #333; - font-size: 14px; -} diff --git a/apps/storybook/stories/page.css b/apps/storybook/stories/page.css deleted file mode 100644 index 77c81d2..0000000 --- a/apps/storybook/stories/page.css +++ /dev/null @@ -1,68 +0,0 @@ -.storybook-page { - margin: 0 auto; - padding: 48px 20px; - max-width: 600px; - color: #333; - font-size: 14px; - line-height: 24px; - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -.storybook-page h2 { - display: inline-block; - vertical-align: top; - margin: 0 0 4px; - font-weight: 700; - font-size: 32px; - line-height: 1; -} - -.storybook-page p { - margin: 1em 0; -} - -.storybook-page a { - color: inherit; -} - -.storybook-page ul { - margin: 1em 0; - padding-left: 30px; -} - -.storybook-page li { - margin-bottom: 8px; -} - -.storybook-page .tip { - display: inline-block; - vertical-align: top; - margin-right: 10px; - border-radius: 1em; - background: #e7fdd8; - padding: 4px 12px; - color: #357a14; - font-weight: 700; - font-size: 11px; - line-height: 12px; -} - -.storybook-page .tip-wrapper { - margin-top: 40px; - margin-bottom: 40px; - font-size: 13px; - line-height: 20px; -} - -.storybook-page .tip-wrapper svg { - display: inline-block; - vertical-align: top; - margin-top: 3px; - margin-right: 4px; - width: 12px; - height: 12px; -} - -.storybook-page .tip-wrapper svg path { - fill: #1ea7fd; -} diff --git a/apps/storybook/tsconfig.json b/apps/storybook/tsconfig.json index 572b7ad..4c174a2 100644 --- a/apps/storybook/tsconfig.json +++ b/apps/storybook/tsconfig.json @@ -14,7 +14,8 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@repo/styles" : ["../../packages/ui/src/styles.css"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index fd85386..5e7ce44 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -1,26 +1,2 @@ @import "tailwindcss"; -@import "@repo/tailwind-config"; - -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} +@import "@repo/tailwind-config"; \ No newline at end of file diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index ce359ee..1deb397 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -3,6 +3,7 @@ import './globals.css' import type { Metadata } from 'next' import QueryProvider from './QueryClientProvider' import localFont from 'next/font/local' +import { Column } from '@repo/ui/components/Layout/Column' export const metadata: Metadata = { title: 'Create Turborepo', @@ -23,7 +24,13 @@ export default function RootLayout({ return ( - {children} + +
+ + {children} + +
+
) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 65ad9ad..ec0ff95 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,116 +1,13 @@ -import Image from "next/image"; -import { Card } from "@repo/ui/card"; -import { Gradient } from "@repo/ui/gradient"; -import { TurborepoLogo } from "@repo/ui/turborepo-logo"; - -const LINKS = [ - { - title: "Docs", - href: "https://turborepo.com/docs", - description: "Find in-depth information about Turborepo features and API.", - }, - { - title: "Learn", - href: "https://turborepo.com/docs/handbook", - description: "Learn more about monorepos with our handbook.", - }, - { - title: "Templates", - href: "https://turborepo.com/docs/getting-started/from-example", - description: "Choose from over 15 examples and deploy with a single click.", - }, - { - title: "Deploy", - href: "https://vercel.com/new", - description: - "Instantly deploy your Turborepo to a shareable URL with Vercel.", - }, -]; +import { Flex } from '@repo/ui/components/Layout/Flex' export default function Page() { return ( -
-
-

- examples/with-tailwind -  - web -

- -
- -
-
-
-
- Turborepo -
-
- -
- -
- -
-
- -
- - Turborepo logo - - - - - - - - - - -
-
-
- -
- {LINKS.map(({ title, href, description }) => ( - - {description} - - ))} -
-
- ); + <> + +
gkldf
+
gkldf
+
gkldf
+
+ + ) } diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index e8759ff..744a692 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -1,4 +1,12 @@ -import { nextJsConfig } from "@repo/eslint-config/next-js"; +import { nextJsConfig } from '@repo/eslint-config/next-js' /** @type {import("eslint").Linter.Config} */ -export default nextJsConfig; +export default [ + ...nextJsConfig, + { + files: ['**/*.{ts,tsx}'], + rules: { + 'react/prop-types': 'off', + }, + }, +] diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index f6ebad8..fe120fc 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -10,8 +10,10 @@ /* alias */ "baseUrl": ".", "paths": { - "@/*": ["src/*"] - } + "@/*": ["src/*"], + "@repo/ui/components/*": ["../../packages/ui/src/components/*"] + }, + }, "include": [ "**/*.ts", diff --git a/packages/tailwind-config/shared-styles.css b/packages/tailwind-config/shared-styles.css index 77a57d5..3d139ec 100644 --- a/packages/tailwind-config/shared-styles.css +++ b/packages/tailwind-config/shared-styles.css @@ -4,6 +4,7 @@ --color-main: #313D4C; --color-blue: #3182F7; + --color-red: #EF4452; --color-gray-50: #F3F4F6; --color-gray-100: #E4E7EB; @@ -11,4 +12,11 @@ --color-gray-300: #6C7887; --color-gray-400: #3D3D3D; --color-gray-500: #403E3F; +} + +.scrollbar-hide { + &::-webkit-scrollbar { + display: none; + } + scrollbar-width: none; } \ No newline at end of file diff --git a/packages/ui/eslint.config.js b/packages/ui/eslint.config.js new file mode 100644 index 0000000..05f1c9e --- /dev/null +++ b/packages/ui/eslint.config.js @@ -0,0 +1,12 @@ +import { config } from '@repo/eslint-config/react-internal' + +/** @type {import("eslint").Linter.Config} */ +export default [ + ...config, + { + files: ['**/*.{ts,tsx}'], + rules: { + 'react/prop-types': 'off', + }, + }, +] diff --git a/packages/ui/eslint.config.mjs b/packages/ui/eslint.config.mjs deleted file mode 100644 index 19170f8..0000000 --- a/packages/ui/eslint.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { config } from "@repo/eslint-config/react-internal"; - -/** @type {import("eslint").Linter.Config} */ -export default config; diff --git a/packages/ui/package.json b/packages/ui/package.json index c854fc3..7d84a5a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -27,6 +27,7 @@ "@repo/eslint-config": "workspace:*", "@repo/tailwind-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@storybook/nextjs": "^9.1.0", "@tailwindcss/cli": "^4.1.5", "@types/react": "^19.1.0", "eslint": "^9.32.0", diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx deleted file mode 100644 index 8306da9..0000000 --- a/packages/ui/src/card.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { type ReactNode } from "react"; - -export function Card({ - title, - children, - href, -}: { - title: string; - children: ReactNode; - href: string; -}) { - return ( - -

- {title}{" "} - - -> - -

-

- {children} -

-
- ); -} diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx new file mode 100644 index 0000000..1020219 --- /dev/null +++ b/packages/ui/src/components/Button/Button.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Button } from './Button' + +const meta = { + title: 'Components/Button', + component: Button, + tags: ['autodocs'], + argTypes: { + size: { + control: { type: 'radio' }, + options: ['small', 'medium', 'large'], + }, + as: { + control: { type: 'text' }, + description: 'Polymorphic prop: 렌더링할 HTML 태그나 컴포넌트', + }, + className: { + control: false, + }, + children: { + control: 'text', + }, + }, + args: { + size: 'medium', + children: 'Button', + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Small: Story = { + args: { + size: 'small', + children: 'Button', + }, +} + +export const Medium: Story = { + args: { + size: 'medium', + children: 'Medium Button', + }, +} + +export const Large: Story = { + args: { + size: 'large', + children: 'Large Button', + }, +} + +export const AsLink: Story = { + args: { + as: 'a', + href: '#', + size: 'medium', + children: 'Link Button', + }, +} diff --git a/packages/ui/src/components/Button/Button.tsx b/packages/ui/src/components/Button/Button.tsx new file mode 100644 index 0000000..06c6cf7 --- /dev/null +++ b/packages/ui/src/components/Button/Button.tsx @@ -0,0 +1,42 @@ +import type { ElementType, JSX, PropsWithChildren } from 'react' +import type { PolymorphicComponentProps } from '../../polymorphics' +import { cn } from '../../utils/cn' +import { Text } from '../Text' +import { BUTTON_FONT_SIZE, BUTTON_SIZE } from './consts' + +export interface ButtonProps { + // color: 'primary' | 'secondary' | 'mono' + // variant: 'filled' | 'outlined' | 'subtle' | 'text' + size: 'small' | 'medium' | 'large' +} + +export type Props = PolymorphicComponentProps< + C, + ButtonProps +> +export type ButtonType = ( + props: PropsWithChildren>, +) => JSX.Element + +export const Button: ButtonType = ({ as, className, size, children }) => { + const Component = as || 'button' + + return ( + + + {children} + + + ) +} diff --git a/packages/ui/src/components/Button/consts.ts b/packages/ui/src/components/Button/consts.ts new file mode 100644 index 0000000..d4bcb8a --- /dev/null +++ b/packages/ui/src/components/Button/consts.ts @@ -0,0 +1,16 @@ +import type { ButtonProps } from './Button' +import type { TypographyVariant } from '../Text/Text' + +export const BUTTON_SIZE: { [k in ButtonProps['size']]: string } = { + large: 'ui:h-[60px] ui:min-h-[60px] ui:w-[335px] ui:min-w-[335px]', + medium: 'ui:h-[50px] ui:min-h-[50px] ui:w-[180px] ui:min-w-[180px]', + small: 'ui:h-[40px] ui:min-h-[40px] ui:w-[80px] ui:min-w-[80px]', +} + +export const BUTTON_FONT_SIZE: { + [k in ButtonProps['size']]: TypographyVariant +} = { + large: 'heading1', + medium: 'heading2', + small: 'title1', +} diff --git a/packages/ui/src/components/Button/index.tsx b/packages/ui/src/components/Button/index.tsx new file mode 100644 index 0000000..4d0a670 --- /dev/null +++ b/packages/ui/src/components/Button/index.tsx @@ -0,0 +1 @@ +export { Button } from './Button' diff --git a/packages/ui/src/components/Chip/Chip.stories.tsx b/packages/ui/src/components/Chip/Chip.stories.tsx new file mode 100644 index 0000000..0fcd17e --- /dev/null +++ b/packages/ui/src/components/Chip/Chip.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Chip } from './Chip' +import { Flex } from '../Layout' + +const meta: Meta = { + title: 'Components/Chip', + component: Chip, + tags: ['autodocs'], +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: () => ( + + + + + + + ), +} + +export const ClickableChips: Story = { + render: () => ( + + {( + [ + 'SOLO_FRIENDLY', + 'VALUE_FOR_MONEY', + 'GOOD_AMBIENCE', + 'KIND_SERVICE', + ] as const + ).map((type) => ( + { + console.log(`${type} 클릭됨!`) + }} + /> + ))} + + ), +} diff --git a/packages/ui/src/components/Chip/Chip.tsx b/packages/ui/src/components/Chip/Chip.tsx new file mode 100644 index 0000000..5081739 --- /dev/null +++ b/packages/ui/src/components/Chip/Chip.tsx @@ -0,0 +1,103 @@ +'use client' +import type { ElementType, JSX, PropsWithChildren } from 'react' +import type { PolymorphicComponentProps } from '../../polymorphics' +import { useState } from 'react' +import { cn } from '../../utils/cn' +import { Icon } from '../Icon' +import { Text } from '../Text' + +const CHIP_TAGS = { + SOLO_FRIENDLY: { + label: '혼밥하기 좋은', + icon: 'fingerUp', + }, + VALUE_FOR_MONEY: { + label: '가성비 좋은', + icon: 'calculator', + }, + GOOD_AMBIENCE: { + label: '분위기 좋은', + icon: 'blingBling', + }, + KIND_SERVICE: { + label: '친절해요', + icon: 'waiter', + }, +} as const + +type ChipTagKey = keyof typeof CHIP_TAGS + +export type ChipProps = PolymorphicComponentProps< + C, + { + type: ChipTagKey + onToggle?: () => void + } +> + +export type ChipType = ( + props: PropsWithChildren>, +) => JSX.Element + +/** + * Chip 컴포넌트 + * + * - 아이콘과 라벨을 가진 토글 가능한 UI 요소입니다. + * - 클릭 시 내부 상태 `isActive`를 토글하며, `onToggle` 콜백을 실행합니다. + * - 다양한 HTML 요소(`as` prop)를 지정하여 렌더링할 수 있습니다. + * + * @template C 렌더링할 HTML 태그 타입 (기본값: 'button') + * + * @param as 렌더링할 HTML 태그 또는 컴포넌트 + * @param className 추가 CSS 클래스 + * @param type 표시할 Chip 타입 + * @param onToggle 클릭 시 실행할 콜백 함수 + * @param restProps 나머지 Props + * + * @returns 렌더링된 Chip 요소 + * + * @example + * console.log('클릭됨')} /> + */ +export const Chip: ChipType = ({ + as, + className, + type, + onToggle, + ...restProps +}) => { + const Component = as || 'button' + const { icon, label } = CHIP_TAGS[type] + const [isActive, setIsActive] = useState(false) + + const onClick = () => { + if (onToggle) { + onToggle() + setIsActive((prev) => !prev) + } + } + + return ( + + + + {label} + + + ) +} diff --git a/packages/ui/src/components/Chip/index.tsx b/packages/ui/src/components/Chip/index.tsx new file mode 100644 index 0000000..52516f8 --- /dev/null +++ b/packages/ui/src/components/Chip/index.tsx @@ -0,0 +1 @@ +export { Chip } from './Chip' diff --git a/packages/ui/src/components/Header/Header.stories.tsx b/packages/ui/src/components/Header/Header.stories.tsx new file mode 100644 index 0000000..1af3dcc --- /dev/null +++ b/packages/ui/src/components/Header/Header.stories.tsx @@ -0,0 +1,56 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import type { ReactNode } from 'react' +import { Header, OnlyLeftHeader } from './Header' +import { Icon } from '../Icon' +import { Text } from '../Text' + +const meta: Meta = { + title: 'Components/Header', + component: Header, + parameters: { + layout: 'fullscreen', + }, +} + +export default meta +type Story = StoryObj + +const Box = ({ children }: { children: ReactNode }) => ( +
+
{children}
+
+) + +// 기본 헤더: left, center, right 모두 존재 +export const Default: Story = { + render: () => ( + +
} + center={페이지 제목} + right={} + /> + + ), +} + +// right 없는 경우: invisible placeholder로 center 정렬 유지 +export const WithoutRight: Story = { + render: () => ( + +
} + center={페이지 제목} + /> + + ), +} + +// left만 존재 +export const LeftOnly: Story = { + render: () => ( + + + + ), +} diff --git a/packages/ui/src/components/Header/Header.tsx b/packages/ui/src/components/Header/Header.tsx new file mode 100644 index 0000000..bf0461d --- /dev/null +++ b/packages/ui/src/components/Header/Header.tsx @@ -0,0 +1,34 @@ +import { Flex, JustifyBetween } from '../Layout' +import type { ReactNode } from 'react' +import { Icon } from '../Icon' +import { Text } from '../Text' +import type { IconType } from '../Icon/IconMap' + +type Props = { + left?: ReactNode + center?: ReactNode + right?: ReactNode +} + +export const Header = ({ left, center, right }: Props) => { + return ( + + {left} + {center} + {right ??
{left}
} +
+ ) +} + +export const OnlyLeftHeader = ({ + icon, + name, +}: { + icon: IconType + name: string +}) => ( + + + {name} + +) diff --git a/packages/ui/src/components/Header/index.tsx b/packages/ui/src/components/Header/index.tsx new file mode 100644 index 0000000..faba5df --- /dev/null +++ b/packages/ui/src/components/Header/index.tsx @@ -0,0 +1 @@ +export { Header, OnlyLeftHeader } from './Header' diff --git a/packages/ui/src/components/Icon/Icon.stories.tsx b/packages/ui/src/components/Icon/Icon.stories.tsx new file mode 100644 index 0000000..68d5220 --- /dev/null +++ b/packages/ui/src/components/Icon/Icon.stories.tsx @@ -0,0 +1,41 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Icon } from './Icon' +import { IconList } from './IconMap' +import { Column } from '../Layout' + +const meta: Meta = { + title: 'Components/Icon', + component: Icon, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} + +export default meta +type Story = StoryObj + +// 단일 아이콘 +export const Default: Story = { + args: { + type: 'chicken', + size: 48, + }, +} + +// 전체 아이콘 +export const AllIcons: Story = { + render: () => ( + + {IconList.map((type) => ( + + + {type} + + ))} + + ), +} diff --git a/packages/ui/src/components/Icon/Icon.tsx b/packages/ui/src/components/Icon/Icon.tsx new file mode 100644 index 0000000..c7e0b32 --- /dev/null +++ b/packages/ui/src/components/Icon/Icon.tsx @@ -0,0 +1,51 @@ +import { iconMap, type IconType } from './IconMap' +import { SVGProps } from 'react' +import '@repo/tailwind-config' + +const IconColor = { + '--color-main': '#313D4C', + '--color-blue': '#3182F7', + '--color-red': '#EF4452', + '--color-white': '#FFFFFF', + '--color-gray-50': '#F3F4F6', + '--color-gray-100': '#E4E7EB', + '--color-gray-200': '#B0B9C2', + '--color-gray-300': '#6C7887', + '--color-gray-400': '#3D3D3D', + '--color-gray-500': '#403E3F', +} as const + +type IconColorType = keyof typeof IconColor + +export type Props = { + type: IconType + size?: number + color?: IconColorType +} & Omit, 'color'> + +/** + * `iconMap`에 정의된 `type` 값을 기반으로 SVG 아이콘을 렌더링합니다. + * `size`를 지정하면 아이콘의 `width`와 `height`가 동일하게 변경됩니다. + * + * @example + * // 기본 아이콘 + * + * + * @param props - Icon 컴포넌트 속성 + * @param props.type - 렌더링할 아이콘의 키값 (`iconMap`의 key) + * @param [props.size=24] - 아이콘의 크기 (width, height에 동일 적용) + * @param [props.color] - 아이콘의 색상 + * @returns 렌더링된 SVG 아이콘 + */ +export function Icon({ type, size = 24, color, ...props }: Props) { + const Icon = iconMap[type] + + return ( + + ) +} diff --git a/packages/ui/src/components/Icon/IconMap.ts b/packages/ui/src/components/Icon/IconMap.ts new file mode 100644 index 0000000..798c4f6 --- /dev/null +++ b/packages/ui/src/components/Icon/IconMap.ts @@ -0,0 +1,101 @@ +// 메뉴 +import { Asian } from './assets/icons/menu/asian' +import { Bunsik } from './assets/icons/menu/bunsik' +import { Burger } from './assets/icons/menu/burger' +import { Cafe } from './assets/icons/menu/cafe' +import { Chicken } from './assets/icons/menu/chicken' +import { Chinese } from './assets/icons/menu/chinese' +import { Japanese } from './assets/icons/menu/japanese' +import { Korean } from './assets/icons/menu/korean' +import { Lunchbox } from './assets/icons/menu/lunchbox' +import { Meat } from './assets/icons/menu/meat' +import { Mexican } from './assets/icons/menu/mexican' +import { Pizza } from './assets/icons/menu/pizza' +import { Salad } from './assets/icons/menu/salad' +import { Soup } from './assets/icons/menu/soup' +import { Western } from './assets/icons/menu/western' +// 네비게이션 +import { Home } from './assets/icons/navigation/home' +import { Map } from './assets/icons/navigation/map' +import { CirclePlus } from './assets/icons/navigation/circlePlus' +import { Heart as NavHeart } from './assets/icons/navigation/heart' +import { User as NavUSer } from './assets/icons/navigation/user' +// 태그 +import { FingerUp } from './assets/icons/tag/fingerUp' +import { Calculator } from './assets/icons/tag/calculator' +import { BlingBling } from './assets/icons/tag/blingBling' +import { Waiter } from './assets/icons/tag/waiter' +// 헤더 +import { MarkerWithMap } from './assets/icons/header/markerWithMap' +import { Logo } from './assets/icons/header/logo' +import { Heart as HeaderHeart } from './assets/icons/header/heart' +import { User as HeaderUser } from './assets/icons/header/user' +import { ShakingHeart } from './assets/icons/header/shakingHeart' + +import { ArrowLeft } from './assets/icons/arrowLeft' +import { ArrowRight } from './assets/icons/arrowRight' +import { Search } from './assets/icons/search' +import { FireHeart } from './assets/icons/fireHeart' +import { Fire } from './assets/icons/fire' +import { Marker } from './assets/icons/marker' +import { Pin } from './assets/icons/pin' +import { Note } from './assets/icons/note' +import { Smile } from './assets/icons/smile' +import { Cry } from './assets/icons/cry' +import { KakaoLogo } from './assets/icons/kakaoLogo' +import { Crosshairs } from './assets/icons/crosshairs' + +export const iconMap = { + // 메뉴 + asian: Asian, // 아시안 + bunsik: Bunsik, // 분식 + burger: Burger, // 햄버거 + cafe: Cafe, // 카페 + chicken: Chicken, // 치킨 + chinese: Chinese, // 중식 + japanese: Japanese, // 일식 + korean: Korean, // 한식 + lunchbox: Lunchbox, // 도시락 + meat: Meat, // 고기·구이 + mexican: Mexican, // 멕시칸 + pizza: Pizza, // 피자 + salad: Salad, // 샐러드 + soup: Soup, // 찜·탕 + western: Western, // 양식 + + // 네비게이션 + home: Home, + map: Map, + circlePlus: CirclePlus, + navHeart: NavHeart, + navUser: NavUSer, + + // 태그 + fingerUp: FingerUp, + calculator: Calculator, + blingBling: BlingBling, + waiter: Waiter, + + // 헤더 + logo: Logo, + markerWithMap: MarkerWithMap, + headerHeart: HeaderHeart, + headerUser: HeaderUser, + shakingHeart: ShakingHeart, + + arrowLeft: ArrowLeft, + arrowRight: ArrowRight, + search: Search, + fireHeart: FireHeart, + fire: Fire, + marker: Marker, + pin: Pin, + note: Note, + smile: Smile, + cry: Cry, + kakkoLogo: KakaoLogo, + crosshairs: Crosshairs, +} + +export type IconType = keyof typeof iconMap +export const IconList = Object.keys(iconMap) as IconType[] diff --git a/packages/ui/src/components/Icon/assets/icons/arrowLeft.tsx b/packages/ui/src/components/Icon/assets/icons/arrowLeft.tsx new file mode 100644 index 0000000..05df706 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/arrowLeft.tsx @@ -0,0 +1,24 @@ +import { SVGProps } from 'react' + +export const ArrowLeft = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/arrowRight.tsx b/packages/ui/src/components/Icon/assets/icons/arrowRight.tsx new file mode 100644 index 0000000..5d9132d --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/arrowRight.tsx @@ -0,0 +1,24 @@ +import { SVGProps } from 'react' + +export const ArrowRight = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/crosshairs.tsx b/packages/ui/src/components/Icon/assets/icons/crosshairs.tsx new file mode 100644 index 0000000..952f992 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/crosshairs.tsx @@ -0,0 +1,34 @@ +import { SVGProps } from 'react' + +export const Crosshairs = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/cry.tsx b/packages/ui/src/components/Icon/assets/icons/cry.tsx new file mode 100644 index 0000000..de5b4c1 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/cry.tsx @@ -0,0 +1,53 @@ +import { SVGProps } from 'react' + +export const Cry = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/fire.tsx b/packages/ui/src/components/Icon/assets/icons/fire.tsx new file mode 100644 index 0000000..30dec34 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/fire.tsx @@ -0,0 +1,33 @@ +import { SVGProps } from 'react' + +export const Fire = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/fireHeart.tsx b/packages/ui/src/components/Icon/assets/icons/fireHeart.tsx new file mode 100644 index 0000000..46e7982 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/fireHeart.tsx @@ -0,0 +1,37 @@ +import { SVGProps } from 'react' + +export const FireHeart = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/header/heart.tsx b/packages/ui/src/components/Icon/assets/icons/header/heart.tsx new file mode 100644 index 0000000..d0942a5 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/header/heart.tsx @@ -0,0 +1,24 @@ +import { SVGProps } from 'react' + +export const Heart = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/header/logo.tsx b/packages/ui/src/components/Icon/assets/icons/header/logo.tsx new file mode 100644 index 0000000..e667cdc --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/header/logo.tsx @@ -0,0 +1,49 @@ +import { SVGProps } from 'react' + +export const Logo = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/header/markerWithMap.tsx b/packages/ui/src/components/Icon/assets/icons/header/markerWithMap.tsx new file mode 100644 index 0000000..4bb75a8 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/header/markerWithMap.tsx @@ -0,0 +1,49 @@ +import { SVGProps } from 'react' + +export const MarkerWithMap = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/header/shakingHeart.tsx b/packages/ui/src/components/Icon/assets/icons/header/shakingHeart.tsx new file mode 100644 index 0000000..02d0302 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/header/shakingHeart.tsx @@ -0,0 +1,49 @@ +import { SVGProps } from 'react' + +export const ShakingHeart = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/header/user.tsx b/packages/ui/src/components/Icon/assets/icons/header/user.tsx new file mode 100644 index 0000000..ec125ab --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/header/user.tsx @@ -0,0 +1,28 @@ +import { SVGProps } from 'react' + +export const User = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/kakaoLogo.tsx b/packages/ui/src/components/Icon/assets/icons/kakaoLogo.tsx new file mode 100644 index 0000000..b134a45 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/kakaoLogo.tsx @@ -0,0 +1,30 @@ +import { SVGProps } from 'react' + +export const KakaoLogo = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/marker.tsx b/packages/ui/src/components/Icon/assets/icons/marker.tsx new file mode 100644 index 0000000..1eb1341 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/marker.tsx @@ -0,0 +1,34 @@ +import { SVGProps } from 'react' + +export const Marker = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/asian.tsx b/packages/ui/src/components/Icon/assets/icons/menu/asian.tsx new file mode 100644 index 0000000..f3c120b --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/asian.tsx @@ -0,0 +1,77 @@ +import { SVGProps } from 'react' + +export const Asian = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/bunsik.tsx b/packages/ui/src/components/Icon/assets/icons/menu/bunsik.tsx new file mode 100644 index 0000000..16f2207 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/bunsik.tsx @@ -0,0 +1,48 @@ +import { SVGProps } from 'react' + +export const Bunsik = ({ width, height }: SVGProps) => ( + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/burger.tsx b/packages/ui/src/components/Icon/assets/icons/menu/burger.tsx new file mode 100644 index 0000000..2cdad26 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/burger.tsx @@ -0,0 +1,73 @@ +import { SVGProps } from 'react' + +export const Burger = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/cafe.tsx b/packages/ui/src/components/Icon/assets/icons/menu/cafe.tsx new file mode 100644 index 0000000..843874b --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/cafe.tsx @@ -0,0 +1,53 @@ +import { SVGProps } from 'react' + +export const Cafe = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/chicken.tsx b/packages/ui/src/components/Icon/assets/icons/menu/chicken.tsx new file mode 100644 index 0000000..5157f76 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/chicken.tsx @@ -0,0 +1,45 @@ +import { SVGProps } from 'react' + +export const Chicken = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/chinese.tsx b/packages/ui/src/components/Icon/assets/icons/menu/chinese.tsx new file mode 100644 index 0000000..5f2bfda --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/chinese.tsx @@ -0,0 +1,205 @@ +import { SVGProps } from 'react' + +export const Chinese = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/japanese.tsx b/packages/ui/src/components/Icon/assets/icons/menu/japanese.tsx new file mode 100644 index 0000000..c77f9ad --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/japanese.tsx @@ -0,0 +1,49 @@ +import { SVGProps } from 'react' + +export const Japanese = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/korean.tsx b/packages/ui/src/components/Icon/assets/icons/menu/korean.tsx new file mode 100644 index 0000000..783c319 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/korean.tsx @@ -0,0 +1,77 @@ +import { SVGProps } from 'react' + +export const Korean = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/lunchbox.tsx b/packages/ui/src/components/Icon/assets/icons/menu/lunchbox.tsx new file mode 100644 index 0000000..f1a1f63 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/lunchbox.tsx @@ -0,0 +1,73 @@ +import { SVGProps } from 'react' + +export const Lunchbox = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/meat.tsx b/packages/ui/src/components/Icon/assets/icons/menu/meat.tsx new file mode 100644 index 0000000..d18e4b7 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/meat.tsx @@ -0,0 +1,37 @@ +import { SVGProps } from 'react' + +export const Meat = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/mexican.tsx b/packages/ui/src/components/Icon/assets/icons/menu/mexican.tsx new file mode 100644 index 0000000..3c871a9 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/mexican.tsx @@ -0,0 +1,57 @@ +import { SVGProps } from 'react' + +export const Mexican = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/pizza.tsx b/packages/ui/src/components/Icon/assets/icons/menu/pizza.tsx new file mode 100644 index 0000000..88d72b0 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/pizza.tsx @@ -0,0 +1,57 @@ +import { SVGProps } from 'react' + +export const Pizza = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/salad.tsx b/packages/ui/src/components/Icon/assets/icons/menu/salad.tsx new file mode 100644 index 0000000..fde340e --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/salad.tsx @@ -0,0 +1,61 @@ +import { SVGProps } from 'react' + +export const Salad = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/soup.tsx b/packages/ui/src/components/Icon/assets/icons/menu/soup.tsx new file mode 100644 index 0000000..43a771e --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/soup.tsx @@ -0,0 +1,57 @@ +import { SVGProps } from 'react' + +export const Soup = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/menu/western.tsx b/packages/ui/src/components/Icon/assets/icons/menu/western.tsx new file mode 100644 index 0000000..4616a8e --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/menu/western.tsx @@ -0,0 +1,121 @@ +import { SVGProps } from 'react' + +export const Western = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/navigation/circlePlus.tsx b/packages/ui/src/components/Icon/assets/icons/navigation/circlePlus.tsx new file mode 100644 index 0000000..36c1564 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/navigation/circlePlus.tsx @@ -0,0 +1,21 @@ +import { SVGProps } from 'react' + +export const CirclePlus = ({ + width, + height, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/navigation/heart.tsx b/packages/ui/src/components/Icon/assets/icons/navigation/heart.tsx new file mode 100644 index 0000000..2eb4612 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/navigation/heart.tsx @@ -0,0 +1,34 @@ +import { SVGProps } from 'react' + +export const Heart = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/navigation/home.tsx b/packages/ui/src/components/Icon/assets/icons/navigation/home.tsx new file mode 100644 index 0000000..ef27ef1 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/navigation/home.tsx @@ -0,0 +1,23 @@ +import { SVGProps } from 'react' +import '@repo/tailwind-config' + +export const Home = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/navigation/map.tsx b/packages/ui/src/components/Icon/assets/icons/navigation/map.tsx new file mode 100644 index 0000000..6c847fc --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/navigation/map.tsx @@ -0,0 +1,22 @@ +import { SVGProps } from 'react' + +export const Map = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/navigation/user.tsx b/packages/ui/src/components/Icon/assets/icons/navigation/user.tsx new file mode 100644 index 0000000..bba8024 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/navigation/user.tsx @@ -0,0 +1,22 @@ +import { SVGProps } from 'react' + +export const User = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/note.tsx b/packages/ui/src/components/Icon/assets/icons/note.tsx new file mode 100644 index 0000000..1e5fbb4 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/note.tsx @@ -0,0 +1,57 @@ +import { SVGProps } from 'react' + +export const Note = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/pin.tsx b/packages/ui/src/components/Icon/assets/icons/pin.tsx new file mode 100644 index 0000000..cb59544 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/pin.tsx @@ -0,0 +1,33 @@ +import { SVGProps } from 'react' + +export const Pin = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/search.tsx b/packages/ui/src/components/Icon/assets/icons/search.tsx new file mode 100644 index 0000000..927e12f --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/search.tsx @@ -0,0 +1,29 @@ +import { SVGProps } from 'react' + +export const Search = ({ + width, + height, + color, + ...props +}: SVGProps) => ( + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/smile.tsx b/packages/ui/src/components/Icon/assets/icons/smile.tsx new file mode 100644 index 0000000..2ba52e0 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/smile.tsx @@ -0,0 +1,45 @@ +import { SVGProps } from 'react' + +export const Smile = ({ width, height, ...props }: SVGProps) => ( + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/tag/blingBling.tsx b/packages/ui/src/components/Icon/assets/icons/tag/blingBling.tsx new file mode 100644 index 0000000..0314d71 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/tag/blingBling.tsx @@ -0,0 +1,41 @@ +import { SVGProps } from 'react' + +export const BlingBling = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/tag/calculator.tsx b/packages/ui/src/components/Icon/assets/icons/tag/calculator.tsx new file mode 100644 index 0000000..fbd46f6 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/tag/calculator.tsx @@ -0,0 +1,61 @@ +import { SVGProps } from 'react' + +export const Calculator = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/tag/fingerUp.tsx b/packages/ui/src/components/Icon/assets/icons/tag/fingerUp.tsx new file mode 100644 index 0000000..9e66918 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/tag/fingerUp.tsx @@ -0,0 +1,53 @@ +import { SVGProps } from 'react' + +export const FingerUp = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/assets/icons/tag/waiter.tsx b/packages/ui/src/components/Icon/assets/icons/tag/waiter.tsx new file mode 100644 index 0000000..031fa43 --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/tag/waiter.tsx @@ -0,0 +1,85 @@ +import { SVGProps } from 'react' + +export const Waiter = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/ui/src/components/Icon/index.tsx b/packages/ui/src/components/Icon/index.tsx new file mode 100644 index 0000000..d78603a --- /dev/null +++ b/packages/ui/src/components/Icon/index.tsx @@ -0,0 +1 @@ +export { Icon } from './Icon' diff --git a/packages/ui/src/components/Layout/Column/Column.tsx b/packages/ui/src/components/Layout/Column/Column.tsx new file mode 100644 index 0000000..1d4e634 --- /dev/null +++ b/packages/ui/src/components/Layout/Column/Column.tsx @@ -0,0 +1,16 @@ +import { cn } from '../../../utils/cn' +import type { FlexType } from '../Flex/Flex' + +export const Column: FlexType = ({ as, className, children, ...restProps }) => { + const Component = as || 'div' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Layout/Column/index.ts b/packages/ui/src/components/Layout/Column/index.ts new file mode 100644 index 0000000..ab6f9fb --- /dev/null +++ b/packages/ui/src/components/Layout/Column/index.ts @@ -0,0 +1 @@ +export { Column } from './Column' diff --git a/packages/ui/src/components/Layout/Flex/Flex.tsx b/packages/ui/src/components/Layout/Flex/Flex.tsx new file mode 100644 index 0000000..26611dd --- /dev/null +++ b/packages/ui/src/components/Layout/Flex/Flex.tsx @@ -0,0 +1,25 @@ +import type { PropsWithChildren, ElementType, JSX } from 'react' +import type { PolymorphicComponentProps } from '../../../polymorphics' +import { cn } from '../../../utils/cn' + +export type FlexProps = PolymorphicComponentProps< + C, + { className?: string } +> + +export type FlexType = ( + props: PropsWithChildren>, +) => JSX.Element + +export const Flex: FlexType = ({ as, className, children, ...restProps }) => { + const Component = as || 'div' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Layout/Flex/index.ts b/packages/ui/src/components/Layout/Flex/index.ts new file mode 100644 index 0000000..12035e6 --- /dev/null +++ b/packages/ui/src/components/Layout/Flex/index.ts @@ -0,0 +1 @@ +export { Flex } from './Flex' diff --git a/packages/ui/src/components/Layout/JustifyAround/JustifyAround.tsx b/packages/ui/src/components/Layout/JustifyAround/JustifyAround.tsx new file mode 100644 index 0000000..df1dc93 --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyAround/JustifyAround.tsx @@ -0,0 +1,21 @@ +import { cn } from '../../../utils/cn' +import type { FlexType } from '../Flex/Flex' + +export const JustifyAround: FlexType = ({ + as, + className, + children, + ...restProps +}) => { + const Component = as || 'div' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Layout/JustifyAround/index.ts b/packages/ui/src/components/Layout/JustifyAround/index.ts new file mode 100644 index 0000000..8e4f781 --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyAround/index.ts @@ -0,0 +1 @@ +export { JustifyAround } from './JustifyAround' diff --git a/packages/ui/src/components/Layout/JustifyBetween/JustifyBetween.tsx b/packages/ui/src/components/Layout/JustifyBetween/JustifyBetween.tsx new file mode 100644 index 0000000..84b6977 --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyBetween/JustifyBetween.tsx @@ -0,0 +1,21 @@ +import { cn } from '../../../utils/cn' +import type { FlexType } from '../Flex/Flex' + +export const JustifyBetween: FlexType = ({ + as, + className, + children, + ...restProps +}) => { + const Component = as || 'div' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Layout/JustifyBetween/index.ts b/packages/ui/src/components/Layout/JustifyBetween/index.ts new file mode 100644 index 0000000..72b3492 --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyBetween/index.ts @@ -0,0 +1 @@ +export { JustifyBetween } from './JustifyBetween' diff --git a/packages/ui/src/components/Layout/JustifyEnd/JustifyEnd.tsx b/packages/ui/src/components/Layout/JustifyEnd/JustifyEnd.tsx new file mode 100644 index 0000000..4ab92cf --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyEnd/JustifyEnd.tsx @@ -0,0 +1,21 @@ +import { cn } from '../../../utils/cn' +import type { FlexType } from '../Flex/Flex' + +export const JustifyEnd: FlexType = ({ + as, + className, + children, + ...restProps +}) => { + const Component = as || 'div' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Layout/JustifyEnd/index.ts b/packages/ui/src/components/Layout/JustifyEnd/index.ts new file mode 100644 index 0000000..06a7ca2 --- /dev/null +++ b/packages/ui/src/components/Layout/JustifyEnd/index.ts @@ -0,0 +1 @@ +export { JustifyEnd } from './JustifyEnd' diff --git a/packages/ui/src/components/Layout/Layout.stories.tsx b/packages/ui/src/components/Layout/Layout.stories.tsx new file mode 100644 index 0000000..d1c0cfc --- /dev/null +++ b/packages/ui/src/components/Layout/Layout.stories.tsx @@ -0,0 +1,68 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Flex, Column, JustifyAround, JustifyBetween, JustifyEnd } from '.' +import '../../styles.css' + +const meta = { + title: 'components/Layout', + component: Flex, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Basic: Story = { + render: (args) => ( + +
+
+
+ + ), +} + +export const BasicJustifyEnd: Story = { + render: (args) => ( + +
+
+
+ + ), +} + +export const BasicJustifyAround: Story = { + render: (args) => ( + +
+
+
+ + ), +} + +export const BasicJustifyBetween: Story = { + render: (args) => ( + +
+
+
+ + ), +} + +export const BasicColumn: Story = { + render: (args) => ( + +
+
+
+ + ), +} diff --git a/packages/ui/src/components/Layout/index.tsx b/packages/ui/src/components/Layout/index.tsx new file mode 100644 index 0000000..b24b33b --- /dev/null +++ b/packages/ui/src/components/Layout/index.tsx @@ -0,0 +1,5 @@ +export * from './Flex' +export * from './Column' +export * from './JustifyAround' +export * from './JustifyEnd' +export * from './JustifyBetween' diff --git a/packages/ui/src/components/SearchBar/SearchBar.stories.tsx b/packages/ui/src/components/SearchBar/SearchBar.stories.tsx new file mode 100644 index 0000000..9876df7 --- /dev/null +++ b/packages/ui/src/components/SearchBar/SearchBar.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { SearchBar } from './SearchBar' + +const meta: Meta = { + title: 'Components/SearchBar', + component: SearchBar, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + href: '#', + }, +} diff --git a/packages/ui/src/components/SearchBar/SearchBar.tsx b/packages/ui/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000..4b21f7a --- /dev/null +++ b/packages/ui/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,29 @@ +import { Icon } from '../Icon' +import { Flex } from '../Layout' +import { cn } from '../../utils/cn' +import { Text } from '../Text' + +export const SearchBar = ({ href }: { href: string }) => { + return ( + + + + 식당을 검색해주세요 + + + ) +} diff --git a/packages/ui/src/components/SearchBar/index.tsx b/packages/ui/src/components/SearchBar/index.tsx new file mode 100644 index 0000000..848468a --- /dev/null +++ b/packages/ui/src/components/SearchBar/index.tsx @@ -0,0 +1 @@ +export { SearchBar } from './SearchBar' diff --git a/packages/ui/src/components/Text/Text.stories.tsx b/packages/ui/src/components/Text/Text.stories.tsx new file mode 100644 index 0000000..8b5125f --- /dev/null +++ b/packages/ui/src/components/Text/Text.stories.tsx @@ -0,0 +1,77 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Text } from './Text' + +const meta: Meta = { + title: 'Typography/Text', + component: Text, + args: { + children: 'Sample Text', + variant: 'body1', + }, + argTypes: { + as: { + control: { type: 'text' }, + description: 'HTML 태그 또는 컴포넌트', + }, + fontSize: { + control: { type: 'select' }, + options: ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl'], + }, + fontWeight: { + control: { type: 'select' }, + options: [ + 'thin', + 'extralight', + 'light', + 'normal', + 'medium', + 'semibold', + 'bold', + 'extrabold', + 'black', + ], + }, + variant: { + control: { type: 'select' }, + options: [ + undefined, + 'heading1', + 'heading2', + 'title1', + 'title2', + 'title3', + 'body1', + 'body2', + 'body3', + 'caption1', + 'caption2', + ], + }, + }, +} + +export default meta +type Story = StoryObj + +export const Playground: Story = { + args: { + children: 'Playground Text', + }, +} + +export const Variants: Story = { + render: () => ( +
+ Heading1 + Heading2 + Title1 + Title2 + Title3 + Body1 + Body2 + Body3 + Caption1 + Caption2 +
+ ), +} diff --git a/packages/ui/src/components/Text/Text.tsx b/packages/ui/src/components/Text/Text.tsx new file mode 100644 index 0000000..4f6d493 --- /dev/null +++ b/packages/ui/src/components/Text/Text.tsx @@ -0,0 +1,138 @@ +import type { PropsWithChildren, ElementType, JSX } from 'react' +import type { PolymorphicComponentProps } from '../../polymorphics' +import { cn } from '../../utils/cn' + +type FontSize = + | 'xs' // 12px + | 'sm' // 14px + | 'base' // 16px + | 'lg' // 18px + | 'xl' // 20px + | '2xl' // 24px + | '3xl' // 30px + | '4xl' // 36px + | '5xl' // 48px + +type FontWeight = + | 'thin' // Figma: Thin, 100 + | 'extralight' // Figma: Extra Light, 200 + | 'light' // Figma: Light, 300 + | 'normal' // Figma: Regular / Normal, 400 + | 'medium' // Figma: Medium, 500 + | 'semibold' // Figma: Semi Bold, 600 + | 'bold' // Figma: Bold, 700 + | 'extrabold' // Figma: Extra Bold, 800 + | 'black' // Figma: Black, 900 + +export type TypographyVariant = + | 'heading1' + | 'heading2' + | 'title1' + | 'title2' + | 'title3' + | 'body1' + | 'body2' + | 'body3' + | 'caption1' + | 'caption2' + +const fontSizeClasses: Record = { + xs: 'ui:text-xs', + sm: 'ui:text-sm', + base: 'ui:text-base', + lg: 'ui:text-lg', + xl: 'ui:text-xl', + '2xl': 'ui:text-2xl', + '3xl': 'ui:text-3xl', + '4xl': 'ui:text-4xl', + '5xl': 'ui:text-5xl', +} + +const fontWeightClasses: Record = { + thin: 'ui:font-thin', + extralight: 'ui:font-extralight', + light: 'ui:font-light', + normal: 'ui:font-normal', + medium: 'ui:font-medium', + semibold: 'ui:font-semibold', + bold: 'ui:font-bold', + extrabold: 'ui:font-extrabold', + black: 'ui:font-black', +} + +export const typographyVariants: Record = { + heading1: 'ui:text-[22px] ui:font-bold ui:leading-[1.4]', // 22px, Bold + heading2: 'ui:text-xl ui:font-semibold ui:leading-[1.4]', // 20px, SemiBold + title1: 'ui:text-lg ui:font-semibold ui:leading-[1.4]', // 18px, SemiBold + title2: 'ui:text-base ui:font-bold ui:leading-[1.4]', // 16px, Bold + title3: 'ui:text-base ui:font-semibold ui:leading-[1.4]', // 16px, SemiBold + body1: 'ui:text-sm ui:font-medium ui:leading-[1.5]', // 14px, Medium + body2: 'ui:text-sm ui:font-normal ui:leading-[1.5]', // 14px, Normal + body3: 'ui:text-sm ui:font-light ui:leading-[1.5]', // 14px, Light + caption1: 'ui:text-xs ui:font-medium ui:leading-[1.5]', // 12px, Medium + caption2: 'ui:text-xs ui:font-light ui:leading-[1.5]', // 12px, Light +} + +export type TextProps = PolymorphicComponentProps< + C, + { + className?: string + fontSize?: FontSize + fontWeight?: FontWeight + variant?: TypographyVariant + } +> + +export type TextType = ( + props: PropsWithChildren>, +) => JSX.Element + +/** + * Text 컴포넌트 + * + * - 다양한 HTML 태그(`as` prop)로 렌더링 가능한 폴리모픽 컴포넌트입니다. + * - `variant`가 지정되면 사전에 정의된 타이포그래피 스타일을 적용합니다. + * - `variant`가 없으면 `fontSize`와 `fontWeight` 조합으로 스타일을 구성합니다. + * + * @template C HTML 태그 타입 (기본값: 'p') + * + * @param as 렌더링할 HTML 태그 또는 컴포넌트 + * @param className 추가로 적용할 CSS 클래스 + * @param fontSize 폰트 크기 프리셋 (variant 미지정 시만 적용) + * @param fontWeight 폰트 굵기 프리셋 (variant 미지정 시만 적용) + * @param variant 사전 정의된 타이포그래피 스타일 키 + * @param children 텍스트 또는 하위 요소 + * @param restProps 나머지 Props + * @returns 렌더링된 텍스트 요소 + * + * @example + * Heading 1 텍스트 + * @example + * 굵은 18px 텍스트 + * @example + * 캡션 텍스트 + */ +export const Text: TextType = ({ + as, + className, + fontSize, + fontWeight, + variant, + children, + ...restProps +}) => { + const Component = as || 'p' + + const variantClass = variant ? typographyVariants[variant] : '' + const sizeClass = fontSize ? fontSizeClasses[fontSize] : '' + const weightClass = fontWeight ? fontWeightClasses[fontWeight] : '' + + return ( + + {children} + + ) +} diff --git a/packages/ui/src/components/Text/index.tsx b/packages/ui/src/components/Text/index.tsx new file mode 100644 index 0000000..c79f9ee --- /dev/null +++ b/packages/ui/src/components/Text/index.tsx @@ -0,0 +1 @@ +export { Text } from './Text' diff --git a/packages/ui/src/components/Textarea/Textarea.stories.tsx b/packages/ui/src/components/Textarea/Textarea.stories.tsx new file mode 100644 index 0000000..5a1fe2f --- /dev/null +++ b/packages/ui/src/components/Textarea/Textarea.stories.tsx @@ -0,0 +1,48 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import { Textarea } from './Textarea' +import { useState } from 'react' + +const meta: Meta = { + title: 'Components/Textarea', + component: Textarea, + parameters: { + layout: 'centered', + }, +} + +export default meta +type Story = StoryObj + +// Playground용 Wrapper 컴포넌트로 useState 관리 +const TextareaWrapper = ( + props: Partial>, +) => { + const [value, setValue] = useState('') + return ( +
+