├── .eslintrc.cjs ├── .gitignore ├── .storybook ├── main.ts └── preview.ts ├── .yarnrc.yml ├── README.md ├── cypress.config.ts ├── cypress ├── e2e │ ├── 1-signup │ │ └── signup.cy.js │ ├── 2-login │ │ └── login.cy.js │ └── 3-order │ │ └── order.cy.ts ├── fixtures │ ├── example.json │ ├── menu.json │ └── restaurant-list.json └── support │ ├── commands.ts │ ├── component-index.html │ ├── component.ts │ └── e2e.ts ├── index.html ├── jest.config.cjs ├── package-lock.json ├── package.json ├── public └── ic-logo.png ├── src ├── App.css ├── App.tsx ├── __test__ │ ├── Login.spec.tsx │ └── Signup.spec.tsx ├── atoms │ └── order.ts ├── components │ ├── Footer.tsx │ ├── Header.tsx │ └── OrderType.tsx ├── hooks │ ├── useFoodTypeList.ts │ ├── useIntersection.ts │ ├── useIntersectionObserver.ts │ ├── useLogin.ts │ ├── useRestaurantDetail.ts │ ├── useRestaurantList.ts │ └── useSignup.ts ├── index.css ├── libs │ ├── axios.ts │ └── order │ │ ├── OrderContext │ │ ├── context.tsx │ │ ├── index.tsx │ │ ├── provider.tsx │ │ ├── reducer.tsx │ │ ├── state.tsx │ │ ├── types.ts │ │ └── useOrder.tsx │ │ └── index.ts ├── main.tsx ├── mixins │ ├── styles.ts │ └── types.ts ├── pages │ ├── FoodTypePage.tsx │ ├── LoginPage.tsx │ ├── OrderDetailPage.tsx │ ├── OrderTypePage.tsx │ ├── PageLayout.tsx │ ├── RestaurantCard.tsx │ ├── RestaurantDetailPage.tsx │ ├── RestaurantListPage.tsx │ ├── Routes.tsx │ └── SignupPage.tsx ├── stories │ ├── Button.stories.ts │ ├── Button.tsx │ ├── Configure.mdx │ ├── Header.stories.ts │ ├── Header.tsx │ ├── Page.stories.ts │ ├── Page.tsx │ ├── assets │ │ ├── accessibility.png │ │ ├── accessibility.svg │ │ ├── addon-library.png │ │ ├── assets.png │ │ ├── context.png │ │ ├── discord.svg │ │ ├── docs.png │ │ ├── figma-plugin.png │ │ ├── github.svg │ │ ├── share.png │ │ ├── styling.png │ │ ├── testing.png │ │ ├── theming.png │ │ ├── tutorials.svg │ │ └── youtube.svg │ ├── button.css │ ├── header.css │ └── page.css ├── theme.ts └── vite-env.d.ts ├── storybook-static ├── assets │ ├── Button-8daf7d8a.js │ ├── Button-8daf7d8a.js.map │ ├── Button-9a01ec51.css │ ├── Button.stories-2e1c076d.js │ ├── Button.stories-2e1c076d.js.map │ ├── Color-6VNJS4EI-ac592a52.js │ ├── Color-6VNJS4EI-ac592a52.js.map │ ├── Configure-83e363c2.js │ ├── Configure-83e363c2.js.map │ ├── DocsRenderer-NNNQARDV-cb22ad80.js │ ├── DocsRenderer-NNNQARDV-cb22ad80.js.map │ ├── Header-8f8703e8.js │ ├── Header-8f8703e8.js.map │ ├── Header-a6911580.css │ ├── Header.stories-dfd929b9.js │ ├── Header.stories-dfd929b9.js.map │ ├── Page.stories-08c0e118.js │ ├── Page.stories-08c0e118.js.map │ ├── Page.stories-ece1482a.css │ ├── WithTooltip-4HIR6TLV-bd1d30d6.js │ ├── WithTooltip-4HIR6TLV-bd1d30d6.js.map │ ├── _commonjsHelpers-de833af9.js │ ├── _commonjsHelpers-de833af9.js.map │ ├── accessibility-cd6d60f7.png │ ├── addon-library-bc7ba705.png │ ├── config-de709ab3.js │ ├── config-de709ab3.js.map │ ├── context-c612d889.png │ ├── discord-f7d1b78c.svg │ ├── docs-5b0c7100.png │ ├── figma-plugin-b0a5ad2d.png │ ├── formatter-SWP5E3XI-a797fb94.js │ ├── formatter-SWP5E3XI-a797fb94.js.map │ ├── github-cdfc3270.svg │ ├── iframe-a3b0aa37.js │ ├── iframe-a3b0aa37.js.map │ ├── index-228e512d.js │ ├── index-228e512d.js.map │ ├── index-356e4a49.js │ ├── index-356e4a49.js.map │ ├── index-76fb7be0.js │ ├── index-76fb7be0.js.map │ ├── index-932496f1.js │ ├── index-932496f1.js.map │ ├── index-9eea8125.js │ ├── index-9eea8125.js.map │ ├── index-a1cf9e47.js │ ├── index-a1cf9e47.js.map │ ├── index-d37d4223.js │ ├── index-d37d4223.js.map │ ├── jsx-runtime-ffb262ed.js │ ├── jsx-runtime-ffb262ed.js.map │ ├── preview-108c1c3c.js │ ├── preview-108c1c3c.js.map │ ├── preview-2059b184.js │ ├── preview-2059b184.js.map │ ├── preview-6751e51d.js │ ├── preview-6751e51d.js.map │ ├── preview-87eac49b.js │ ├── preview-87eac49b.js.map │ ├── preview-b3c37142.js │ ├── preview-b3c37142.js.map │ ├── preview-b8d6c68d.js │ ├── preview-b8d6c68d.js.map │ ├── preview-ba2273f4.js │ ├── preview-ba2273f4.js.map │ ├── preview-bed967c6.js │ ├── preview-bed967c6.js.map │ ├── preview-e11ff2f7.js │ ├── preview-e11ff2f7.js.map │ ├── react-18-063a39db.js │ ├── react-18-063a39db.js.map │ ├── share-b59d6c77.png │ ├── styling-c83082e0.png │ ├── syntaxhighlighter-NMPM6SWI-ceeb6cee.js │ ├── syntaxhighlighter-NMPM6SWI-ceeb6cee.js.map │ ├── testing-6a59f681.png │ ├── theming-b6e819c3.png │ ├── tutorials-adff6365.svg │ └── youtube-9f26eb0b.svg ├── favicon.svg ├── ic-logo.png ├── iframe.html ├── index.html ├── index.json ├── project.json ├── sb-addons │ ├── essentials-actions-2 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-backgrounds-3 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-controls-1 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-measure-6 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-outline-7 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-toolbars-5 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── essentials-viewport-4 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── interactions-9 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ ├── links-0 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt │ └── onboarding-8 │ │ ├── manager-bundle.js │ │ └── manager-bundle.js.LEGAL.txt ├── sb-common-assets │ ├── fonts.css │ ├── nunito-sans-bold-italic.woff2 │ ├── nunito-sans-bold.woff2 │ ├── nunito-sans-italic.woff2 │ └── nunito-sans-regular.woff2 ├── sb-manager │ ├── WithTooltip-4HIR6TLV-YPPZ2DMB.js │ ├── chunk-DAJ4OSDJ.js │ ├── chunk-FWZ33S65.js │ ├── chunk-NFZCBIX3.js │ ├── chunk-SZNM6KS3.js │ ├── chunk-ZEU7PDD3.js │ ├── formatter-SWP5E3XI-7BGIK6BL.js │ ├── globals.js │ ├── index.js │ ├── runtime.js │ └── syntaxhighlighter-NMPM6SWI-GZTSOZ5L.js ├── sb-preview │ ├── globals.js │ └── runtime.js └── stories.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | es6: true, 6 | node: true, 7 | browser: true, 8 | }, 9 | 10 | parser: '@typescript-eslint/parser', 11 | parserOptions: { 12 | ecmaFeatures: { jsx: true }, 13 | jsx: true, 14 | useJSXTextNode: true, 15 | }, 16 | 17 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'eslint-config-prettier', 'plugin:react/recommended', 'plugin:cypress/recommended', 'plugin:storybook/recommended'], 18 | plugins: ['@typescript-eslint', 'import', 'prettier', 'react', 'react-hooks'], 19 | settings: { react: { version: 'detect' } }, 20 | 21 | rules: { 22 | 'prettier/prettier': 'error', 23 | 'no-implicit-coercion': 'error', 24 | 25 | 'no-undef': 'off', 26 | 27 | indent: 'off', 28 | '@typescript-eslint/indent': 'off', 29 | semi: 'off', 30 | 31 | '@typescript-eslint/no-non-null-assertion': 'off', 32 | '@typescript-eslint/no-explicit-any': 'off', 33 | 34 | 'no-extra-boolean-cast': 'off', 35 | 36 | 'getter-return': 'warn', 37 | 38 | '@typescript-eslint/explicit-function-return-type': 'off', 39 | 40 | '@typescript-eslint/no-use-before-define': 'off', 41 | 42 | '@typescript-eslint/no-empty-interface': 'off', 43 | 44 | '@typescript-eslint/no-parameter-properties': 'off', 45 | 46 | 'no-restricted-imports': [ 47 | 'error', 48 | { 49 | paths: [ 50 | { 51 | name: 'util', 52 | importNames: ['isArray'], 53 | message: '`Array.isArray`를 대신 사용해주세요!', 54 | }, 55 | ], 56 | }, 57 | ], 58 | 59 | 'no-async-promise-executor': 'warn', 60 | '@typescript-eslint/prefer-as-const': 'warn', 61 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'warn', 62 | '@typescript-eslint/ban-types': 'warn', 63 | '@typescript-eslint/no-inferrable-types': 'warn', 64 | '@typescript-eslint/no-empty-function': 'off', 65 | '@typescript-eslint/naming-convention': [ 66 | 'error', 67 | { 68 | format: ['camelCase', 'UPPER_CASE', 'PascalCase'], 69 | selector: 'variable', 70 | leadingUnderscore: 'allow', 71 | }, 72 | { format: ['camelCase', 'PascalCase'], selector: 'function' }, 73 | { format: ['PascalCase'], selector: 'interface' }, 74 | { format: ['PascalCase'], selector: 'typeAlias' }, 75 | ], 76 | '@typescript-eslint/explicit-module-boundary-types': 'off', 77 | '@typescript-eslint/array-type': ['error', { default: 'array-simple' }], 78 | '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }], 79 | '@typescript-eslint/member-ordering': [ 80 | 'error', 81 | { 82 | default: [ 83 | 'public-static-field', 84 | 'private-static-field', 85 | 'public-instance-field', 86 | 'private-instance-field', 87 | 'public-constructor', 88 | 'private-constructor', 89 | 'public-instance-method', 90 | 'private-instance-method', 91 | ], 92 | }, 93 | ], 94 | 'no-warning-comments': [ 95 | 'warn', 96 | { 97 | terms: ['TODO', 'FIXME', 'XXX', 'BUG'], 98 | location: 'anywhere', 99 | }, 100 | ], 101 | 'prefer-const': 'error', 102 | 'no-var': 'error', 103 | curly: ['error', 'all'], 104 | eqeqeq: ['error', 'always', { null: 'ignore' }], 105 | 'import/no-duplicates': 'error', 106 | 107 | 'react/prop-types': 'off', 108 | 109 | 'react/display-name': 'off', 110 | 'react-hooks/rules-of-hooks': 'error', 111 | 'react-hooks/exhaustive-deps': 'error', 112 | 113 | 'react/jsx-no-target-blank': 'error', 114 | 115 | '@typescript-eslint/no-var-requires': 'warn', 116 | 'react/react-in-jsx-scope': 'off', 117 | }, 118 | }; 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook/react-vite"; 2 | 3 | const config: StorybookConfig = { 4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], 5 | addons: [ 6 | "@storybook/addon-links", 7 | "@storybook/addon-essentials", 8 | "@storybook/addon-onboarding", 9 | "@storybook/addon-interactions", 10 | "@storybook/addon-a11y", 11 | ], 12 | framework: { 13 | name: "@storybook/react-vite", 14 | options: {}, 15 | }, 16 | docs: { 17 | autodocs: "tag", 18 | }, 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/react"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | controls: { 7 | matchers: { 8 | color: /(background|color)$/i, 9 | date: /Date$/i, 10 | }, 11 | }, 12 | }, 13 | }; 14 | 15 | export default preview; 16 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.0.0.cjs 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-basics 2 | 3 | ## Overview 4 | 5 | Welcome to react-basics! This repository is a comprehensive collection of various React.js functionalities and state management solutions. It's designed to demonstrate the use of Redux, Context API, Zustand, Recoil, along with various testing methods and optimization techniques in a React.js environment. Check out each feature branch based on your need if you would like to see detailed implementation of each functionality 6 | 7 | ## Features 8 | 9 | - **Redux Implementation**: State management using Redux, showcasing actions, reducers, and store configurations. 10 | - **Context API Usage**: Utilizing Context API for state management and theme toggling examples. 11 | - **Zustand State Management**: Simple, fast and scalable state management using Zustand. 12 | - **Recoil for State Management**: Implementing Recoil to manage atom and selector based states. 13 | - **Testing**: Includes test cases for components and state management implementations. 14 | - **Optimization Techniques**: Demonstrations of React performance optimization techniques. 15 | 16 | ## Prerequisites 17 | Before you begin, ensure you have met the following requirements: 18 | 19 | - Node.js (version 12 or above) 20 | - Yarn or npm installed 21 | - Basic understanding of React.js concepts 22 | 23 | ## Installation and Setup 24 | 25 | 1. clone the repository 26 | ```bash 27 | git clone https://github.com/jasonkang14/react-basics.git 28 | cd react-basics 29 | ``` 30 | 31 | 2. Install dependencies: 32 | ```bash 33 | yarn install 34 | # or 35 | npm install 36 | ``` 37 | 38 | ## Running the Project with Vite 39 | To run the project using Vite, follow these steps: 40 | 41 | 1. Start the development server: 42 | ```bash 43 | # the original command is `dev` but I have changed it to `start` in package.json to integrate some tools easier 44 | yarn start 45 | # or 46 | npm run start 47 | ``` 48 | 49 | 2. This will start the Vite development server. 50 | 51 | Open your browser: 52 | Navigate to http://localhost:5173 to view the app. 53 | 54 | ## Building for Production 55 | To build the project for production, run: 56 | ```bash 57 | yarn build 58 | # or 59 | npm run build 60 | ``` 61 | 62 | This command will generate a `dist` folder which contains the production build of your app. 63 | 64 | ## Contributing 65 | Contributions to enhance this project are welcome. Feel free to fork this repository and submit pull requests. 66 | 67 | 68 | ## Contact 69 | If you have any questions or suggestions, please feel free to contact me at jasonkang14@gmail.com. 70 | 71 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: "http://localhost:5173", 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /cypress/e2e/1-signup/signup.cy.js: -------------------------------------------------------------------------------- 1 | describe("회원가입 테스트", () => { 2 | it("사용자는 이메일과 비밀번호를 사용해서 회원가입한다", () => { 3 | // given - 회원가입 페이지에 접근한다 4 | cy.visit('/signup'); 5 | cy.get("[data-cy=signupButton]").as('signupButton'); 6 | cy.get('@signupButton').should('be.disabled'); 7 | 8 | // when - 이메일과 비밀번호를 입력한다, 비밀번호와 비밀번호 확인값이 일치한다. 9 | cy.get("[data-cy=emailInput]").as('emailInput'); 10 | cy.get("[data-cy=passwordInput]").as('passwordInput'); 11 | cy.get("[data-cy=confirmPasswordInput]").as('confirmPasswordInput'); 12 | 13 | cy.get('@emailInput').type('email@email.com'); 14 | cy.get('@passwordInput').type('password'); 15 | cy.get('@confirmPasswordInput').type('password'); 16 | 17 | cy.get('@passwordInput').invoke('val').then((passwordValue) => { 18 | expect(passwordValue.trim()).to.not.be.empty; 19 | cy.get('@confirmPasswordInput').invoke('val').should('eq', passwordValue); 20 | cy.get('@signupButton').should('not.be.disabled'); 21 | }) 22 | 23 | // then - 회원가입 버튼이 활성화되어 회원가입에 성공하고 로그인 페이지로 이동한다 24 | cy.intercept( 25 | { 26 | method: 'POST', 27 | url: '/user/signup' 28 | }, 29 | { msg: "SUCCESS" } 30 | ).as('signup'); 31 | cy.get('@signupButton').click(); 32 | cy.url().should('include', '/login'); 33 | }); 34 | 35 | it('비밀번호와 비밀번호 확인값이 일치하지 않는경우 에러메세지가 나타난다', () => { 36 | // given - 가입 페이지로 이동 37 | cy.visit('/signup'); 38 | 39 | // when - 비밀번호와 비밀번호 확인에 각각 다른 값이 입력된다 40 | cy.get('[data-cy=passwordInput]').type('password123'); 41 | cy.get('[data-cy=confirmPasswordInput]').type('mismatchedpassword'); 42 | 43 | // then - 에러 메시지 확인 44 | cy.get('[data-testid=error-message]').should('be.visible').and('have.text', '비밀번호가 일치하지 않습니다'); 45 | }); 46 | 47 | }) -------------------------------------------------------------------------------- /cypress/e2e/2-login/login.cy.js: -------------------------------------------------------------------------------- 1 | describe("로그인 화면", () => { 2 | it("사용자는 아이디와 비밀번호를 사용해서 로그인한다", () => { 3 | // given - 로그인 페이지에 접근한다 4 | cy.visit("/login"); 5 | 6 | cy.get("[data-cy=emailInput]").as("emailInput"); 7 | cy.get("[data-cy=passwordInput]").as("passwordInput"); 8 | 9 | // when - 아이디와 비밀번호를 입력하고 로그인 버튼을 클릭한다 10 | cy.get("@emailInput").type("test@email.com"); 11 | cy.get("@passwordInput").type("password"); 12 | 13 | cy.get("@emailInput").invoke("val").should("eq", "test@email.com"); 14 | cy.get("@passwordInput").invoke("val").should("eq", "password"); 15 | 16 | cy.intercept( 17 | { 18 | method: "POST", 19 | url: "/user/login", 20 | }, 21 | { token: "AUTH_TOKEN" } 22 | ).as("login"); 23 | 24 | cy.get("[data-cy=loginButton]").should("exist").click(); 25 | // then - 로그인에 성공하고 메인화면으로 이동한다 26 | cy.url().should((url) => { 27 | const currentUrl = new URL(url); 28 | expect(currentUrl.pathname).to.equal("/"); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /cypress/e2e/3-order/order.cy.ts: -------------------------------------------------------------------------------- 1 | describe("주문을 테스트 한다", () => { 2 | it("사용자는 배달/포장 중 원하는 유형을 선택할 수 있다", () => { 3 | cy.visit("/"); 4 | 5 | cy.get("[data-cy=deliveryBtn]").should("be.visible").as("deliveryBtn"); 6 | cy.get("[data-cy=pickupBtn]").should("be.visible").as("pickupBtn"); 7 | 8 | cy.get("@deliveryBtn").click(); 9 | cy.url().should("include", "/food-type"); 10 | }); 11 | 12 | it("사용자는 음식 종류를 선택할 수 있다", () => { 13 | cy.visit("/food-type"); 14 | 15 | cy.intercept( 16 | { 17 | method: "GET", 18 | url: "/restaurant/food-type", 19 | }, 20 | [ 21 | { 22 | id: 1, 23 | name: "피자", 24 | icon: "https://kr.object.ncloudstorage.com/icons/ic-pizza.png", 25 | }, 26 | { 27 | id: 2, 28 | name: "동남아", 29 | icon: "https://kr.object.ncloudstorage.com/icons/ic-asian.png", 30 | }, 31 | { 32 | id: 3, 33 | name: "햄버거", 34 | icon: "https://kr.object.ncloudstorage.com/icons/ic-burger.png", 35 | }, 36 | { 37 | id: 4, 38 | name: "디저트", 39 | icon: "https://kr.object.ncloudstorage.com/icons/ic-cake.png", 40 | }, 41 | { 42 | id: 5, 43 | name: "치킨", 44 | icon: "https://kr.object.ncloudstorage.com/icons/ic-chicken.png", 45 | }, 46 | { 47 | id: 6, 48 | name: "탕,찌개", 49 | icon: "https://kr.object.ncloudstorage.com/icons/ic-hotpot.png", 50 | }, 51 | { 52 | id: 7, 53 | name: "고기", 54 | icon: "https://kr.object.ncloudstorage.com/icons/ic-meat.png", 55 | }, 56 | { 57 | id: 8, 58 | name: "중식", 59 | icon: "https://kr.object.ncloudstorage.com/icons/ic-noodle.png", 60 | }, 61 | { 62 | id: 9, 63 | name: "샐러드", 64 | icon: "https://kr.object.ncloudstorage.com/icons/ic-salad.png", 65 | }, 66 | ] 67 | ); 68 | 69 | cy.get("[data-cy=1]").should("be.visible").as("pizzaBtn"); 70 | cy.get("@pizzaBtn").click(); 71 | 72 | cy.url().should("include", "/food-type/1"); 73 | }); 74 | 75 | it("사용자는 원하는 레스토랑을 선택할 수 있다", () => { 76 | cy.visit("/food-type/1"); 77 | cy.intercept( 78 | { 79 | method: "GET", 80 | url: "/restaurant/food-type/1", 81 | }, 82 | { 83 | fixture: "restaurant-list.json", 84 | } 85 | ); 86 | 87 | cy.fixture("restaurant-list.json").then((restaurantList) => { 88 | cy.get(`[data-cy=${restaurantList[0].id}]`) 89 | .should("be.visible") 90 | .as("restaurantBtn"); 91 | cy.get("@restaurantBtn").click(); 92 | 93 | cy.url().should("include", "/restaurant/1"); 94 | }); 95 | }); 96 | 97 | it("사용자는 원하는 메뉴를 장바구니에 담고, 원하는 음식 갯수를 변경할 수 있다", () => { 98 | cy.visit("/restaurant/1"); 99 | cy.intercept( 100 | { 101 | method: "GET", 102 | url: "/restaurant/1", 103 | }, 104 | { 105 | fixture: "menu.json", 106 | } 107 | ); 108 | 109 | cy.fixture("menu.json").then((menu) => { 110 | cy.get(`[data-cy=${menu.menu_set[0].id}]`) 111 | .should("be.visible") 112 | .as("foodBtn"); 113 | cy.get("@foodBtn").click(); 114 | 115 | cy.url().should("include", "/order"); 116 | cy.get("[data-cy=counter]").as("counter"); 117 | cy.get("@counter").should("contain", 1); 118 | cy.get("[data-cy=incrementBtn]").should("be.visible").click(); 119 | cy.get("@counter").should("contain", 2); 120 | cy.get("[data-cy=decrementBtn]").should("be.visible").click(); 121 | cy.get("@counter").should("contain", 1); 122 | cy.get("[data-cy=completeBtn]").should("be.visible").click(); 123 | cy.url().should("include", "/"); 124 | }); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/fixtures/menu.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "menu_set": [ 4 | { 5 | "id": 1, 6 | "name": "페퍼로니피자", 7 | "description": "올타임 베스트 근본피자", 8 | "price": 18000, 9 | "picture": "https://kr.object.ncloudstorage.com/icons/pepperoni-pizza.jpeg", 10 | "restaurant": 1 11 | }, 12 | { 13 | "id": 2, 14 | "name": "프로슈토 피자", 15 | "description": "살짝 있어보이는 느낌", 16 | "price": 21000, 17 | "picture": "https://kr.object.ncloudstorage.com/icons/prosciutto-pizza.jpeg", 18 | "restaurant": 1 19 | }, 20 | { 21 | "id": 3, 22 | "name": "트러플 머시룸 피자", 23 | "description": "고급진 트러플향이 뿜뿜", 24 | "price": 17000, 25 | "picture": "https://kr.object.ncloudstorage.com/icons/mushroom-pizza.jpeg", 26 | "restaurant": 1 27 | }, 28 | { 29 | "id": 4, 30 | "name": "마르게리따 피자", 31 | "description": "이탈리안 클래식", 32 | "price": 17000, 33 | "picture": "https://kr.object.ncloudstorage.com/icons/margherita-pizza.jpeg", 34 | "restaurant": 1 35 | }, 36 | { 37 | "id": 5, 38 | "name": "치즈피자", 39 | "description": "와인안주로 기가막힘", 40 | "price": 19000, 41 | "picture": "https://kr.object.ncloudstorage.com/icons/cheese-pizza.jpeg", 42 | "restaurant": 1 43 | }, 44 | { 45 | "id": 6, 46 | "name": "치즈오븐스파게티", 47 | "description": "어릴때 많이먹던 바로그맛", 48 | "price": 14000, 49 | "picture": "https://kr.object.ncloudstorage.com/icons/cheese-oven-spaghetti.jpeg", 50 | "restaurant": 1 51 | } 52 | ], 53 | "name": "Quae.피자", 54 | "phone": "02-1993-4995", 55 | "address": "서울시 Eos.구 Et.동 14", 56 | "minPrice": 21000, 57 | "ratings": 4.75, 58 | "foodType": 1 59 | } -------------------------------------------------------------------------------- /cypress/fixtures/restaurant-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Quae.피자", 5 | "ratings": 4.75, 6 | "minPrice": 21000, 7 | "icon": "https://kr.object.ncloudstorage.com/icons/ic-pizza.png" 8 | }, 9 | { 10 | "id": 2, 11 | "name": "Nam.피자", 12 | "ratings": 3.96, 13 | "minPrice": 34000, 14 | "icon": "https://kr.object.ncloudstorage.com/icons/ic-pizza.png" 15 | }, 16 | { 17 | "id": 3, 18 | "name": "Quia.피자", 19 | "ratings": 3.3, 20 | "minPrice": 30000, 21 | "icon": "https://kr.object.ncloudstorage.com/icons/ic-pizza.png" 22 | }, 23 | { 24 | "id": 4, 25 | "name": "Quae.피자", 26 | "ratings": 1.2, 27 | "minPrice": 30000, 28 | "icon": "https://kr.object.ncloudstorage.com/icons/ic-pizza.png" 29 | }, 30 | { 31 | "id": 5, 32 | "name": "Ea.피자", 33 | "ratings": 1.83, 34 | "minPrice": 12000, 35 | "icon": "https://kr.object.ncloudstorage.com/icons/ic-pizza.png" 36 | } 37 | ] -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } -------------------------------------------------------------------------------- /cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Components App 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /cypress/support/component.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/component.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | 22 | import { mount } from 'cypress/react18' 23 | 24 | // Augment the Cypress namespace to include type definitions for 25 | // your custom command. 26 | // Alternatively, can be defined in cypress/support/component.d.ts 27 | // with a at the top of your spec. 28 | declare global { 29 | namespace Cypress { 30 | interface Chainable { 31 | mount: typeof mount 32 | } 33 | } 34 | } 35 | 36 | Cypress.Commands.add('mount', mount) 37 | 38 | // Example use: 39 | // cy.mount() -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 테스트코드 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'jest-environment-jsdom', 4 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "cypress": "npx cypress open", 11 | "preview": "vite preview", 12 | "unit-test": "jest --watchAll", 13 | "start-server": "npm start", 14 | "test": "cypress run", 15 | "ci": "start-server-and-test start-server http://localhost:5173 test", 16 | "storybook": "storybook dev -p 6006", 17 | "build-storybook": "storybook build" 18 | }, 19 | "dependencies": { 20 | "@babel/runtime": "^7.22.6", 21 | "@emotion/react": "^11.11.1", 22 | "@emotion/styled": "^11.11.0", 23 | "@tanstack/react-query": "^4.29.19", 24 | "axios": "^1.4.0", 25 | "emotion-normalize": "^11.0.1", 26 | "history": "^5.3.0", 27 | "immer": "^10.0.2", 28 | "react": "^18.2.0", 29 | "react-dom": "^18.2.0", 30 | "react-query": "^3.39.3", 31 | "react-router-dom": "^6.14.1", 32 | "recoil": "^0.7.7", 33 | "vite-tsconfig-paths": "^4.2.0" 34 | }, 35 | "devDependencies": { 36 | "@babel/plugin-transform-runtime": "^7.22.9", 37 | "@babel/preset-env": "^7.22.9", 38 | "@babel/preset-react": "^7.22.5", 39 | "@babel/preset-typescript": "^7.22.5", 40 | "@storybook/addon-a11y": "^7.5.3", 41 | "@storybook/addon-essentials": "^7.5.3", 42 | "@storybook/addon-interactions": "^7.5.3", 43 | "@storybook/addon-links": "^7.5.3", 44 | "@storybook/addon-onboarding": "^1.0.8", 45 | "@storybook/blocks": "^7.5.3", 46 | "@storybook/react": "^7.5.3", 47 | "@storybook/react-vite": "^7.5.3", 48 | "@storybook/testing-library": "^0.2.2", 49 | "@testing-library/dom": "^9.3.3", 50 | "@testing-library/jest-dom": "^6.1.4", 51 | "@testing-library/react": "^14.0.0", 52 | "@testing-library/user-event": "^14.5.1", 53 | "@types/cypress": "^1.1.3", 54 | "@types/jest": "^29.5.7", 55 | "@types/react": "^18.0.37", 56 | "@types/react-dom": "^18.0.11", 57 | "@typescript-eslint/eslint-plugin": "^5.59.0", 58 | "@typescript-eslint/parser": "^5.59.0", 59 | "@vitejs/plugin-react": "^4.0.0", 60 | "cypress": "^13.5.0", 61 | "eslint": "^8.38.0", 62 | "eslint-plugin-cypress": "^2.15.1", 63 | "eslint-plugin-react-hooks": "^4.6.0", 64 | "eslint-plugin-react-refresh": "^0.3.4", 65 | "eslint-plugin-storybook": "^0.6.15", 66 | "jest": "^29.7.0", 67 | "jest-environment-jsdom": "^29.7.0", 68 | "nock": "^13.3.8", 69 | "start-server-and-test": "^2.0.2", 70 | "storybook": "^7.5.3", 71 | "ts-jest": "^29.1.1", 72 | "typescript": "^5.0.2", 73 | "vite": "^4.3.9" 74 | }, 75 | "packageManager": "yarn@4.0.0" 76 | } 77 | -------------------------------------------------------------------------------- /public/ic-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/public/ic-logo.png -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: #1d2745; 3 | --secondary: #1de5d4; 4 | --tertiary: #f52c50; 5 | --white: #ffffff; 6 | --mono-100: #f1f1f1; 7 | --mono-200: #bebebe; 8 | --mono-300: #d6d7d9; 9 | --error: #d01e1e; 10 | } 11 | 12 | #root { 13 | width: 100vw; 14 | height: 100vh; 15 | margin: 0 auto; 16 | } -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import normalize from "emotion-normalize"; 2 | import "./App.css"; 3 | import { css, Global } from "@emotion/react"; 4 | import { PageLayout } from "pages/PageLayout"; 5 | import { router } from "pages/Routes"; 6 | import { RecoilRoot } from "recoil"; 7 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 8 | import { RouterProvider } from "react-router-dom"; 9 | import { OrderProvider } from "libs/order"; 10 | 11 | const queryClient = new QueryClient(); 12 | 13 | export default function App() { 14 | return ( 15 | <> 16 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/__test__/Login.spec.tsx: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | 3 | import * as nock from "nock"; 4 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 5 | import { 6 | fireEvent, 7 | render, 8 | renderHook, 9 | screen, 10 | waitFor, 11 | } from "@testing-library/react"; 12 | import LoginPage from "../pages/LoginPage"; 13 | import { RouterProvider, createMemoryRouter } from "react-router-dom"; 14 | import useLogin from "../hooks/useLogin"; 15 | 16 | const queryClient = new QueryClient({ 17 | defaultOptions: {}, 18 | }); 19 | 20 | const wrapper = ({ children }) => ( 21 | {children} 22 | ); 23 | 24 | describe("로그인 테스트", () => { 25 | beforeEach(() => { 26 | jest.spyOn(console, "error").mockImplementation(() => {}); 27 | }); 28 | 29 | afterAll(() => { 30 | jest.restoreAllMocks(); 31 | }); 32 | 33 | test("로그인에 실패하면 에러메세지가 나타난다", async () => { 34 | //given - 로그인 화면이 그려진다 35 | const routes = [ 36 | { 37 | path: "/login", 38 | element: , 39 | }, 40 | ]; 41 | 42 | const router = createMemoryRouter(routes, { 43 | initialEntries: ["/login"], 44 | initialIndex: 0, 45 | }); 46 | 47 | render( 48 | 49 | 50 | 51 | ); 52 | 53 | // when - 사용자가 로그인에 실패한다 54 | nock("https://server.byeongjinkang.com") 55 | .post("/user/login/", { 56 | username: "wrong@email.com", 57 | password: "wrongPassword", 58 | }) 59 | .reply(400, { msg: "NO_SUCH_USER" }); 60 | 61 | const emailInput = screen.getByLabelText("이메일"); 62 | const passwordInput = screen.getByLabelText("비밀번호"); 63 | 64 | fireEvent.change(emailInput, { target: { value: "wrong@email.com" } }); 65 | fireEvent.change(passwordInput, { target: { value: "wrongPassword" } }); 66 | 67 | const loginButton = screen.getByRole("button", { name: "로그인" }); 68 | fireEvent.click(loginButton); 69 | 70 | const { result } = renderHook(() => useLogin(), { wrapper }); 71 | 72 | // then - 에러메세지가 나타남 73 | await waitFor(() => result.current.isError); 74 | const errorMessage = await screen.findByTestId("error-message"); 75 | expect(errorMessage).toBeInTheDocument(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/__test__/Signup.spec.tsx: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | 3 | import { fireEvent, render, screen } from "@testing-library/react"; 4 | import SignupPage from "../pages/SignupPage"; 5 | import { RouterProvider, createMemoryRouter } from "react-router-dom"; 6 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 7 | 8 | const queryClient = new QueryClient({ 9 | defaultOptions: {}, 10 | }); 11 | 12 | describe("회원가입 테스트", () => { 13 | beforeEach(() => { 14 | const routes = [ 15 | { 16 | path: "/signup", 17 | element: , 18 | }, 19 | ]; 20 | 21 | const router = createMemoryRouter(routes, { 22 | initialEntries: ["/signup"], 23 | initialIndex: 0, 24 | }); 25 | 26 | render( 27 | 28 | 29 | 30 | ); 31 | }); 32 | 33 | test("비밀번호와 비밀번호 확인 값이 일치하지 않으면 에러메세지가 표시된다", async () => { 34 | // given - 회원가입 페이지가 그려짐 35 | 36 | // when - 비밀번호와 비밀번호 확인 값이 일치하지 않음 37 | 38 | const passwordInput = screen.getByLabelText("비밀번호"); 39 | const confirmPasswordInput = screen.getByLabelText("비밀번호 확인"); 40 | 41 | fireEvent.change(passwordInput, { target: { value: "password" } }); 42 | fireEvent.change(confirmPasswordInput, { 43 | target: { value: "wrongPassword" }, 44 | }); 45 | 46 | // then - 에러메세지가 표시됨 47 | const errorMessage = await screen.findByTestId("error-message"); 48 | expect(errorMessage).toBeInTheDocument(); 49 | }); 50 | 51 | test("이메일을 입력하고, 비밀번호와 비밀번호 확인값이 일치하면 회원가입 버튼이 활성화된다", async () => { 52 | // given - 회원가입 페이지가 그려짐 53 | 54 | const signupButton = screen.getByRole("button", { name: "회원가입" }); 55 | expect(signupButton).toBeDisabled(); 56 | 57 | // when - 이메일 입력, 비밀번호, 비밀번호 확인 일치 58 | const emailInput = screen.getByLabelText("이메일"); 59 | const passwordInput = screen.getByLabelText("비밀번호"); 60 | const confirmPasswordInput = screen.getByLabelText("비밀번호 확인"); 61 | 62 | fireEvent.change(emailInput, { 63 | target: { value: "button-activated@email.com" }, 64 | }); 65 | fireEvent.change(passwordInput, { target: { value: "password" } }); 66 | fireEvent.change(confirmPasswordInput, { 67 | target: { value: "password" }, 68 | }); 69 | 70 | // then - 회원가입 버튼 활성화 71 | expect(signupButton).toBeEnabled(); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/atoms/order.ts: -------------------------------------------------------------------------------- 1 | import { INewOrder, ITargetRestaurant } from "libs/order"; 2 | import { atom, selector } from "recoil"; 3 | 4 | export const newOrderState = atom({ 5 | key: "newOrderState", 6 | default: [], 7 | }); 8 | 9 | export const totalPriceState = selector({ 10 | key: "totalPriceState", 11 | get: ({ get }) => { 12 | const items = get(newOrderState); // your atom containing the list of objects 13 | 14 | return items.reduce((total, item) => total + item.price * item.count, 0); 15 | }, 16 | }); 17 | 18 | export const targetRestaurantState = atom({ 19 | key: "targetRestaurantState", 20 | default: { 21 | id: 0, 22 | name: "", 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { flexColumn, flexRow } from "mixins/styles"; 3 | 4 | export default function Footer() { 5 | return ( 6 | 7 | 8 | order 14 | 주문 15 | 16 | 17 | profile 23 | 내정보 24 | 25 | 26 | ); 27 | } 28 | 29 | const StyledFooter = styled.footer` 30 | ${flexRow}; 31 | justify-content: space-evenly; 32 | position: fixed; 33 | bottom: 0; 34 | width: 100%; 35 | z-index: 5; 36 | height: 72px; 37 | box-shadow: 0 -5px 10px rgba(0, 0, 0, 0.2); 38 | background-color: var(--white); 39 | `; 40 | 41 | const FooterItem = styled.button` 42 | ${flexColumn}; 43 | align-items: center; 44 | color: var(--primary); 45 | `; 46 | -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { flexRow } from "mixins/styles"; 3 | 4 | export default function Header() { 5 | return ( 6 | 7 | 16 | 17 | ); 18 | } 19 | 20 | const StyledHeader = styled.header` 21 | ${flexRow} 22 | position: fixed; 23 | width: 100vw; 24 | top: 0; 25 | background-color: var(--primary); 26 | height: 64px; 27 | border-bottom-left-radius: 16px; 28 | border-bottom-right-radius: 16px; 29 | `; 30 | 31 | const Button = styled.button` 32 | ${flexRow}; 33 | column-gap: 2px; 34 | font-weight: bold; 35 | `; 36 | -------------------------------------------------------------------------------- /src/components/OrderType.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { flexRow } from "../mixins/styles"; 3 | import { OrderCategory } from "../mixins/types"; 4 | import { MouseEventHandler } from "react"; 5 | 6 | interface IOrderType { 7 | orderType: OrderCategory; 8 | icon: string; 9 | testId: string; 10 | handleOrderTypeClick: MouseEventHandler; 11 | } 12 | 13 | const orderCategory = { 14 | delivery: "배달", 15 | pickup: "포장", 16 | }; 17 | 18 | export default function OrderType({ 19 | orderType, 20 | icon, 21 | handleOrderTypeClick, 22 | testId, 23 | }: IOrderType) { 24 | return ( 25 | 26 | 31 | {orderCategory[orderType]} 32 | 33 | ); 34 | } 35 | 36 | const OrderTypeBtn = styled.button` 37 | ${flexRow}; 38 | width: 100%; 39 | column-gap: 24px; 40 | color: var(--primary); 41 | font-weight: bold; 42 | font-size: 24px; 43 | border: 1px solid var(--primary); 44 | border-radius: 4px; 45 | padding: 24px; 46 | `; 47 | -------------------------------------------------------------------------------- /src/hooks/useFoodTypeList.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import axiosClient from "libs/axios"; 3 | 4 | interface IFoodType { 5 | id: number; 6 | name: string; 7 | icon: string; 8 | } 9 | 10 | const useFoodTypeList = () => { 11 | return useQuery(["foodTypeList"], async () => { 12 | const { data } = await axiosClient.get( 13 | "/restaurant/food-type/" 14 | ); 15 | return data; 16 | }); 17 | }; 18 | 19 | export default useFoodTypeList; 20 | -------------------------------------------------------------------------------- /src/hooks/useIntersection.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | 3 | const useIntersection = (targetRef, options = {}) => { 4 | const [isIntersecting, setIsIntersecting] = useState(false); 5 | 6 | const intersectionCallback = (entries) => { 7 | entries.forEach((entry) => { 8 | setIsIntersecting(entry.isIntersecting); 9 | }); 10 | }; 11 | 12 | const observer = useRef( 13 | new IntersectionObserver(intersectionCallback, options) 14 | ); 15 | 16 | useEffect(() => { 17 | const target = targetRef.current; 18 | 19 | if (target) { 20 | observer.current.observe(target); 21 | 22 | return () => { 23 | observer.current.disconnect(); 24 | }; 25 | } 26 | }, [targetRef, options]); 27 | 28 | return isIntersecting; 29 | }; 30 | 31 | export default useIntersection; 32 | -------------------------------------------------------------------------------- /src/hooks/useIntersectionObserver.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, RefObject } from "react"; 2 | 3 | function useIntersectionObserver(ref: RefObject): boolean { 4 | const [isVisible, setIsVisible] = useState(false); 5 | 6 | useEffect(() => { 7 | const observer = new IntersectionObserver( 8 | ([entry]) => { 9 | setIsVisible(entry.isIntersecting); 10 | }, 11 | { 12 | root: null, 13 | rootMargin: "0px", 14 | threshold: 0.1, 15 | } 16 | ); 17 | 18 | if (ref.current) { 19 | observer.observe(ref.current); 20 | } 21 | 22 | return () => { 23 | if (ref.current) { 24 | observer.unobserve(ref.current); 25 | } 26 | }; 27 | }, [ref]); 28 | 29 | return isVisible; 30 | } 31 | 32 | export default useIntersectionObserver; 33 | -------------------------------------------------------------------------------- /src/hooks/useLogin.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from "@tanstack/react-query"; 2 | import axiosClient from "../libs/axios"; 3 | // import axios from "axios"; 4 | 5 | interface LoginResponse { 6 | token: string; 7 | } 8 | 9 | export interface LoginProps { 10 | username: string; 11 | password: string; 12 | } 13 | 14 | const postLogin = async ({ username, password }: LoginProps) => { 15 | const { data } = await axiosClient.post("/user/login/", { 16 | email: username, 17 | password, 18 | }); 19 | 20 | const token = data.token; 21 | localStorage.setItem("token", token); 22 | return data; 23 | }; 24 | 25 | const useLogin = () => 26 | useMutation( 27 | ["login"], 28 | async (loginInfo: LoginProps) => postLogin(loginInfo), 29 | { 30 | onError() { 31 | throw Error("login failed"); 32 | }, 33 | } 34 | ); 35 | 36 | export default useLogin; 37 | -------------------------------------------------------------------------------- /src/hooks/useRestaurantDetail.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import axiosClient from "libs/axios"; 3 | import { IMenu } from "mixins/types"; 4 | 5 | interface IRestaurantDetail { 6 | id: number; 7 | name: string; 8 | minPrice: number; 9 | menu_set: IMenu[]; 10 | } 11 | 12 | const useRestaurantDetail = (restaurantId: number) => { 13 | return useQuery( 14 | ["restaurantDetail"], 15 | async () => { 16 | const { data } = await axiosClient.get( 17 | `/restaurant/${restaurantId}` 18 | ); 19 | return data; 20 | }, 21 | { 22 | cacheTime: 0, 23 | staleTime: 0, 24 | enabled: restaurantId > 0, 25 | } 26 | ); 27 | }; 28 | 29 | export default useRestaurantDetail; 30 | -------------------------------------------------------------------------------- /src/hooks/useRestaurantList.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import axiosClient from "libs/axios"; 3 | import { IRestaurant } from "libs/order"; 4 | 5 | const useRestaurantList = (foodTypeId: number) => { 6 | return useQuery( 7 | ["restaurantList"], 8 | async () => { 9 | const { data } = await axiosClient.get( 10 | `/restaurant/food-type/${foodTypeId}` 11 | ); 12 | return data; 13 | }, 14 | {} 15 | ); 16 | }; 17 | 18 | export default useRestaurantList; 19 | -------------------------------------------------------------------------------- /src/hooks/useSignup.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from "@tanstack/react-query"; 2 | import axiosClient from "../libs/axios"; 3 | 4 | export interface SignupProps { 5 | username: string; 6 | password: string; 7 | } 8 | 9 | const postSignup = async ({ username, password }: SignupProps) => { 10 | const { data } = await axiosClient.post("/user/signup/", { 11 | email: username, 12 | password, 13 | }); 14 | 15 | return data; 16 | }; 17 | 18 | const useSignup = () => 19 | useMutation( 20 | ["signUp"], 21 | async (SignupInfo: SignupProps) => postSignup(SignupInfo), 22 | { 23 | onError() { 24 | throw Error("Signup failed"); 25 | }, 26 | } 27 | ); 28 | 29 | export default useSignup; 30 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/libs/axios.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const axiosClient = axios.create({ 4 | baseURL: "https://server.byeongjinkang.com", 5 | timeout: 8000, 6 | }); 7 | 8 | axiosClient?.interceptors.request.use((config) => { 9 | const token = localStorage.getItem("token"); 10 | 11 | if (token) { 12 | config.headers.Authorization = `Token ${token}`; 13 | } 14 | return config; 15 | }); 16 | 17 | export default axiosClient; 18 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/context.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | import { State, initialState } from "./state"; 4 | import { INewOrder, IRestaurant, ITargetRestaurant } from "."; 5 | 6 | /** Context */ 7 | export type ContextType = State & { 8 | addItemToOrder: (newOrder: INewOrder) => void; 9 | increaseItemCount: (menuId: number) => void; 10 | decreaseItemCount: (menuId: number) => void; 11 | resetOrder: () => void; 12 | setRestaurant: (targetRestaurant: ITargetRestaurant) => void; 13 | getRestaurantList: (foodTypeId: number) => void; 14 | totalPrice: number; 15 | restaurant: ITargetRestaurant; 16 | restaurantList: IRestaurant[]; 17 | }; 18 | 19 | const stub = (): never => { 20 | throw new Error("You forgot to wrap your component in ."); 21 | }; 22 | 23 | export const initialContext: ContextType = { 24 | ...initialState, 25 | 26 | addItemToOrder: stub, 27 | increaseItemCount: stub, 28 | decreaseItemCount: stub, 29 | resetOrder: stub, 30 | setRestaurant: stub, 31 | getRestaurantList: stub, 32 | 33 | totalPrice: 0, 34 | restaurant: { 35 | id: 0, 36 | name: "", 37 | }, 38 | restaurantList: [], 39 | }; 40 | 41 | const OrderContext = createContext(initialContext); 42 | 43 | export default OrderContext; 44 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as OrderProvider } from "./provider"; 2 | export { default as useOrder } from "./useOrder"; 3 | 4 | export type { ContextType } from "./context"; 5 | export { default as OrderContext } from "./context"; 6 | 7 | export type { 8 | AudioInputDeviceInfo, 9 | AudioOutputDeviceInfo, 10 | VideoInputDeviceInfo, 11 | INewOrder, 12 | IRestaurant, 13 | ITargetRestaurant, 14 | } from "./types"; 15 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/provider.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useReducer } from "react"; 2 | 3 | import OrderContext, { initialContext } from "./context"; 4 | import type { ContextType } from "./context"; 5 | import { reducer } from "./reducer"; 6 | import { initialState } from "./state"; 7 | import { INewOrder, IRestaurant, ITargetRestaurant } from "."; 8 | import axiosClient from "libs/axios"; 9 | 10 | const OrderProvider = ({ 11 | children, 12 | }: { 13 | children: ReactElement; 14 | }): JSX.Element => { 15 | const [state, dispatch] = useReducer(reducer, initialState); 16 | const { newOrder } = state; 17 | 18 | useEffect(() => { 19 | const totalPrice = newOrder.reduce((total, item) => { 20 | return total + item.count * item.price; 21 | }, 0); 22 | 23 | dispatch({ type: "SET_TOTAL_PRICE", payload: totalPrice }); 24 | }, [newOrder]); 25 | 26 | const resetOrder = () => { 27 | dispatch({ type: "RESET_ORDER" }); 28 | }; 29 | 30 | const addItemToOrder = (newOrder: INewOrder) => { 31 | dispatch({ type: "ADD_ITEM_TO_ORDER", payload: newOrder }); 32 | }; 33 | 34 | const increaseItemCount = (menuId: number) => { 35 | console.log("menuId: ", menuId); 36 | dispatch({ type: "INCREASE_ITEM_COUNT", payload: menuId }); 37 | }; 38 | 39 | const decreaseItemCount = (menuId: number) => { 40 | dispatch({ type: "DECREASE_ITEM_COUNT", payload: menuId }); 41 | }; 42 | 43 | const setRestaurant = (targetRestaurant: ITargetRestaurant) => { 44 | dispatch({ type: "SET_TARGET_RESTAURANT", payload: targetRestaurant }); 45 | }; 46 | 47 | const getRestaurantList = async (foodTypeId: number) => { 48 | const { data } = await axiosClient.get( 49 | `/restaurant/food-type/${foodTypeId}` 50 | ); 51 | dispatch({ type: "SET_RESTAURANT_LIST", payload: data }); 52 | }; 53 | 54 | const orderContext: ContextType = { 55 | ...initialContext, 56 | ...state, 57 | resetOrder, 58 | addItemToOrder, 59 | increaseItemCount, 60 | decreaseItemCount, 61 | setRestaurant, 62 | getRestaurantList, 63 | }; 64 | 65 | return ( 66 | 67 | {children} 68 | 69 | ); 70 | }; 71 | 72 | export default OrderProvider; 73 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/reducer.tsx: -------------------------------------------------------------------------------- 1 | import { produce } from "immer"; 2 | 3 | import { initialState, type State } from "./state"; 4 | import type { INewOrder, IRestaurant, ITargetRestaurant } from "./types"; 5 | 6 | /** Actions */ 7 | export type Action = 8 | | { type: "RESET_ORDER" } 9 | | { type: "ADD_ITEM_TO_ORDER"; payload: INewOrder } 10 | | { type: "INCREASE_ITEM_COUNT"; payload: number } 11 | | { type: "DECREASE_ITEM_COUNT"; payload: number } 12 | | { type: "SET_TOTAL_PRICE"; payload: number } 13 | | { type: "SET_TARGET_RESTAURANT"; payload: ITargetRestaurant } 14 | | { type: "SET_RESTAURANT_LIST"; payload: IRestaurant[] }; 15 | 16 | /** Reducer */ 17 | export const reducer = (prevState: State, action: Action): State => { 18 | switch (action.type) { 19 | case "RESET_ORDER": 20 | return initialState; 21 | 22 | case "ADD_ITEM_TO_ORDER": 23 | return produce(prevState, (draft) => { 24 | draft.newOrder.push(action.payload); 25 | }); 26 | 27 | case "INCREASE_ITEM_COUNT": 28 | return produce(prevState, (draft) => { 29 | const targetMenu = draft.newOrder.find( 30 | (order) => order.id === action.payload 31 | ); 32 | 33 | if (!targetMenu) return; 34 | targetMenu.count += 1; 35 | }); 36 | 37 | case "DECREASE_ITEM_COUNT": 38 | return produce(prevState, (draft) => { 39 | const targetMenu = draft.newOrder.find( 40 | (order) => order.id === action.payload 41 | ); 42 | 43 | if (!targetMenu) return; 44 | targetMenu.count -= 1; 45 | }); 46 | 47 | case "SET_TOTAL_PRICE": 48 | return produce(prevState, (draft) => { 49 | draft.totalPrice = action.payload; 50 | }); 51 | 52 | case "SET_TARGET_RESTAURANT": 53 | return produce(prevState, (draft) => { 54 | draft.restaurant = action.payload; 55 | }); 56 | 57 | case "SET_RESTAURANT_LIST": 58 | return produce(prevState, (draft) => { 59 | draft.restaurantList = action.payload; 60 | }); 61 | 62 | default: 63 | return prevState; 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/state.tsx: -------------------------------------------------------------------------------- 1 | import { INewOrder, IRestaurant, ITargetRestaurant } from "."; 2 | 3 | export type State = { 4 | newOrder: INewOrder[]; 5 | totalPrice: number; 6 | restaurant: ITargetRestaurant; 7 | restaurantList: IRestaurant[]; 8 | }; 9 | 10 | export const initialState: State = { 11 | newOrder: [], 12 | totalPrice: 0, 13 | restaurantList: [], 14 | restaurant: { 15 | id: 0, 16 | name: "", 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/types.ts: -------------------------------------------------------------------------------- 1 | // TODO: 'idle' state 2 | export type CallState = 3 | | "dialing" 4 | | "ringing" 5 | | "established" 6 | | "connected" 7 | | "reconnecting" 8 | | "reconnected" 9 | | "ended"; 10 | 11 | export type AudioInputDeviceInfo = { 12 | current?: InputDeviceInfo; 13 | available: InputDeviceInfo[]; 14 | }; 15 | export type AudioOutputDeviceInfo = { 16 | current?: MediaDeviceInfo; 17 | available: MediaDeviceInfo[]; 18 | }; 19 | export type VideoInputDeviceInfo = { 20 | current?: InputDeviceInfo; 21 | available: InputDeviceInfo[]; 22 | }; 23 | 24 | export interface INewOrder { 25 | id: number; 26 | name: string; 27 | count: number; 28 | price: number; 29 | picture: string; 30 | } 31 | 32 | export interface ITargetRestaurant { 33 | id: number; 34 | name: string; 35 | } 36 | 37 | export interface IRestaurant { 38 | id: number; 39 | name: string; 40 | ratings: number; 41 | minPrice: number; 42 | icon: string; 43 | } 44 | -------------------------------------------------------------------------------- /src/libs/order/OrderContext/useOrder.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import OrderContext from "./context"; 4 | 5 | const useOrder = () => useContext(OrderContext); 6 | 7 | export default useOrder; 8 | -------------------------------------------------------------------------------- /src/libs/order/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./OrderContext"; 2 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /src/mixins/styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from "@emotion/react"; 2 | 3 | export const flexRow = css` 4 | display: flex; 5 | flex-direction: row; 6 | align-items: center; 7 | `; 8 | 9 | export const flexColumn = css` 10 | display: flex; 11 | flex-direction: column; 12 | `; 13 | -------------------------------------------------------------------------------- /src/mixins/types.ts: -------------------------------------------------------------------------------- 1 | export type OrderCategory = "delivery" | "pickup"; 2 | export interface IMenu { 3 | id: number; 4 | name: string; 5 | description: string; 6 | price: number; 7 | picture: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/FoodTypePage.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import useFoodTypeList from "hooks/useFoodTypeList"; 3 | import { flexColumn, flexRow } from "mixins/styles"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | export default function FoodTypePage() { 7 | const navigate = useNavigate(); 8 | const { data: foodTypeList } = useFoodTypeList(); 9 | 10 | const handleFoodTypeClick = (foodTypeId: number) => { 11 | navigate(`/food-type/${foodTypeId}`); 12 | }; 13 | 14 | return ( 15 | 16 | {foodTypeList?.map((foodType) => ( 17 | 18 | {foodType.name} handleFoodTypeClick(foodType.id)} 24 | /> 25 | {foodType.name} 26 | 27 | ))} 28 | 29 | ); 30 | } 31 | 32 | const Wrapper = styled.div` 33 | ${flexRow}; 34 | margin-top: 64px; 35 | padding-top: 24px; 36 | gap: 24px; 37 | justify-content: center; 38 | align-items: center; 39 | flex-wrap: wrap; 40 | `; 41 | 42 | const FoodType = styled.button` 43 | ${flexColumn}; 44 | align-items: center; 45 | font-weight: bold; 46 | row-gap: 8px; 47 | color: var(--primary); 48 | `; 49 | -------------------------------------------------------------------------------- /src/pages/LoginPage.tsx: -------------------------------------------------------------------------------- 1 | import useLogin from "../hooks/useLogin"; 2 | import { useEffect, useState } from "react"; 3 | import { css } from "@emotion/react"; 4 | import styled from "@emotion/styled"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | export default function LoginPage() { 8 | const navigate = useNavigate(); 9 | const [email, setEmail] = useState(""); 10 | const [password, setPassword] = useState(""); 11 | const { mutate: handleLogin, isError, isSuccess } = useLogin(); 12 | 13 | useEffect(() => { 14 | if (isSuccess) { 15 | navigate("/"); 16 | } 17 | }, [isSuccess]); 18 | 19 | return ( 20 | 21 |
22 |
23 | 이메일로 로그인 24 | 25 | close 29 | 30 |
31 | 32 | 33 | 34 | ) => 40 | setEmail(e.target.value) 41 | } 42 | /> 43 | {isError && 로그인 정보를 확인해주세요} 44 | 45 | 46 | 47 | ) => 53 | setPassword(e.target.value) 54 | } 55 | /> 56 | 57 | {isError && ( 58 | 59 | 로그인 정보를 확인해주세요 60 | 61 | )} 62 | 63 |
64 | handleLogin({ username: email, password })} 68 | > 69 | 로그인 70 | 71 |
72 | ); 73 | } 74 | 75 | const ColumnSpaceBetween = css` 76 | display: flex; 77 | flex-direction: column; 78 | justify-content: space-between; 79 | `; 80 | 81 | const Wrapper = styled.div` 82 | ${ColumnSpaceBetween} 83 | height: 100%; 84 | background-color: var(--white); 85 | padding: 0 16px; 86 | `; 87 | 88 | const Header = styled.header` 89 | display: flex; 90 | justify-content: space-between; 91 | align-items: center; 92 | margin-top: 24px; 93 | `; 94 | 95 | const CloseButton = styled.button``; 96 | 97 | const Title = styled.h1` 98 | color: var(--primary); 99 | `; 100 | 101 | const InputSection = styled.section` 102 | margin-top: 40px; 103 | position: relative; 104 | div { 105 | &:last-child { 106 | margin-top: 24px; 107 | } 108 | } 109 | `; 110 | 111 | const Label = styled.label` 112 | margin-bottom: 16px; 113 | font-size: 14px; 114 | line-height: 21px; 115 | color: var(--white); 116 | `; 117 | 118 | const Input = styled.input` 119 | margin-bottom: 24px; 120 | 121 | padding-bottom: 8px; 122 | border-bottom: 1px solid var(--mono-300); 123 | color: var(--mono-300); 124 | 125 | &:focus { 126 | color: var(--secondary); 127 | border-bottom: 1px solid var(--secondary); 128 | } 129 | `; 130 | 131 | const LoginButton = styled.button` 132 | width: 100%; 133 | padding: 16px; 134 | border-radius: 4px; 135 | background-color: ${(props) => 136 | props.disabled ? "var(--mono-100)" : "var(--primary)"}; 137 | color: ${(props) => (props.disabled ? "var(--mono-200)" : "var(--white)")}; 138 | margin-bottom: 24px; 139 | `; 140 | 141 | const ErrorMessage = styled.h6` 142 | font-size: 12px; 143 | line-height: 18px; 144 | color: var(--error); 145 | position: absolute; 146 | bottom: 0; 147 | `; 148 | 149 | const InputWrapper = styled.div` 150 | ${ColumnSpaceBetween} 151 | `; 152 | -------------------------------------------------------------------------------- /src/pages/OrderDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { 3 | newOrderState, 4 | targetRestaurantState, 5 | totalPriceState, 6 | } from "atoms/order"; 7 | import { flexColumn, flexRow } from "mixins/styles"; 8 | import { useEffect } from "react"; 9 | import { useNavigate } from "react-router-dom"; 10 | import { useRecoilState, useRecoilValue } from "recoil"; 11 | 12 | export default function OrderDetailPage() { 13 | const navigate = useNavigate(); 14 | const totalPrice = useRecoilValue(totalPriceState); 15 | const restaurant = useRecoilValue(targetRestaurantState); 16 | const [newOrder, changeCount] = useRecoilState(newOrderState); 17 | 18 | useEffect(() => { 19 | if (!newOrder.length) { 20 | navigate(`/restaurant/${restaurant.id}`); 21 | } 22 | }, [newOrder]); 23 | 24 | const handleMoreBtnClick = () => { 25 | navigate(`/restaurant/${restaurant.id}`); 26 | }; 27 | 28 | const handleConfirmBtnClick = () => { 29 | alert("주문이 완료되었습니다"); 30 | navigate("/"); 31 | }; 32 | 33 | const handleIncrementBtnClick = (menuId: number) => { 34 | changeCount((oldArray) => 35 | oldArray.map((item) => 36 | item.id === menuId ? { ...item, count: item.count + 1 } : item 37 | ) 38 | ); 39 | }; 40 | 41 | const handleDecrementBtnClick = (menuId: number) => { 42 | changeCount((oldArray) => 43 | oldArray.map((item) => 44 | item.id === menuId ? { ...item, count: item.count - 1 } : item 45 | ) 46 | ); 47 | }; 48 | 49 | return ( 50 | 51 | {restaurant.name} 52 | {newOrder.map((menu) => ( 53 | 54 | {menu.name} 55 | 56 | {menu.name} 57 | {`${menu.price.toLocaleString()}원`} 58 | 59 | 60 | handleDecrementBtnClick(menu.id)} 63 | > 64 | - 65 | 66 | {menu.count} 67 | handleIncrementBtnClick(menu.id)} 70 | > 71 | + 72 | 73 | 74 | 75 | 76 | ))} 77 | {`주문금액: ${totalPrice.toLocaleString()}원`} 78 | 79 | 더담기 80 | 81 | 주문완료 82 | 83 | 84 | 85 | ); 86 | } 87 | 88 | const Wrapper = styled.div` 89 | ${flexColumn} 90 | row-gap: 24px; 91 | height: 100%; 92 | margin-top: 64px; 93 | padding: 24px; 94 | color: var(--primary); 95 | `; 96 | 97 | const Title = styled.h2``; 98 | 99 | const MenuWrap = styled.div` 100 | ${flexRow}; 101 | column-gap: 16px; 102 | align-items: start; 103 | `; 104 | 105 | const MenuInfo = styled.div` 106 | ${flexColumn}; 107 | `; 108 | 109 | const MenuName = styled.h3``; 110 | 111 | const MenuPrice = styled.h4``; 112 | 113 | const CounterSection = styled.section` 114 | ${flexRow} 115 | width: 120px; 116 | margin-top: 8px; 117 | font-size: 16px; 118 | align-items: center; 119 | border: 1px solid var(--primary); 120 | border-radius: 3px; 121 | justify-content: space-around; 122 | `; 123 | 124 | const DecrementBtn = styled.button` 125 | color: var(--primary); 126 | padding: 0; 127 | font-size: 24px; 128 | `; 129 | 130 | const IncrementBtn = styled(DecrementBtn)` 131 | margin-bottom: 2px; 132 | `; 133 | 134 | const ConfirmBtn = styled.button` 135 | background-color: var(--primary); 136 | border: none; 137 | outline: none; 138 | `; 139 | 140 | const MoreBtn = styled(ConfirmBtn)``; 141 | 142 | const TotalPrice = styled.h4` 143 | margin-top: 24px; 144 | `; 145 | 146 | const ButtonWrap = styled.div` 147 | ${flexRow}; 148 | column-gap: 16px; 149 | `; 150 | -------------------------------------------------------------------------------- /src/pages/OrderTypePage.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import OrderType from "../components/OrderType"; 3 | import { flexColumn } from "../mixins/styles"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | export default function OrderTypePage() { 7 | const navigate = useNavigate(); 8 | const handleDeliveryBtnClick = () => { 9 | navigate("/food-type"); 10 | }; 11 | const handlePickupBtnClick = () => { 12 | navigate("/food-type"); 13 | }; 14 | 15 | return ( 16 | 17 | 23 | 29 | 30 | ); 31 | } 32 | 33 | const Wrapper = styled.main` 34 | ${flexColumn}; 35 | margin-top: 64px; 36 | padding: 24px 16px; 37 | row-gap: 16px; 38 | `; 39 | -------------------------------------------------------------------------------- /src/pages/PageLayout.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | import { ReactNode } from "react"; 4 | 5 | export function PageLayout({ children }: { children: ReactNode }) { 6 | return {children}; 7 | } 8 | 9 | const Wrapper = styled.div` 10 | max-width: 100%; 11 | width: 100vw; 12 | height: 100vh; 13 | padding: 0; 14 | margin: 0; 15 | background-color: var(--white); 16 | `; 17 | -------------------------------------------------------------------------------- /src/pages/RestaurantCard.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | import { targetRestaurantState } from "atoms/order"; 4 | // import useIntersection from "hooks/useIntersection"; 5 | 6 | import { IRestaurant } from "libs/order"; 7 | import { flexColumn, flexRow } from "mixins/styles"; 8 | // import { useRef } from "react"; 9 | 10 | import { useNavigate } from "react-router-dom"; 11 | import { useSetRecoilState } from "recoil"; 12 | 13 | interface IRestaurantCardProps { 14 | restaurant: IRestaurant; 15 | } 16 | 17 | export default function RestaurantCard({ restaurant }: IRestaurantCardProps) { 18 | const navigate = useNavigate(); 19 | const setRestaurant = useSetRecoilState(targetRestaurantState); 20 | 21 | const handleRestaurantClick = ( 22 | restaurantId: number, 23 | restaurantName: string 24 | ) => { 25 | setRestaurant({ id: restaurantId, name: restaurantName }); 26 | navigate(`/restaurant/${restaurantId}`, { replace: true }); 27 | }; 28 | 29 | return ( 30 | handleRestaurantClick(restaurant.id, restaurant.name)} 34 | > 35 | {/* {isIntersecting && ( */} 36 | <> 37 | food-type 38 | 39 | {restaurant.name} 40 | 41 | ratings 47 | {restaurant.ratings} 48 | 49 | 최소주문: {restaurant.minPrice} 50 | 51 | 52 | {/* )} */} 53 | 54 | ); 55 | } 56 | 57 | const MinPrice = styled.h6` 58 | margin: 0; 59 | color: rgba(0, 0, 0, 0.6); 60 | `; 61 | 62 | const RatingsWrap = styled.div` 63 | ${flexRow} 64 | column-gap: 4px; 65 | `; 66 | 67 | const Ratings = styled.h6` 68 | color: grey; 69 | margin: 0; 70 | `; 71 | 72 | const RestaurantInfo = styled.div` 73 | ${flexColumn} 74 | row-gap: 8px; 75 | `; 76 | 77 | const RestaurantBtn = styled.button` 78 | ${flexRow} 79 | column-gap: 16px; 80 | width: 100%; 81 | border-bottom: 1px solid grey; 82 | background-color: var(--white); 83 | padding: 24px; 84 | z-index: 1; 85 | border-radius: 0; 86 | `; 87 | 88 | const RestaurantName = styled.h5` 89 | color: var(--primary); 90 | font-weight: bold; 91 | font-size: 18px; 92 | margin: 0; 93 | `; 94 | -------------------------------------------------------------------------------- /src/pages/RestaurantDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { newOrderState } from "atoms/order"; 3 | import useRestaurantDetail from "hooks/useRestaurantDetail"; 4 | 5 | import { flexColumn, flexRow } from "mixins/styles"; 6 | import { IMenu } from "mixins/types"; 7 | import { useNavigate, useParams } from "react-router-dom"; 8 | import { useRecoilState } from "recoil"; 9 | 10 | export default function RestaurantDetailPage() { 11 | const navigate = useNavigate(); 12 | const [order, addItemToOrder] = useRecoilState(newOrderState); 13 | // const { addItemToOrder } = useOrder(); 14 | const { id: restaurantId } = useParams(); 15 | const { data: restaurant } = useRestaurantDetail( 16 | restaurantId ? parseInt(restaurantId) : 0 17 | ); 18 | 19 | const handleMenuClick = (menu: IMenu) => { 20 | addItemToOrder([ 21 | ...order, 22 | { 23 | name: menu.name, 24 | id: menu.id, 25 | price: menu.price, 26 | count: 1, 27 | picture: menu.picture, 28 | }, 29 | ]); 30 | navigate("/order"); 31 | }; 32 | 33 | return ( 34 | 35 | {restaurant?.name} 36 | {restaurant?.menu_set.map((menu) => ( 37 | handleMenuClick(menu)}> 38 | 39 | {menu.name} 40 | {menu.description} 41 | {`${menu.price.toString().slice(0, 2)},${menu.price 42 | .toString() 43 | .slice(2)}원`} 44 | 45 | {menu.name} 46 | 47 | ))} 48 | 49 | ); 50 | } 51 | 52 | const Wrapper = styled.div` 53 | ${flexColumn} 54 | row-gap: 16px; 55 | margin-top: 64px; 56 | padding: 24px; 57 | `; 58 | 59 | const MenuName = styled.h2` 60 | color: var(--primary); 61 | `; 62 | 63 | const MenuDescription = styled.h4` 64 | color: grey; 65 | `; 66 | 67 | const MenuPrice = styled.h5` 68 | color: black; 69 | `; 70 | 71 | const RestaurantName = styled.h1` 72 | color: var(--primary); 73 | `; 74 | 75 | const MenuWrap = styled.div` 76 | ${flexRow}; 77 | justify-content: space-between; 78 | cursor: pointer; 79 | padding-bottom: 16px; 80 | border-bottom: 1px solid grey; 81 | `; 82 | 83 | const MenuInfo = styled.div` 84 | ${flexColumn} 85 | `; 86 | -------------------------------------------------------------------------------- /src/pages/RestaurantListPage.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | import useRestaurantList from "hooks/useRestaurantList"; 4 | import { Profiler } from "react"; 5 | import { useParams } from "react-router-dom"; 6 | 7 | import RestaurantCard from "./RestaurantCard"; 8 | // import useIntersection from "hooks/useIntersection"; 9 | 10 | export default function RestaurantListPage() { 11 | const { id: foodTypeId } = useParams(); 12 | // const targetRef = useRef(null); 13 | // const isIntersecting = useIntersection(targetRef, { 14 | // threshold: 0.5, // 예시: 50% 이상이 화면에 보일 때 감지 15 | // }); 16 | 17 | const { data: restaurantList } = useRestaurantList( 18 | foodTypeId ? parseInt(foodTypeId) : 0 19 | ); 20 | 21 | function onRender( 22 | id, 23 | phase, 24 | actualDuration, 25 | baseDuration, 26 | startTime, 27 | commitTime 28 | ) { 29 | console.log("id: ", id); 30 | console.log("phase: ", phase); 31 | console.log("actualDuration: ", actualDuration); 32 | console.log("baseDuration: ", baseDuration); 33 | console.log("startTime: ", startTime); 34 | console.log("commitTime: ", commitTime); 35 | } 36 | return ( 37 | 38 | 39 | {restaurantList?.map((restaurant) => ( 40 | 41 | ))} 42 | 43 | 44 | ); 45 | } 46 | 47 | const Wrapper = styled.div` 48 | margin-top: 64px; 49 | `; 50 | 51 | // const EmptyDiv = styled.div` 52 | // height: 24px; 53 | // `; 54 | -------------------------------------------------------------------------------- /src/pages/Routes.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Route, 3 | createRoutesFromElements, 4 | createBrowserRouter, 5 | } from "react-router-dom"; 6 | 7 | import LoginPage from "./LoginPage"; 8 | 9 | import FoodTypePage from "./FoodTypePage"; 10 | import RestaurantListPage from "./RestaurantListPage"; 11 | import RestaurantDetailPage from "./RestaurantDetailPage"; 12 | import OrderDetailPage from "./OrderDetailPage"; 13 | import SignupPage from "./SignupPage"; 14 | import OrderTypePage from "./OrderTypePage"; 15 | 16 | export const router = createBrowserRouter( 17 | createRoutesFromElements( 18 | 19 | } /> 20 | } /> 21 | } /> 22 | } /> 23 | } /> 24 | } /> 25 | } /> 26 | 27 | ) 28 | ); 29 | -------------------------------------------------------------------------------- /src/pages/SignupPage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { css } from "@emotion/react"; 3 | import styled from "@emotion/styled"; 4 | import { useNavigate } from "react-router-dom"; 5 | import useSignup from "../hooks/useSignup"; 6 | 7 | export default function SignupPage() { 8 | const navigate = useNavigate(); 9 | const [email, setEmail] = useState(""); 10 | const [password, setPassword] = useState(""); 11 | const [confirmPassword, setConfirmPassword] = useState(""); 12 | const { mutate: handleSignup, isSuccess } = useSignup(); 13 | 14 | useEffect(() => { 15 | if (isSuccess) { 16 | alert("회원가입 성공"); 17 | navigate("/login"); 18 | } 19 | }, [isSuccess]); 20 | 21 | return ( 22 | 23 |
24 |
25 | 이메일로 회원가입 26 | 27 | close 31 | 32 |
33 | 34 | 35 | 36 | ) => 42 | setEmail(e.target.value) 43 | } 44 | /> 45 | 46 | 47 | 48 | ) => 54 | setPassword(e.target.value) 55 | } 56 | /> 57 | 58 | 59 | 60 | ) => 66 | setConfirmPassword(e.target.value) 67 | } 68 | /> 69 | 70 | {password != confirmPassword && ( 71 | 72 | 비밀번호가 일치하지 않습니다 73 | 74 | )} 75 | 76 |
77 | 78 | handleSignup({ username: email, password })} 82 | > 83 | 회원가입 84 | 85 |
86 | ); 87 | } 88 | 89 | const ColumnSpaceBetween = css` 90 | display: flex; 91 | flex-direction: column; 92 | justify-content: space-between; 93 | `; 94 | 95 | const Wrapper = styled.div` 96 | ${ColumnSpaceBetween} 97 | height: 100%; 98 | background-color: var(--white); 99 | padding: 0 16px; 100 | 101 | > button { 102 | margin-bottom: 24px; 103 | } 104 | `; 105 | 106 | const Header = styled.header` 107 | display: flex; 108 | justify-content: space-between; 109 | align-items: center; 110 | margin-top: 24px; 111 | `; 112 | 113 | const CloseButton = styled.button``; 114 | 115 | const Title = styled.h1` 116 | color: var(--primary); 117 | `; 118 | 119 | const InputSection = styled.section` 120 | position: relative; 121 | margin-top: 40px; 122 | `; 123 | 124 | const Label = styled.label` 125 | margin-bottom: 16px; 126 | font-size: 14px; 127 | line-height: 21px; 128 | color: var(--primary); 129 | `; 130 | 131 | const Input = styled.input` 132 | margin-bottom: 24px; 133 | 134 | padding-bottom: 8px; 135 | border-bottom: 1px solid var(--mono-300); 136 | color: var(--mono-300); 137 | 138 | &:focus { 139 | color: var(--secondary); 140 | border-bottom: 1px solid var(--secondary); 141 | } 142 | `; 143 | 144 | const SignupButton = styled.button` 145 | width: 100%; 146 | padding: 16px; 147 | border-radius: 4px; 148 | background-color: ${(props) => 149 | props.disabled ? "var(--mono-100)" : "var(--primary)"}; 150 | color: ${(props) => (props.disabled ? "var(--mono-200)" : "var(--white)")}; 151 | margin-bottom: 24px; 152 | `; 153 | 154 | const ErrorMessage = styled.h6` 155 | font-size: 12px; 156 | line-height: 18px; 157 | color: var(--error); 158 | position: absolute; 159 | bottom: 0; 160 | `; 161 | 162 | const InputWrapper = styled.div` 163 | ${ColumnSpaceBetween} 164 | `; 165 | -------------------------------------------------------------------------------- /src/stories/Button.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Button } from './Button'; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export 6 | const meta = { 7 | title: 'Example/Button', 8 | component: Button, 9 | parameters: { 10 | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'centered', 12 | }, 13 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs 14 | tags: ['autodocs'], 15 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes 16 | argTypes: { 17 | backgroundColor: { control: 'color' }, 18 | }, 19 | } satisfies Meta; 20 | 21 | export default meta; 22 | type Story = StoryObj; 23 | 24 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 25 | export const Primary: Story = { 26 | args: { 27 | primary: true, 28 | label: 'Button', 29 | }, 30 | }; 31 | 32 | export const Secondary: Story = { 33 | args: { 34 | label: 'Button', 35 | }, 36 | }; 37 | 38 | export const Large: Story = { 39 | args: { 40 | size: 'large', 41 | label: 'Button', 42 | }, 43 | }; 44 | 45 | export const Small: Story = { 46 | args: { 47 | size: 'small', 48 | label: 'Button', 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /src/stories/Button.tsx: -------------------------------------------------------------------------------- 1 | // import React from "react"; 2 | import "./button.css"; 3 | import "../App.css"; 4 | 5 | interface ButtonProps { 6 | /** 7 | * Is this the principal call to action on the page? 8 | */ 9 | primary?: boolean; 10 | /** 11 | * What background color to use 12 | */ 13 | backgroundColor?: string; 14 | /** 15 | * How large should the button be? 16 | */ 17 | size?: "small" | "medium" | "large"; 18 | /** 19 | * Button contents 20 | */ 21 | label: string; 22 | /** 23 | * Optional click handler 24 | */ 25 | onClick?: () => void; 26 | /** 27 | * Optional disabled 28 | */ 29 | disabled?: boolean; 30 | } 31 | 32 | /** 33 | * Primary UI component for user interaction 34 | */ 35 | export const Button = ({ 36 | primary = false, 37 | disabled = true, 38 | size = "medium", 39 | backgroundColor, 40 | label, 41 | ...props 42 | }: ButtonProps) => { 43 | const mode = primary 44 | ? "storybook-button--primary" 45 | : "storybook-button--secondary"; 46 | return ( 47 | 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /src/stories/Header.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Header } from './Header'; 4 | 5 | const meta = { 6 | title: 'Example/Header', 7 | component: Header, 8 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs 9 | tags: ['autodocs'], 10 | parameters: { 11 | // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout 12 | layout: 'fullscreen', 13 | }, 14 | } satisfies Meta; 15 | 16 | export default meta; 17 | type Story = StoryObj; 18 | 19 | export const LoggedIn: Story = { 20 | args: { 21 | user: { 22 | name: 'Jane Doe', 23 | }, 24 | }, 25 | }; 26 | 27 | export const LoggedOut: Story = {}; 28 | -------------------------------------------------------------------------------- /src/stories/Header.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | 3 | import { Button } from "./Button"; 4 | import "./header.css"; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | interface HeaderProps { 11 | user?: User; 12 | onLogin: () => void; 13 | onLogout: () => void; 14 | onCreateAccount: () => void; 15 | } 16 | 17 | export const Header = ({ 18 | user, 19 | onLogin, 20 | onLogout, 21 | onCreateAccount, 22 | }: HeaderProps) => ( 23 |
24 |
25 |
26 | 32 | 33 | 37 | 41 | 45 | 46 | 47 |

Acme

48 |
49 |
50 | {user ? ( 51 | <> 52 | 53 | Welcome, {user.name}! 54 | 55 |
69 |
70 |
71 | ); 72 | -------------------------------------------------------------------------------- /src/stories/Page.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import { within, userEvent } from '@storybook/testing-library'; 3 | 4 | import { Page } from './Page'; 5 | 6 | const meta = { 7 | title: 'Example/Page', 8 | component: Page, 9 | parameters: { 10 | // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'fullscreen', 12 | }, 13 | } satisfies Meta; 14 | 15 | export default meta; 16 | type Story = StoryObj; 17 | 18 | export const LoggedOut: Story = {}; 19 | 20 | // More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing 21 | export const LoggedIn: Story = { 22 | play: async ({ canvasElement }) => { 23 | const canvas = within(canvasElement); 24 | const loginButton = await canvas.getByRole('button', { 25 | name: /Log in/i, 26 | }); 27 | await userEvent.click(loginButton); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/stories/Page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Header } from './Header'; 4 | import './page.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | export const Page: React.FC = () => { 11 | const [user, setUser] = React.useState(); 12 | 13 | return ( 14 |
15 |
setUser({ name: 'Jane Doe' })} 18 | onLogout={() => setUser(undefined)} 19 | onCreateAccount={() => setUser({ name: 'Jane Doe' })} 20 | /> 21 | 22 |
23 |

Pages in Storybook

24 |

25 | We recommend building UIs with a{' '} 26 | 27 | component-driven 28 | {' '} 29 | process starting with atomic components and ending with pages. 30 |

31 |

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

36 |
    37 |
  • 38 | Use a higher-level connected component. Storybook helps you compose such data from the 39 | "args" of child component stories 40 |
  • 41 |
  • 42 | Assemble data in the page component from your services. You can mock these services out 43 | using Storybook. 44 |
  • 45 |
46 |

47 | Get a guided tutorial on component-driven development at{' '} 48 | 49 | Storybook tutorials 50 | 51 | . Read more in the{' '} 52 | 53 | docs 54 | 55 | . 56 |

57 |
58 | Tip Adjust the width of the canvas with the{' '} 59 | 60 | 61 | 66 | 67 | 68 | Viewports addon in the toolbar 69 |
70 |
71 |
72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /src/stories/assets/accessibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/accessibility.png -------------------------------------------------------------------------------- /src/stories/assets/accessibility.svg: -------------------------------------------------------------------------------- 1 | 2 | Accessibility 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/stories/assets/addon-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/addon-library.png -------------------------------------------------------------------------------- /src/stories/assets/assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/assets.png -------------------------------------------------------------------------------- /src/stories/assets/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/context.png -------------------------------------------------------------------------------- /src/stories/assets/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/stories/assets/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/docs.png -------------------------------------------------------------------------------- /src/stories/assets/figma-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/figma-plugin.png -------------------------------------------------------------------------------- /src/stories/assets/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/stories/assets/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/share.png -------------------------------------------------------------------------------- /src/stories/assets/styling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/styling.png -------------------------------------------------------------------------------- /src/stories/assets/testing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/testing.png -------------------------------------------------------------------------------- /src/stories/assets/theming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/src/stories/assets/theming.png -------------------------------------------------------------------------------- /src/stories/assets/tutorials.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/stories/assets/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/stories/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 4px; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | 11 | 12 | .storybook-button--primary { 13 | color: rgb(255, 255, 255); 14 | background-color: var(--primary); 15 | } 16 | .storybook-button--secondary { 17 | color: #333; 18 | background-color: var(--secondary); 19 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 20 | } 21 | 22 | .storybook-button:disabled{ 23 | background-color: var(--mono-100); 24 | color: var(--mono-200); 25 | } 26 | .storybook-button--small { 27 | font-size: 12px; 28 | padding: 10px 16px; 29 | } 30 | .storybook-button--medium { 31 | font-size: 14px; 32 | padding: 11px 20px; 33 | } 34 | .storybook-button--large { 35 | font-size: 16px; 36 | padding: 12px 24px; 37 | } 38 | -------------------------------------------------------------------------------- /src/stories/header.css: -------------------------------------------------------------------------------- 1 | .storybook-header { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | .storybook-header svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | .storybook-header h1 { 16 | font-weight: 700; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | .storybook-header button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .storybook-header .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /src/stories/page.css: -------------------------------------------------------------------------------- 1 | .storybook-page { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | .storybook-page h2 { 12 | font-weight: 700; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | .storybook-page p { 21 | margin: 1em 0; 22 | } 23 | 24 | .storybook-page a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | .storybook-page ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | .storybook-page li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | .storybook-page .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | .storybook-page .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | .storybook-page .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | .storybook-page .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /src/theme.ts: -------------------------------------------------------------------------------- 1 | export const theme = { 2 | colors: { 3 | primary: "#1d2745", 4 | secondary: "#1de5d4", 5 | tertiary: "#f52c50", 6 | white: "#ffffff", 7 | mono1: "#f1f1f1", 8 | mono2: "#bebebe", 9 | mono3: "#d6d7d9", 10 | error: "#d01e1e", 11 | }, 12 | fontSize: "16px", 13 | }; 14 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /storybook-static/assets/Button-8daf7d8a.js: -------------------------------------------------------------------------------- 1 | import{j as i}from"./jsx-runtime-ffb262ed.js";const t=({primary:e=!1,disabled:o=!0,size:a="medium",backgroundColor:n,label:r,...l})=>{const u=e?"storybook-button--primary":"storybook-button--secondary";return i.jsx("button",{type:"button",className:["storybook-button",`storybook-button--${a}`,u].join(" "),style:{backgroundColor:n},disabled:o,...l,children:r})};try{t.displayName="Button",t.__docgenInfo={description:"Primary UI component for user interaction",displayName:"Button",props:{primary:{defaultValue:{value:"false"},description:"Is this the principal call to action on the page?",name:"primary",required:!1,type:{name:"boolean"}},backgroundColor:{defaultValue:null,description:"What background color to use",name:"backgroundColor",required:!1,type:{name:"string"}},size:{defaultValue:{value:"medium"},description:"How large should the button be?",name:"size",required:!1,type:{name:"enum",value:[{value:'"small"'},{value:'"medium"'},{value:'"large"'}]}},label:{defaultValue:null,description:"Button contents",name:"label",required:!0,type:{name:"string"}},onClick:{defaultValue:null,description:"Optional click handler",name:"onClick",required:!1,type:{name:"(() => void)"}},disabled:{defaultValue:{value:"true"},description:"Optional disabled",name:"disabled",required:!1,type:{name:"boolean"}}}}}catch{}export{t as B}; 2 | //# sourceMappingURL=Button-8daf7d8a.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/Button-8daf7d8a.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Button-8daf7d8a.js","sources":["../../src/stories/Button.tsx"],"sourcesContent":["import React from \"react\";\nimport \"./button.css\";\nimport \"../App.css\";\n\ninterface ButtonProps {\n /**\n * Is this the principal call to action on the page?\n */\n primary?: boolean;\n /**\n * What background color to use\n */\n backgroundColor?: string;\n /**\n * How large should the button be?\n */\n size?: \"small\" | \"medium\" | \"large\";\n /**\n * Button contents\n */\n label: string;\n /**\n * Optional click handler\n */\n onClick?: () => void;\n /**\n * Optional disabled\n */\n disabled?: boolean;\n}\n\n/**\n * Primary UI component for user interaction\n */\nexport const Button = ({\n primary = false,\n disabled = true,\n size = \"medium\",\n backgroundColor,\n label,\n ...props\n}: ButtonProps) => {\n const mode = primary\n ? \"storybook-button--primary\"\n : \"storybook-button--secondary\";\n return (\n \n {label}\n \n );\n};\n"],"names":["Button","primary","disabled","size","backgroundColor","label","props","mode","jsx"],"mappings":"8CAkCO,MAAAA,EAAA,CAAA,CAAgB,QAAAC,EAAA,GACX,SAAAC,EAAA,GACC,KAAAC,EAAA,SACJ,gBAAAC,EACP,MAAAC,EACA,GAAAC,CAEF,IAAA,CACE,MAAAC,EAAAN,EAAA,4BAAA,8BAGA,OAAAO,EAAA,IACE,SAAC,CAAA,KAAA,SACM,UAAA,CAAA,mBAAA,qBAAAL,CAAA,GAAAI,CAAA,EAAA,KAC8D,GACjE,EACF,MAAA,CAAA,gBAAAH,CAAA,EACyB,SAAAF,EACzB,GAAAI,EACI,SAAAD,CAEH,CAAA,CAGP"} -------------------------------------------------------------------------------- /storybook-static/assets/Button-9a01ec51.css: -------------------------------------------------------------------------------- 1 | .storybook-button{font-family:Nunito Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:700;border:0;border-radius:4px;cursor:pointer;display:inline-block;line-height:1}.storybook-button--primary{color:#fff;background-color:var(--primary)}.storybook-button--secondary{color:#333;background-color:var(--secondary);box-shadow:#00000026 0 0 0 1px inset}.storybook-button:disabled{background-color:var(--mono-100);color:var(--mono-200)}.storybook-button--small{font-size:12px;padding:10px 16px}.storybook-button--medium{font-size:14px;padding:11px 20px}.storybook-button--large{font-size:16px;padding:12px 24px}:root{--primary: #1d2745;--secondary: #1de5d4;--tertiary: #f52c50;--white: #ffffff;--mono-100: #f1f1f1;--mono-200: #bebebe;--mono-300: #d6d7d9;--error: #d01e1e}#root{width:100vw;height:100vh;margin:0 auto} 2 | -------------------------------------------------------------------------------- /storybook-static/assets/Button.stories-2e1c076d.js: -------------------------------------------------------------------------------- 1 | import{B as b}from"./Button-8daf7d8a.js";import"./jsx-runtime-ffb262ed.js";import"./index-76fb7be0.js";import"./_commonjsHelpers-de833af9.js";const f={title:"Example/Button",component:b,parameters:{layout:"centered"},tags:["autodocs"],argTypes:{backgroundColor:{control:"color"}}},r={args:{primary:!0,label:"Button"}},a={args:{label:"Button"}},e={args:{size:"large",label:"Button"}},o={args:{size:"small",label:"Button"}};var s,t,n;r.parameters={...r.parameters,docs:{...(s=r.parameters)==null?void 0:s.docs,source:{originalSource:`{ 2 | args: { 3 | primary: true, 4 | label: 'Button' 5 | } 6 | }`,...(n=(t=r.parameters)==null?void 0:t.docs)==null?void 0:n.source}}};var c,l,m;a.parameters={...a.parameters,docs:{...(c=a.parameters)==null?void 0:c.docs,source:{originalSource:`{ 7 | args: { 8 | label: 'Button' 9 | } 10 | }`,...(m=(l=a.parameters)==null?void 0:l.docs)==null?void 0:m.source}}};var p,u,i;e.parameters={...e.parameters,docs:{...(p=e.parameters)==null?void 0:p.docs,source:{originalSource:`{ 11 | args: { 12 | size: 'large', 13 | label: 'Button' 14 | } 15 | }`,...(i=(u=e.parameters)==null?void 0:u.docs)==null?void 0:i.source}}};var d,g,B;o.parameters={...o.parameters,docs:{...(d=o.parameters)==null?void 0:d.docs,source:{originalSource:`{ 16 | args: { 17 | size: 'small', 18 | label: 'Button' 19 | } 20 | }`,...(B=(g=o.parameters)==null?void 0:g.docs)==null?void 0:B.source}}};const E=["Primary","Secondary","Large","Small"];export{e as Large,r as Primary,a as Secondary,o as Small,E as __namedExportsOrder,f as default}; 21 | //# sourceMappingURL=Button.stories-2e1c076d.js.map 22 | -------------------------------------------------------------------------------- /storybook-static/assets/Button.stories-2e1c076d.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Button.stories-2e1c076d.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"} -------------------------------------------------------------------------------- /storybook-static/assets/DocsRenderer-NNNQARDV-cb22ad80.js: -------------------------------------------------------------------------------- 1 | import{_ as p}from"./iframe-a3b0aa37.js";import{R as e,r as c}from"./index-76fb7be0.js";import{r as l,u}from"./react-18-063a39db.js";import{C as h,A as E,H as d,D as x}from"./index-228e512d.js";import"../sb-preview/runtime.js";import"./_commonjsHelpers-de833af9.js";import"./index-932496f1.js";import"./index-d37d4223.js";import"./index-356e4a49.js";var _={code:h,a:E,...d},D=class extends c.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){let{showException:r}=this.props;r(t)}render(){let{hasError:t}=this.state,{children:r}=this.props;return t?null:e.createElement(e.Fragment,null,r)}},F=class{constructor(){this.render=async(t,r,o)=>{let n={..._,...r==null?void 0:r.components},s=x;return new Promise((m,a)=>{p(()=>import("./index-9eea8125.js"),["./index-9eea8125.js","./index-a1cf9e47.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js"],import.meta.url).then(({MDXProvider:i})=>l(e.createElement(D,{showException:a,key:Math.random()},e.createElement(i,{components:n},e.createElement(s,{context:t,docsParameter:r}))),o)).then(()=>m())})},this.unmount=t=>{u(t)}}};export{F as DocsRenderer,_ as defaultComponents}; 2 | //# sourceMappingURL=DocsRenderer-NNNQARDV-cb22ad80.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/DocsRenderer-NNNQARDV-cb22ad80.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"mappings":"8VAIG,IAACA,EAAkB,CAAC,KAAKC,EAAgB,EAAEC,EAAU,GAAGC,CAAU,EAAEC,EAAc,cAAcC,EAAS,UAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,MAAM,CAAC,SAAS,EAAE,CAAE,CAAC,OAAO,0BAA0B,CAAC,MAAO,CAAC,SAAS,EAAE,CAAC,CAAC,kBAAkBC,EAAI,CAAC,GAAG,CAAC,cAAAC,CAAa,EAAE,KAAK,MAAMA,EAAcD,CAAG,CAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAAE,CAAQ,EAAE,KAAK,MAAM,CAAC,SAAAC,CAAQ,EAAE,KAAK,MAAM,OAAOD,EAAS,KAAKE,EAAM,cAAcA,EAAM,SAAS,KAAKD,CAAQ,CAAC,CAAC,EAAEE,EAAa,KAAK,CAAC,aAAa,CAAC,KAAK,OAAO,MAAMC,EAAQC,EAAcC,IAAU,CAAC,IAAIC,EAAW,CAAC,GAAGf,EAAkB,GAAGa,GAAA,YAAAA,EAAe,UAAU,EAAEG,EAAMC,EAAK,OAAO,IAAI,QAAQ,CAACC,EAAQC,IAAS,CAACC,EAAA,WAAO,qBAAe,wHAAE,KAAK,CAAC,CAAC,YAAAC,CAAW,IAAIC,EAAcZ,EAAM,cAAcN,EAAc,CAAC,cAAce,EAAO,IAAI,KAAK,OAAQ,GAAET,EAAM,cAAcW,EAAY,CAAC,WAAAN,CAAU,EAAEL,EAAM,cAAcM,EAAM,CAAC,QAAAJ,EAAQ,cAAAC,CAAa,CAAC,CAAC,CAAC,EAAEC,CAAO,CAAC,EAAE,KAAK,IAAII,EAAO,CAAE,CAAE,CAAC,CAAC,EAAE,KAAK,QAAQJ,GAAS,CAACS,EAAeT,CAAO,CAAE,CAAE,CAAC","names":["defaultComponents","CodeOrSourceMdx","AnchorMdx","HeadersMdx","ErrorBoundary","Component","err","showException","hasError","children","React","DocsRenderer","context","docsParameter","element","components","TDocs","Docs","resolve","reject","__vitePreload","MDXProvider","renderElement","unmountElement"],"sources":["../../node_modules/@storybook/addon-docs/dist/chunk-HLWAVYOI.mjs"],"sourcesContent":["import React, { Component } from 'react';\nimport { renderElement, unmountElement } from '@storybook/react-dom-shim';\nimport { CodeOrSourceMdx, AnchorMdx, HeadersMdx, Docs } from '@storybook/blocks';\n\nvar defaultComponents={code:CodeOrSourceMdx,a:AnchorMdx,...HeadersMdx},ErrorBoundary=class extends Component{constructor(){super(...arguments);this.state={hasError:!1};}static getDerivedStateFromError(){return {hasError:!0}}componentDidCatch(err){let{showException}=this.props;showException(err);}render(){let{hasError}=this.state,{children}=this.props;return hasError?null:React.createElement(React.Fragment,null,children)}},DocsRenderer=class{constructor(){this.render=async(context,docsParameter,element)=>{let components={...defaultComponents,...docsParameter?.components},TDocs=Docs;return new Promise((resolve,reject)=>{import('@mdx-js/react').then(({MDXProvider})=>renderElement(React.createElement(ErrorBoundary,{showException:reject,key:Math.random()},React.createElement(MDXProvider,{components},React.createElement(TDocs,{context,docsParameter}))),element)).then(()=>resolve());})},this.unmount=element=>{unmountElement(element);};}};\n\nexport { DocsRenderer, defaultComponents };\n"],"file":"assets/DocsRenderer-NNNQARDV-cb22ad80.js"} -------------------------------------------------------------------------------- /storybook-static/assets/Header-8f8703e8.js: -------------------------------------------------------------------------------- 1 | import{j as e}from"./jsx-runtime-ffb262ed.js";import{B as n}from"./Button-8daf7d8a.js";const r=({user:l,onLogin:s,onLogout:i,onCreateAccount:a})=>e.jsx("header",{children:e.jsxs("div",{className:"storybook-header",children:[e.jsxs("div",{children:[e.jsx("svg",{width:"32",height:"32",viewBox:"0 0 32 32",xmlns:"http://www.w3.org/2000/svg",children:e.jsxs("g",{fill:"none",fillRule:"evenodd",children:[e.jsx("path",{d:"M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z",fill:"#FFF"}),e.jsx("path",{d:"M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z",fill:"#555AB9"}),e.jsx("path",{d:"M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z",fill:"#91BAF8"})]})}),e.jsx("h1",{children:"Acme"})]}),e.jsx("div",{children:l?e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"welcome",children:["Welcome, ",e.jsx("b",{children:l.name}),"!"]}),e.jsx(n,{size:"small",onClick:i,label:"Log out"})]}):e.jsxs(e.Fragment,{children:[e.jsx(n,{size:"small",onClick:s,label:"Log in"}),e.jsx(n,{primary:!0,size:"small",onClick:a,label:"Sign up"})]})})]})});try{r.displayName="Header",r.__docgenInfo={description:"",displayName:"Header",props:{user:{defaultValue:null,description:"",name:"user",required:!1,type:{name:"User"}},onLogin:{defaultValue:null,description:"",name:"onLogin",required:!0,type:{name:"() => void"}},onLogout:{defaultValue:null,description:"",name:"onLogout",required:!0,type:{name:"() => void"}},onCreateAccount:{defaultValue:null,description:"",name:"onCreateAccount",required:!0,type:{name:"() => void"}}}}}catch{}export{r as H}; 2 | //# sourceMappingURL=Header-8f8703e8.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/Header-8f8703e8.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Header-8f8703e8.js","sources":["../../src/stories/Header.tsx"],"sourcesContent":["import React from 'react';\n\nimport { Button } from './Button';\nimport './header.css';\n\ntype User = {\n name: string;\n};\n\ninterface HeaderProps {\n user?: User;\n onLogin: () => void;\n onLogout: () => void;\n onCreateAccount: () => void;\n}\n\nexport const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (\n
\n
\n
\n \n \n \n \n \n \n \n

Acme

\n
\n
\n {user ? (\n <>\n \n Welcome, {user.name}!\n \n
\n
\n
\n);\n"],"names":["Header","user","onLogin","onLogout","onCreateAccount","jsx","jsxs","Fragment","Button"],"mappings":"uFAgBO,MAAAA,EAAA,CAAA,CAAA,KAAAC,EAAA,QAAAC,EAAA,SAAAC,EAAA,gBAAAC,CAAA,IAAAC,EAAA,IAAA,SAAA,CAAA,SAAAC,EAAAA,KAAA,MAAA,CAAA,UAAA,mBAAA,SAAA,CAGDA,OAAA,MAAA,CAAA,SAAA,CACED,MAAA,MAAA,CAAA,MAAA,KAAA,OAAA,KAAA,QAAA,YAAA,MAAA,6BAAA,SAAAC,EAAA,KAAA,IAAA,CAAA,KAAA,OAAA,SAAA,UAAA,SAAA,CAEID,EAAA,IAAA,OAAC,CAAA,EAAA,oFACG,KAAA,MACG,CAAA,EACPA,EAAA,IACA,OAAC,CAAA,EAAA,mEACG,KAAA,SACG,CAAA,EACPA,EAAA,IACA,OAAC,CAAA,EAAA,iEACG,KAAA,SACG,CAAA,CACP,CAAA,CAAA,CAAA,CAAA,EAEJA,EAAAA,IAAA,KAAA,CAAA,SAAA,MAAA,CAAA,CACQ,EAAA,EACVA,EAAA,IAAA,MAAA,CAAA,SAAAJ,EAAAK,OAAAC,EAAAA,SAAA,CAAA,SAAA,CAIMD,EAAAA,KAAA,OAAA,CAAA,UAAA,UAAA,SAAA,CAA0B,YAAAD,EAAAA,IAAA,IAAA,CAAA,SAAAJ,EAAA,IAAA,CAAA,EACD,GAAI,EAAA,EAC7BI,MAAAG,EAAA,CAAA,KAAA,QAAA,QAAAL,EAAA,MAAA,UAAA,CACwD,CAAA,CAAA,EAAAG,EAAAA,KAAAC,EAAA,SAAA,CAAA,SAAA,CAIxDF,MAAAG,EAAA,CAAA,KAAA,QAAA,QAAAN,EAAA,MAAA,SAAA,EAAsDG,EAAAA,IAAAG,EAAA,CAAA,QAAA,GAAA,KAAA,QAAA,QAAAJ,EAAA,MAAA,SAAA,CAAA,CACiB,CAAA,CAAA,CAAA,CAAA,CAG7E,CAAA,CAAA,CAAA,CAAA"} -------------------------------------------------------------------------------- /storybook-static/assets/Header-a6911580.css: -------------------------------------------------------------------------------- 1 | .storybook-header{font-family:Nunito Sans,Helvetica Neue,Helvetica,Arial,sans-serif;border-bottom:1px solid rgba(0,0,0,.1);padding:15px 20px;display:flex;align-items:center;justify-content:space-between}.storybook-header svg{display:inline-block;vertical-align:top}.storybook-header h1{font-weight:700;font-size:20px;line-height:1;margin:6px 0 6px 10px;display:inline-block;vertical-align:top}.storybook-header button+button{margin-left:10px}.storybook-header .welcome{color:#333;font-size:14px;margin-right:10px} 2 | -------------------------------------------------------------------------------- /storybook-static/assets/Header.stories-dfd929b9.js: -------------------------------------------------------------------------------- 1 | import{H as c}from"./Header-8f8703e8.js";import"./jsx-runtime-ffb262ed.js";import"./index-76fb7be0.js";import"./_commonjsHelpers-de833af9.js";import"./Button-8daf7d8a.js";const l={title:"Example/Header",component:c,tags:["autodocs"],parameters:{layout:"fullscreen"}},e={args:{user:{name:"Jane Doe"}}},r={};var a,o,s;e.parameters={...e.parameters,docs:{...(a=e.parameters)==null?void 0:a.docs,source:{originalSource:`{ 2 | args: { 3 | user: { 4 | name: 'Jane Doe' 5 | } 6 | } 7 | }`,...(s=(o=e.parameters)==null?void 0:o.docs)==null?void 0:s.source}}};var t,n,m;r.parameters={...r.parameters,docs:{...(t=r.parameters)==null?void 0:t.docs,source:{originalSource:"{}",...(m=(n=r.parameters)==null?void 0:n.docs)==null?void 0:m.source}}};const L=["LoggedIn","LoggedOut"];export{e as LoggedIn,r as LoggedOut,L as __namedExportsOrder,l as default}; 8 | //# sourceMappingURL=Header.stories-dfd929b9.js.map 9 | -------------------------------------------------------------------------------- /storybook-static/assets/Header.stories-dfd929b9.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Header.stories-dfd929b9.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"} -------------------------------------------------------------------------------- /storybook-static/assets/Page.stories-ece1482a.css: -------------------------------------------------------------------------------- 1 | .storybook-page{font-family:Nunito Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;line-height:24px;padding:48px 20px;margin:0 auto;max-width:600px;color:#333}.storybook-page h2{font-weight:700;font-size:32px;line-height:1;margin:0 0 4px;display:inline-block;vertical-align:top}.storybook-page p{margin:1em 0}.storybook-page a{text-decoration:none;color:#1ea7fd}.storybook-page ul{padding-left:30px;margin:1em 0}.storybook-page li{margin-bottom:8px}.storybook-page .tip{display:inline-block;border-radius:1em;font-size:11px;line-height:12px;font-weight:700;background:#e7fdd8;color:#66bf3c;padding:4px 12px;margin-right:10px;vertical-align:top}.storybook-page .tip-wrapper{font-size:13px;line-height:20px;margin-top:40px;margin-bottom:40px}.storybook-page .tip-wrapper svg{display:inline-block;height:12px;width:12px;margin-right:4px;vertical-align:top;margin-top:3px}.storybook-page .tip-wrapper svg path{fill:#1ea7fd} 2 | -------------------------------------------------------------------------------- /storybook-static/assets/WithTooltip-4HIR6TLV-bd1d30d6.js: -------------------------------------------------------------------------------- 1 | import{W as e,W as h,a as l}from"./index-228e512d.js";import"./iframe-a3b0aa37.js";import"../sb-preview/runtime.js";import"./index-76fb7be0.js";import"./_commonjsHelpers-de833af9.js";import"./index-932496f1.js";import"./index-d37d4223.js";import"./index-356e4a49.js";export{e as WithToolTipState,h as WithTooltip,l as WithTooltipPure}; 2 | //# sourceMappingURL=WithTooltip-4HIR6TLV-bd1d30d6.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/WithTooltip-4HIR6TLV-bd1d30d6.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"WithTooltip-4HIR6TLV-bd1d30d6.js","sources":[],"sourcesContent":[],"names":[],"mappings":""} -------------------------------------------------------------------------------- /storybook-static/assets/_commonjsHelpers-de833af9.js: -------------------------------------------------------------------------------- 1 | var u=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function f(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function l(e){if(e.__esModule)return e;var r=e.default;if(typeof r=="function"){var t=function o(){return this instanceof o?Reflect.construct(r,arguments,this.constructor):r.apply(this,arguments)};t.prototype=r.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t}export{l as a,u as c,f as g}; 2 | //# sourceMappingURL=_commonjsHelpers-de833af9.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/_commonjsHelpers-de833af9.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"_commonjsHelpers-de833af9.js","sources":[],"sourcesContent":[],"names":[],"mappings":""} -------------------------------------------------------------------------------- /storybook-static/assets/accessibility-cd6d60f7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/accessibility-cd6d60f7.png -------------------------------------------------------------------------------- /storybook-static/assets/addon-library-bc7ba705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/addon-library-bc7ba705.png -------------------------------------------------------------------------------- /storybook-static/assets/context-c612d889.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/context-c612d889.png -------------------------------------------------------------------------------- /storybook-static/assets/discord-f7d1b78c.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /storybook-static/assets/docs-5b0c7100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/docs-5b0c7100.png -------------------------------------------------------------------------------- /storybook-static/assets/figma-plugin-b0a5ad2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/figma-plugin-b0a5ad2d.png -------------------------------------------------------------------------------- /storybook-static/assets/github-cdfc3270.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /storybook-static/assets/iframe-a3b0aa37.js: -------------------------------------------------------------------------------- 1 | import"../sb-preview/runtime.js";(function(){const _=document.createElement("link").relList;if(_&&_.supports&&_.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))c(t);new MutationObserver(t=>{for(const e of t)if(e.type==="childList")for(const o of e.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&c(o)}).observe(document,{childList:!0,subtree:!0});function s(t){const e={};return t.integrity&&(e.integrity=t.integrity),t.referrerPolicy&&(e.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?e.credentials="include":t.crossOrigin==="anonymous"?e.credentials="omit":e.credentials="same-origin",e}function c(t){if(t.ep)return;t.ep=!0;const e=s(t);fetch(t.href,e)}})();const E="modulepreload",d=function(i,_){return new URL(i,_).href},O={},r=function(_,s,c){if(!s||s.length===0)return _();const t=document.getElementsByTagName("link");return Promise.all(s.map(e=>{if(e=d(e,c),e in O)return;O[e]=!0;const o=e.endsWith(".css"),m=o?'[rel="stylesheet"]':"";if(!!c)for(let l=t.length-1;l>=0;l--){const a=t[l];if(a.href===e&&(!o||a.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${e}"]${m}`))return;const n=document.createElement("link");if(n.rel=o?"stylesheet":E,o||(n.as="script",n.crossOrigin=""),n.href=e,document.head.appendChild(n),o)return new Promise((l,a)=>{n.addEventListener("load",l),n.addEventListener("error",()=>a(new Error(`Unable to preload CSS for ${e}`)))})})).then(()=>_()).catch(e=>{const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=e,window.dispatchEvent(o),!o.defaultPrevented)throw e})},{createBrowserChannel:p}=__STORYBOOK_MODULE_CHANNELS__,{addons:f}=__STORYBOOK_MODULE_PREVIEW_API__,u=p({page:"preview"});f.setChannel(u);window.__STORYBOOK_ADDONS_CHANNEL__=u;window.CONFIG_TYPE==="DEVELOPMENT"&&(window.__STORYBOOK_SERVER_CHANNEL__=u);const R={"./src/stories/Configure.mdx":async()=>r(()=>import("./Configure-83e363c2.js"),["./Configure-83e363c2.js","./jsx-runtime-ffb262ed.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js","./index-228e512d.js","./index-932496f1.js","./index-d37d4223.js","./index-356e4a49.js","./index-a1cf9e47.js"],import.meta.url),"./src/stories/Page.stories.ts":async()=>r(()=>import("./Page.stories-08c0e118.js"),["./Page.stories-08c0e118.js","./_commonjsHelpers-de833af9.js","./index-356e4a49.js","./jsx-runtime-ffb262ed.js","./index-76fb7be0.js","./Header-8f8703e8.js","./Button-8daf7d8a.js","./Button-9a01ec51.css","./Header-a6911580.css","./Page.stories-ece1482a.css"],import.meta.url),"./src/stories/Header.stories.ts":async()=>r(()=>import("./Header.stories-dfd929b9.js"),["./Header.stories-dfd929b9.js","./Header-8f8703e8.js","./jsx-runtime-ffb262ed.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js","./Button-8daf7d8a.js","./Button-9a01ec51.css","./Header-a6911580.css"],import.meta.url),"./src/stories/Button.stories.ts":async()=>r(()=>import("./Button.stories-2e1c076d.js"),["./Button.stories-2e1c076d.js","./Button-8daf7d8a.js","./jsx-runtime-ffb262ed.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js","./Button-9a01ec51.css"],import.meta.url)};async function P(i){return R[i]()}const{composeConfigs:w,PreviewWeb:T,ClientApi:L}=__STORYBOOK_MODULE_PREVIEW_API__,I=async()=>{const i=await Promise.all([r(()=>import("./config-de709ab3.js"),["./config-de709ab3.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js","./react-18-063a39db.js","./index-932496f1.js","./index-356e4a49.js"],import.meta.url),r(()=>import("./preview-87eac49b.js"),["./preview-87eac49b.js","./index-d37d4223.js"],import.meta.url),r(()=>import("./preview-e11ff2f7.js"),[],import.meta.url),r(()=>import("./preview-bed967c6.js"),[],import.meta.url),r(()=>import("./preview-108c1c3c.js"),["./preview-108c1c3c.js","./index-356e4a49.js"],import.meta.url),r(()=>import("./preview-2059b184.js"),[],import.meta.url),r(()=>import("./preview-b8d6c68d.js"),["./preview-b8d6c68d.js","./index-356e4a49.js"],import.meta.url),r(()=>import("./preview-b3c37142.js"),[],import.meta.url),r(()=>import("./preview-6751e51d.js"),["./preview-6751e51d.js","./_commonjsHelpers-de833af9.js"],import.meta.url),r(()=>import("./preview-ba2273f4.js"),[],import.meta.url)]);return w(i)};window.__STORYBOOK_PREVIEW__=window.__STORYBOOK_PREVIEW__||new T;window.__STORYBOOK_STORY_STORE__=window.__STORYBOOK_STORY_STORE__||window.__STORYBOOK_PREVIEW__.storyStore;window.__STORYBOOK_CLIENT_API__=window.__STORYBOOK_CLIENT_API__||new L({storyStore:window.__STORYBOOK_PREVIEW__.storyStore});window.__STORYBOOK_PREVIEW__.initialize({importFn:P,getProjectAnnotations:I});export{r as _}; 2 | //# sourceMappingURL=iframe-a3b0aa37.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/iframe-a3b0aa37.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "olDAAA,sBAAAA,CAAA,gCACI,QAAAC,CAAA,mCAEMC,EAAUF,EAAqB,CAAE,KAAM,SAAW,GACxDC,EAAO,WAAWC,CAAO,EACzB,OAAO,6BAA+BA,EAElC,OAAO,cAAgB,gBACzB,OAAO,6BAA+BA,GCR5C,MAAMC,EAAY,CACV,8BAA+B,SAAWC,EAAA,IAAC,OAAO,yBAAiF,gPACzI,gCAAiC,SAAYA,EAAA,WAAO,4BAAmF,uRACvI,kCAAmC,SAAYA,EAAA,WAAO,8BAAqF,qOAC3I,kCAAmC,SAAYA,EAAA,WAAO,8BAAqF,EAAC,mLAC9I,EAEW,eAAeC,EAASC,EAAM,CACjC,OAAOH,EAAUG,CAAI,GAC7B,CCTA,qBAAAC,EAAA,WAAAC,EAAA,UAAAC,CAAA,mCAKQC,EAAwB,SAAY,CACxC,MAAMC,EAAU,MAAM,QAAQ,IAAI,CAACP,EAAA,WAAO,sBAA0B,wKACxEA,EAAA,WAAO,uBAAgC,mEACvCA,EAAA,WAAO,uBAA0C,sBACjDA,EAAA,WAAO,uBAA6C,sBACpDA,EAAA,WAAO,uBAAiD,mEACxDA,EAAA,WAAO,uBAA6C,sBACpDA,EAAA,WAAO,uBAA6C,mEACpDA,EAAA,WAAO,uBAA+C,sBACtDA,EAAA,WAAO,uBAAuC,8EAC9CA,EAAA,WAAO,uBAAwB,EAAC,oBAAC,EAC7B,OAAOG,EAAeI,CAAO,CAC9B,EAEC,OAAO,sBAAwB,OAAO,uBAAyB,IAAIH,EAEnE,OAAO,0BAA4B,OAAO,2BAA6B,OAAO,sBAAsB,WACpG,OAAO,yBAA2B,OAAO,0BAA4B,IAAIC,EAAU,CAAE,WAAY,OAAO,sBAAsB,UAAY,GAC1I,OAAO,sBAAsB,WAAW,CAAE,SAAAJ,EAAU,sBAAAK,CAAqB,CAAE", 4 | "names": [ 5 | "createBrowserChannel", 6 | "addons", 7 | "channel", 8 | "importers", 9 | "__vitePreload", 10 | "importFn", 11 | "path", 12 | "composeConfigs", 13 | "PreviewWeb", 14 | "ClientApi", 15 | "getProjectAnnotations", 16 | "configs" 17 | ], 18 | "sources": [ 19 | "../../../../../../virtual:/@storybook/builder-vite/setup-addons.js", 20 | "../../../../../../virtual:/@storybook/builder-vite/storybook-stories.js", 21 | "../../../../../../virtual:/@storybook/builder-vite/vite-app.js" 22 | ], 23 | "sourcesContent": [ 24 | "import { createBrowserChannel } from '@storybook/channels';\n import { addons } from '@storybook/preview-api';\n\n const channel = createBrowserChannel({ page: 'preview' });\n addons.setChannel(channel);\n window.__STORYBOOK_ADDONS_CHANNEL__ = channel;\n \n if (window.CONFIG_TYPE === 'DEVELOPMENT'){\n window.__STORYBOOK_SERVER_CHANNEL__ = channel;\n }", 25 | "const importers = {\n './src/stories/Configure.mdx': async () => import('/@fs//Users/jasonkang/personal/server-frontend-test/src/stories/Configure.mdx'),\n './src/stories/Page.stories.ts': async () => import('/@fs//Users/jasonkang/personal/server-frontend-test/src/stories/Page.stories.ts'),\n './src/stories/Header.stories.ts': async () => import('/@fs//Users/jasonkang/personal/server-frontend-test/src/stories/Header.stories.ts'),\n './src/stories/Button.stories.ts': async () => import('/@fs//Users/jasonkang/personal/server-frontend-test/src/stories/Button.stories.ts')\n };\n\n export async function importFn(path) {\n return importers[path]();\n }", 26 | "import { composeConfigs, PreviewWeb, ClientApi } from '@storybook/preview-api';\n import '/virtual:/@storybook/builder-vite/setup-addons.js';\n import { importFn } from '/virtual:/@storybook/builder-vite/storybook-stories.js';\n \n \n const getProjectAnnotations = async () => {\n const configs = await Promise.all([import('@storybook/react/preview'),\nimport('@storybook/addon-links/preview'),\nimport('@storybook/addon-essentials/docs/preview'),\nimport('@storybook/addon-essentials/actions/preview'),\nimport('@storybook/addon-essentials/backgrounds/preview'),\nimport('@storybook/addon-essentials/measure/preview'),\nimport('@storybook/addon-essentials/outline/preview'),\nimport('@storybook/addon-essentials/highlight/preview'),\nimport('@storybook/addon-interactions/preview'),\nimport('/.storybook/preview.ts')])\n return composeConfigs(configs);\n }\n\n window.__STORYBOOK_PREVIEW__ = window.__STORYBOOK_PREVIEW__ || new PreviewWeb();\n \n window.__STORYBOOK_STORY_STORE__ = window.__STORYBOOK_STORY_STORE__ || window.__STORYBOOK_PREVIEW__.storyStore;\n window.__STORYBOOK_CLIENT_API__ = window.__STORYBOOK_CLIENT_API__ || new ClientApi({ storyStore: window.__STORYBOOK_PREVIEW__.storyStore });\n window.__STORYBOOK_PREVIEW__.initialize({ importFn, getProjectAnnotations });\n \n if (import.meta.hot) {\n import.meta.hot.accept('/virtual:/@storybook/builder-vite/storybook-stories.js', (newModule) => {\n // importFn has changed so we need to patch the new one in\n window.__STORYBOOK_PREVIEW__.onStoriesChanged({ importFn: newModule.importFn });\n });\n\n import.meta.hot.accept([\"@storybook/react/preview\",\"@storybook/addon-links/preview\",\"@storybook/addon-essentials/docs/preview\",\"@storybook/addon-essentials/actions/preview\",\"@storybook/addon-essentials/backgrounds/preview\",\"@storybook/addon-essentials/measure/preview\",\"@storybook/addon-essentials/outline/preview\",\"@storybook/addon-essentials/highlight/preview\",\"@storybook/addon-interactions/preview\",\"/.storybook/preview.ts\"], () => {\n \n const getProjectAnnotations = async () => {\n const configs = await Promise.all([import('@storybook/react/preview'),\nimport('@storybook/addon-links/preview'),\nimport('@storybook/addon-essentials/docs/preview'),\nimport('@storybook/addon-essentials/actions/preview'),\nimport('@storybook/addon-essentials/backgrounds/preview'),\nimport('@storybook/addon-essentials/measure/preview'),\nimport('@storybook/addon-essentials/outline/preview'),\nimport('@storybook/addon-essentials/highlight/preview'),\nimport('@storybook/addon-interactions/preview'),\nimport('/.storybook/preview.ts')])\n return composeConfigs(configs);\n }\n // getProjectAnnotations has changed so we need to patch the new one in\n window.__STORYBOOK_PREVIEW__.onGetProjectAnnotationsChanged({ getProjectAnnotations });\n });\n };" 27 | ], 28 | "file": "assets/iframe-a3b0aa37.js" 29 | } 30 | -------------------------------------------------------------------------------- /storybook-static/assets/index-356e4a49.js: -------------------------------------------------------------------------------- 1 | function l(o){for(var f=[],i=1;itypeof t=="function"?t(e):{...e,...t},[e,t])}const i={};function f({components:t,children:e,disableParentContext:r}){let n;return r?n=typeof t=="function"?t({}):t||i:n=a(t),o.createElement(u.Provider,{value:n},e)}export{u as M,f as a,a as u,c as w}; 2 | //# sourceMappingURL=index-a1cf9e47.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/index-a1cf9e47.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index-a1cf9e47.js","sources":["../../node_modules/@mdx-js/react/lib/index.js"],"sourcesContent":["/**\n * @typedef {import('react').ReactNode} ReactNode\n * @typedef {import('mdx/types.js').MDXComponents} Components\n *\n * @typedef Props\n * Configuration.\n * @property {Components | MergeComponents | null | undefined} [components]\n * Mapping of names for JSX components to React components.\n * @property {boolean | null | undefined} [disableParentContext=false]\n * Turn off outer component context.\n * @property {ReactNode | null | undefined} [children]\n * Children.\n *\n * @callback MergeComponents\n * Custom merge function.\n * @param {Components} currentComponents\n * Current components from the context.\n * @returns {Components}\n * Merged components.\n */\n\nimport React from 'react'\n\n/**\n * @type {import('react').Context}\n * @deprecated\n * This export is marked as a legacy feature.\n * That means it’s no longer recommended for use as it might be removed\n * in a future major release.\n *\n * Please use `useMDXComponents` to get context based components and\n * `MDXProvider` to set context based components instead.\n */\nexport const MDXContext = React.createContext({})\n\n/**\n * @param {import('react').ComponentType} Component\n * @deprecated\n * This export is marked as a legacy feature.\n * That means it’s no longer recommended for use as it might be removed\n * in a future major release.\n *\n * Please use `useMDXComponents` to get context based components instead.\n */\nexport function withMDXComponents(Component) {\n return boundMDXComponent\n\n /**\n * @param {Record & {components?: Components | null | undefined}} props\n * @returns {JSX.Element}\n */\n function boundMDXComponent(props) {\n const allComponents = useMDXComponents(props.components)\n return React.createElement(Component, {...props, allComponents})\n }\n}\n\n/**\n * Get current components from the MDX Context.\n *\n * @param {Components | MergeComponents | null | undefined} [components]\n * Additional components to use or a function that takes the current\n * components and filters/merges/changes them.\n * @returns {Components}\n * Current components.\n */\nexport function useMDXComponents(components) {\n const contextComponents = React.useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return React.useMemo(() => {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n }, [contextComponents, components])\n}\n\n/** @type {Components} */\nconst emptyObject = {}\n\n/**\n * Provider for MDX context\n *\n * @param {Props} props\n * @returns {JSX.Element}\n */\nexport function MDXProvider({components, children, disableParentContext}) {\n /** @type {Components} */\n let allComponents\n\n if (disableParentContext) {\n allComponents =\n typeof components === 'function'\n ? components({})\n : components || emptyObject\n } else {\n allComponents = useMDXComponents(components)\n }\n\n return React.createElement(\n MDXContext.Provider,\n {value: allComponents},\n children\n )\n}\n"],"names":["MDXContext","React","withMDXComponents","Component","boundMDXComponent","props","allComponents","useMDXComponents","components","contextComponents","emptyObject","MDXProvider","children","disableParentContext"],"mappings":"wCAiCY,MAACA,EAAaC,EAAM,cAAc,EAAE,EAWzC,SAASC,EAAkBC,EAAW,CAC3C,OAAOC,EAMP,SAASA,EAAkBC,EAAO,CAChC,MAAMC,EAAgBC,EAAiBF,EAAM,UAAU,EACvD,OAAOJ,EAAM,cAAcE,EAAW,CAAC,GAAGE,EAAO,cAAAC,CAAa,CAAC,CAChE,CACH,CAWO,SAASC,EAAiBC,EAAY,CAC3C,MAAMC,EAAoBR,EAAM,WAAWD,CAAU,EAGrD,OAAOC,EAAM,QAAQ,IAEf,OAAOO,GAAe,WACjBA,EAAWC,CAAiB,EAG9B,CAAC,GAAGA,EAAmB,GAAGD,CAAU,EAC1C,CAACC,EAAmBD,CAAU,CAAC,CACpC,CAGA,MAAME,EAAc,CAAE,EAQf,SAASC,EAAY,CAAC,WAAAH,EAAY,SAAAI,EAAU,qBAAAC,CAAoB,EAAG,CAExE,IAAIP,EAEJ,OAAIO,EACFP,EACE,OAAOE,GAAe,WAClBA,EAAW,CAAA,CAAE,EACbA,GAAcE,EAEpBJ,EAAgBC,EAAiBC,CAAU,EAGtCP,EAAM,cACXD,EAAW,SACX,CAAC,MAAOM,CAAa,EACrBM,CACD,CACH","x_google_ignoreList":[0]} -------------------------------------------------------------------------------- /storybook-static/assets/index-d37d4223.js: -------------------------------------------------------------------------------- 1 | var A=Object.create,g=Object.defineProperty,j=Object.getOwnPropertyDescriptor,h=Object.getOwnPropertyNames,m=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty,P=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports),d=(r,e,i,u)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of h(e))!x.call(r,a)&&a!==i&&g(r,a,{get:()=>e[a],enumerable:!(u=j(e,a))||u.enumerable});return r},S=(r,e,i)=>(i=r!=null?A(m(r)):{},d(e||!r||!r.__esModule?g(i,"default",{value:r,enumerable:!0}):i,r)),U=P(r=>{Object.defineProperty(r,"__esModule",{value:!0}),r.isEqual=function(){var e=Object.prototype.toString,i=Object.getPrototypeOf,u=Object.getOwnPropertySymbols?function(a){return Object.keys(a).concat(Object.getOwnPropertySymbols(a))}:Object.keys;return function(a,c){return function f(t,n,o){var y,p,l,s=e.call(t),w=e.call(n);if(t===n)return!0;if(t==null||n==null)return!1;if(o.indexOf(t)>-1&&o.indexOf(n)>-1)return!0;if(o.push(t,n),s!=w||(y=u(t),p=u(n),y.length!=p.length||y.some(function(O){return!f(t[O],n[O],o)})))return!1;switch(s.slice(8,-1)){case"Symbol":return t.valueOf()==n.valueOf();case"Date":case"Number":return+t==+n||+t!=+t&&+n!=+n;case"RegExp":case"Function":case"String":case"Boolean":return""+t==""+n;case"Set":case"Map":y=t.entries(),p=n.entries();do if(!f((l=y.next()).value,p.next().value,o))return!1;while(!l.done);return!0;case"ArrayBuffer":t=new Uint8Array(t),n=new Uint8Array(n);case"DataView":t=new Uint8Array(t.buffer),n=new Uint8Array(n.buffer);case"Float32Array":case"Float64Array":case"Int8Array":case"Int16Array":case"Int32Array":case"Uint8Array":case"Uint16Array":case"Uint32Array":case"Uint8ClampedArray":case"Arguments":case"Array":if(t.length!=n.length)return!1;for(l=0;lr.map(e=>typeof e<"u").filter(Boolean).length,q=(r,e)=>{let{exists:i,eq:u,neq:a,truthy:c}=r;if(v([i,u,a,c])>1)throw new Error(`Invalid conditional test ${JSON.stringify({exists:i,eq:u,neq:a})}`);if(typeof u<"u")return(0,b.isEqual)(e,u);if(typeof a<"u")return!(0,b.isEqual)(e,a);if(typeof i<"u"){let f=typeof e<"u";return i?f:!f}return typeof c>"u"||c?!!e:!e},E=(r,e,i)=>{if(!r.if)return!0;let{arg:u,global:a}=r.if;if(v([u,a])!==1)throw new Error(`Invalid conditional value ${JSON.stringify({arg:u,global:a})}`);let c=u?e[u]:i[a];return q(r.if,c)},I=r=>r.toLowerCase().replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi,"-").replace(/-+/g,"-").replace(/^-+/,"").replace(/-+$/,"");export{I as L,E as v}; 2 | //# sourceMappingURL=index-d37d4223.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/jsx-runtime-ffb262ed.js: -------------------------------------------------------------------------------- 1 | import{r as l}from"./index-76fb7be0.js";var f={exports:{}},n={};/** 2 | * @license React 3 | * react-jsx-runtime.production.min.js 4 | * 5 | * Copyright (c) Facebook, Inc. and its affiliates. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE file in the root directory of this source tree. 9 | */var u=l,m=Symbol.for("react.element"),x=Symbol.for("react.fragment"),y=Object.prototype.hasOwnProperty,a=u.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,v={key:!0,ref:!0,__self:!0,__source:!0};function i(t,r,p){var e,o={},s=null,_=null;p!==void 0&&(s=""+p),r.key!==void 0&&(s=""+r.key),r.ref!==void 0&&(_=r.ref);for(e in r)y.call(r,e)&&!v.hasOwnProperty(e)&&(o[e]=r[e]);if(t&&t.defaultProps)for(e in r=t.defaultProps,r)o[e]===void 0&&(o[e]=r[e]);return{$$typeof:m,type:t,key:s,ref:_,props:o,_owner:a.current}}n.Fragment=x;n.jsx=i;n.jsxs=i;f.exports=n;var d=f.exports;export{d as j}; 10 | //# sourceMappingURL=jsx-runtime-ffb262ed.js.map 11 | -------------------------------------------------------------------------------- /storybook-static/assets/jsx-runtime-ffb262ed.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"jsx-runtime-ffb262ed.js","sources":["../../node_modules/react/cjs/react-jsx-runtime.production.min.js","../../node_modules/react/jsx-runtime.js"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"react\"),k=Symbol.for(\"react.element\"),l=Symbol.for(\"react.fragment\"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n"],"names":["f","require$$0","k","l","m","n","p","q","c","a","g","b","d","e","h","reactJsxRuntime_production_min","jsxRuntimeModule"],"mappings":";;;;;;;;GASa,IAAIA,EAAEC,EAAiBC,EAAE,OAAO,IAAI,eAAe,EAAEC,EAAE,OAAO,IAAI,gBAAgB,EAAEC,EAAE,OAAO,UAAU,eAAeC,EAAEL,EAAE,mDAAmD,kBAAkBM,EAAE,CAAC,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,EAAE,EAClP,SAASC,EAAEC,EAAEC,EAAEC,EAAE,CAAC,IAAIC,EAAEC,EAAE,GAAGC,EAAE,KAAKC,EAAE,KAAcJ,IAAT,SAAaG,EAAE,GAAGH,GAAYD,EAAE,MAAX,SAAiBI,EAAE,GAAGJ,EAAE,KAAcA,EAAE,MAAX,SAAiBK,EAAEL,EAAE,KAAK,IAAIE,KAAKF,EAAEL,EAAE,KAAKK,EAAEE,CAAC,GAAG,CAACL,EAAE,eAAeK,CAAC,IAAIC,EAAED,CAAC,EAAEF,EAAEE,CAAC,GAAG,GAAGH,GAAGA,EAAE,aAAa,IAAIG,KAAKF,EAAED,EAAE,aAAaC,EAAWG,EAAED,CAAC,IAAZ,SAAgBC,EAAED,CAAC,EAAEF,EAAEE,CAAC,GAAG,MAAM,CAAC,SAAST,EAAE,KAAKM,EAAE,IAAIK,EAAE,IAAIC,EAAE,MAAMF,EAAE,OAAOP,EAAE,OAAO,CAAC,YAAkBF,EAAaY,EAAA,IAACR,EAAEQ,EAAA,KAAaR,ECPxWS,EAAA,QAAiBf","x_google_ignoreList":[0,1]} -------------------------------------------------------------------------------- /storybook-static/assets/preview-108c1c3c.js: -------------------------------------------------------------------------------- 1 | import{d as E}from"./index-356e4a49.js";const{global:M}=__STORYBOOK_MODULE_GLOBAL__,{logger:h}=__STORYBOOK_MODULE_CLIENT_LOGGER__;var p="backgrounds",{document:s,window:B}=M,S=()=>B.matchMedia("(prefers-reduced-motion: reduce)").matches,x=(r,e=[],a)=>{if(r==="transparent")return"transparent";if(e.find(t=>t.value===r))return r;let n=e.find(t=>t.name===a);if(n)return n.value;if(a){let t=e.map(o=>o.name).join(", ");h.warn(E` 2 | Backgrounds Addon: could not find the default color "${a}". 3 | These are the available colors for your story based on your configuration: 4 | ${t}. 5 | `)}return"transparent"},v=r=>{(Array.isArray(r)?r:[r]).forEach(O)},O=r=>{var a;let e=s.getElementById(r);e&&((a=e.parentElement)==null||a.removeChild(e))},w=(r,e)=>{let a=s.getElementById(r);if(a)a.innerHTML!==e&&(a.innerHTML=e);else{let n=s.createElement("style");n.setAttribute("id",r),n.innerHTML=e,s.head.appendChild(n)}},A=(r,e,a)=>{var t;let n=s.getElementById(r);if(n)n.innerHTML!==e&&(n.innerHTML=e);else{let o=s.createElement("style");o.setAttribute("id",r),o.innerHTML=e;let i=`addon-backgrounds-grid${a?`-docs-${a}`:""}`,d=s.getElementById(i);d?(t=d.parentElement)==null||t.insertBefore(o,d):s.head.appendChild(o)}};const{useMemo:f,useEffect:k}=__STORYBOOK_MODULE_PREVIEW_API__;var L=(r,e)=>{var c;let{globals:a,parameters:n}=e,t=(c=a[p])==null?void 0:c.value,o=n[p],i=f(()=>o.disable?"transparent":x(t,o.values,o.default),[o,t]),d=f(()=>i&&i!=="transparent",[i]),g=e.viewMode==="docs"?`#anchor--${e.id} .docs-story`:".sb-show-main",u=f(()=>{let l="transition: background-color 0.3s;";return` 6 | ${g} { 7 | background: ${i} !important; 8 | ${S()?"":l} 9 | } 10 | `},[i,g]);return k(()=>{let l=e.viewMode==="docs"?`addon-backgrounds-docs-${e.id}`:"addon-backgrounds-color";if(!d){v(l);return}A(l,u,e.viewMode==="docs"?e.id:null)},[d,u,e]),r()},T=(r,e)=>{var y;let{globals:a,parameters:n}=e,t=n[p].grid,o=((y=a[p])==null?void 0:y.grid)===!0&&t.disable!==!0,{cellAmount:i,cellSize:d,opacity:g}=t,u=e.viewMode==="docs",c=n.layout===void 0||n.layout==="padded"?16:0,l=t.offsetX??(u?20:c),m=t.offsetY??(u?20:c),$=f(()=>{let b=e.viewMode==="docs"?`#anchor--${e.id} .docs-story`:".sb-show-main",_=[`${d*i}px ${d*i}px`,`${d*i}px ${d*i}px`,`${d}px ${d}px`,`${d}px ${d}px`].join(", ");return` 11 | ${b} { 12 | background-size: ${_} !important; 13 | background-position: ${l}px ${m}px, ${l}px ${m}px, ${l}px ${m}px, ${l}px ${m}px !important; 14 | background-blend-mode: difference !important; 15 | background-image: linear-gradient(rgba(130, 130, 130, ${g}) 1px, transparent 1px), 16 | linear-gradient(90deg, rgba(130, 130, 130, ${g}) 1px, transparent 1px), 17 | linear-gradient(rgba(130, 130, 130, ${g/2}) 1px, transparent 1px), 18 | linear-gradient(90deg, rgba(130, 130, 130, ${g/2}) 1px, transparent 1px) !important; 19 | } 20 | `},[d]);return k(()=>{let b=e.viewMode==="docs"?`addon-backgrounds-grid-docs-${e.id}`:"addon-backgrounds-grid";if(!o){v(b);return}w(b,$)},[o,$,e]),r()},I=[T,L],R={[p]:{grid:{cellSize:20,opacity:.5,cellAmount:5},values:[{name:"light",value:"#F8F8F8"},{name:"dark",value:"#333333"}]}},G={[p]:null};export{I as decorators,G as globals,R as parameters}; 21 | //# sourceMappingURL=preview-108c1c3c.js.map 22 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-87eac49b.js: -------------------------------------------------------------------------------- 1 | import"./index-d37d4223.js";var O="links";const{global:E}=__STORYBOOK_MODULE_GLOBAL__,{makeDecorator:l,addons:_}=__STORYBOOK_MODULE_PREVIEW_API__,{STORY_CHANGED:L,SELECT_STORY:c}=__STORYBOOK_MODULE_CORE_EVENTS__;var{document:i,HTMLElement:m}=E,d=e=>_.getChannel().emit(c,e),o=e=>{let{target:t}=e;if(!(t instanceof m))return;let s=t,{sbKind:a,sbStory:r}=s.dataset;(a||r)&&(e.preventDefault(),d({kind:a,story:r}))},n=!1,v=()=>{n||(n=!0,i.addEventListener("click",o))},k=()=>{n&&(n=!1,i.removeEventListener("click",o))},R=l({name:"withLinks",parameterName:O,wrapper:(e,t)=>(v(),_.getChannel().once(L,k),e(t))}),T=[R];export{T as decorators}; 2 | //# sourceMappingURL=preview-87eac49b.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-87eac49b.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"preview-87eac49b.js","sources":["../../node_modules/@storybook/addon-links/dist/chunk-JT3VIYBO.mjs","../../node_modules/@storybook/addon-links/dist/chunk-DXNAW7Q2.mjs","../../node_modules/@storybook/addon-links/dist/preview.mjs"],"sourcesContent":["var ADDON_ID=\"storybook/links\",PARAM_KEY=\"links\",constants_default={NAVIGATE:`${ADDON_ID}/navigate`,REQUEST:`${ADDON_ID}/request`,RECEIVE:`${ADDON_ID}/receive`};\n\nexport { ADDON_ID, PARAM_KEY, constants_default };\n","import { PARAM_KEY } from './chunk-JT3VIYBO.mjs';\nimport { global } from '@storybook/global';\nimport { makeDecorator, addons } from '@storybook/preview-api';\nimport { STORY_CHANGED, SELECT_STORY } from '@storybook/core-events';\nimport { toId } from '@storybook/csf';\n\nvar{document,HTMLElement}=global;function parseQuery(queryString){let query={},pairs=(queryString[0]===\"?\"?queryString.substring(1):queryString).split(\"&\").filter(Boolean);for(let i=0;iaddons.getChannel().emit(SELECT_STORY,params),hrefTo=(title,name)=>new Promise(resolve=>{let{location}=document,query=parseQuery(location.search),existingId=[].concat(query.id)[0],titleToLink=title||existingId.split(\"--\",2)[0],path=`/story/${toId(titleToLink,name)}`,sbPath=location.pathname.replace(/iframe\\.html$/,\"\"),url=`${location.origin+sbPath}?${Object.entries({path}).map(item=>`${item[0]}=${item[1]}`).join(\"&\")}`;resolve(url);}),valueOrCall=args=>value=>typeof value==\"function\"?value(...args):value,linkTo=(idOrTitle,nameInput)=>(...args)=>{let resolver=valueOrCall(args),title=resolver(idOrTitle),name=nameInput?resolver(nameInput):!1;title?.match(/--/)&&!name?navigate({storyId:title}):name&&title?navigate({kind:title,story:name}):title?navigate({kind:title}):name&&navigate({story:name});},linksListener=e=>{let{target}=e;if(!(target instanceof HTMLElement))return;let element=target,{sbKind:kind,sbStory:story}=element.dataset;(kind||story)&&(e.preventDefault(),navigate({kind,story}));},hasListener=!1,on=()=>{hasListener||(hasListener=!0,document.addEventListener(\"click\",linksListener));},off=()=>{hasListener&&(hasListener=!1,document.removeEventListener(\"click\",linksListener));},withLinks=makeDecorator({name:\"withLinks\",parameterName:PARAM_KEY,wrapper:(getStory,context)=>(on(),addons.getChannel().once(STORY_CHANGED,off),getStory(context))});\n\nexport { hrefTo, linkTo, navigate, withLinks };\n","import './chunk-VJY7NXNQ.mjs';\nimport { withLinks } from './chunk-DXNAW7Q2.mjs';\nimport './chunk-JT3VIYBO.mjs';\n\nvar decorators=[withLinks];\n\nexport { decorators };\n"],"names":["PARAM_KEY","global","makeDecorator","addons","STORY_CHANGED","SELECT_STORY","document","HTMLElement","navigate","params","linksListener","target","element","kind","story","hasListener","on","off","withLinks","getStory","context","decorators"],"mappings":"4BAAG,IAA4BA,EAAU,QCCzC,KAAA,CAAA,OAAAC,CAAA,EAAA,4BACA,CAAA,cAAAC,EAAA,OAAAC,CAAA,EAAA,iCACA,CAAA,cAAAC,EAAA,aAAAC,CAAA,EAAA,iCAGA,GAAG,CAAC,SAAAC,EAAS,YAAAC,CAAW,EAAEN,EAAoSO,EAASC,GAAQN,EAAO,WAAU,EAAG,KAAKE,EAAaI,CAAM,EAAyvBC,EAAc,GAAG,CAAC,GAAG,CAAC,OAAAC,CAAM,EAAE,EAAE,GAAG,EAAEA,aAAkBJ,GAAa,OAAO,IAAIK,EAAQD,EAAO,CAAC,OAAOE,EAAK,QAAQC,CAAK,EAAEF,EAAQ,SAASC,GAAMC,KAAS,EAAE,eAAc,EAAGN,EAAS,CAAC,KAAAK,EAAK,MAAAC,CAAK,CAAC,EAAG,EAAEC,EAAY,GAAGC,EAAG,IAAI,CAACD,IAAcA,EAAY,GAAGT,EAAS,iBAAiB,QAAQI,CAAa,EAAG,EAAEO,EAAI,IAAI,CAACF,IAAcA,EAAY,GAAGT,EAAS,oBAAoB,QAAQI,CAAa,EAAG,EAAEQ,EAAUhB,EAAc,CAAC,KAAK,YAAY,cAAcF,EAAU,QAAQ,CAACmB,EAASC,KAAWJ,IAAKb,EAAO,WAAU,EAAG,KAAKC,EAAca,CAAG,EAAEE,EAASC,CAAO,EAAE,CAAC,ECF/pDC,EAAW,CAACH,CAAS","x_google_ignoreList":[0,1,2]} -------------------------------------------------------------------------------- /storybook-static/assets/preview-b3c37142.js: -------------------------------------------------------------------------------- 1 | var i="storybook/highlight",d="storybookHighlight",r=`${i}/add`,s=`${i}/reset`;const{global:O}=__STORYBOOK_MODULE_GLOBAL__,{addons:g}=__STORYBOOK_MODULE_PREVIEW_API__,{STORY_CHANGED:E}=__STORYBOOK_MODULE_CORE_EVENTS__;var{document:l}=O,H=(e="#FF4785",t="dashed")=>` 2 | outline: 2px ${t} ${e}; 3 | outline-offset: 2px; 4 | box-shadow: 0 0 0 6px rgba(255,255,255,0.6); 5 | `,I=e=>({outline:`2px dashed ${e}`,outlineOffset:2,boxShadow:"0 0 0 6px rgba(255,255,255,0.6)"}),_=g.getChannel(),T=e=>{let t=d;n();let o=Array.from(new Set(e.elements)),h=l.createElement("style");h.setAttribute("id",t),h.innerHTML=o.map(a=>`${a}{ 6 | ${H(e.color,e.style)} 7 | }`).join(" "),l.head.appendChild(h)},n=()=>{var o;let e=d,t=l.getElementById(e);t&&((o=t.parentNode)==null||o.removeChild(t))};_.on(E,n);_.on(s,n);_.on(r,T);export{I as highlightObject,H as highlightStyle}; 8 | //# sourceMappingURL=preview-b3c37142.js.map 9 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-b3c37142.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"preview-b3c37142.js","sources":["../../node_modules/@storybook/addon-highlight/dist/chunk-33ALZPRS.mjs","../../node_modules/@storybook/addon-highlight/dist/preview.mjs"],"sourcesContent":["var ADDON_ID=\"storybook/highlight\",HIGHLIGHT_STYLE_ID=\"storybookHighlight\",HIGHLIGHT=`${ADDON_ID}/add`,RESET_HIGHLIGHT=`${ADDON_ID}/reset`;\n\nexport { HIGHLIGHT, HIGHLIGHT_STYLE_ID, RESET_HIGHLIGHT };\n","import { RESET_HIGHLIGHT, HIGHLIGHT, HIGHLIGHT_STYLE_ID } from './chunk-33ALZPRS.mjs';\nimport { global } from '@storybook/global';\nimport { addons } from '@storybook/preview-api';\nimport { STORY_CHANGED } from '@storybook/core-events';\n\nvar {document}=global,highlightStyle=(color=\"#FF4785\",style=\"dashed\")=>`\n outline: 2px ${style} ${color};\n outline-offset: 2px;\n box-shadow: 0 0 0 6px rgba(255,255,255,0.6);\n`,highlightObject=color=>({outline:`2px dashed ${color}`,outlineOffset:2,boxShadow:\"0 0 0 6px rgba(255,255,255,0.6)\"}),channel=addons.getChannel(),highlight=infos=>{let id=HIGHLIGHT_STYLE_ID;resetHighlight();let elements=Array.from(new Set(infos.elements)),sheet=document.createElement(\"style\");sheet.setAttribute(\"id\",id),sheet.innerHTML=elements.map(target=>`${target}{\n ${highlightStyle(infos.color,infos.style)}\n }`).join(\" \"),document.head.appendChild(sheet);},resetHighlight=()=>{let id=HIGHLIGHT_STYLE_ID,sheetToBeRemoved=document.getElementById(id);sheetToBeRemoved&&sheetToBeRemoved.parentNode?.removeChild(sheetToBeRemoved);};channel.on(STORY_CHANGED,resetHighlight);channel.on(RESET_HIGHLIGHT,resetHighlight);channel.on(HIGHLIGHT,highlight);\n\nexport { highlightObject, highlightStyle };\n"],"names":["ADDON_ID","HIGHLIGHT_STYLE_ID","HIGHLIGHT","RESET_HIGHLIGHT","global","addons","STORY_CHANGED","document","highlightStyle","color","style","highlightObject","channel","highlight","infos","id","resetHighlight","elements","sheet","target","_a","sheetToBeRemoved"],"mappings":"AAAA,IAAIA,EAAS,sBAAsBC,EAAmB,qBAAqBC,EAAU,GAAGF,CAAQ,OAAOG,EAAgB,GAAGH,CAAQ,SCClI,KAAA,CAAA,OAAAI,CAAA,EAAA,4BACA,CAAA,OAAAC,CAAA,EAAA,iCACA,CAAA,cAAAC,CAAA,EAAA,iCAEG,GAAC,CAAC,SAAAC,CAAQ,EAAEH,EAAOI,EAAe,CAACC,EAAM,UAAUC,EAAM,WAAW;AAAA,iBACtDA,CAAK,IAAID,CAAK;AAAA;AAAA;AAAA,EAG7BE,EAAgBF,IAAQ,CAAC,QAAQ,cAAcA,CAAK,GAAG,cAAc,EAAE,UAAU,iCAAiC,GAAGG,EAAQP,EAAO,WAAU,EAAGQ,EAAUC,GAAO,CAAC,IAAIC,EAAGd,EAAmBe,IAAiB,IAAIC,EAAS,MAAM,KAAK,IAAI,IAAIH,EAAM,QAAQ,CAAC,EAAEI,EAAMX,EAAS,cAAc,OAAO,EAAEW,EAAM,aAAa,KAAKH,CAAE,EAAEG,EAAM,UAAUD,EAAS,IAAIE,GAAQ,GAAGA,CAAM;AAAA,YACrWX,EAAeM,EAAM,MAAMA,EAAM,KAAK,CAAC;AAAA,WACxC,EAAE,KAAK,GAAG,EAAEP,EAAS,KAAK,YAAYW,CAAK,CAAE,EAAEF,EAAe,IAAI,CDX7E,IAAAI,ECW8E,IAAIL,EAAGd,EAAmBoB,EAAiBd,EAAS,eAAeQ,CAAE,EAAEM,KAAkBD,EAAAC,EAAiB,aAAjB,MAAAD,EAA6B,YAAYC,GAAkB,EAAET,EAAQ,GAAGN,EAAcU,CAAc,EAAEJ,EAAQ,GAAGT,EAAgBa,CAAc,EAAEJ,EAAQ,GAAGV,EAAUW,CAAS","x_google_ignoreList":[0,1]} -------------------------------------------------------------------------------- /storybook-static/assets/preview-ba2273f4.js: -------------------------------------------------------------------------------- 1 | const e={parameters:{actions:{argTypesRegex:"^on[A-Z].*"},controls:{matchers:{color:/(background|color)$/i,date:/Date$/i}}}};export{e as default}; 2 | //# sourceMappingURL=preview-ba2273f4.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-ba2273f4.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"preview-ba2273f4.js","sources":["../../.storybook/preview.ts"],"sourcesContent":["import type { Preview } from \"@storybook/react\";\n\nconst preview: Preview = {\n parameters: {\n actions: { argTypesRegex: \"^on[A-Z].*\" },\n controls: {\n matchers: {\n color: /(background|color)$/i,\n date: /Date$/i,\n },\n },\n },\n};\n\nexport default preview;\n"],"names":["preview"],"mappings":"AAEA,MAAMA,EAAmB,CACvB,WAAY,CACV,QAAS,CAAE,cAAe,YAAa,EACvC,SAAU,CACR,SAAU,CACR,MAAO,uBACP,KAAM,QACR,CACF,CACF,CACF"} -------------------------------------------------------------------------------- /storybook-static/assets/preview-bed967c6.js: -------------------------------------------------------------------------------- 1 | var h="storybook/actions",D=`${h}/action-event`;let a;const b=new Uint8Array(16);function v(){if(!a&&(a=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!a))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return a(b)}const o=[];for(let t=0;t<256;++t)o.push((t+256).toString(16).slice(1));function x(t,e=0){return o[t[e+0]]+o[t[e+1]]+o[t[e+2]]+o[t[e+3]]+"-"+o[t[e+4]]+o[t[e+5]]+"-"+o[t[e+6]]+o[t[e+7]]+"-"+o[t[e+8]]+o[t[e+9]]+"-"+o[t[e+10]]+o[t[e+11]]+o[t[e+12]]+o[t[e+13]]+o[t[e+14]]+o[t[e+15]]}const A=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),d={randomUUID:A};function R(t,e,r){if(d.randomUUID&&!e&&!t)return d.randomUUID();t=t||{};const n=t.random||(t.rng||v)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,e){r=r||0;for(let i=0;i<16;++i)e[r+i]=n[i];return e}return x(n)}const{addons:U}=__STORYBOOK_MODULE_PREVIEW_API__;var j={depth:10,clearOnStoryChange:!0,limit:50},l=(t,e)=>{let r=Object.getPrototypeOf(t);return!r||e(r)?r:l(r,e)},E=t=>!!(typeof t=="object"&&t&&l(t,e=>/^Synthetic(?:Base)?Event$/.test(e.constructor.name))&&typeof t.persist=="function"),I=t=>{if(E(t)){let e=Object.create(t.constructor.prototype,Object.getOwnPropertyDescriptors(t));e.persist();let r=Object.getOwnPropertyDescriptor(e,"view"),n=r==null?void 0:r.value;return typeof n=="object"&&(n==null?void 0:n.constructor.name)==="Window"&&Object.defineProperty(e,"view",{...r,value:Object.create(n.constructor.prototype)}),e}return t};function y(t,e={}){let r={...j,...e},n=function(...i){let c=U.getChannel(),p=R(),s=5,u=i.map(I),m=i.length>1?u:u[0],O={id:p,count:0,data:{name:t,args:m},options:{...r,maxDepth:s+(r.depth||3),allowFunction:r.allowFunction||!1}};c.emit(D,O)};return n.isAction=!0,n}var g=(t,e)=>typeof e[t]>"u"&&!(t in e),T=t=>{let{initialArgs:e,argTypes:r,parameters:{actions:n}}=t;if(!n||n.disable||!n.argTypesRegex||!r)return{};let i=new RegExp(n.argTypesRegex);return Object.entries(r).filter(([c])=>!!i.test(c)).reduce((c,[p,s])=>(g(p,e)&&(c[p]=y(p)),c),{})},w=t=>{let{initialArgs:e,argTypes:r,parameters:{actions:n}}=t;return n!=null&&n.disable||!r?{}:Object.entries(r).filter(([i,c])=>!!c.action).reduce((i,[c,p])=>(g(c,e)&&(i[c]=y(typeof p.action=="string"?p.action:c)),i),{})},_=[w,T];export{_ as argsEnhancers}; 2 | //# sourceMappingURL=preview-bed967c6.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-e11ff2f7.js: -------------------------------------------------------------------------------- 1 | import{_ as e}from"./iframe-a3b0aa37.js";import"../sb-preview/runtime.js";var a={docs:{renderer:async()=>{let{DocsRenderer:r}=await e(()=>import("./DocsRenderer-NNNQARDV-cb22ad80.js"),["./DocsRenderer-NNNQARDV-cb22ad80.js","./iframe-a3b0aa37.js","./index-76fb7be0.js","./_commonjsHelpers-de833af9.js","./react-18-063a39db.js","./index-932496f1.js","./index-228e512d.js","./index-d37d4223.js","./index-356e4a49.js"],import.meta.url);return new r}}};export{a as parameters}; 2 | //# sourceMappingURL=preview-e11ff2f7.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/preview-e11ff2f7.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"mappings":"0EAAG,IAACA,EAAW,CAAC,KAAK,CAAC,SAAS,SAAS,CAAC,GAAG,CAAC,aAAAC,CAAY,EAAE,YAAM,OAAO,qCAA6B,0PAAE,OAAO,IAAIA,CAAY,CAAC,CAAC","names":["parameters","DocsRenderer"],"sources":["../../node_modules/@storybook/addon-docs/dist/preview.mjs"],"sourcesContent":["var parameters={docs:{renderer:async()=>{let{DocsRenderer}=await import('./DocsRenderer-NNNQARDV.mjs');return new DocsRenderer}}};\n\nexport { parameters };\n"],"file":"assets/preview-e11ff2f7.js"} -------------------------------------------------------------------------------- /storybook-static/assets/react-18-063a39db.js: -------------------------------------------------------------------------------- 1 | import{R as c,r as n}from"./index-76fb7be0.js";import{r as m}from"./index-932496f1.js";var a={},s=m;a.createRoot=s.createRoot,a.hydrateRoot=s.hydrateRoot;var o=new Map,R=({callback:e,children:t})=>{let r=n.useRef();return n.useLayoutEffect(()=>{r.current!==e&&(r.current=e,e())},[e]),t},p=async(e,t)=>{let r=await d(t);return new Promise(u=>{r.render(c.createElement(R,{callback:()=>u(null)},e))})},E=(e,t)=>{let r=o.get(e);r&&(r.unmount(),o.delete(e))},d=async e=>{let t=o.get(e);return t||(t=a.createRoot(e),o.set(e,t)),t};export{p as r,E as u}; 2 | //# sourceMappingURL=react-18-063a39db.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/react-18-063a39db.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"react-18-063a39db.js","sources":["../../node_modules/react-dom/client.js","../../node_modules/@storybook/react-dom-shim/dist/react-18.mjs"],"sourcesContent":["'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n","import React, { useRef, useLayoutEffect } from 'react';\nimport ReactDOM from 'react-dom/client';\n\nvar nodes=new Map,WithCallback=({callback,children})=>{let once=useRef();return useLayoutEffect(()=>{once.current!==callback&&(once.current=callback,callback());},[callback]),children},renderElement=async(node,el)=>{let root=await getReactRoot(el);return new Promise(resolve=>{root.render(React.createElement(WithCallback,{callback:()=>resolve(null)},node));})},unmountElement=(el,shouldUseNewRootApi)=>{let root=nodes.get(el);root&&(root.unmount(),nodes.delete(el));},getReactRoot=async el=>{let root=nodes.get(el);return root||(root=ReactDOM.createRoot(el),nodes.set(el,root)),root};\n\nexport { renderElement, unmountElement };\n"],"names":["m","require$$0","client","nodes","WithCallback","callback","children","once","useRef","useLayoutEffect","renderElement","node","el","root","getReactRoot","resolve","React","unmountElement","shouldUseNewRootApi","ReactDOM"],"mappings":"gGAEIA,EAAIC,EAEYC,EAAA,WAAGF,EAAE,WACJE,EAAA,YAAGF,EAAE,YCFvB,IAACG,EAAM,IAAI,IAAIC,EAAa,CAAC,CAAC,SAAAC,EAAS,SAAAC,CAAQ,IAAI,CAAC,IAAIC,EAAKC,EAAM,OAAA,EAAG,OAAOC,kBAAgB,IAAI,CAACF,EAAK,UAAUF,IAAWE,EAAK,QAAQF,EAASA,EAAQ,EAAI,EAAE,CAACA,CAAQ,CAAC,EAAEC,CAAQ,EAAEI,EAAc,MAAMC,EAAKC,IAAK,CAAC,IAAIC,EAAK,MAAMC,EAAaF,CAAE,EAAE,OAAO,IAAI,QAAQG,GAAS,CAACF,EAAK,OAAOG,EAAM,cAAcZ,EAAa,CAAC,SAAS,IAAIW,EAAQ,IAAI,CAAC,EAAEJ,CAAI,CAAC,CAAE,CAAC,CAAC,EAAEM,EAAe,CAACL,EAAGM,IAAsB,CAAC,IAAIL,EAAKV,EAAM,IAAIS,CAAE,EAAEC,IAAOA,EAAK,UAAUV,EAAM,OAAOS,CAAE,EAAG,EAAEE,EAAa,MAAMF,GAAI,CAAC,IAAIC,EAAKV,EAAM,IAAIS,CAAE,EAAE,OAAOC,IAAOA,EAAKM,EAAS,WAAWP,CAAE,EAAET,EAAM,IAAIS,EAAGC,CAAI,GAAGA,CAAI","x_google_ignoreList":[0,1]} -------------------------------------------------------------------------------- /storybook-static/assets/share-b59d6c77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/share-b59d6c77.png -------------------------------------------------------------------------------- /storybook-static/assets/styling-c83082e0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/styling-c83082e0.png -------------------------------------------------------------------------------- /storybook-static/assets/syntaxhighlighter-NMPM6SWI-ceeb6cee.js: -------------------------------------------------------------------------------- 1 | import{S as c,c as l,s as n}from"./index-228e512d.js";import"./iframe-a3b0aa37.js";import"../sb-preview/runtime.js";import"./index-76fb7be0.js";import"./_commonjsHelpers-de833af9.js";import"./index-932496f1.js";import"./index-d37d4223.js";import"./index-356e4a49.js";export{c as SyntaxHighlighter,l as createCopyToClipboardFunction,n as default}; 2 | //# sourceMappingURL=syntaxhighlighter-NMPM6SWI-ceeb6cee.js.map 3 | -------------------------------------------------------------------------------- /storybook-static/assets/syntaxhighlighter-NMPM6SWI-ceeb6cee.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"syntaxhighlighter-NMPM6SWI-ceeb6cee.js","sources":[],"sourcesContent":[],"names":[],"mappings":""} -------------------------------------------------------------------------------- /storybook-static/assets/testing-6a59f681.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/testing-6a59f681.png -------------------------------------------------------------------------------- /storybook-static/assets/theming-b6e819c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/assets/theming-b6e819c3.png -------------------------------------------------------------------------------- /storybook-static/assets/tutorials-adff6365.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /storybook-static/assets/youtube-9f26eb0b.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /storybook-static/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /storybook-static/ic-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/ic-logo.png -------------------------------------------------------------------------------- /storybook-static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @storybook/cli - Storybook 7 | 8 | 9 | 10 | 11 | 12 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 | 100 | 101 | 102 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /storybook-static/index.json: -------------------------------------------------------------------------------- 1 | {"v":4,"entries":{"configure-your-project--docs":{"id":"configure-your-project--docs","title":"Configure your project","name":"Docs","importPath":"./src/stories/Configure.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"example-button--docs":{"id":"example-button--docs","title":"Example/Button","name":"Docs","importPath":"./src/stories/Button.stories.ts","type":"docs","tags":["autodocs","docs"],"storiesImports":[]},"example-button--primary":{"type":"story","id":"example-button--primary","name":"Primary","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"]},"example-button--secondary":{"type":"story","id":"example-button--secondary","name":"Secondary","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"]},"example-button--large":{"type":"story","id":"example-button--large","name":"Large","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"]},"example-button--small":{"type":"story","id":"example-button--small","name":"Small","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"]},"example-header--docs":{"id":"example-header--docs","title":"Example/Header","name":"Docs","importPath":"./src/stories/Header.stories.ts","type":"docs","tags":["autodocs","docs"],"storiesImports":[]},"example-header--logged-in":{"type":"story","id":"example-header--logged-in","name":"Logged In","title":"Example/Header","importPath":"./src/stories/Header.stories.ts","tags":["autodocs","story"]},"example-header--logged-out":{"type":"story","id":"example-header--logged-out","name":"Logged Out","title":"Example/Header","importPath":"./src/stories/Header.stories.ts","tags":["autodocs","story"]},"example-page--logged-out":{"type":"story","id":"example-page--logged-out","name":"Logged Out","title":"Example/Page","importPath":"./src/stories/Page.stories.ts","tags":["story"]},"example-page--logged-in":{"type":"story","id":"example-page--logged-in","name":"Logged In","title":"Example/Page","importPath":"./src/stories/Page.stories.ts","tags":["play-fn","story"]}}} 2 | -------------------------------------------------------------------------------- /storybook-static/project.json: -------------------------------------------------------------------------------- 1 | {"generatedAt":1700475262129,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":true,"refCount":0,"packageManager":{"type":"npm","version":"10.2.0"},"preview":{"usesGlobals":false},"framework":{"name":"@storybook/react-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/react","storybookVersion":"7.5.3","storybookVersionSpecifier":"^7.5.3","language":"typescript","storybookPackages":{"@storybook/addon-a11y":{"version":"7.5.3"},"@storybook/blocks":{"version":"7.5.3"},"@storybook/react":{"version":"7.5.3"},"@storybook/react-vite":{"version":"7.5.3"},"@storybook/testing-library":{"version":"0.2.2"},"eslint-plugin-storybook":{"version":"0.6.15"},"storybook":{"version":"7.5.3"}},"addons":{"@storybook/addon-links":{"version":"7.5.3"},"@storybook/addon-essentials":{"version":"7.5.3"},"@storybook/addon-onboarding":{"version":"1.0.8"},"@storybook/addon-interactions":{"version":"7.5.3"}}} 2 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-actions-2/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-actions-2/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-backgrounds-3/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-backgrounds-3/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-controls-1/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- 1 | Bundled license information: 2 | 3 | telejson/dist/index.mjs: 4 | /*! 5 | * isobject 6 | * 7 | * Copyright (c) 2014-2017, Jon Schlinkert. 8 | * Released under the MIT License. 9 | */ 10 | /** 11 | * @license 12 | * Lodash (Custom Build) 13 | * Build: `lodash modularize exports="es" -o ./` 14 | * Copyright OpenJS Foundation and other contributors 15 | * Released under MIT license 16 | * Based on Underscore.js 1.8.3 17 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 18 | */ 19 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-measure-6/manager-bundle.js: -------------------------------------------------------------------------------- 1 | try{ 2 | var r="storybook/measure-addon",u=`${r}/tool`;var a=__REACT__,{Children:M,Component:B,Fragment:P,Profiler:D,PureComponent:x,StrictMode:N,Suspense:v,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:H,cloneElement:F,createContext:K,createElement:W,createFactory:Y,createRef:G,forwardRef:w,isValidElement:U,lazy:V,memo:q,useCallback:p,useContext:z,useDebugValue:Z,useEffect:S,useImperativeHandle:$,useLayoutEffect:j,useMemo:J,useReducer:Q,useRef:X,useState:ee,version:oe}=__REACT__;var se=__STORYBOOKAPI__,{ActiveTabs:le,Consumer:ue,ManagerContext:ce,Provider:me,addons:c,combineParameters:ie,controlOrMetaKey:pe,controlOrMetaSymbol:Se,eventMatchesShortcut:de,eventToShortcut:_e,isMacLike:Te,isShortcutTaken:be,keyToSymbol:Oe,merge:ye,mockChannel:Ce,optionOrAltSymbol:Ee,shortcutMatchesShortcut:he,shortcutToHumanString:fe,types:d,useAddonState:Ae,useArgTypes:ge,useArgs:Ie,useChannel:Re,useGlobalTypes:ke,useGlobals:_,useParameter:Le,useSharedState:Me,useStoryPrepared:Be,useStorybookApi:T,useStorybookState:Pe}=__STORYBOOKAPI__;var He=__STORYBOOKCOMPONENTS__,{A:Fe,ActionBar:Ke,AddonPanel:We,Badge:Ye,Bar:Ge,Blockquote:we,Button:Ue,ClipboardCode:Ve,Code:qe,DL:ze,Div:Ze,DocumentWrapper:$e,ErrorFormatter:je,FlexBar:Je,Form:Qe,H1:Xe,H2:eo,H3:oo,H4:to,H5:ro,H6:ao,HR:no,IconButton:b,IconButtonSkeleton:so,Icons:O,Img:lo,LI:uo,Link:co,ListItem:mo,Loader:io,OL:po,P:So,Placeholder:_o,Pre:To,ResetWrapper:bo,ScrollArea:Oo,Separator:yo,Spaced:Co,Span:Eo,StorybookIcon:ho,StorybookLogo:fo,Symbols:Ao,SyntaxHighlighter:go,TT:Io,TabBar:Ro,TabButton:ko,TabWrapper:Lo,Table:Mo,Tabs:Bo,TabsState:Po,TooltipLinkList:Do,TooltipMessage:xo,TooltipNote:No,UL:vo,WithTooltip:Ho,WithTooltipPure:Fo,Zoom:Ko,codeCommon:Wo,components:Yo,createCopyToClipboardFunction:Go,getStoryHref:wo,icons:Uo,interleaveSeparators:Vo,nameSpaceClassNames:qo,resetComponents:zo,withReset:Zo}=__STORYBOOKCOMPONENTS__;var y=()=>{let[n,m]=_(),{measureEnabled:s}=n,i=T(),l=p(()=>m({measureEnabled:!s}),[m,s]);return S(()=>{i.setAddonShortcut(r,{label:"Toggle Measure [M]",defaultShortcut:["M"],actionName:"measure",showInMenu:!1,action:l})},[l,i]),a.createElement(b,{key:u,active:s,title:"Enable measure",onClick:l},a.createElement(O,{icon:"ruler"}))};c.register(r,()=>{c.add(u,{type:d.TOOL,title:"Measure",match:({viewMode:n})=>n==="story",render:()=>a.createElement(y,null)})}); 3 | }catch(e){ console.error("[Storybook] One of your manager-entries failed: " + import.meta.url, e); } 4 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-measure-6/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-measure-6/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-outline-7/manager-bundle.js: -------------------------------------------------------------------------------- 1 | try{ 2 | var a="storybook/outline",u="outline";var n=__REACT__,{Children:L,Component:x,Fragment:M,Profiler:v,PureComponent:D,StrictMode:N,Suspense:H,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:F,cloneElement:K,createContext:Y,createElement:W,createFactory:G,createRef:w,forwardRef:U,isValidElement:V,lazy:q,memo:p,useCallback:m,useContext:z,useDebugValue:Z,useEffect:S,useImperativeHandle:$,useLayoutEffect:j,useMemo:J,useReducer:Q,useRef:X,useState:ee,version:te}=__REACT__;var le=__STORYBOOKAPI__,{ActiveTabs:se,Consumer:ue,ManagerContext:ce,Provider:ie,addons:c,combineParameters:pe,controlOrMetaKey:me,controlOrMetaSymbol:Se,eventMatchesShortcut:_e,eventToShortcut:de,isMacLike:Oe,isShortcutTaken:Te,keyToSymbol:be,merge:ye,mockChannel:Ae,optionOrAltSymbol:Ce,shortcutMatchesShortcut:he,shortcutToHumanString:fe,types:_,useAddonState:Ee,useArgTypes:ge,useArgs:Re,useChannel:ke,useGlobalTypes:Ie,useGlobals:d,useParameter:Pe,useSharedState:Be,useStoryPrepared:Le,useStorybookApi:O,useStorybookState:xe}=__STORYBOOKAPI__;var He=__STORYBOOKCOMPONENTS__,{A:Fe,ActionBar:Ke,AddonPanel:Ye,Badge:We,Bar:Ge,Blockquote:we,Button:Ue,ClipboardCode:Ve,Code:qe,DL:ze,Div:Ze,DocumentWrapper:$e,ErrorFormatter:je,FlexBar:Je,Form:Qe,H1:Xe,H2:et,H3:tt,H4:ot,H5:rt,H6:at,HR:nt,IconButton:T,IconButtonSkeleton:lt,Icons:b,Img:st,LI:ut,Link:ct,ListItem:it,Loader:pt,OL:mt,P:St,Placeholder:_t,Pre:dt,ResetWrapper:Ot,ScrollArea:Tt,Separator:bt,Spaced:yt,Span:At,StorybookIcon:Ct,StorybookLogo:ht,Symbols:ft,SyntaxHighlighter:Et,TT:gt,TabBar:Rt,TabButton:kt,TabWrapper:It,Table:Pt,Tabs:Bt,TabsState:Lt,TooltipLinkList:xt,TooltipMessage:Mt,TooltipNote:vt,UL:Dt,WithTooltip:Nt,WithTooltipPure:Ht,Zoom:Ft,codeCommon:Kt,components:Yt,createCopyToClipboardFunction:Wt,getStoryHref:Gt,icons:wt,interleaveSeparators:Ut,nameSpaceClassNames:Vt,resetComponents:qt,withReset:zt}=__STORYBOOKCOMPONENTS__;var A=p(function(){let[r,y]=d(),i=O(),l=[!0,"true"].includes(r[u]),s=m(()=>y({[u]:!l}),[l]);return S(()=>{i.setAddonShortcut(a,{label:"Toggle Outline [O]",defaultShortcut:["O"],actionName:"outline",showInMenu:!1,action:s})},[s,i]),n.createElement(T,{key:"outline",active:l,title:"Apply outlines to the preview",onClick:s},n.createElement(b,{icon:"outline"}))});c.register(a,()=>{c.add(a,{title:"Outline",type:_.TOOL,match:({viewMode:r})=>!!(r&&r.match(/^(story|docs)$/)),render:()=>n.createElement(A,null)})}); 3 | }catch(e){ console.error("[Storybook] One of your manager-entries failed: " + import.meta.url, e); } 4 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-outline-7/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-outline-7/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-toolbars-5/manager-bundle.js: -------------------------------------------------------------------------------- 1 | try{ 2 | var l=__REACT__,{Children:le,Component:ne,Fragment:ie,Profiler:se,PureComponent:ce,StrictMode:ue,Suspense:me,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:pe,cloneElement:de,createContext:be,createElement:Se,createFactory:Te,createRef:ye,forwardRef:fe,isValidElement:Ce,lazy:ve,memo:_e,useCallback:_,useContext:Ie,useDebugValue:Oe,useEffect:E,useImperativeHandle:xe,useLayoutEffect:Ee,useMemo:ge,useReducer:he,useRef:L,useState:R,version:ke}=__REACT__;var Pe=__STORYBOOKAPI__,{ActiveTabs:Me,Consumer:Ne,ManagerContext:we,Provider:He,addons:g,combineParameters:Ve,controlOrMetaKey:De,controlOrMetaSymbol:Fe,eventMatchesShortcut:Ge,eventToShortcut:We,isMacLike:Ke,isShortcutTaken:Ye,keyToSymbol:$e,merge:ze,mockChannel:Ue,optionOrAltSymbol:je,shortcutMatchesShortcut:qe,shortcutToHumanString:Ze,types:B,useAddonState:Je,useArgTypes:Qe,useArgs:Xe,useChannel:et,useGlobalTypes:P,useGlobals:h,useParameter:tt,useSharedState:ot,useStoryPrepared:rt,useStorybookApi:M,useStorybookState:at}=__STORYBOOKAPI__;var ct=__STORYBOOKCOMPONENTS__,{A:ut,ActionBar:mt,AddonPanel:pt,Badge:dt,Bar:bt,Blockquote:St,Button:Tt,ClipboardCode:yt,Code:ft,DL:Ct,Div:vt,DocumentWrapper:_t,ErrorFormatter:It,FlexBar:Ot,Form:xt,H1:Et,H2:gt,H3:ht,H4:kt,H5:At,H6:Lt,HR:Rt,IconButton:N,IconButtonSkeleton:Bt,Icons:k,Img:Pt,LI:Mt,Link:Nt,ListItem:wt,Loader:Ht,OL:Vt,P:Dt,Placeholder:Ft,Pre:Gt,ResetWrapper:Wt,ScrollArea:Kt,Separator:w,Spaced:Yt,Span:$t,StorybookIcon:zt,StorybookLogo:Ut,Symbols:jt,SyntaxHighlighter:qt,TT:Zt,TabBar:Jt,TabButton:Qt,TabWrapper:Xt,Table:eo,Tabs:to,TabsState:oo,TooltipLinkList:H,TooltipMessage:ro,TooltipNote:ao,UL:lo,WithTooltip:V,WithTooltipPure:no,Zoom:io,codeCommon:so,components:co,createCopyToClipboardFunction:uo,getStoryHref:mo,icons:po,interleaveSeparators:bo,nameSpaceClassNames:So,resetComponents:To,withReset:yo}=__STORYBOOKCOMPONENTS__;var G=({active:o,title:t,icon:e,description:r,onClick:a})=>l.createElement(N,{active:o,title:r,onClick:a},e&&l.createElement(k,{icon:e}),t?`\xA0${t}`:null),W=["reset"],K=o=>o.filter(t=>!W.includes(t.type)).map(t=>t.value),b="addon-toolbars",Y=async(o,t,e)=>{e&&e.next&&await o.setAddonShortcut(b,{label:e.next.label,defaultShortcut:e.next.keys,actionName:`${t}:next`,action:e.next.action}),e&&e.previous&&await o.setAddonShortcut(b,{label:e.previous.label,defaultShortcut:e.previous.keys,actionName:`${t}:previous`,action:e.previous.action}),e&&e.reset&&await o.setAddonShortcut(b,{label:e.reset.label,defaultShortcut:e.reset.keys,actionName:`${t}:reset`,action:e.reset.action})},$=o=>t=>{let{id:e,toolbar:{items:r,shortcuts:a}}=t,d=M(),[S,i]=h(),n=L([]),s=S[e],I=_(()=>{i({[e]:""})},[i]),O=_(()=>{let p=n.current,c=p.indexOf(s),m=c===p.length-1?0:c+1,T=n.current[m];i({[e]:T})},[n,s,i]),u=_(()=>{let p=n.current,c=p.indexOf(s),m=c>-1?c:0,T=m===0?p.length-1:m-1,y=n.current[T];i({[e]:y})},[n,s,i]);return E(()=>{a&&Y(d,e,{next:{...a.next,action:O},previous:{...a.previous,action:u},reset:{...a.reset,action:I}})},[d,e,a,O,u,I]),E(()=>{n.current=K(r)},[]),l.createElement(o,{cycleValues:n.current,...t})},D=({currentValue:o,items:t})=>o!=null&&t.find(e=>e.value===o&&e.type!=="reset"),z=({currentValue:o,items:t})=>{let e=D({currentValue:o,items:t});if(e)return e.icon},U=({currentValue:o,items:t})=>{let e=D({currentValue:o,items:t});if(e)return e.title},j=({left:o,right:t,title:e,value:r,icon:a,hideIcon:d,onClick:S,currentValue:i})=>{let n=a&&l.createElement(k,{style:{opacity:1},icon:a}),s={id:r??"_reset",active:i===r,right:t,title:e,left:o,onClick:S};return a&&!d&&(s.left=n),s},q=$(({id:o,name:t,description:e,toolbar:{icon:r,items:a,title:d,preventDynamicIcon:S,dynamicTitle:i}})=>{let[n,s]=h(),[I,O]=R(!1),u=n[o],p=!!u,c=r,m=d;S||(c=z({currentValue:u,items:a})||c),i&&(m=U({currentValue:u,items:a})||m),!m&&!c&&console.warn(`Toolbar '${t}' has no title or icon`);let T=_(y=>{s({[o]:y})},[u,s]);return l.createElement(V,{placement:"top",tooltip:({onHide:y})=>{let F=a.filter(({type:x})=>{let A=!0;return x==="reset"&&!u&&(A=!1),A}).map(x=>j({...x,currentValue:u,onClick:()=>{T(x.value),y()}}));return l.createElement(H,{links:F})},closeOnOutsideClick:!0,onVisibleChange:O},l.createElement(G,{active:I||p,description:e||"",icon:c,title:m||""}))}),Z={type:"item",value:""},J=(o,t)=>({...t,name:t.name||o,description:t.description||o,toolbar:{...t.toolbar,items:t.toolbar.items.map(e=>{let r=typeof e=="string"?{value:e,title:e}:e;return r.type==="reset"&&t.toolbar.icon&&(r.icon=t.toolbar.icon,r.hideIcon=!0),{...Z,...r}})}}),Q=()=>{let o=P(),t=Object.keys(o).filter(e=>!!o[e].toolbar);return t.length?l.createElement(l.Fragment,null,l.createElement(w,null),t.map(e=>{let r=J(e,o[e]);return l.createElement(q,{key:e,id:e,...r})})):null};g.register(b,()=>g.add(b,{title:b,type:B.TOOL,match:()=>!0,render:()=>l.createElement(Q,null)})); 3 | }catch(e){ console.error("[Storybook] One of your manager-entries failed: " + import.meta.url, e); } 4 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-toolbars-5/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-toolbars-5/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/essentials-viewport-4/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/essentials-viewport-4/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/interactions-9/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/interactions-9/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/links-0/manager-bundle.js: -------------------------------------------------------------------------------- 1 | try{ 2 | var e="storybook/links";var a={NAVIGATE:`${e}/navigate`,REQUEST:`${e}/request`,RECEIVE:`${e}/receive`};var O=__STORYBOOKAPI__,{ActiveTabs:b,Consumer:p,ManagerContext:k,Provider:I,addons:n,combineParameters:g,controlOrMetaKey:v,controlOrMetaSymbol:M,eventMatchesShortcut:P,eventToShortcut:R,isMacLike:C,isShortcutTaken:D,keyToSymbol:f,merge:x,mockChannel:K,optionOrAltSymbol:G,shortcutMatchesShortcut:N,shortcutToHumanString:V,types:Y,useAddonState:$,useArgTypes:B,useArgs:Q,useChannel:U,useGlobalTypes:q,useGlobals:H,useParameter:L,useSharedState:j,useStoryPrepared:w,useStorybookApi:z,useStorybookState:F}=__STORYBOOKAPI__;n.register(e,t=>{t.on(a.REQUEST,({kind:u,name:S})=>{let c=t.storyId(u,S);t.emit(a.RECEIVE,c)})}); 3 | }catch(e){ console.error("[Storybook] One of your manager-entries failed: " + import.meta.url, e); } 4 | -------------------------------------------------------------------------------- /storybook-static/sb-addons/links-0/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-addons/links-0/manager-bundle.js.LEGAL.txt -------------------------------------------------------------------------------- /storybook-static/sb-addons/onboarding-8/manager-bundle.js.LEGAL.txt: -------------------------------------------------------------------------------- 1 | Bundled license information: 2 | 3 | @storybook/addon-onboarding/dist/manager.mjs: 4 | /*! Bundled license information: 5 | 6 | exenv/index.js: 7 | (*! 8 | Copyright (c) 2015 Jed Watson. 9 | Based on code that is Copyright 2013-2015, Facebook, Inc. 10 | All rights reserved. 11 | *) 12 | 13 | react-is/cjs/react-is.production.min.js: 14 | (** @license React v16.13.1 15 | * react-is.production.min.js 16 | * 17 | * Copyright (c) Facebook, Inc. and its affiliates. 18 | * 19 | * This source code is licensed under the MIT license found in the 20 | * LICENSE file in the root directory of this source tree. 21 | *) 22 | 23 | popper.js/dist/esm/popper.js: 24 | (**! 25 | * @fileOverview Kickass library to create and place poppers near their reference elements. 26 | * @version 1.16.1 27 | * @license 28 | * Copyright (c) 2016 Federico Zivolo and contributors 29 | * 30 | * Permission is hereby granted, free of charge, to any person obtaining a copy 31 | * of this software and associated documentation files (the "Software"), to deal 32 | * in the Software without restriction, including without limitation the rights 33 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | * copies of the Software, and to permit persons to whom the Software is 35 | * furnished to do so, subject to the following conditions: 36 | * 37 | * The above copyright notice and this permission notice shall be included in all 38 | * copies or substantial portions of the Software. 39 | * 40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 46 | * SOFTWARE. 47 | *) 48 | */ 49 | -------------------------------------------------------------------------------- /storybook-static/sb-common-assets/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Nunito Sans'; 3 | font-style: normal; 4 | font-weight: 400; 5 | font-display: swap; 6 | src: url('./nunito-sans-regular.woff2') format('woff2'); 7 | } 8 | 9 | @font-face { 10 | font-family: 'Nunito Sans'; 11 | font-style: italic; 12 | font-weight: 400; 13 | font-display: swap; 14 | src: url('./nunito-sans-italic.woff2') format('woff2'); 15 | } 16 | 17 | @font-face { 18 | font-family: 'Nunito Sans'; 19 | font-style: normal; 20 | font-weight: 700; 21 | font-display: swap; 22 | src: url('./nunito-sans-bold.woff2') format('woff2'); 23 | } 24 | 25 | @font-face { 26 | font-family: 'Nunito Sans'; 27 | font-style: italic; 28 | font-weight: 700; 29 | font-display: swap; 30 | src: url('./nunito-sans-bold-italic.woff2') format('woff2'); 31 | } 32 | -------------------------------------------------------------------------------- /storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 -------------------------------------------------------------------------------- /storybook-static/sb-common-assets/nunito-sans-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-common-assets/nunito-sans-bold.woff2 -------------------------------------------------------------------------------- /storybook-static/sb-common-assets/nunito-sans-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-common-assets/nunito-sans-italic.woff2 -------------------------------------------------------------------------------- /storybook-static/sb-common-assets/nunito-sans-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonkang14/react-basics/f5ec784575852b4ef3324df5b3bc822a5f173f21/storybook-static/sb-common-assets/nunito-sans-regular.woff2 -------------------------------------------------------------------------------- /storybook-static/sb-manager/WithTooltip-4HIR6TLV-YPPZ2DMB.js: -------------------------------------------------------------------------------- 1 | import{WithToolTipState,WithTooltipPure}from"./chunk-FWZ33S65.js";import"./chunk-NFZCBIX3.js";import"./chunk-ZEU7PDD3.js";export{WithToolTipState,WithToolTipState as WithTooltip,WithTooltipPure}; 2 | -------------------------------------------------------------------------------- /storybook-static/sb-manager/globals.js: -------------------------------------------------------------------------------- 1 | var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var globals_exports={};__export(globals_exports,{definitions:()=>definitions});module.exports=__toCommonJS(globals_exports);var exports_default={react:["Children","Component","Fragment","Profiler","PureComponent","StrictMode","Suspense","__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","cloneElement","createContext","createElement","createFactory","createRef","forwardRef","isValidElement","lazy","memo","useCallback","useContext","useDebugValue","useEffect","useImperativeHandle","useLayoutEffect","useMemo","useReducer","useRef","useState","version"],"react-dom":["__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","createPortal","findDOMNode","flushSync","hydrate","render","unmountComponentAtNode","unstable_batchedUpdates","unstable_createPortal","unstable_renderSubtreeIntoContainer","version"],"@storybook/components":["A","ActionBar","AddonPanel","Badge","Bar","Blockquote","Button","ClipboardCode","Code","DL","Div","DocumentWrapper","ErrorFormatter","FlexBar","Form","H1","H2","H3","H4","H5","H6","HR","IconButton","IconButtonSkeleton","Icons","Img","LI","Link","ListItem","Loader","OL","P","Placeholder","Pre","ResetWrapper","ScrollArea","Separator","Spaced","Span","StorybookIcon","StorybookLogo","Symbols","SyntaxHighlighter","TT","TabBar","TabButton","TabWrapper","Table","Tabs","TabsState","TooltipLinkList","TooltipMessage","TooltipNote","UL","WithTooltip","WithTooltipPure","Zoom","codeCommon","components","createCopyToClipboardFunction","getStoryHref","icons","interleaveSeparators","nameSpaceClassNames","resetComponents","withReset"],"@storybook/channels":["Channel","PostMessageTransport","WebsocketTransport","createBrowserChannel","createPostMessageChannel","createWebSocketChannel"],"@storybook/core-events":["CHANNEL_CREATED","CONFIG_ERROR","CURRENT_STORY_WAS_SET","DOCS_PREPARED","DOCS_RENDERED","FORCE_REMOUNT","FORCE_RE_RENDER","GLOBALS_UPDATED","IGNORED_EXCEPTION","NAVIGATE_URL","PLAY_FUNCTION_THREW_EXCEPTION","PRELOAD_ENTRIES","PREVIEW_BUILDER_PROGRESS","PREVIEW_KEYDOWN","REGISTER_SUBSCRIPTION","REQUEST_WHATS_NEW_DATA","RESET_STORY_ARGS","RESULT_WHATS_NEW_DATA","SELECT_STORY","SET_CONFIG","SET_CURRENT_STORY","SET_GLOBALS","SET_INDEX","SET_STORIES","SET_WHATS_NEW_CACHE","SHARED_STATE_CHANGED","SHARED_STATE_SET","STORIES_COLLAPSE_ALL","STORIES_EXPAND_ALL","STORY_ARGS_UPDATED","STORY_CHANGED","STORY_ERRORED","STORY_INDEX_INVALIDATED","STORY_MISSING","STORY_PREPARED","STORY_RENDERED","STORY_RENDER_PHASE_CHANGED","STORY_SPECIFIED","STORY_THREW_EXCEPTION","STORY_UNCHANGED","TELEMETRY_ERROR","TOGGLE_WHATS_NEW_NOTIFICATIONS","UPDATE_GLOBALS","UPDATE_QUERY_PARAMS","UPDATE_STORY_ARGS"],"@storybook/router":["BaseLocationProvider","DEEPLY_EQUAL","Link","Location","LocationProvider","Match","Route","buildArgsParam","deepDiff","getMatch","parsePath","queryFromLocation","queryFromString","stringifyQuery","useNavigate"],"@storybook/theming":["CacheProvider","ClassNames","Global","ThemeProvider","background","color","convert","create","createCache","createGlobal","createReset","css","darken","ensure","ignoreSsrWarning","isPropValid","jsx","keyframes","lighten","styled","themes","typography","useTheme","withTheme"],"@storybook/api":["ActiveTabs","Consumer","ManagerContext","Provider","addons","combineParameters","controlOrMetaKey","controlOrMetaSymbol","eventMatchesShortcut","eventToShortcut","isMacLike","isShortcutTaken","keyToSymbol","merge","mockChannel","optionOrAltSymbol","shortcutMatchesShortcut","shortcutToHumanString","types","useAddonState","useArgTypes","useArgs","useChannel","useGlobalTypes","useGlobals","useParameter","useSharedState","useStoryPrepared","useStorybookApi","useStorybookState"],"@storybook/manager-api":["ActiveTabs","Consumer","ManagerContext","Provider","addons","combineParameters","controlOrMetaKey","controlOrMetaSymbol","eventMatchesShortcut","eventToShortcut","isMacLike","isShortcutTaken","keyToSymbol","merge","mockChannel","optionOrAltSymbol","shortcutMatchesShortcut","shortcutToHumanString","types","useAddonState","useArgTypes","useArgs","useChannel","useGlobalTypes","useGlobals","useParameter","useSharedState","useStoryPrepared","useStorybookApi","useStorybookState"],"@storybook/addons":["addons","types","mockChannel"],"@storybook/client-logger":["deprecate","logger","once","pretty"]};var Keys=(Keys2=>(Keys2.react="__REACT__",Keys2["react-dom"]="__REACTDOM__",Keys2["@storybook/components"]="__STORYBOOKCOMPONENTS__",Keys2["@storybook/channels"]="__STORYBOOKCHANNELS__",Keys2["@storybook/core-events"]="__STORYBOOKCOREEVENTS__",Keys2["@storybook/router"]="__STORYBOOKROUTER__",Keys2["@storybook/theming"]="__STORYBOOKTHEMING__",Keys2["@storybook/api"]="__STORYBOOKAPI__",Keys2["@storybook/manager-api"]="__STORYBOOKAPI__",Keys2["@storybook/addons"]="__STORYBOOKADDONS__",Keys2["@storybook/client-logger"]="__STORYBOOKCLIENTLOGGER__",Keys2))(Keys||{});var createModuleInfo=m=>({type:"esm",varName:Keys[m],namedExports:exports_default[m],defaultExport:!0}),definitions=Object.keys(Keys).reduce((acc,key)=>(acc[key]=createModuleInfo(key),acc),{});0&&(module.exports={definitions}); 2 | -------------------------------------------------------------------------------- /storybook-static/sb-manager/index.js: -------------------------------------------------------------------------------- 1 | import{Provider,Root,renderStorybookUI}from"./chunk-DAJ4OSDJ.js";import"./chunk-SZNM6KS3.js";import"./chunk-FWZ33S65.js";import"./chunk-NFZCBIX3.js";import"./chunk-ZEU7PDD3.js";export{Provider,Root,renderStorybookUI}; 2 | -------------------------------------------------------------------------------- /storybook-static/sb-manager/syntaxhighlighter-NMPM6SWI-GZTSOZ5L.js: -------------------------------------------------------------------------------- 1 | import{SyntaxHighlighter2,createCopyToClipboardFunction,syntaxhighlighter_default}from"./chunk-SZNM6KS3.js";import"./chunk-NFZCBIX3.js";import"./chunk-ZEU7PDD3.js";export{SyntaxHighlighter2 as SyntaxHighlighter,createCopyToClipboardFunction,syntaxhighlighter_default as default}; 2 | -------------------------------------------------------------------------------- /storybook-static/sb-preview/globals.js: -------------------------------------------------------------------------------- 1 | "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var globals_exports={};__export(globals_exports,{globals:()=>globals});module.exports=__toCommonJS(globals_exports);var globals={"@storybook/addons":"__STORYBOOK_MODULE_ADDONS__","@storybook/global":"__STORYBOOK_MODULE_GLOBAL__","@storybook/channel-postmessage":"__STORYBOOK_MODULE_CHANNEL_POSTMESSAGE__","@storybook/channel-websocket":"__STORYBOOK_MODULE_CHANNEL_WEBSOCKET__","@storybook/channels":"__STORYBOOK_MODULE_CHANNELS__","@storybook/client-api":"__STORYBOOK_MODULE_CLIENT_API__","@storybook/client-logger":"__STORYBOOK_MODULE_CLIENT_LOGGER__","@storybook/core-client":"__STORYBOOK_MODULE_CORE_CLIENT__","@storybook/core-events":"__STORYBOOK_MODULE_CORE_EVENTS__","@storybook/preview-web":"__STORYBOOK_MODULE_PREVIEW_WEB__","@storybook/preview-api":"__STORYBOOK_MODULE_PREVIEW_API__","@storybook/store":"__STORYBOOK_MODULE_STORE__"};0&&(module.exports={globals}); 2 | -------------------------------------------------------------------------------- /storybook-static/stories.json: -------------------------------------------------------------------------------- 1 | {"v":3,"stories":{"configure-your-project--docs":{"id":"configure-your-project--docs","title":"Configure your project","name":"Docs","importPath":"./src/stories/Configure.mdx","storiesImports":[],"tags":["unattached-mdx","docs"],"kind":"Configure your project","story":"Docs","parameters":{"__id":"configure-your-project--docs","docsOnly":true,"fileName":"./src/stories/Configure.mdx"}},"example-button--docs":{"id":"example-button--docs","title":"Example/Button","name":"Docs","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","docs"],"storiesImports":[],"kind":"Example/Button","story":"Docs","parameters":{"__id":"example-button--docs","docsOnly":true,"fileName":"./src/stories/Button.stories.ts"}},"example-button--primary":{"id":"example-button--primary","name":"Primary","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"],"kind":"Example/Button","story":"Primary","parameters":{"__id":"example-button--primary","docsOnly":false,"fileName":"./src/stories/Button.stories.ts"}},"example-button--secondary":{"id":"example-button--secondary","name":"Secondary","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"],"kind":"Example/Button","story":"Secondary","parameters":{"__id":"example-button--secondary","docsOnly":false,"fileName":"./src/stories/Button.stories.ts"}},"example-button--large":{"id":"example-button--large","name":"Large","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"],"kind":"Example/Button","story":"Large","parameters":{"__id":"example-button--large","docsOnly":false,"fileName":"./src/stories/Button.stories.ts"}},"example-button--small":{"id":"example-button--small","name":"Small","title":"Example/Button","importPath":"./src/stories/Button.stories.ts","tags":["autodocs","story"],"kind":"Example/Button","story":"Small","parameters":{"__id":"example-button--small","docsOnly":false,"fileName":"./src/stories/Button.stories.ts"}},"example-header--docs":{"id":"example-header--docs","title":"Example/Header","name":"Docs","importPath":"./src/stories/Header.stories.ts","tags":["autodocs","docs"],"storiesImports":[],"kind":"Example/Header","story":"Docs","parameters":{"__id":"example-header--docs","docsOnly":true,"fileName":"./src/stories/Header.stories.ts"}},"example-header--logged-in":{"id":"example-header--logged-in","name":"Logged In","title":"Example/Header","importPath":"./src/stories/Header.stories.ts","tags":["autodocs","story"],"kind":"Example/Header","story":"Logged In","parameters":{"__id":"example-header--logged-in","docsOnly":false,"fileName":"./src/stories/Header.stories.ts"}},"example-header--logged-out":{"id":"example-header--logged-out","name":"Logged Out","title":"Example/Header","importPath":"./src/stories/Header.stories.ts","tags":["autodocs","story"],"kind":"Example/Header","story":"Logged Out","parameters":{"__id":"example-header--logged-out","docsOnly":false,"fileName":"./src/stories/Header.stories.ts"}},"example-page--logged-out":{"id":"example-page--logged-out","name":"Logged Out","title":"Example/Page","importPath":"./src/stories/Page.stories.ts","tags":["story"],"kind":"Example/Page","story":"Logged Out","parameters":{"__id":"example-page--logged-out","docsOnly":false,"fileName":"./src/stories/Page.stories.ts"}},"example-page--logged-in":{"id":"example-page--logged-in","name":"Logged In","title":"Example/Page","importPath":"./src/stories/Page.stories.ts","tags":["play-fn","story"],"kind":"Example/Page","story":"Logged In","parameters":{"__id":"example-page--logged-in","docsOnly":false,"fileName":"./src/stories/Page.stories.ts"}}}} 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "baseUrl": "src", 9 | "allowSyntheticDefaultImports": true, 10 | "types": ["cypress"], 11 | "allowImportingTsExtensions": true, 12 | 13 | 14 | /* Bundler mode */ 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": true, 24 | "noImplicitAny": false, 25 | "noUnusedParameters": true, 26 | "noFallthroughCasesInSwitch": true, 27 | }, 28 | "include": ["src", "cypress/e2e/3-order/order-cy.ts", "**/*.ts"], 29 | "references": [{ "path": "./tsconfig.node.json" }] 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import tsconfigPaths from "vite-tsconfig-paths"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tsconfigPaths()], 8 | }); 9 | --------------------------------------------------------------------------------