diff --git a/README.md b/README.md index af19b7ef1..a9c7d1769 100644 --- a/README.md +++ b/README.md @@ -1 +1,52 @@ # react-modules + +### Modal component + +- [x] 모달 props + - [x] 모달 위치 + - [x] 모달 타이틀 + - [x] 모달 내용 + - [x] 모달 닫는 방식 + - [x] prop 이름 : closeButtonPosition : 'top' | 'bottom' +- [x] 모달 event + - [x] 열기 + - [x] 닫기 - deem 눌러도 닫혀야된다. + - [ ] 확인 - optional +- [x] npm으로 배포하기 +- [x] 설치 후 import해서 사용하기 + +### Payment custom hook + +- 유효성 검사 결과와 에러 정보를 반환한다. + +- [x] useCardNumber + - [x] 숫자여야한다. + - [x] 16자리여야한다. +- [x] useCardHolder + - [x] 영어 대문자+공백만 입력가능하다. + - [x] 공백 포함 15자까지만 가능하다. +- [x] useExpiryDate + - [x] 월은 1~12만 입력 가능하다. (월도 두자리로 입력해달라는 description 추가 ) + - [x] 년도 2자리 숫자만 입력 가능하다. + - [x] 년,월 조합을 봤을 때 오늘보다 과거이면 에러를 낸다. +- [x] useCVC + - [x] 3자리 숫자만 입력 가능하다. +- [x] useCardType + - [x] 선택한 값이 있는지 검증한다. +- [x] usePassword + - [x] 2자리 숫자만 입력 가능하다. +- [x] npm으로 배포하기 +- [x] 설치 후 import해서 사용하기 + + ### Storybook + + - [x] 모달 위치에 대한 테스트 시나리오 + - [x] 모달 내용에 대한 테스트 시나리오 + - [x] 모달 이벤트 핸들러에 대한 테스트 시나리오 + + ### RTL + + - [ ] 페이먼트 유효성 검사 커스텀 훅의 동작을 검증 + - [ ] 다양한 입력 값에 대한 커스텀 훅의 결과 + - [ ] 유효성 통과하는 경우 + - [ ] 유효성 통과하지 않는 경우 diff --git a/components/.prettierrc b/components/.prettierrc new file mode 100644 index 000000000..efd721c1c --- /dev/null +++ b/components/.prettierrc @@ -0,0 +1,10 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "trailingComma": "es5", + "semi": true, + "singleQuote": false, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "auto" +} diff --git a/components/README.md b/components/README.md index 47f87aca6..b63255df8 100644 --- a/components/README.md +++ b/components/README.md @@ -1 +1,63 @@ -# Button Module +# choco-modal-component + +## Install + +이 라이브러리를 사용하기 위해서는 다음 패키지를 설치해야 합니다: + +```bash +npm install choco-modal-component +``` + +## Usage + +React 컴포넌트에서 모달 라이브러리를 사용하려면 다음 단계를 따르세요 + +```jsx +import { useModal, Modal } from "choco-modal-component"; + +function App() { + const { isOpen, openModal, closeModal } = useModal(); + + return ( + <> + + + + {/* 모달 내용 */} + + + ); +} + +export default App; +``` + +## API + +### ModalComponent + +`ModalComponent`는 다음 props를 받습니다: + +| props | description | +| ---------------------------- | --------------------------------------------------------------- | +| `modalPosition` (필수) | 모달의 위치입니다. 가능한 값은 `"center"`와 `"bottom"`입니다. | +| `title` (필수) | 모달의 제목입니다. | +| `children` (필수) | 모달의 내용입니다. | +| `closeButtonPosition` (필수) | 닫기 버튼의 위치입니다. 가능한 값은 `"top"`과 `"bottom"`입니다. | + +### useModal + +`useModal` 훅은 다음 속성을 가진 객체를 반환합니다: + +| props | description | +| ---------------- | --------------------------------------------------------- | +| `isOpen` | 모달이 현재 열려 있는지 여부를 나타내는 boolean 값입니다. | +| `openModal` | 모달을 열기 위한 함수입니다. | +| `closeModal` | 모달을 닫기 위한 함수입니다. | +| `ModalComponent` | 지정된 props로 모달을 렌더링하는 컴포넌트입니다. | diff --git a/components/custom.d.ts b/components/custom.d.ts new file mode 100644 index 000000000..66b245666 --- /dev/null +++ b/components/custom.d.ts @@ -0,0 +1,4 @@ +declare module "*.png" { + const value: string; + export default value; +} diff --git a/components/package-lock.json b/components/package-lock.json index 9fbf5f751..f2e82ab25 100644 --- a/components/package-lock.json +++ b/components/package-lock.json @@ -1,13 +1,17 @@ { - "name": "compoents", - "version": "0.0.1", + "name": "choco-modal-component", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "compoents", - "version": "0.0.1", + "name": "choco-modal-component", + "version": "0.0.3", "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "choco-modal-component": "^0.0.2", + "choriver-modal-component": "^0.0.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -28,6 +32,7 @@ "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.5.0", + "chromatic": "^11.3.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", @@ -37,6 +42,10 @@ "typescript": "^5.2.2", "vite": "^5.2.0", "vite-plugin-dts": "^3.9.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5" } }, "node_modules/@adobe/css-tools": { @@ -74,7 +83,6 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, "dependencies": { "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" @@ -283,7 +291,6 @@ "version": "7.24.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", - "dev": true, "dependencies": { "@babel/types": "^7.24.0" }, @@ -405,7 +412,6 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -414,7 +420,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -460,7 +465,6 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -1927,7 +1931,6 @@ "version": "7.24.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1974,7 +1977,6 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -2026,15 +2028,163 @@ "node": ">=10.0.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "dev": true, "peerDependencies": { "react": ">=16.8.0" } }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -5469,6 +5619,11 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -6270,7 +6425,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -6282,7 +6436,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -6290,8 +6443,7 @@ "node_modules/ansi-styles/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/anymatch": { "version": "3.1.3", @@ -6428,6 +6580,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", @@ -6733,7 +6899,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -6780,7 +6945,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -6802,6 +6966,22 @@ "node": "*" } }, + "node_modules/choco-modal-component": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/choco-modal-component/-/choco-modal-component-0.0.2.tgz", + "integrity": "sha512-DFV82sc9hnMAKT+Z5JvdyLGysVXBFSxBHZlDZgfpwZjQfToOMRhKsayGQZmSVEGktdnsKMV31+bDG1dYlFcuPw==", + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "choriver-modal-component": "^0.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -6838,6 +7018,37 @@ "node": ">= 6" } }, + "node_modules/choriver-components": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/choriver-components/-/choriver-components-0.0.1.tgz", + "integrity": "sha512-ztcDZr5lDQE7Uuaxrfizgu3i+kvyIteCnVjneH0L2WstzB90Cj4U141YjoPK3Lq5GDtlwZ6/ksF15XIdRAC7eA==", + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5" + } + }, + "node_modules/choriver-modal-component": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/choriver-modal-component/-/choriver-modal-component-0.0.2.tgz", + "integrity": "sha512-tVMdN7UWg1NV1l4RiQtCCdmKD/B99M7N8SCDZXFNz0ORiiXIEpf/H1Chyf7tp5bNqt4hWwOJnRvql51Ya1YoNA==", + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "choriver-components": "^0.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -7184,6 +7395,21 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7216,8 +7442,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/de-indent": { "version": "1.0.2", @@ -7647,7 +7872,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -7774,7 +7998,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -8674,6 +8897,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8848,7 +9076,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9089,7 +9316,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -9149,7 +9375,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -9205,6 +9430,19 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -9293,7 +9531,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -9309,7 +9546,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -9433,8 +9669,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { "version": "1.0.4", @@ -9492,7 +9727,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -10476,8 +10710,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -10587,8 +10820,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -11586,7 +11818,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -11598,7 +11829,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -11667,8 +11897,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.10.2", @@ -11705,7 +11934,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -11754,8 +11982,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -12381,8 +12608,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -12497,7 +12723,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -13176,11 +13401,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -13192,7 +13421,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -13452,7 +13680,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -14317,6 +14544,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/components/package.json b/components/package.json index ec502ac0f..1932ddbfe 100644 --- a/components/package.json +++ b/components/package.json @@ -1,7 +1,7 @@ { - "name": "compoents", - "version": "0.0.1", - "description": "npm modules", + "name": "choco-modal-component", + "version": "0.0.3", + "description": "초코가 만든 Modal component 라이브러리", "type": "module", "types": "dist/index.d.ts", "main": "dist/index.js", @@ -18,6 +18,10 @@ "build-storybook": "storybook build" }, "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "choco-modal-component": "^0.0.2", + "choriver-modal-component": "^0.0.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -38,6 +42,7 @@ "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.5.0", + "chromatic": "^11.3.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", @@ -47,5 +52,9 @@ "typescript": "^5.2.2", "vite": "^5.2.0", "vite-plugin-dts": "^3.9.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5" } } diff --git a/components/public/image/closeButton.png b/components/public/image/closeButton.png new file mode 100644 index 000000000..99e06ba66 Binary files /dev/null and b/components/public/image/closeButton.png differ diff --git a/components/src/App.tsx b/components/src/App.tsx index 829d564cf..94d28a730 100644 --- a/components/src/App.tsx +++ b/components/src/App.tsx @@ -1,10 +1,34 @@ +import { useModal, Modal } from "choco-modal-component"; import React from "react"; import "./App.css"; function App() { + const { isOpen, openModal, closeModal } = useModal(); + return ( <> + +
+ 리액트에서의 컴포넌트도 캡슐화 되어 있고, 재사용 및 재구성할 수 있습니다. 리액트를 이용해 + 애플리케이션을 개발한다는 것은 레고를 이용해 조립하는 것과 비슷합니다. 차이점은 부품이 + 부족할 일이 없고, 반드시 한 번은 재사용할 컴포넌트를 만들어야 한다는 것입니다. 그리고 이 + 컴포넌트는 상호작용하며 함께 동작합니다. 그리고 상호작용하는 여러 컴포넌트들을 '조합' 해서 + 새로운 형태의 '합성' 컴포넌트를 만들 수 있습니다. 컴포넌트의 조합은 리액트의 가장 큰 장점 + 중 하나인데, 컴포넌트를 만든 후에는 재사용을 통해 애플리케이션의 다른 부분에서도 사용할 수 + 있습니다. 재사용 가능한 특징은 애플리케이션의 중요한 요소입니다. 복제된 여러 컴포넌트들이 + 독립적으로 실행할 수 있을 때 우리는 사이드 이펙트를 줄이고, 개발의 효율성을 더욱 극대화할 + 수 있습니다. 페이스북의 경우 서로 여러개의 채팅창을 보여주고 있지만 독립적으로 작동할 수 + 있습니다. 이는 재사용 가능한 컴포넌트를 만들었기 때문에 가능한 것이죠. +
+

Component Modules

+ ); } diff --git a/components/src/lib/CloseButton/CloseButton.style.ts b/components/src/lib/CloseButton/CloseButton.style.ts new file mode 100644 index 000000000..c383c591a --- /dev/null +++ b/components/src/lib/CloseButton/CloseButton.style.ts @@ -0,0 +1,17 @@ +import styled from "@emotion/styled"; + +export const ButtonWrapper = styled.button` + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: none; + background: none; + cursor: pointer; + + img { + width: 24px; + height: 24px; + } +`; diff --git a/components/src/lib/CloseButton/CloseButton.tsx b/components/src/lib/CloseButton/CloseButton.tsx new file mode 100644 index 000000000..d03295393 --- /dev/null +++ b/components/src/lib/CloseButton/CloseButton.tsx @@ -0,0 +1,13 @@ +import { MouseEventHandler } from "react"; +import { ButtonWrapper } from "./CloseButton.style"; + +interface CloseButtonProps { + children: React.ReactElement | string; + onClick: MouseEventHandler; +} + +const CloseButton = ({ children, onClick }: CloseButtonProps) => { + return {children}; +}; + +export default CloseButton; diff --git a/components/src/lib/Modal/Modal.style.ts b/components/src/lib/Modal/Modal.style.ts new file mode 100644 index 000000000..3f62ee164 --- /dev/null +++ b/components/src/lib/Modal/Modal.style.ts @@ -0,0 +1,76 @@ +import styled from "@emotion/styled"; +import { ModalProps } from "./Modal"; + +interface ModalDimProps { + isOpen: boolean; +} + +export const ModalDim = styled.div` + display: ${({ isOpen }) => (isOpen ? "block" : "none")}; + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.35); + z-index: 1000; +`; + +export const ModalContainer = styled.div>` + display: flex; + z-index: 1001; + flex-direction: column; + ${({ closeButtonPosition }) => + closeButtonPosition === "bottom" && "justify-content: space-between;"} + gap: 16px; + position: absolute; + min-height: 216px; + max-height: 70%; + padding: 24px 32px; + box-sizing: border-box; + border-radius: 8px; + background: rgba(255, 255, 255, 1); + color: rgba(0, 0, 0, 1); + + ${({ modalPosition }) => { + switch (modalPosition) { + case "center": + return ` + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 304px; + `; + case "bottom": + return ` + bottom: 0; + left: 0; + right: 0; + width: 100%; + `; + default: + return ""; + } + }} +`; + +export const ModalHeader = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + h1 { + width: 100%; + font-family: Noto Sans KR; + font-size: 18px; + font-weight: 700; + line-height: 26.06px; + text-align: left; + color: rgba(0, 0, 0, 1); + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +`; diff --git a/components/src/lib/Modal/Modal.tsx b/components/src/lib/Modal/Modal.tsx new file mode 100644 index 000000000..44e0bcc94 --- /dev/null +++ b/components/src/lib/Modal/Modal.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import CloseButton from "../CloseButton/CloseButton"; +import Button from "../common/Button"; +import { ModalContainer, ModalDim, ModalHeader } from "./Modal.style"; + +export interface ModalProps { + modalPosition: "center" | "bottom"; + title: string; + children: React.ReactNode; + closeButtonPosition: "top" | "bottom"; + isOpen: boolean; + onClose: (e: React.MouseEvent) => void; +} + +export const Modal = ({ + modalPosition, + title, + children, + closeButtonPosition, + isOpen, + onClose, +}: ModalProps) => { + if (!isOpen) return null; + + return ( + + + +

{title}

+ {closeButtonPosition === "top" && ( + onClose(e)}> + + + )} +
+ {children} + {closeButtonPosition === "bottom" && ( +