diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0a72520 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/package-lock.json b/package-lock.json index 43e807c..79b5858 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,19 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/user-event": "^13.5.0", + "@types/react-custom-scrollbars": "^4.0.10", "react": "^18.1.0", + "react-custom-scrollbars": "^4.2.1", "react-dom": "^18.1.0", + "react-icons": "^4.3.1", + "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", + "recoil": "^0.7.3-alpha.2", + "styled-components": "^5.3.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/styled-components": "^5.1.25" } }, "node_modules/@ampproject/remapping": { @@ -1972,6 +1981,29 @@ "postcss": "^8.3" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", + "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", + "dependencies": { + "@emotion/memoize": "^0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, "node_modules/@eslint/eslintrc": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", @@ -3515,6 +3547,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -3618,6 +3660,14 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-custom-scrollbars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/react-custom-scrollbars/-/react-custom-scrollbars-4.0.10.tgz", + "integrity": "sha512-1T430E+usndUjymkXB8k/zGpWehggircq/QaQMuFLMJceccAcD9vcmbUXF1LjeVP/+P4wI/bad6BF1E+7IGlqA==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.0.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.3.tgz", @@ -3674,6 +3724,17 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/styled-components": { + "version": "5.1.25", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz", + "integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.3", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", @@ -4156,6 +4217,11 @@ "node": ">=0.4.0" } }, + "node_modules/add-px-to-style": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", + "integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo=" + }, "node_modules/address": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", @@ -4702,6 +4768,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz", + "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-module-imports": "^7.16.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.11", + "picomatch": "^2.3.0" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, "node_modules/babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", @@ -5016,6 +5102,11 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -5482,6 +5573,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", @@ -5663,6 +5762,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", + "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -6114,6 +6223,16 @@ "utila": "~0.4" } }, + "node_modules/dom-css": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", + "integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=", + "dependencies": { + "add-px-to-style": "1.0.0", + "prefix-style": "2.0.1", + "to-camel-case": "1.0.0" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -7909,6 +8028,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -7990,6 +8114,27 @@ "he": "bin/he" } }, + "node_modules/history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "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/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -13010,6 +13155,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prefix-style": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", + "integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY=" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -13272,6 +13422,20 @@ "node": ">=14" } }, + "node_modules/react-custom-scrollbars": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz", + "integrity": "sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts=", + "dependencies": { + "dom-css": "^2.0.0", + "prop-types": "^15.5.10", + "raf": "^3.1.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -13406,6 +13570,14 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-icons": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -13419,6 +13591,30 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", + "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", + "dependencies": { + "history": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", + "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", + "dependencies": { + "history": "^5.2.0", + "react-router": "6.3.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -13515,6 +13711,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.7.3-alpha.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.3-alpha.2.tgz", + "integrity": "sha512-8LL4T8Bh7WMA5/ka+420PCenaUFie0457kyxX7cy/gsyRlbIGYpyNkAyU0znCvtHTN2clVYkTjGF6zKJ/DzzJg==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -14172,6 +14387,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -14569,6 +14789,36 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", + "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", + "hasInstallScript": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, "node_modules/stylehacks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", @@ -14959,6 +15209,14 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" }, + "node_modules/to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", + "dependencies": { + "to-space-case": "^1.0.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -14967,6 +15225,11 @@ "node": ">=4" } }, + "node_modules/to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14978,6 +15241,14 @@ "node": ">=8.0" } }, + "node_modules/to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "dependencies": { + "to-no-case": "^1.0.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -17516,6 +17787,29 @@ "postcss-value-parser": "^4.2.0" } }, + "@emotion/is-prop-valid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", + "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", + "requires": { + "@emotion/memoize": "^0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, "@eslint/eslintrc": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", @@ -18620,6 +18914,16 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -18723,6 +19027,14 @@ "csstype": "^3.0.2" } }, + "@types/react-custom-scrollbars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/react-custom-scrollbars/-/react-custom-scrollbars-4.0.10.tgz", + "integrity": "sha512-1T430E+usndUjymkXB8k/zGpWehggircq/QaQMuFLMJceccAcD9vcmbUXF1LjeVP/+P4wI/bad6BF1E+7IGlqA==", + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "18.0.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.3.tgz", @@ -18779,6 +19091,17 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/styled-components": { + "version": "5.1.25", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz", + "integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "@types/testing-library__jest-dom": { "version": "5.14.3", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", @@ -19134,6 +19457,11 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, + "add-px-to-style": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", + "integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo=" + }, "address": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", @@ -19520,6 +19848,23 @@ "@babel/helper-define-polyfill-provider": "^0.3.1" } }, + "babel-plugin-styled-components": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz", + "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-module-imports": "^7.16.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.11", + "picomatch": "^2.3.0" + } + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, "babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", @@ -19765,6 +20110,11 @@ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -20120,6 +20470,11 @@ "postcss-selector-parser": "^6.0.9" } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + }, "css-declaration-sorter": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", @@ -20227,6 +20582,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "css-to-react-native": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", + "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -20560,6 +20925,16 @@ "utila": "~0.4" } }, + "dom-css": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", + "integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=", + "requires": { + "add-px-to-style": "1.0.0", + "prefix-style": "2.0.1", + "to-camel-case": "1.0.0" + } + }, "dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -21857,6 +22232,11 @@ "duplexer": "^0.1.2" } }, + "hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -21911,6 +22291,29 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "requires": { + "@babel/runtime": "^7.7.6" + } + }, + "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==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "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==" + } + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -25400,6 +25803,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "prefix-style": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", + "integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY=" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -25592,6 +26000,16 @@ "whatwg-fetch": "^3.6.2" } }, + "react-custom-scrollbars": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz", + "integrity": "sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts=", + "requires": { + "dom-css": "^2.0.0", + "prop-types": "^15.5.10", + "raf": "^3.1.0" + } + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -25692,6 +26110,12 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "react-icons": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -25702,6 +26126,23 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, + "react-router": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", + "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", + "requires": { + "history": "^5.2.0" + } + }, + "react-router-dom": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", + "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", + "requires": { + "history": "^5.2.0", + "react-router": "6.3.0" + } + }, "react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -25775,6 +26216,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.7.3-alpha.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.3-alpha.2.tgz", + "integrity": "sha512-8LL4T8Bh7WMA5/ka+420PCenaUFie0457kyxX7cy/gsyRlbIGYpyNkAyU0znCvtHTN2clVYkTjGF6zKJ/DzzJg==", + "requires": { + "hamt_plus": "1.0.2" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -26254,6 +26703,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -26553,6 +27007,23 @@ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", "requires": {} }, + "styled-components": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", + "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + } + }, "stylehacks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", @@ -26850,11 +27321,24 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" }, + "to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", + "requires": { + "to-space-case": "^1.0.0" + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, + "to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -26863,6 +27347,14 @@ "is-number": "^7.0.0" } }, + "to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "requires": { + "to-no-case": "^1.0.0" + } + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index 7f031bc..3829677 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,15 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/user-event": "^13.5.0", + "@types/react-custom-scrollbars": "^4.0.10", "react": "^18.1.0", + "react-custom-scrollbars": "^4.2.1", "react-dom": "^18.1.0", + "react-icons": "^4.3.1", + "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", + "recoil": "^0.7.3-alpha.2", + "styled-components": "^5.3.5", "web-vitals": "^2.1.4" }, "scripts": { @@ -34,5 +40,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/styled-components": "^5.1.25" } } diff --git a/public/assets/user0.jpg b/public/assets/user0.jpg new file mode 100644 index 0000000..9db8ee3 Binary files /dev/null and b/public/assets/user0.jpg differ diff --git a/public/assets/user1.jpg b/public/assets/user1.jpg new file mode 100644 index 0000000..4322f40 Binary files /dev/null and b/public/assets/user1.jpg differ diff --git a/public/assets/user2.jpg b/public/assets/user2.jpg new file mode 100644 index 0000000..46d6afb Binary files /dev/null and b/public/assets/user2.jpg differ diff --git a/public/assets/user3.jpg b/public/assets/user3.jpg new file mode 100644 index 0000000..7124a99 Binary files /dev/null and b/public/assets/user3.jpg differ diff --git a/public/assets/user4.jpg b/public/assets/user4.jpg new file mode 100644 index 0000000..d0b39cb Binary files /dev/null and b/public/assets/user4.jpg differ diff --git a/public/assets/user5.jpg b/public/assets/user5.jpg new file mode 100644 index 0000000..8ad62c3 Binary files /dev/null and b/public/assets/user5.jpg differ diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 772afaf..0000000 --- a/src/App.js +++ /dev/null @@ -1,5 +0,0 @@ -function App() { - return
15๊ธฐ ํŒŒ์ดํŒ… ๐Ÿ’–
; -} - -export default App; diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..a20fed0 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,37 @@ +import { Route, Routes } from 'react-router-dom'; +import GlobalStyle from './Globalstyle'; +import styled from 'styled-components'; +import FriendList from './routes/FriendList'; +import ChatList from './routes/ChatList'; +import Setting from './routes/Setting'; +import MessengerBox from './components/Message/MessengerBox'; +import { RecoilRoot } from 'recoil'; + +const App = () => { + return ( + + + + + } /> + } /> + } /> + } /> + + + + ); +}; +export default App; + +const MessengerContainer = styled.div` + /* ๋„ˆ๋น„ | ์Šคํƒ€์ผ | ์ƒ‰ */ + + margin: 0 auto; + margin-top: 5rem; + background: white; + border-radius: 0.5rem; + height: 43rem; + width: 26rem; + justify-content: flex-start; +`; diff --git a/src/Globalstyle.tsx b/src/Globalstyle.tsx new file mode 100644 index 0000000..cd1c58f --- /dev/null +++ b/src/Globalstyle.tsx @@ -0,0 +1,29 @@ +import { createGlobalStyle } from 'styled-components'; + +//ํŽ˜์ด์ง€ ์ „์ฒด์— ์ ์šฉ๋  style +const GlobalStyle = createGlobalStyle` + +button { + &:hover{ + cursor: pointer; + } + border: none; + background: none; + font-size: 17px; +} + +::-webkit-scrollbar { + width: 0.9rem; + margin: 0; + } + + ::-webkit-scrollbar-thumb { + height: 17%; + background-color: #c2bbbb; + border-radius: 10px; + background-clip: padding-box; + border: 0.3rem solid transparent; + } +`; + +export default GlobalStyle; diff --git a/src/components/ChatList/Chattings.tsx b/src/components/ChatList/Chattings.tsx new file mode 100644 index 0000000..b8603c7 --- /dev/null +++ b/src/components/ChatList/Chattings.tsx @@ -0,0 +1,34 @@ +import styled from 'styled-components'; +import { IChattingsProps } from '../../interface/interface'; +import { + LinkToChat, + ListItem, + ProfileImg, + UserName, +} from '../layout/CommonStyle'; +const Chattings = ({ userId, userName, message }: IChattingsProps) => { + return ( + + + + + {userName} + {message} + + + + ); +}; + +export default Chattings; + +const ChattingRooms = styled.div` + display: flex; + padding-left: 1rem; + margin: 0.5rem; +`; + +const LastMessage = styled.div` + font-weight: lighter; + font-size: 14px; +`; diff --git a/src/components/FriendList/Friends.tsx b/src/components/FriendList/Friends.tsx new file mode 100644 index 0000000..fd210a6 --- /dev/null +++ b/src/components/FriendList/Friends.tsx @@ -0,0 +1,34 @@ +import styled from 'styled-components'; +import { IFriendsProps } from '../../interface/interface'; +import { + LinkToChat, + ListItem, + ProfileImg, + UserName, +} from '../layout/CommonStyle'; +const Friends = ({ userProfile, userName, userStatus }: IFriendsProps) => { + return ( + + + + + {userName} + {userStatus} + + + + ); +}; + +export default Friends; + +const FriendList = styled.div` + display: flex; + padding-left: 1rem; + margin: 0.5rem; +`; + +const UserStatus = styled.div` + font-weight: lighter; + font-size: 14px; +`; diff --git a/src/components/FriendList/SearchUser.tsx b/src/components/FriendList/SearchUser.tsx new file mode 100644 index 0000000..aa16174 --- /dev/null +++ b/src/components/FriendList/SearchUser.tsx @@ -0,0 +1,43 @@ +import styled from 'styled-components'; +import { BsSearch } from 'react-icons/bs'; +import useInput from '../../hooks/useInput'; +import { useEffect } from 'react'; +import { IFilteredUser } from '../../interface/interface'; + +const SearchUser = ({ filteredUser }: IFilteredUser) => { + const { textinput, handleInputChange } = useInput(''); + + useEffect(() => filteredUser(textinput), [filteredUser, textinput]); + return ( + + + + + ); +}; +export default SearchUser; + +const SearchInput = styled.input` + margin-left: 1rem; + width: 16rem; + height: 1.5rem; + border-radius: 0.5rem; + border: 0.08rem solid #c2bbbb; + padding-left: 1rem; +`; +const SearchContainer = styled.div` + padding: 1rem; + height: 2rem; + text-align: center; + + .searchLogo { + line-height: 3rem; + position: relative; + top: 0.3rem; + } +`; diff --git a/src/components/Message/MessageList.tsx b/src/components/Message/MessageList.tsx new file mode 100644 index 0000000..1004e11 --- /dev/null +++ b/src/components/Message/MessageList.tsx @@ -0,0 +1,37 @@ +import { useEffect, useRef } from 'react'; +import styled from 'styled-components'; +import SingleMessage from './SingleMessage'; +import { IMessageData } from '../../interface/interface'; + +const MessageList = ({ messageData }: { messageData: Array }) => { + const scrollbarRef = useRef(null); + + const scrollToBottom = () => { + if (scrollbarRef.current) { + scrollbarRef.current.scrollTop = scrollbarRef.current.scrollHeight; + } + }; + useEffect(() => { + scrollToBottom(); + }, [messageData]); + + return ( + + + {messageData.map((chat: IMessageData) => ( + + ))} + + + ); +}; + +export default MessageList; + +const ListContainer = styled.div` + height: 26rem; + padding: 1rem; + overflow-y: auto; + overflow-x: hidden; +`; +const ShowList = styled.div``; diff --git a/src/components/Message/MessengerBox.tsx b/src/components/Message/MessengerBox.tsx new file mode 100644 index 0000000..2bb4fff --- /dev/null +++ b/src/components/Message/MessengerBox.tsx @@ -0,0 +1,41 @@ +import UserNav from '../layout/UserNav'; +import { useState } from 'react'; +import user from '../../data/user.json'; +import message from '../../data/message.json'; +import MessengerInput from './MessengerInput'; +import MessageList from './MessageList'; +import { useParams } from 'react-router-dom'; +import { useRecoilState } from 'recoil'; +import { messageDataState } from '../../state/MessageDataState'; + +const MessengerBox = () => { + const otherUser = useParams(); + const userindex = message.findIndex( + (message) => message.userName === otherUser.userName + ); + const userMessageData = message[userindex]; + + const [currentUser, setCurrentUser] = useState(user[0]); + const [messageData, setMessageData] = useRecoilState(messageDataState); + setMessageData(userMessageData.messages); + console.log(messageData); + + return ( + <> + + + + + + ); +}; + +export default MessengerBox; diff --git a/src/components/Message/MessengerInput.tsx b/src/components/Message/MessengerInput.tsx new file mode 100644 index 0000000..1fb1abd --- /dev/null +++ b/src/components/Message/MessengerInput.tsx @@ -0,0 +1,91 @@ +import { useCallback } from 'react'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import styled from 'styled-components'; +import useInput from '../../hooks/useInput'; +import { IMessageData, IMessengerInputProps } from '../../interface/interface'; +import { messageDataState } from '../../state/MessageDataState'; +const MessengerInput = ({ currentUser, messageData }: any) => { + const { textinput, handleInputChange, handleInputInitialize } = useInput(''); + + const setMessageData = useSetRecoilState(messageDataState); + console.log(setMessageData); + const handleInputSubmit = useCallback( + (e: React.FormEvent) => { + //๊ณต๋ฐฑ์ด ์•„๋‹ ๋•Œ์—๋งŒ send ๊ฐ€๋Šฅ + if (textinput.replace(/\s+/g, '')) { + const date = new Date(); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const messageObject = { + userId: currentUser.userId, + userName: currentUser.userName, + text: textinput, + time: `${hours}:${minutes}`, + }; + + setMessageData([...messageData, messageObject]); + + //๊ณต๋ฐฑ์ผ ๊ฒฝ์šฐ alert + } else { + alert('๋ฉ”์„ธ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š” ! '); + } + + handleInputInitialize(); + + //์ƒˆ๋กœ๊ณ ์นจ ๋ฐฉ์ง€ + e.preventDefault(); + }, + [messageData, textinput] + ); + console.log(messageData); + + return ( + + + + + + ๐Ÿ•ธ + + ); +}; + +export default MessengerInput; + +const SubmitForm = styled.form` + align-items: center; + border-top-style: solid; + border-color: #c2bbbb; + border-width: 0.08rem; + padding: 0; + height: 6rem; + display: flex; + justify-content: space-evenly; +`; + +const InputBoxWrapper = styled.div` + padding-left: 2rem; +`; + +const InputBox = styled.input` + padding-left: 1rem; + line-height: 5.5rem; + border-color: #c2bbbb; + border-width: 0.08rem; + border-style: solid; + border-radius: 0.5rem; + width: 18rem; + height: 2.3rem; + margin: 0; + line-height: 6rem; +`; +const SendButton = styled.button` + line-height: 6rem; + background-color: transparent; + font-size: 1.5rem; +`; diff --git a/src/components/Message/SingleMessage.tsx b/src/components/Message/SingleMessage.tsx new file mode 100644 index 0000000..14f3592 --- /dev/null +++ b/src/components/Message/SingleMessage.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import { IMessageData, IUserNameProps } from '../../interface/interface'; +const SingleMessage = ({ chat }: { chat: IMessageData }) => { + const { userId, userName, text, time } = chat; + return ( + + + + {userName} + {text} + + {time} + + ); +}; + +export default React.memo(SingleMessage); + +const SingleMessageContainer = styled.div` + padding: 1rem; + display: flex; + ${({ userName }) => + userName === 'chaaerim' + ? css` + flex-direction: row-reverse; + ` + : css` + justify-content: flex-start; + `} +`; + +const UserName = styled.div` + font-size: 14px; + padding: 0.2rem; + ${({ userName }) => + userName === 'chaaerim' + ? css` + margin-left: auto; + ` + : css` + justify-content: flex-start; + `} +`; +const MessageText = styled.div` + align-items: center; + font-size: 13px; + padding: 0.5rem; + line-height: 1.5rem; + ${({ userName }) => + userName === 'chaaerim' + ? css` + margin-left: 0.5rem; + background-color: #195bd4; + border-radius: 0.5rem 0 0.5rem 0.5rem; + color: white; + ` + : css` + background-color: #e7e3e3; + border-radius: 0 0.5rem 0.5rem 0.5rem; + margin-right: 0.5rem; + `}; +`; + +const ContextContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const ShowTime = styled.div` + color: #c4c3c3; + font-size: 11px; + margin-top: auto; +`; + +const ProfileImg = styled.img` + width: 2.5rem; + height: 2.5rem; + border-radius: 70%; +`; diff --git a/src/components/layout/CommonStyle.tsx b/src/components/layout/CommonStyle.tsx new file mode 100644 index 0000000..27d3504 --- /dev/null +++ b/src/components/layout/CommonStyle.tsx @@ -0,0 +1,40 @@ +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; + +export const HeaderContains = styled.div` + height: rem; + border-bottom: 0.08rem solid #c2bbbb; + padding: 1.5rem; + line-height: 4rem; + font-size: 25px; + font-weight: lighter; + border-width: 100%; + justify-content: flex-start; +`; + +export const HeaderText = styled.span` + padding-left: 0.5rem; +`; +export const ProfileImg = styled.img` + width: 3.5rem; + height: 3.5rem; + border-radius: 70%; + margin: 0.5rem; +`; + +export const LinkToChat = styled(Link)` + display: flex; + text-decoration: none; + color: black; +`; + +export const UserName = styled.div` + padding-bottom: 0.2rem; + font-size: 15px; +`; + +export const ListItem = styled.div` + display: flex; + flex-direction: column; + padding: 1rem; +`; diff --git a/src/components/layout/UnderNavBar.tsx b/src/components/layout/UnderNavBar.tsx new file mode 100644 index 0000000..838c129 --- /dev/null +++ b/src/components/layout/UnderNavBar.tsx @@ -0,0 +1,40 @@ +import styled from 'styled-components'; +import { AiOutlineUser } from 'react-icons/ai'; +import { BsChatDots } from 'react-icons/bs'; +import { AiOutlineSetting } from 'react-icons/ai'; +import { Link } from 'react-router-dom'; +const UnderNavBar = () => { + return ( + + + + + + + + + + + + ); +}; + +export default UnderNavBar; + +const NavBarContainer = styled.div` + display: flex; + justify-content: space-evenly; + border-top: 0.08rem solid #c2bbbb; + border-width: 100%; + line-height: 2.5; + position: fixed; + top: 44rem; + align-items: center; + text-align: center; + width: 26rem; +`; +const StyledLink = styled(Link)` + text-decoration: none; + color: black; + padding: 1rem 3rem 0 3rem; +`; diff --git a/src/components/layout/UserNav.tsx b/src/components/layout/UserNav.tsx new file mode 100644 index 0000000..3e8b42a --- /dev/null +++ b/src/components/layout/UserNav.tsx @@ -0,0 +1,67 @@ +import styled from 'styled-components'; +import user from '../../data/user.json'; +import { IUserNavProps } from '../../interface/interface'; +import { LinkToChat } from './CommonStyle'; + +const UserNav = ({ currentUser, setCurrentUser, otherUser }: IUserNavProps) => { + const userName = currentUser.userName; + const userindex = user.findIndex( + (user) => user.userName === otherUser.userName + ); + + const switchUser = () => { + currentUser.userId === 'user0' + ? setCurrentUser(user[userindex]) + : setCurrentUser(user[0]); + }; + + return ( + + + ใ€ˆ + + + + {userName} + Typing . . . + + + ); +}; + +export default UserNav; + +const BackToMessageList = styled.button` + font-size: 1rem; + line-height: 8rem; + padding-left: 1rem; +`; + +const UserNavContainer = styled.div` + display: flex; + border-bottom-style: solid; + border-color: #c2bbbb; + border-width: 0.08rem; + text-align: center; + margin: 0 auto; + padding-top: 1rem; + height: 8rem; +`; + +const SwitchButton = styled.button` + background-color: transparent; + margin: auto; + padding-right: 2rem; +`; +const ProfileImg = styled.img` + width: 4.5rem; + height: 4.5rem; + border-radius: 70%; +`; +const ProfileName = styled.div` + font-size: 1rem; +`; +const UserStatus = styled.div` + color: #c4c3c3; + font-size: 0.85rem; +`; diff --git a/src/data/message.json b/src/data/message.json new file mode 100644 index 0000000..3b24712 --- /dev/null +++ b/src/data/message.json @@ -0,0 +1,171 @@ +[ + { + "userId": "user0", + "userName": "chaaerim", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "๊ณผ์ œ๋Š” ๋ฏธ๋ฆฌ๋ฏธ๋ฆฌ", + "time": "12:10" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "ใ…œใ……ใ…œ", + "time": "12:11" + } + ] + }, + { + "userId": "user1", + "userName": "PeterParker", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "With great power", + "time": "11:10" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "comes great responsibility", + "time": "11:11" + }, + { + "userId": "user1", + "userName": "PeterParker", + "text": "Come on,", + "time": "11:16" + }, + { + "userId": "user1", + "userName": "PeterParker", + "text": "Peter Tingle!", + "time": "11:17" + } + ] + }, + + { + "userId": "user2", + "userName": "MJ", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "I kinda expect", + "time": "12:30" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "disappointment. ", + "time": "12:32" + }, + { + "userId": "user2", + "userName": "MJ", + "text": "If you expect disappointment,", + "time": "11:16" + }, + { + "userId": "user2", + "userName": "MJ", + "text": "then you can never really be disappointed. ", + "time": "11:17" + } + ] + }, + { + "userId": "user3", + "userName": "ํ–„์‹์ด", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "Is He, ", + "time": "10:27" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "Though? ", + "time": "10:29" + }, + { + "userId": "user3", + "userName": "ํ–„์‹์ด", + "text": "Loki, I Thought The World Of You.", + "time": "10:52" + }, + { + "userId": "user3", + "userName": "ํ–„์‹์ด", + "text": "I Thought We Were Gonna Fight Side By Side Forever.", + "time": "10:53" + } + ] + }, + { + "userId": "user4", + "userName": "BlackPanther", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "You get to decide ", + "time": "02:07" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "what kind of king you are going to be. ", + "time": "02:12" + }, + { + "userId": "user4", + "userName": "BlackPanther", + "text": "I am not king of all people.", + "time": "02:13" + }, + { + "userId": "user4", + "userName": "BlackPanther", + "text": "I am King of Wakanda.", + "time": "02:15" + } + ] + }, + { + "userId": "user5", + "userName": "IronMan", + "messages": [ + { + "userId": "user0", + "userName": "chaaerim", + "text": "Everybody wants a happy ending, right?", + "time": "02:07" + }, + { + "userId": "user0", + "userName": "chaaerim", + "text": "But it doesnโ€™t always roll that way.", + "time": "02:12" + }, + { + "userId": "user5", + "userName": "IronMan", + "text": "Weโ€™re the Avengers.", + "time": "02:13" + }, + { + "userId": "user5", + "userName": "IronMan", + "text": "We gotta finish this.", + "time": "02:15" + } + ] + } +] diff --git a/src/data/user.json b/src/data/user.json new file mode 100644 index 0000000..d4f66cc --- /dev/null +++ b/src/data/user.json @@ -0,0 +1,39 @@ +[ + { + "userId": "user0", + "userName": "chaaerim", + "userProfile": "user0.jpg", + "userStatus": "ใ…‹ใ……ใ…‹" + }, + + { + "userId": "user1", + "userName": "PeterParker", + "userProfile": "user1.jpg", + "userStatus": "i am not spiderman" + }, + { + "userId": "user2", + "userName": "MJ", + "userProfile": "user2.jpg", + "userStatus": "We're going to kick some" + }, + { + "userId": "user3", + "userName": "ํ–„์‹์ด", + "userProfile": "user3.jpg", + "userStatus": "๋‚œ ์˜ˆ๋ป" + }, + { + "userId": "user4", + "userName": "BlackPanther", + "userProfile": "user4.jpg", + "userStatus": "WAKANDA FOREVER" + }, + { + "userId": "user5", + "userName": "IronMan", + "userProfile": "user5.jpg", + "userStatus": "I love you 3000" + } +] diff --git a/src/hooks/useInput.tsx b/src/hooks/useInput.tsx new file mode 100644 index 0000000..1b89651 --- /dev/null +++ b/src/hooks/useInput.tsx @@ -0,0 +1,27 @@ +import { useCallback, useState } from 'react'; + +const useInput = (initialInput: string) => { + const [textinput, setTextInput] = useState(initialInput); + + //input์ฐฝ์— ์ž…๋ ฅ๋œ todo๋ฅผ todoText๋กœ ์„ค์ • + const handleInputChange = useCallback( + (e: React.ChangeEvent): void => { + { + const { + target: { value }, + } = e; + setTextInput(value); + console.log(value); + } + }, + [] + ); + + // input์ฐฝ ๊ฐ’ ๋น„์šฐ๊ธฐ + const handleInputInitialize = () => { + setTextInput(''); + }; + + return { textinput, handleInputChange, handleInputInitialize }; +}; +export default useInput; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 593edf1..0000000 --- a/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App'; - -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - - - -); diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..05b2d6e --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; + +ReactDOM.render( + + + + + , + document.getElementById('root') +); diff --git a/src/interface/interface.tsx b/src/interface/interface.tsx new file mode 100644 index 0000000..fb9d902 --- /dev/null +++ b/src/interface/interface.tsx @@ -0,0 +1,56 @@ +import { Params } from 'react-router-dom'; + +interface IMessageData { + userId: string; + userName: string; + text: string; + time: string; +} + +interface ICurrentUser { + userId: string; + userName: string; + userProfile: string; + userStatus: string; +} + +interface IUserNameProps { + userName: string; +} + +interface IUserNavProps { + currentUser: ICurrentUser; + setCurrentUser: (element: ICurrentUser) => void; + otherUser: Readonly>; +} + +interface IMessengerInputProps { + currentUser: ICurrentUser; + messageData: IMessageData[]; + setMessageData: (charList: Array) => void; +} + +interface IChattingsProps { + userId: string; + userName: string; + message: string; +} + +interface IFriendsProps { + userProfile: string; + userName: string; + userStatus: string; +} + +interface IFilteredUser { + filteredUser: (searchText: string) => void; +} +export type { + IMessageData, + IMessengerInputProps, + IUserNavProps, + IUserNameProps, + IChattingsProps, + IFriendsProps, + IFilteredUser, +}; diff --git a/src/routes/ChatList.tsx b/src/routes/ChatList.tsx new file mode 100644 index 0000000..cf89240 --- /dev/null +++ b/src/routes/ChatList.tsx @@ -0,0 +1,34 @@ +import styled from 'styled-components'; +import { HeaderContains, HeaderText } from '../components/layout/CommonStyle'; +import UnderNavBar from '../components/layout/UnderNavBar'; +import Messages from '../components/ChatList/Chattings'; +import message from '../data/message.json'; +import { BsChatDots } from 'react-icons/bs'; + +const ChatList = () => { + return ( + + + + Message + + + {message.map((userMessage) => ( + + ))} + + + + ); +}; + +export default ChatList; + +const ChatListContainer = styled.div``; + +const Content = styled.div``; diff --git a/src/routes/FriendList.tsx b/src/routes/FriendList.tsx new file mode 100644 index 0000000..815fe97 --- /dev/null +++ b/src/routes/FriendList.tsx @@ -0,0 +1,52 @@ +import styled from 'styled-components'; +import user from '../data/user.json'; +import Friends from '../components/FriendList/Friends'; +import UnderNavBar from '../components/layout/UnderNavBar'; +import { HeaderContains, HeaderText } from '../components/layout/CommonStyle'; +import SearchUser from '../components/FriendList/SearchUser'; +import { useState } from 'react'; +import { AiOutlineUser } from 'react-icons/ai'; + +const FriendList = () => { + const [searchedUser, setsearchedUser] = useState(user); + const filteredUser = (searchText: string) => { + if (!searchText) { + setsearchedUser(user); + } else { + const filtered = user.filter((element) => { + return element.userName + .toUpperCase() + .includes(searchText.toUpperCase()); + }); + setsearchedUser(filtered); + } + }; + return ( + + + + Friends + + + + {searchedUser.map((user) => ( + + ))} + + + + ); +}; + +export default FriendList; + +const FriendListContainer = styled.div``; +const Content = styled.div` + height: 31.8rem; + overflow: auto; +`; diff --git a/src/routes/Setting.tsx b/src/routes/Setting.tsx new file mode 100644 index 0000000..dd01244 --- /dev/null +++ b/src/routes/Setting.tsx @@ -0,0 +1,61 @@ +import styled from 'styled-components'; +import { CgProfile } from 'react-icons/cg'; +import { AiOutlineLock } from 'react-icons/ai'; +import { FiSettings } from 'react-icons/fi'; +import { MdPayment } from 'react-icons/md'; +import UnderNavBar from '../components/layout/UnderNavBar'; +import { HeaderContains, HeaderText } from '../components/layout/CommonStyle'; +import { AiOutlineSetting } from 'react-icons/ai'; + +const Setting = () => { + return ( + <> + + + + Setting + + + + + Profile + + + + Privacy + + + + General + + + + Payments + + + + + + ); +}; + +export default Setting; + +const SettingContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const Content = styled.div` + display: flex; + justify-content: space-evenly; + flex-direction: column; + align-items: center; +`; + +const SettingButton = styled.button` + display: flex; + justify-content: space-evenly; + margin: 3rem; + width: 8rem; +`; diff --git a/src/state/MessageDataState.tsx b/src/state/MessageDataState.tsx new file mode 100644 index 0000000..daa2898 --- /dev/null +++ b/src/state/MessageDataState.tsx @@ -0,0 +1,7 @@ +import { atom } from 'recoil'; +import { IMessageData } from '../interface/interface'; + +export const messageDataState = atom({ + key: 'messageDataState', + default: [], +}); diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000..ca49eb1 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,2 @@ +declare module '*.jpg'; +declare module '*.jpeg'; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..669bc8f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "typeRoots": ["node_modules/@types", "src/types"] + }, + "include": ["src"] +}