diff --git a/package-lock.json b/package-lock.json index 73afbd8..0f574d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,20 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", - "@types/node": "^16.11.60", + "@types/node": "^16.11.62", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.4.2", "react-scripts": "5.0.1", - "typescript": "^4.8.3", + "recoil": "^0.7.5", + "styled-components": "^5.3.6", + "typescript": "^4.8.4", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/styled-components": "^5.1.26" } }, "node_modules/@adobe/css-tools": { @@ -2140,6 +2146,29 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", + "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "dependencies": { + "@emotion/memoize": "^0.8.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "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.3.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", @@ -3083,6 +3112,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", + "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==", + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3757,6 +3794,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", @@ -3816,9 +3863,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "node_modules/@types/node": { - "version": "16.11.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.60.tgz", - "integrity": "sha512-kYIYa1D1L+HDv5M5RXQeEu1o0FKA6yedZIoyugm/MBPROkLpX4L7HRxMrPVyo8bnvjpW/wDlqFNGzXNMb7AdRw==" + "version": "16.11.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.62.tgz", + "integrity": "sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -3916,6 +3963,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.26", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", + "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", @@ -4948,6 +5006,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": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + }, "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", @@ -5261,6 +5339,11 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==" + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -5700,6 +5783,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": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -5881,6 +5972,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", @@ -8188,6 +8289,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": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -8269,6 +8375,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/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -13774,6 +13893,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz", + "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==", + "dependencies": { + "@remix-run/router": "1.0.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz", + "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==", + "dependencies": { + "@remix-run/router": "1.0.2", + "react-router": "6.4.2" + }, + "engines": { + "node": ">=14" + }, + "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", @@ -13878,6 +14027,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz", + "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==", + "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", @@ -14548,6 +14716,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", @@ -14938,6 +15111,36 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", + "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", + "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", @@ -15457,9 +15660,9 @@ } }, "node_modules/typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17953,6 +18156,29 @@ "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", "requires": {} }, + "@emotion/is-prop-valid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", + "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "requires": { + "@emotion/memoize": "^0.8.0" + } + }, + "@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "@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.3.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", @@ -18631,6 +18857,11 @@ "source-map": "^0.7.3" } }, + "@remix-run/router": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", + "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==" + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -19112,6 +19343,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", @@ -19171,9 +19412,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "@types/node": { - "version": "16.11.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.60.tgz", - "integrity": "sha512-kYIYa1D1L+HDv5M5RXQeEu1o0FKA6yedZIoyugm/MBPROkLpX4L7HRxMrPVyo8bnvjpW/wDlqFNGzXNMb7AdRw==" + "version": "16.11.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.62.tgz", + "integrity": "sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ==" }, "@types/parse-json": { "version": "4.0.0", @@ -19271,6 +19512,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.26", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", + "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", @@ -20016,6 +20268,23 @@ "@babel/helper-define-polyfill-provider": "^0.3.3" } }, + "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": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + }, "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", @@ -20260,6 +20529,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": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==" + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -20585,6 +20859,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": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" + }, "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -20692,6 +20971,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", @@ -22366,6 +22655,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": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -22420,6 +22714,21 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "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", @@ -26219,6 +26528,23 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, + "react-router": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz", + "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==", + "requires": { + "@remix-run/router": "1.0.2" + } + }, + "react-router-dom": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz", + "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==", + "requires": { + "@remix-run/router": "1.0.2", + "react-router": "6.4.2" + } + }, "react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -26300,6 +26626,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz", + "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==", + "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", @@ -26789,6 +27123,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", @@ -27081,6 +27420,23 @@ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", "requires": {} }, + "styled-components": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", + "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", + "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", @@ -27472,9 +27828,9 @@ } }, "typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==" + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/package.json b/package.json index 665ea56..e85a73b 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,16 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", - "@types/node": "^16.11.60", + "@types/node": "^16.11.62", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.4.2", "react-scripts": "5.0.1", - "typescript": "^4.8.3", + "recoil": "^0.7.5", + "styled-components": "^5.3.6", + "typescript": "^4.8.4", "web-vitals": "^2.1.4" }, "scripts": { @@ -39,5 +42,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/styled-components": "^5.1.26" } } diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index a11777c..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/img/chat.png b/public/img/chat.png new file mode 100644 index 0000000..3aaff2a Binary files /dev/null and b/public/img/chat.png differ diff --git a/public/img/full_chat.png b/public/img/full_chat.png new file mode 100644 index 0000000..de459e0 Binary files /dev/null and b/public/img/full_chat.png differ diff --git a/public/img/full_profile.png b/public/img/full_profile.png new file mode 100644 index 0000000..e43f22f Binary files /dev/null and b/public/img/full_profile.png differ diff --git a/public/img/full_setting.png b/public/img/full_setting.png new file mode 100644 index 0000000..c612000 Binary files /dev/null and b/public/img/full_setting.png differ diff --git a/public/img/profile.png b/public/img/profile.png new file mode 100644 index 0000000..4b8a932 Binary files /dev/null and b/public/img/profile.png differ diff --git a/public/img/search.png b/public/img/search.png new file mode 100644 index 0000000..48b8e6d Binary files /dev/null and b/public/img/search.png differ diff --git a/public/img/setting.png b/public/img/setting.png new file mode 100644 index 0000000..3ed03f5 Binary files /dev/null and b/public/img/setting.png differ diff --git a/public/img/user0.png b/public/img/user0.png new file mode 100644 index 0000000..35f0691 Binary files /dev/null and b/public/img/user0.png differ diff --git a/public/img/user1.png b/public/img/user1.png new file mode 100644 index 0000000..547dc14 Binary files /dev/null and b/public/img/user1.png differ diff --git a/public/img/user2.png b/public/img/user2.png new file mode 100644 index 0000000..adca236 Binary files /dev/null and b/public/img/user2.png differ diff --git a/public/img/user3.png b/public/img/user3.png new file mode 100644 index 0000000..382baf0 Binary files /dev/null and b/public/img/user3.png differ diff --git a/public/img/user4.png b/public/img/user4.png new file mode 100644 index 0000000..aa178c8 Binary files /dev/null and b/public/img/user4.png differ diff --git a/public/img/user5.png b/public/img/user5.png new file mode 100644 index 0000000..72d37d4 Binary files /dev/null and b/public/img/user5.png differ diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index fc44b0a..0000000 Binary files a/public/logo192.png and /dev/null differ diff --git a/public/logo512.png b/public/logo512.png deleted file mode 100644 index a4e47a6..0000000 Binary files a/public/logo512.png and /dev/null differ diff --git a/src/App.tsx b/src/App.tsx index 5d90092..c12f4e6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,32 @@ +import styled from "styled-components"; +import { Routes, Route } from "react-router-dom"; +import FriendsList from "./pages/FriendsList"; +import ChatList from "./pages/ChatList"; +import Setting from "./pages/Setting"; +import ChatRoom from "./pages/ChatRoom"; +import InitialPage from "./pages/InitialPage"; + function App() { - return
화이팅!!
; + return ( + + + } /> + } /> + } /> + } /> + } /> + + + ); } +const Container = styled.div` + box-shadow: 1px 1px 2px 2px lightgrey; + margin: 0 auto; + margin-top: 2rem; + border-radius: 0.5rem; + width: 24rem; + height: 43rem; +`; + export default App; diff --git a/src/components/ChattingRoom/ChatBody.tsx b/src/components/ChattingRoom/ChatBody.tsx new file mode 100644 index 0000000..70fd451 --- /dev/null +++ b/src/components/ChattingRoom/ChatBody.tsx @@ -0,0 +1,58 @@ +import { useRef, useEffect } from "react"; +import { useRecoilState, useRecoilValue } from "recoil"; +import styled from "styled-components"; +import { chat } from "../../interface/interface"; +import { chatList, roomId } from "../../store/recoil/recoil"; +import ChatBubble from "./ChatBubble"; + +function ChatBody() { + const room = useRecoilValue(roomId); + const chatting = useRecoilValue(chatList); + + const scrollRef = useRef(null); + + const scrollBottom = () => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + } + }; + + useEffect(() => { + scrollBottom(); + }, [chatting]); + + return ( + + {chatting[room - 1].chat.map((chat: chat) => ( + + ))} + + ); +} + +const ChatBodyContainer = styled.div` + overflow-y: auto; + background: pink; + height: 30rem; + justify-content: flex-start; + display: flex; + flex-direction: column; + padding: 1rem; + + &::-webkit-scrollbar { + width: 4px; + } + &::-webkit-scrollbar-thumb { + border-radius: 2px; + background: #7d7d7d; + } +`; + +export default ChatBody; diff --git a/src/components/ChattingRoom/ChatBubble.tsx b/src/components/ChattingRoom/ChatBubble.tsx new file mode 100644 index 0000000..03e3929 --- /dev/null +++ b/src/components/ChattingRoom/ChatBubble.tsx @@ -0,0 +1,75 @@ +import { useRecoilState, useRecoilValue } from "recoil"; +import styled from "styled-components"; +import { chat } from "../../interface/interface"; +import { currentId } from "../../store/recoil/recoil"; +import UserData from "../../store/UserData.json"; +import { UserProfileImage } from "../style"; + +interface bubbleStyle { + color?: string; + flexDirection?: string; +} + +function ChatBubble({ chatId, senderId, receiverId, text, time }: chat) { + const currentUser = useRecoilValue(currentId); + + return ( + <> + {currentUser === senderId ? ( + + {time} +
+ {UserData.users[senderId].userName} + {text} +
+
+ ) : ( + + +
+ {UserData.users[senderId].userName} + + {text} + +
+ {time} +
+ )} + + ); +} + +const BubbleList = styled.div<{ justifyContent?: string }>` + display: flex; + justify-content: ${((props) => props.justifyContent) || "flex-start"}; + // justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; +`; + +const Bubble = styled.div` + display: flex; + width: auto; + border-radius: 0.3rem; + background-color: ${(props) => props.color || "#dee2e6"}; + font-size: 13px; + flex-direction: ${(props) => props.flexDirection || "row-reverse"}; + padding: 8px; +`; + +const UserName = styled.div<{ flex?: string }>` + font-size: 0.5rem; + margin-bottom: 0.1rem; + display: flex; + flex-direction: ${(props) => props.flex || "row-reverse"}; +`; + +const TimeStyle = styled.div` + font-size: 0.4rem; + margin-top: 2rem; + padding: 0.3rem; +`; + +export default ChatBubble; diff --git a/src/components/ChattingRoom/ChatRoomHeader.tsx b/src/components/ChattingRoom/ChatRoomHeader.tsx new file mode 100644 index 0000000..83c7718 --- /dev/null +++ b/src/components/ChattingRoom/ChatRoomHeader.tsx @@ -0,0 +1,57 @@ +import { Link } from "react-router-dom"; +import { useRecoilState, useRecoilValue } from "recoil"; +import styled from "styled-components"; +import { currentId, roomId } from "../../store/recoil/recoil"; +import UserData from "../../store/UserData.json"; +import UserProfile from "../profile/HedaerProfile"; + +function ChatRoomHeader() { + let userData = UserData.users; + let room = useRecoilValue(roomId); + const [userId, setUserId] = useRecoilState(currentId); + + // const newUserData = UserData.users.filter((user) => { + // user.userId === 0 || user.userId === room; + // }); + + return ( +
+ {userData.map((user) => + user.userId === 0 || user.userId === room ? ( + + ) : ( + <> + ) + )} + setUserId(0)}> + + +
+ ); +} + +const Header = styled.div` + position: relative; + margin: 0 auto; + background: #ffffff; + height: 6rem; + border-radius: 0.5rem 0.5rem 0rem 0rem; + display: flex; + flex-direction: row; +`; +const CloseBtn = styled.div` + position: absolute; + right: 1rem; + top: 1rem; + width: 1rem; + height: 1rem; + background: #fa5858; + border-radius: 1rem; +`; + +export default ChatRoomHeader; diff --git a/src/components/ChattingRoom/SendMessage.tsx b/src/components/ChattingRoom/SendMessage.tsx new file mode 100644 index 0000000..76af52a --- /dev/null +++ b/src/components/ChattingRoom/SendMessage.tsx @@ -0,0 +1,79 @@ +import { useRecoilState, useRecoilValue } from "recoil"; +import styled from "styled-components"; +import useInput from "../../hooks/useInput"; +import { chat, chatRoom } from "../../interface/interface"; +import { + chatList, + chattingRoom, + currentId, + roomId, +} from "../../store/recoil/recoil"; + +function SendMessage() { + const { text, handleChange, resetText } = useInput(""); + const room = useRecoilValue(roomId); + const [chatting, setChatting] = useRecoilState(chatList); + const getChatting = useRecoilValue(chattingRoom); + const sender = useRecoilValue(currentId); + const time = new Date(); + + const newChat: chat = { + chatId: Date.now(), + senderId: sender, + receiverId: sender === 0 ? room : 0, + text: text, + time: ("0" + time.getHours()).slice(-2) + ":" + time.getMinutes(), + }; + + const newChatting: chatRoom = { + roomId: room, + chat: [...getChatting[0].chat, newChat], + }; + + function AddText(text: string) { + if (text.trim()) { + setChatting( + chatting.map((chat) => (chat.roomId === room ? newChatting : chat)) + ); + } + } + + function onSend(e: any) { + e.preventDefault(); + AddText(text); + resetText(); + } + + return ( + + + 전송 + + ); +} + +const InputContainer = styled.form` + margin: 0 auto; + background: #ffffff; + height: 5rem; + border-radius: 0rem 0rem 0.5rem 0.5rem; + display: flex; + flex-direction: "row"; +`; + +const Input = styled.input` + width: 18rem; + margin: 0.5rem; + height: 3.5rem; + border: none; +`; + +const SendButton = styled.button` + width: 4rem; + height: 3.7rem; + margin-top: 0.5rem; + background-color: #f9eb54; + border: none; +`; + +export default SendMessage; diff --git a/src/components/Navi.tsx b/src/components/Navi.tsx new file mode 100644 index 0000000..48888b5 --- /dev/null +++ b/src/components/Navi.tsx @@ -0,0 +1,61 @@ +import { Link, useLocation } from "react-router-dom"; +import styled from "styled-components"; + +const Navigation = () => { + const { pathname } = useLocation(); + + return ( + + ); +}; + +const Nav = styled.div` + width: 5rem; + height: 42.9rem; + border-radius: 0.5rem 0 0 0.5rem; + background-color: rgb(230, 230, 230); + //border-right: 1px solid rgb(230, 230, 230); + border: 0.05rem solid #fff; +`; + +const IconWrap = styled.div` + width: 1.7rem; + margin: auto; + height: auto; + display: flex; + flex-direction: column; + padding: 2rem 0; +`; + +const Icon = styled.img` + width: 1.7rem; + height: 1.7rem; + margin: auto; + padding: 1rem 0; +`; + +export default Navigation; diff --git a/src/components/list/ChatLIstItem.tsx b/src/components/list/ChatLIstItem.tsx new file mode 100644 index 0000000..a9c5f05 --- /dev/null +++ b/src/components/list/ChatLIstItem.tsx @@ -0,0 +1,41 @@ +import { useRecoilValue } from "recoil"; +import styled from "styled-components"; +import { profile } from "../../interface/interface"; +import { chatList } from "../../store/recoil/recoil"; +import { Name, NameWrap, ProfileContainer, ProfileImg, Msg } from "../style"; + +const ChatListItem = ({ + userID, + profileImg, + name, + PersonalMessage, +}: profile) => { + const chatting = useRecoilValue(chatList); + + let lastChatIndex = chatting[userID - 1].chat.length - 1; + let lastChat = chatting[userID - 1].chat[lastChatIndex].text; + let lastChatTime = chatting[userID - 1].chat[lastChatIndex].time; + + return ( + + <> + + + {name} + {lastChat} + + + + + ); +}; + +const Time = styled.div` + position: absolute; + color: lightgray; + font-size: 7px; + right: 0rem; + padding-bottom: 1rem; +`; + +export default ChatListItem; diff --git a/src/components/list/FriendsProfile.tsx b/src/components/list/FriendsProfile.tsx new file mode 100644 index 0000000..084e30f --- /dev/null +++ b/src/components/list/FriendsProfile.tsx @@ -0,0 +1,22 @@ +import styled from "styled-components"; +import { profile } from "../../interface/interface"; +import { Name, NameWrap, ProfileContainer, ProfileImg, Msg } from "../style"; + +const FriendsProfile = ({ + userID, + profileImg, + name, + PersonalMessage, +}: profile) => { + return ( + + + + {name} + {PersonalMessage} + + + ); +}; + +export default FriendsProfile; diff --git a/src/components/profile/HedaerProfile.tsx b/src/components/profile/HedaerProfile.tsx new file mode 100644 index 0000000..9bc5470 --- /dev/null +++ b/src/components/profile/HedaerProfile.tsx @@ -0,0 +1,49 @@ +import { useRecoilState } from "recoil"; +import styled from "styled-components"; +import { currentId } from "../../store/recoil/recoil"; +import { UserProfileImage } from "../style"; + +function UserProfile({ + userImage, + userName, + userId, +}: { + userImage: any; + userName?: string; + userId: number; +}) { + const [sender, setSender] = useRecoilState(currentId); + + function onClick() { + setSender(userId); + } + + return ( + + {userId === sender ? ( + + ) : ( + + )} + {userName} + + ); +} + +const UserContainer = styled.div` + display: flex; + flex-direction: column; + text-align: center; + margin-top: 1rem; + margin-left: 1rem; +`; + +const UserName = styled.div` + font-size: 0.7rem; + padding-top: 0.2rem; +`; + +export default UserProfile; diff --git a/src/components/style.tsx b/src/components/style.tsx new file mode 100644 index 0000000..eb6a92d --- /dev/null +++ b/src/components/style.tsx @@ -0,0 +1,80 @@ +import { Link } from "react-router-dom"; +import styled from "styled-components"; + +interface imageStyle { + brightness?: string; + width?: number; + height?: number; +} + +const StyledLink = styled(Link)` + text-decoration: none; + color: #000; +`; + +const UserProfileImage = styled.img` + width: ${(props) => props.width || 3}rem; + height: ${(props) => props.height || 3}rem; + border-radius: 1rem; + border-width: 0.3rem; + filter: ${(props) => props.brightness || "brightness(100%)"}; +`; + +const Container = styled.div` + display: flex; + position: relative; +`; + +const ListWrap = styled.div` + box-sizing: border-box; + padding: 1rem; + width: 19rem; +`; + +const ListDiv = styled.div` + font-weight: bold; + font-size: 1rem; + padding-bottom: 1rem; +`; + +// Profile Style +const ProfileContainer = styled.div` + display: flex; + padding: 5px 0; + width: 100%; +`; +const ProfileImg = styled.img<{ picSize?: number }>` + width: ${(props) => props.picSize || 3}rem; + height: ${(props) => props.picSize || 3}rem; + border-radius: 1rem; +`; +const NameWrap = styled.div` + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + box-sizing: border-box; + padding: 7px; + width: 100%; +`; +const Name = styled.div` + font-size: 13px; + padding-bottom: 3px; +`; +const Msg = styled.div` + color: #7d7d7d; + font-size: 11px; +`; + +export { + StyledLink, + UserProfileImage, + Container, + ListWrap, + ListDiv, + ProfileContainer, + ProfileImg, + Name, + NameWrap, + Msg, +}; diff --git a/src/hooks/useInput.tsx b/src/hooks/useInput.tsx new file mode 100644 index 0000000..3a08923 --- /dev/null +++ b/src/hooks/useInput.tsx @@ -0,0 +1,17 @@ +import React, { useState } from "react"; + +function useInput(initialText: string) { + const [text, setText] = useState(initialText); + + const handleChange = (e: React.ChangeEvent): void => { + setText(e.target.value); + }; + + const resetText = (): void => { + setText(""); + }; + + return { text, handleChange, resetText }; +} + +export default useInput; diff --git a/src/index.tsx b/src/index.tsx index 086c86b..2966356 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,18 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter } from "react-router-dom"; +import { RecoilRoot } from "recoil"; +import App from "./App"; -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement +); root.render( - + + + + + ); diff --git a/src/interface/interface.tsx b/src/interface/interface.tsx new file mode 100644 index 0000000..5fbea29 --- /dev/null +++ b/src/interface/interface.tsx @@ -0,0 +1,21 @@ +interface chat { + chatId: number; + senderId: number; + receiverId: number; + text: string; + time: string; +} + +interface profile { + userID: number; + profileImg: string; + name: string; + PersonalMessage: string; +} + +interface chatRoom { + roomId: number; + chat: chat[]; +} + +export type { chat, profile, chatRoom }; diff --git a/src/pages/ChatList.tsx b/src/pages/ChatList.tsx new file mode 100644 index 0000000..a5ca85f --- /dev/null +++ b/src/pages/ChatList.tsx @@ -0,0 +1,44 @@ +import { useRecoilState } from "recoil"; +import ChatListItem from "../components/list/ChatLIstItem"; +import Navigation from "../components/Navi"; +import { Container, ListDiv, ListWrap, StyledLink } from "../components/style"; +import { currentId, roomId } from "../store/recoil/recoil"; +import UserData from "../store/UserData.json"; + +const ChatList = () => { + let userData = UserData.users; + const [roomNum, setRoomNum] = useRecoilState(roomId); + + return ( + + + + 채팅 + {userData.map((people) => + people.userId != 0 ? ( + { + setRoomNum(people.userId); + }} + > + + + ) : ( + <> + ) + )} + + + ); +}; + +export default ChatList; diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx new file mode 100644 index 0000000..f786333 --- /dev/null +++ b/src/pages/ChatRoom.tsx @@ -0,0 +1,15 @@ +import ChatBody from "../components/ChattingRoom/ChatBody"; +import ChatRoomHeader from "../components/ChattingRoom/ChatRoomHeader"; +import SendMessage from "../components/ChattingRoom/SendMessage"; + +const ChatRoom = () => { + return ( + <> + + + + + ); +}; + +export default ChatRoom; diff --git a/src/pages/FriendsList.tsx b/src/pages/FriendsList.tsx new file mode 100644 index 0000000..13377c9 --- /dev/null +++ b/src/pages/FriendsList.tsx @@ -0,0 +1,122 @@ +import { useState } from "react"; +import { useRecoilState } from "recoil"; +import styled from "styled-components"; +import Navigation from "../components/Navi"; +import FriendsProfile from "../components/list/FriendsProfile"; +import useInput from "../hooks/useInput"; +import { roomId } from "../store/recoil/recoil"; +import UserData from "../store/UserData.json"; +import { Container, ListWrap, StyledLink } from "../components/style"; + +interface DivStyle { + color?: string; + padding?: number; + border?: number; + font?: number; +} + +const FriendsList = () => { + let userData = UserData.users; + const [roomNum, setRoomNum] = useRecoilState(roomId); + const [search, setSearch] = useState(false); + const { text, handleChange } = useInput(""); + let friendsData = userData.filter((user) => user.userId != 0); + + return ( + + + + <> +
친구
+ setSearch((prev) => !prev)} + /> + {search === true ? ( + <> + + {friendsData + .filter((i) => i.userName.includes(text)) + .map((people) => ( + { + setRoomNum(people.userId); + }} + > + + + ))} + + ) : ( + <> +
+ +
+
+ 친구 {userData.length - 1} +
+ + {friendsData.map((people) => ( + { + setRoomNum(people.userId); + }} + > + + + ))} + + )} + +
+
+ ); +}; + +const Div = styled.div` + color: ${(props) => props.color || "#000"}; + font-weight: bold; + padding: ${(props) => props.padding || 0}rem 0; + border-bottom: ${(props) => props.border || 0}px solid lightgray; + font-size: ${(props) => props.font || 0.5}rem; +`; + +const Search = styled.img` + width: 1rem; + height: 1rem; + position: absolute; + right: 1rem; + top: 1.1rem; +`; +const Input = styled.input` + width: 100%; + margin: 1.5rem 0; + background: #f2f2f2; +`; + +export default FriendsList; diff --git a/src/pages/InitialPage.tsx b/src/pages/InitialPage.tsx new file mode 100644 index 0000000..277d625 --- /dev/null +++ b/src/pages/InitialPage.tsx @@ -0,0 +1,39 @@ +import { Link } from "react-router-dom"; +import styled from "styled-components"; + +const InitialPage = () => { + return ( + +
들어가기
+
+ ); +}; + +const Container = styled.div` + box-shadow: 1px 1px 2px 2px lightgrey; + margin: 0 auto; + margin-top: 2rem; + border-radius: 0.5rem; + width: 24rem; + height: 43rem; + background: pink; + display: flex; + align-items: center; + justify-content: center; +`; + +const Div = styled(Link)` + box-sizing: border-box; + font-size: 0.8rem; + padding: 1rem; + border-radius: 1rem; + background: #fff; + width: 10rem; + color: #585858; + text-decoration: none; + display: flex; + justify-content: center; + box-shadow: 0 0 4px #bdbdbd; +`; + +export default InitialPage; diff --git a/src/pages/Setting.tsx b/src/pages/Setting.tsx new file mode 100644 index 0000000..79d58c6 --- /dev/null +++ b/src/pages/Setting.tsx @@ -0,0 +1,32 @@ +import styled from "styled-components"; +import Navigation from "../components/Navi"; +import { Container, ListDiv, ListWrap } from "../components/style"; + +const Setting = () => { + return ( + + + + 더보기 +
아래 링크로 이동해보세요
+
window.open("https://github.com/chaeyeonan")}> + GitHub +
+
window.open("https://12yeonii.tistory.com/")}> + Tistory +
+
+
+ ); +}; + +const Div = styled.div<{ size?: number }>` + font-size: ${(props) => props.size || 1.3}rem; + width: 100%; + text-align: center; + padding: 2rem 0; + color: #1c1c1c; + border-bottom: 1px solid #bdbdbd; +`; + +export default Setting; diff --git a/src/store/Message.json b/src/store/Message.json new file mode 100644 index 0000000..7f76429 --- /dev/null +++ b/src/store/Message.json @@ -0,0 +1,125 @@ +[ + { + "roomId": 1, + "chat": [ + { + "chatId": 1, + "senderId": 0, + "receiverId": 1, + "text": "어디야", + "time": "11:24" + }, + { + "chatId": 2, + "senderId": 1, + "receiverId": 0, + "text": "학문관", + "time": "11:24" + }, + { + "chatId": 3, + "senderId": 0, + "receiverId": 1, + "text": "ㅇㅋ용~", + "time": "11:28" + } + ] + }, + { + "roomId": 2, + "chat": [ + { + "chatId": 1, + "senderId": 2, + "receiverId": 0, + "text": "헤이", + "time": "17:34" + }, + { + "chatId": 2, + "senderId": 0, + "receiverId": 2, + "text": "why", + "time": "18:24" + }, + { + "chatId": 1, + "senderId": 2, + "receiverId": 0, + "text": "그냥 불러봐써", + "time": "19:24" + } + ] + }, + { + "roomId": 3, + "chat": [ + { + "chatId": 1, + "senderId": 3, + "receiverId": 0, + "text": "오늘 점심 뭐 먹을래", + "time": "11:03" + }, + { + "chatId": 2, + "senderId": 0, + "receiverId": 3, + "text": "사케동?", + "time": "11:30" + }, + { + "chatId": 3, + "senderId": 3, + "receiverId": 0, + "text": "굿 ㄱㄱ", + "time": "11:43" + } + ] + }, + { + "roomId": 4, + "chat": [ + { + "chatId": 1, + "senderId": 4, + "receiverId": 0, + "text": "반박하지마", + "time": "11:03" + }, + { + "chatId": 2, + "senderId": 0, + "receiverId": 4, + "text": "알겠어 ㅜㅜ", + "time": "11:30" + } + ] + }, + { + "roomId": 5, + "chat": [ + { + "chatId": 1, + "senderId": 5, + "receiverId": 0, + "text": "나 오늘 어디가게", + "time": "11:03" + }, + { + "chatId": 2, + "senderId": 0, + "receiverId": 5, + "text": "하리보 100주년 전시회", + "time": "11:30" + }, + { + "chatId": 3, + "senderId": 5, + "receiverId": 0, + "text": "정답!", + "time": "11:43" + } + ] + } +] diff --git a/src/store/UserData.json b/src/store/UserData.json new file mode 100644 index 0000000..50e7e63 --- /dev/null +++ b/src/store/UserData.json @@ -0,0 +1,40 @@ +{ + "users": [ + { + "userId": 0, + "userName": "포포", + "profileImage": "user0", + "personalMessage": "내가 주인이다" + }, + { + "userId": 1, + "userName": "곰표", + "profileImage": "user1", + "personalMessage": "부캐 김딸기" + }, + { + "userId": 2, + "userName": "외국인", + "profileImage": "user2", + "personalMessage": "hello~" + }, + { + "userId": 3, + "userName": "승승장구", + "profileImage": "user3", + "personalMessage": "" + }, + { + "userId": 4, + "userName": "반박시.박시현", + "profileImage": "user4", + "personalMessage": "반박하지마" + }, + { + "userId": 5, + "userName": "김보드라", + "profileImage": "user5", + "personalMessage": "" + } + ] +} diff --git a/src/store/recoil/recoil.tsx b/src/store/recoil/recoil.tsx new file mode 100644 index 0000000..8c12b82 --- /dev/null +++ b/src/store/recoil/recoil.tsx @@ -0,0 +1,28 @@ +import { atom, selector } from "recoil"; +import Message from "../Message.json"; +import { chat, chatRoom } from "../../interface/interface"; + +export const currentId = atom({ + key: "currentId", + default: 0, +}); + +export const roomId = atom({ + key: "roomId", + default: 1, +}); + +export const chatList = atom({ + key: "chatList", + default: Message, +}); + +export const chattingRoom = selector({ + key: "chattingRoom", + get: ({ get }) => { + const im_chatroom = get(chatList); + const roomNumber = get(roomId); + const chatRoom = im_chatroom.filter((room) => room.roomId === roomNumber); + return chatRoom; + }, +}); diff --git a/tsconfig.json b/tsconfig.json index a273b0c..9d379a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] }