diff --git a/code/.eslintrc.json b/code/.eslintrc.json index c9c0675c3..d8e07d814 100644 --- a/code/.eslintrc.json +++ b/code/.eslintrc.json @@ -42,6 +42,10 @@ "allowSingleLine": true } ], + "linebreak-style": [ + "off", + "unix" + ], "comma-dangle": [ "error", "never" diff --git a/code/.gitignore b/code/.gitignore deleted file mode 100644 index 4d29575de..000000000 --- a/code/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/code/package-lock.json b/code/package-lock.json index bb51e893e..0b38d641d 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@babel/eslint-parser": "^7.18.9", + "@reduxjs/toolkit": "^1.9.4", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", @@ -16,7 +17,10 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-redux": "^8.0.5", + "styled-components": "^5.3.9", + "uuid": "^8.3.2" }, "devDependencies": { "react-scripts": "5.0.1" @@ -129,7 +133,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -2153,6 +2156,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.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -3124,6 +3150,29 @@ } } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.4.tgz", + "integrity": "sha512-j2R4I+dzt7cWjf50CNS17A/TBX0rH5SmFhKcOQDZlFtFvmSGWfEQvNPjrCI729Am5o1USFWT1PaxV/JflmP/Dg==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3621,6 +3670,15 @@ "@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==", + "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", @@ -3695,6 +3753,11 @@ "integrity": "sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==", "dev": true }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, "node_modules/@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -3713,6 +3776,16 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "node_modules/@types/react": { + "version": "18.0.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.37.tgz", + "integrity": "sha512-4yaZZtkRN3ZIQD3KSEwkfcik8s0SWV+82dlJot1AbGYHCzJkWP3ENBY6wYeDRmKZ6HkrgoGAmR2HqdwYGp6OEw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -3728,6 +3801,11 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + }, "node_modules/@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -3768,6 +3846,11 @@ "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", "dev": true }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -4364,6 +4447,8 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4379,7 +4464,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -4867,6 +4954,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.1.tgz", + "integrity": "sha512-c8lJlszObVQPguHkI+akXv8+Jgb9Ccujx0EetL7oIvwU100LxO6XAGe45qry37wUL40a5U9f23SYrivro2XKhA==", + "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.21", + "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", @@ -5204,6 +5311,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -5691,6 +5806,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.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", @@ -5899,6 +6022,16 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "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", @@ -6095,6 +6228,11 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8515,6 +8653,14 @@ "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/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -8800,10 +8946,9 @@ } }, "node_modules/immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", - "dev": true, + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -11695,8 +11840,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -13940,8 +14084,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -14375,6 +14518,49 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -14531,6 +14717,22 @@ "node": "*" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -14686,6 +14888,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -15206,6 +15413,11 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "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", @@ -15615,6 +15827,35 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.9.tgz", + "integrity": "sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==", + "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", @@ -16340,6 +16581,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16380,7 +16629,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "bin": { "uuid": "dist/bin/uuid" } @@ -17460,7 +17708,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, "requires": { "@babel/types": "^7.18.6" } @@ -18792,6 +19039,29 @@ "dev": true, "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.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -19509,6 +19779,17 @@ "source-map": "^0.7.3" } }, + "@reduxjs/toolkit": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.4.tgz", + "integrity": "sha512-j2R4I+dzt7cWjf50CNS17A/TBX0rH5SmFhKcOQDZlFtFvmSGWfEQvNPjrCI729Am5o1USFWT1PaxV/JflmP/Dg==", + "requires": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + } + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -19876,6 +20157,15 @@ "@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==", + "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", @@ -19950,6 +20240,11 @@ "integrity": "sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==", "dev": true }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -19968,6 +20263,16 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "@types/react": { + "version": "18.0.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.37.tgz", + "integrity": "sha512-4yaZZtkRN3ZIQD3KSEwkfcik8s0SWV+82dlJot1AbGYHCzJkWP3ENBY6wYeDRmKZ6HkrgoGAmR2HqdwYGp6OEw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -19983,6 +20288,11 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + }, "@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -20023,6 +20333,11 @@ "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", "dev": true }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -20457,15 +20772,14 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": { - "ajv": "^8.0.0" - }, + "requires": {}, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "version": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, + "optional": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -20477,7 +20791,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -20831,6 +21147,23 @@ "@babel/helper-define-polyfill-provider": "^0.3.2" } }, + "babel-plugin-styled-components": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.1.tgz", + "integrity": "sha512-c8lJlszObVQPguHkI+akXv8+Jgb9Ccujx0EetL7oIvwU100LxO6XAGe45qry37wUL40a5U9f23SYrivro2XKhA==", + "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.21", + "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", @@ -21099,6 +21432,11 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true }, + "camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -21471,6 +21809,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.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", @@ -21601,6 +21944,16 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, + "css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "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", @@ -21748,6 +22101,11 @@ } } }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -23521,6 +23879,14 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "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" + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -23739,10 +24105,9 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" }, "immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", - "dev": true + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" }, "import-fresh": { "version": "3.3.0", @@ -25874,8 +26239,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.debounce": { "version": "4.0.8", @@ -27349,8 +27713,7 @@ "postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prelude-ls": { "version": "1.2.1", @@ -27678,6 +28041,26 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -27800,6 +28183,20 @@ } } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "requires": {} + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -27924,6 +28321,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -28306,6 +28708,11 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "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", @@ -28620,6 +29027,23 @@ "dev": true, "requires": {} }, + "styled-components": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.9.tgz", + "integrity": "sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==", + "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", @@ -28798,7 +29222,6 @@ "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.14", "postcss-import": "^14.1.0", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.4", @@ -29163,6 +29586,12 @@ "punycode": "^2.1.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -29196,8 +29625,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", diff --git a/code/package.json b/code/package.json index 7aad26ebc..1736960f1 100644 --- a/code/package.json +++ b/code/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@babel/eslint-parser": "^7.18.9", + "@reduxjs/toolkit": "^1.9.4", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", @@ -11,7 +12,10 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-redux": "^8.0.5", + "styled-components": "^5.3.9", + "uuid": "^8.3.2" }, "scripts": { "start": "react-scripts start", diff --git a/code/src/App.js b/code/src/App.js index f2007d229..a2e69930a 100644 --- a/code/src/App.js +++ b/code/src/App.js @@ -1,9 +1,45 @@ import React from 'react'; +import { Provider } from 'react-redux'; +import { combineReducers, configureStore } from '@reduxjs/toolkit'; +// import Header from 'components/Header.js'; +import AddTask from 'components/AddTask.js'; +import TaskList from 'components/TaskList.js'; +import tasks from 'components/reducers/tasks.js'; +import { Container } from 'components/styles/global'; export const App = () => { + const reducer = combineReducers({ // Reducer/Function = () and inside {} indicating + // this will be (a function as?) an object. + // combineReducers exposes reducers from the slices to + // the store in the provider. Creates a new object that meets + // requirements for the configStore function. Processes + // our reducer and spits out something that is suited for the configStore + // function below. + tasks: tasks.reducer // Object (task) as an argument. + }); + // This object has properties which we will populate. Those properties will be + // the name of our redux store slices (eg tasks). + const store = configureStore({ reducer }); // Function + // Here we create the store. Inside the store we provide the reducer. return ( -
- Find me in src/app.js! -
- ); + // Provider as a wrapper for app to function with redux store. Takes one prop = store. + <> + {/*
*/} + + {/* + checked paper + */} + + {/* checked paper */} + + + + + + ) } + +// One slice for the user and another for a specific, eg shopping cart, third slice +// for coloured themes, layout etc. Rule of thumb: If it still is within the same +// domain as as data stored inside of one slice, then no problem. You can start of +// with one slice. If it doesn't function well with other stuff, put them in another slice. \ No newline at end of file diff --git a/code/src/components/AddTask.js b/code/src/components/AddTask.js new file mode 100644 index 000000000..be0cd27fc --- /dev/null +++ b/code/src/components/AddTask.js @@ -0,0 +1,105 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +import React, { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { v4 as uuidv4 } from 'uuid'; +import tasks from 'components/reducers/tasks.js'; +import styled from 'styled-components'; +import { AddButton, DeleteButton, Title } from './styles/global'; +import { TaskCounter } from './TaskCounter'; + +// Object.defineProperty(String.prototype, 'capitalize', { +// value: () => { +// return this.charAt(0).toUpperCase() + this.slice(1); +// }, +// enumerable: false +// }); + +const capitalize = (stringToCapitalize) => { + return stringToCapitalize.charAt(0).toUpperCase() + stringToCapitalize.slice(1); +} + +const NewTask = styled.input` + border: none; + border-bottom: solid blue 3px; + width: 60%; + margin-left: 20px; + font-family: 'Satoshi', sans-serif; + font-size: 1em; + color: blue; + + &:focus { + outline: none; + } +` +const HeaderWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 20px; +` + +const FooterWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-end; + margin: 20px; +` +const AddTask = () => { // A function. + const [inputValue, setInputValue] = useState(''); // State variable to track input. Set to 0. + const dispatch = useDispatch(); // Create/declare dispatch to be able to go to store/tasks. + const onFormSubmit = (event) => { + event.preventDefault(); // Otherwise form will reload. + const addTask = { + id: uuidv4(), // Date.now().toString() + name: capitalize(inputValue), // Method, then input value, sending it there. + isCompleted: false // Not completed yet + } + dispatch(tasks.actions.addTask(addTask)); // Dispatch an action. Dispatch = access store + // and select reducer/action. Referencing the imported reducer "task". + setInputValue(''); // Empty string for user to not have to delete previous input. + }; + + const onDeleteAllTaskBtnClick = () => { // Function. + // Here create what happens at onCLick. Copy dispatch from onFormSubmit above. + dispatch(tasks.actions.deleteAllTask()); // Choose ? as declared? in tasksjs reducer. + // Leave ? empty since no action is happening. + } + // Add this function to onClickEvent below. + // Here comes the html part. + return ( +
+ + + Completed? Delete it! + + +
+ + + + +
+ + + Start over + + +
+ ) +} + +export default AddTask; + +// value={inputValue} in input to clear field. +// Add functionalities before ? \ No newline at end of file diff --git a/code/src/components/CheckboxForm.js b/code/src/components/CheckboxForm.js new file mode 100644 index 000000000..c7e46d82e --- /dev/null +++ b/code/src/components/CheckboxForm.js @@ -0,0 +1,24 @@ +// import React, { useEffect } from 'react'; + +// export const CheckboxForm = () => { +// useEffect(() => { +// document.addEventListener('DOMContentLoaded', function () { +// document.querySelector('form').addEventListener('click', e => { +// const checkboxCL = e.target.classList, +// pState = 'pristine'; +// if {(checkboxCL.contains(pState))} +// checkboxCL.remove(pState); +// }); +// }); +// }, []); + +// return ( +//
+// +// +// +//
+// ); +// } + +// export default CheckboxForm; \ No newline at end of file diff --git a/code/src/components/TaskCounter.js b/code/src/components/TaskCounter.js new file mode 100644 index 000000000..df703a1dc --- /dev/null +++ b/code/src/components/TaskCounter.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import styled from 'styled-components'; + +const TaskCounterText = styled.p` + font-size: em; + display: flex; + font-family: 'Satoshi', sans-serif; + color: blue; + ` + +export const TaskCounter = () => { + const taskList = useSelector((store) => store.tasks.items) + const completedTasks = taskList.filter((task) => task.isCompleted); + + const tasksCompleted = () => { + if (taskList.length === 0) { + return '' + } else if (taskList.length === completedTasks.length) { + return ( +

All tasks completed

+ ) + } else { + return `${completedTasks.length} / ${taskList.length} Completed` + } + } + return ( + + {tasksCompleted()} + + ) +} \ No newline at end of file diff --git a/code/src/components/TaskList.js b/code/src/components/TaskList.js new file mode 100644 index 000000000..6091b30e9 --- /dev/null +++ b/code/src/components/TaskList.js @@ -0,0 +1,227 @@ +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import styled from 'styled-components'; +import tasks from './reducers/tasks.js'; +import mp3 from './assets/check.mp3'; +import { Button } from './styles/global'; +// import { CheckboxForm } from './CheckboxForm.js' + +const InnerWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 5px; + margin: 1.5em auto 0; + padding: 0.75em; + // border: solid green 2px; + + [type="checkbox"] { + width: 200px; + height: 200px; + position: relative; + padding-left; 30px; + outline: 5px solid blue; + line-height: 25px; + background-image: + url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; } + + [type="checkbox"] + label{ + width: 100px; + height: 100px; + position: relative; + padding-left; 30px; + cursor: pointer; + line-height: 25px; + background-image: url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + } + + [type="checkbox"]:checked + label::before{ + content: ""; + position: absolute; + left: 0; + top: 0; + width: 100px; + height: 100px; + background: white; + transform: scale(1); + transition: all .3s ease; + background-image: url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + } + + [type="checkbox"] + label::before{ + content: ""; + position: absolute; + left: 0; + top: 0; + width: 100px; + height: 100px; + background-image: url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + } + + [type="checkbox"]:not(:checked) + label::after{ + content: ""; + position: absolute; + left: 0; + top: 0; + width: 100px; + height: 100px; + background-image: + url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + } + + + [type="checkbox"] + label::after{ + content: ""; + position: absolute; + left: 0; + top: 0; + width: 50px; + height: 50px; + // background-image:url(https://lh3.googleusercontent.com/pw/AJFCJaXZbz_cEXotxm2zQKD4qcxfVQvUDDygGprKDn9fwW4xxLDQmwDESZZgdIj73K6Y1fXAqRsmKb4UoQRWtMe8J3REfORXpse_0_W4zUSkNRmB-L9LnQU=w2400); + // background-size: contain; + background-image: + url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + } + + // [type="checkbox]{ + // box-shadow: 0 0 0 0.125em $bk3 inset; + // display: inline-block; + // position: relative; + // width: 200px; + // height: 200px; + // vertical-align: middle; + // -moz-appearance: none; + // -webkit-appearance: none; + // padding-left; 30px; + // // cursor: pointer; + // line-height: 25px; + // } + + // [type="checkbox"] + label{ + // box-shadow: 0 0 0 0.125em $bk3 inset; + // display: inline-block; + // position: relative; + // width: 200px; + // height: 200px; + // vertical-align: middle; + // -moz-appearance: none; + // -webkit-appearance: none; + // padding-left; 30px; + // // cursor: pointer; + // line-height: 25px; + // } + + // [type="checkbox]&:before, &:after { + // content: ""; + // display: block; + // position: absolute; + // } + + // [type="checkbox"]:checked + label::before{ + // content: ""; + // position: absolute; + // left: 0; + // top: 0; + // width: 100px; + // height: 100px; + // outline: 2px solid black; + // background: white; + // transform: scale(1); + // transition: all .3s ease; + // background-color: transparent; + // } + + // [type="checkbox"]&:before { + // background: + // url("https://s22.postimg.cc/fi4blx4gx/checkmark.png") 0 100% / 1.2em 1.35em no-repeat; + // bottom: 0.2em; + // left: 0.3em; + // width: 0; + // height: 1.35em; + // } + + // [type="checkbox"]&:after { + // background: + // background-repeat: no-repeat; + // border-radius: 0 0.1em 0.1em 0; + // opacity: 0; + // visibility: hidden; + // transform-origin: 0 0.25em; + // width: 12em; + // height: 1em; + // } + ` + +const TaskText = styled.p` + font-size: 2em; + font-family: 'Satoshi', sans-serif; + line-height: 0.5em; + width: 100%; + color: blue; +` +const playSound = () => { + const audio = new Audio(mp3); + audio.play(); +} + +const TaskList = () => { // Function. + const taskList = useSelector((store) => store.tasks.items) // useSelector + // to select the list/slice of tasks from Redux store and from that map over + // them/select an item. We go to our store and grab the items from our task. + const dispatch = useDispatch(); + const onDeleteSingleTaskBtnCLick = (id) => { // Function. Parameter = Id. + dispatch(tasks.actions.deleteSingleTask(id)); // Dispatch the action + } + const onIsCompletedCheckboxToggle = (id) => { // To establish, we need the id. + dispatch(tasks.actions.toggleIfTaskIsCompleted(id)); playSound(); // Since id + // we need to dispatch an action. + } + return ( +
+ +
+ ) +} + +// id as key and singleTask.name in list = printed out value. +// // store = backpack +export default TaskList; + +// CheckboxForm + +// // stop erase animations from firing on load +// document.addEventListener('DOMContentLoaded', function(){ +// document.querySelector({InnerWrapper}).addEventListener('click',e => { +// const checkboxCL = e.target.classList +// pState = "pristine"; + +// if (checkboxCL.contains(pState)) +// checkboxCL.remove(pState); +// }); +// }); \ No newline at end of file diff --git a/code/src/components/assets/check.mp3 b/code/src/components/assets/check.mp3 new file mode 100644 index 000000000..6555c126e Binary files /dev/null and b/code/src/components/assets/check.mp3 differ diff --git a/code/src/components/assets/vecteezy_hand-drawn-blue-check-mark-in-black-square-flat-vector_7042389.jpg b/code/src/components/assets/vecteezy_hand-drawn-blue-check-mark-in-black-square-flat-vector_7042389.jpg new file mode 100644 index 000000000..b452ed2dd Binary files /dev/null and b/code/src/components/assets/vecteezy_hand-drawn-blue-check-mark-in-black-square-flat-vector_7042389.jpg differ diff --git a/code/src/components/reducers/tasks.js b/code/src/components/reducers/tasks.js new file mode 100644 index 000000000..c030341bc --- /dev/null +++ b/code/src/components/reducers/tasks.js @@ -0,0 +1,132 @@ +// The functionality needs to be here before we can access it anywhere else. +// src/reducers/tasks.js +/* eslint-disable max-len */ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { // Variable/function that stores data, + // allows us to create slices below. Equal to an object. + items: [ // Inside variable = properties as an array of objects. Items = tasks. + // { // {} to create first object. + // id: 1, // Property. + // name: 'Watch video on actions & reducers', + // isCompleted: false + // }, + // { // Another object. + // id: 2, + // name: 'Follow redux codealong', + // isCompleted: false + // }, + // { + // id: 3, + // name: 'Fork weekly assignment', + // isCompleted: false + // }, + // { + // id: 4, + // name: 'Create a todo app', + // isCompleted: false + // } + ] +} +// We can access the size of an array, the number of tasks, through property length. + +const tasks = createSlice({ // Imported function as an argument. + name: 'tasks', // Use same name as in const. + initialState, // Referenced without value since JS sees variable above and + // knows we want a property below called initialState and will set the value + // to the value of the variable above called the same. + reducers: { + // Reducers = objects/functions that will be operating/performing different + // operations on our state. When we want something to happen with the state + // we need to create a function that will be able to modify that state. + // Things inside this object are object properties so instead of an = sign + // when we assign value to the object property, we use : . To add a task, + // create a function. + addTask: (store, action) => { // Function (= arrow) w/ 2 arguments, pushing + // data to items. The store references everything in the store state. + // The action is for us to see what we're passing from the component. + store.items = [action.payload, ...store.items]; // Immutable approach. + // Assigning new value. Set. Store.items is set to new array with some + // and previous destructured action payload. + // const copy = store.items; + // copy.push(action.payload); // Push adds at the end of the array. + // copy.unshift(action.payload); // Unshift adds at the beginning of the array. + // store.items = copy; + }, + // Frontend is in the end doing forms, checkboxes in those are painful. + toggleIfTaskIsCompleted: (store, action) => { // Still needs access to store + // because we need to change the checkbox for that task and action to get the + // task id. + const id = action.payload; + console.log('action.payload', action.payload) + const copyOfTaskArrayFromStore = store.items; + const condition = (element) => element.id === id; + const foundIndex = copyOfTaskArrayFromStore.findIndex(condition); + // Change comes below in the splice. We don't want to splice because we want + // the array to have same length as before. Want the task that has been completed. + // isCompleted = property. + copyOfTaskArrayFromStore[foundIndex].isCompleted = !copyOfTaskArrayFromStore[foundIndex].isCompleted; + // ! says it should be the opposite. + store.items = copyOfTaskArrayFromStore; // Assigning store.items to copyOfEtc. + }, + deleteSingleTask: (store, action) => { // Functionality. Action because + // to find single task we need info, unique = the id. + const id = action.payload; // const { id } = destructured, expecting component + // to provide an id. Google, js delete element from array = index and splice. + // Splice to remove a single element if I know the index. js index of object in + // array. { } works when passing/sending/destructuring an object with the + // property id. Otherwise, no { } needed. Assign id. + console.log('action.payload', action.payload) // action.payload is the id. + const copyOfTaskArrayFromStore = store.items; // An array. From state of store. + // Used bc don't want to modify. Operate on copy. _Come back to this_20/4_26:18 + const condition = (element) => element.id === id; // Condition. + // Now modify array. + const foundIndex = copyOfTaskArrayFromStore.findIndex(condition); + copyOfTaskArrayFromStore.splice(foundIndex, 1); + store.items = copyOfTaskArrayFromStore; + }, + deleteAllTask: (store) => { // Won't need payload. + store.items = []; + } + } +}); + +export default tasks; + +// Consistency in store.items = something. Return initialState is returning. Returning instate +// would not work due to already items. Otherwise, empty array, that would work. + +// initialState could also be written as an object instead of outside of const (slice). +// Store as a reference, referencing the initialState and its content, and then we need +// an action. If we want to add something, we get it from a component. To make clear +// that the function takes an argument from a component, we need the action. Can be banana. +// Remember that the first argument references the state of the store, the second argument +// references the data that is passed down from a component. +// Now we push. +// Pushing is the mutable approach. What are we pushing inside our items array? Inside our +// items array we need to push the data that will be from the component/the user. +// store.items.push(action.payload) +// Mutable = Modifying original items array. The action has two properties, the type of +// this action ('pokemons' addPokemon) and the payload (everything we're passing down +// from addPokemon.js/from any component). When we want to access any data put into our +// reducer we need to acces it with action.payload. When you mutate the state of the store +// you might get a browser/peer warning since whenever we do operations on initialState/Store +// we are changing it for several components. We want everything to be the same until the +// time of the operation of us mutating it/pushing the data is over. + +// Immutable approach = Assigning a new value for the whole items array instead of modifying +// it. Instead of attaching something, like with the push, to the end of our array we're +// assigning a whole new value. First it references itself (store.items), then references +// the action.payload. Expecting content of array. + +// const x = ['a','b','c'] +// const y = [x, 'd'] => [['a','b','c'], 'd'] If not using the spread operator. +// const z = [...x, 'd'] => ['a','b','c','d'] The spread operator takes the square +// brackets and removes them, putting everything inside of one array. +// const superObject = {a: 'test', b: 'other test'} +// const supererObject = {...superObject, c: 'third test'} The spread operator can +// also be used to add a property to an object. +// Spread operator removes nested array, you get the data but it is flattened out. + +// Filter is not enough to create a change by itself. Good to display things inside +// state variable but not for permanenting deleting. Semantics. \ No newline at end of file diff --git a/code/src/components/styles/global.js b/code/src/components/styles/global.js new file mode 100644 index 000000000..0027a6729 --- /dev/null +++ b/code/src/components/styles/global.js @@ -0,0 +1,69 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: center; + // border: solid 1px red; + + @media (min-width: 668px) { + width: 50%; + justify-items: center; + margin: 0 auto; + } +` + +// export const Background = styled.div` +// width: 100vw; +// ` + +export const Button = styled.button` + border: none; + // background: yellow; + color: blue; + font-family: 'Satoshi', sans-serif; + background: white; + + &:hover { + color: blue; + } +` +export const AddButton = styled(Button)` + font-family: Roboto; + font-size: 2em; + font-family: 'Satoshi', sans-serif; + + // &:hover { + // color: blue; + // } +` + +export const DeleteButton = styled(Button)` + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + width: 200px; + margin: 0; + font-size: 2em; + font-family: 'Satoshi', sans-serif; + background: white; +` +export const Title = styled.h1` + font-size: 2em; + margin: 10px; + display: flex; + flex-direction: row; + justify-content: rightdk; + align-items: center; + font-family: 'Satoshi', sans-serif; + color: blue; + width: 100%; + margin: 0; +` +export const H2 = styled.h2` + font-size: 2em; + font-family: ' Satoshi'; + color: blue; +` \ No newline at end of file diff --git a/code/src/index.css b/code/src/index.css index 4a1df4db7..901fd8074 100644 --- a/code/src/index.css +++ b/code/src/index.css @@ -5,9 +5,10 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + /* background: url("https://s22.postimg.cc/gzyyouldd/grey-paper-texture.jpg") center top; */ } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} +} \ No newline at end of file