use redux

This commit is contained in:
2018-05-10 18:11:14 +03:00
parent b141cc42fd
commit 5eead875d5
15 changed files with 614 additions and 227 deletions

18
.eslintrc.json Normal file
View File

@@ -0,0 +1,18 @@
{
"extends": ["airbnb", "prettier"],
"rules": {
"react/jsx-filename-extension": [
1,
{
"extensions": [".js", ".jsx"]
}
],
"linebreak-style": "off",
"no-unused-expressions": [
"error",
{
"allowTernary": true
}
]
}
}

View File

@@ -1,4 +1,5 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true
"editor.insertSpaces": true,
"prettier.eslintIntegration": true
}

369
package-lock.json generated
View File

@@ -2019,6 +2019,15 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"common-tags": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz",
"integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==",
"dev": true,
"requires": {
"babel-runtime": "6.26.0"
}
},
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -2696,6 +2705,12 @@
"randombytes": "2.0.6"
}
},
"dlv": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.1.tgz",
"integrity": "sha512-b/kUB0D6RgRGG69h5ExsLnUAwfs5Jndfk1pU2ao7/9mVdsxpUBlkFdTkNJThXw1jrLXpUbIIg+h3um5zXi6sFA==",
"dev": true
},
"dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@@ -3131,6 +3146,41 @@
}
}
},
"eslint-config-airbnb": {
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz",
"integrity": "sha512-zLyOhVWhzB/jwbz7IPSbkUuj7X2ox4PHXTcZkEmDqTvd0baJmJyuxlFPDlZOE/Y5bC+HQRaEkT3FoHo9wIdRiw==",
"dev": true,
"requires": {
"eslint-config-airbnb-base": "12.1.0"
}
},
"eslint-config-airbnb-base": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz",
"integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==",
"dev": true,
"requires": {
"eslint-restricted-globals": "0.1.1"
}
},
"eslint-config-prettier": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz",
"integrity": "sha512-ag8YEyBXsm3nmOv1Hz991VtNNDMRa+MNy8cY47Pl4bw6iuzqKbJajXdqUpiw13STdLLrznxgm1hj9NhxeOYq0A==",
"dev": true,
"requires": {
"get-stdin": "5.0.1"
},
"dependencies": {
"get-stdin": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
"integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
"dev": true
}
}
},
"eslint-config-react-app": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-2.1.0.tgz",
@@ -3202,26 +3252,28 @@
}
},
"eslint-plugin-import": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz",
"integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.11.0.tgz",
"integrity": "sha1-Fa7qN6Z0mdhI6OmBgG1GJ7VQOBY=",
"dev": true,
"requires": {
"builtin-modules": "1.1.1",
"contains-path": "0.1.0",
"debug": "2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "0.3.2",
"eslint-module-utils": "2.2.0",
"has": "1.0.1",
"lodash.cond": "4.5.2",
"lodash": "4.17.10",
"minimatch": "3.0.4",
"read-pkg-up": "2.0.0"
"read-pkg-up": "2.0.0",
"resolve": "1.6.0"
},
"dependencies": {
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"dev": true,
"requires": {
"esutils": "2.0.2",
"isarray": "1.0.0"
@@ -3231,6 +3283,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
"graceful-fs": "4.1.11",
"parse-json": "2.2.0",
@@ -3242,6 +3295,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"dev": true,
"requires": {
"pify": "2.3.0"
}
@@ -3250,6 +3304,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"dev": true,
"requires": {
"load-json-file": "2.0.0",
"normalize-package-data": "2.4.0",
@@ -3260,6 +3315,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"dev": true,
"requires": {
"find-up": "2.1.0",
"read-pkg": "2.0.0"
@@ -3268,14 +3324,16 @@
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
}
}
},
"eslint-plugin-jsx-a11y": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz",
"integrity": "sha512-5I9SpoP7gT4wBFOtXT8/tXNPYohHBVfyVfO17vkbC7r9kEIxYJF12D3pKqhk8+xnk12rfxKClS3WCFpVckFTPQ==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz",
"integrity": "sha1-VFg9GuRCSDFi4EDhPMMYZUZRAOU=",
"dev": true,
"requires": {
"aria-query": "0.7.1",
"array-includes": "3.0.3",
@@ -3283,30 +3341,27 @@
"axobject-query": "0.1.0",
"damerau-levenshtein": "1.0.4",
"emoji-regex": "6.5.1",
"jsx-ast-utils": "1.4.1"
"jsx-ast-utils": "2.0.1"
}
},
"eslint-plugin-react": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz",
"integrity": "sha512-tvjU9u3VqmW2vVuYnE8Qptq+6ji4JltjOjJ9u7VAOxVYkUkyBZWRvNYKbDv5fN+L6wiA+4we9+qQahZ0m63XEA==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz",
"integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==",
"dev": true,
"requires": {
"doctrine": "2.1.0",
"has": "1.0.1",
"jsx-ast-utils": "2.0.1",
"prop-types": "15.6.1"
},
"dependencies": {
"jsx-ast-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
"integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
"requires": {
"array-includes": "3.0.3"
}
}
}
},
"eslint-restricted-globals": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
"integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
"dev": true
},
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
@@ -4710,6 +4765,11 @@
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
},
"hoist-non-react-statics": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz",
"integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w=="
},
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -6325,9 +6385,13 @@
}
},
"jsx-ast-utils": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz",
"integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE="
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
"integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
"dev": true,
"requires": {
"array-includes": "3.0.3"
}
},
"killable": {
"version": "1.0.0",
@@ -6469,6 +6533,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash-es": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
"integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -6494,6 +6563,12 @@
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
},
"lodash.merge": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
"dev": true
},
"lodash.template": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
@@ -6511,6 +6586,12 @@
"lodash._reinterpolate": "3.0.0"
}
},
"lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
"dev": true
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -6521,6 +6602,16 @@
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz",
"integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po="
},
"loglevel-colored-level-prefix": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
"integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"loglevel": "1.6.1"
}
},
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -8582,6 +8673,56 @@
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
},
"prettier": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.12.1.tgz",
"integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU=",
"dev": true
},
"prettier-eslint": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.8.1.tgz",
"integrity": "sha512-8YMkJZnA+XVfEW6fPet05jpNmSQbD+Htbh/QyOxQcVf2GIUEZsnGP7ZScaM9Mq2Ra2261eCu60E7/TRIy9coXQ==",
"dev": true,
"requires": {
"babel-runtime": "6.26.0",
"common-tags": "1.7.2",
"dlv": "1.1.1",
"eslint": "4.10.0",
"indent-string": "3.2.0",
"lodash.merge": "4.6.1",
"loglevel-colored-level-prefix": "1.0.0",
"prettier": "1.12.1",
"pretty-format": "22.4.3",
"require-relative": "0.8.7",
"typescript": "2.8.3",
"typescript-eslint-parser": "11.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"pretty-format": {
"version": "22.4.3",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
"integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==",
"dev": true,
"requires": {
"ansi-regex": "3.0.0",
"ansi-styles": "3.2.1"
}
}
}
},
"pretty-bytes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
@@ -8869,6 +9010,19 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz",
"integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw=="
},
"react-redux": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
"integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==",
"requires": {
"hoist-non-react-statics": "2.5.0",
"invariant": "2.2.4",
"lodash": "4.17.10",
"lodash-es": "4.17.10",
"loose-envify": "1.3.1",
"prop-types": "15.6.1"
}
},
"react-scripts": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.1.4.tgz",
@@ -8915,6 +9069,99 @@
"whatwg-fetch": "2.0.3"
},
"dependencies": {
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"requires": {
"esutils": "2.0.2",
"isarray": "1.0.0"
}
},
"eslint-plugin-import": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz",
"integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==",
"requires": {
"builtin-modules": "1.1.1",
"contains-path": "0.1.0",
"debug": "2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "0.3.2",
"eslint-module-utils": "2.2.0",
"has": "1.0.1",
"lodash.cond": "4.5.2",
"minimatch": "3.0.4",
"read-pkg-up": "2.0.0"
}
},
"eslint-plugin-jsx-a11y": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz",
"integrity": "sha512-5I9SpoP7gT4wBFOtXT8/tXNPYohHBVfyVfO17vkbC7r9kEIxYJF12D3pKqhk8+xnk12rfxKClS3WCFpVckFTPQ==",
"requires": {
"aria-query": "0.7.1",
"array-includes": "3.0.3",
"ast-types-flow": "0.0.7",
"axobject-query": "0.1.0",
"damerau-levenshtein": "1.0.4",
"emoji-regex": "6.5.1",
"jsx-ast-utils": "1.4.1"
}
},
"eslint-plugin-react": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz",
"integrity": "sha512-tvjU9u3VqmW2vVuYnE8Qptq+6ji4JltjOjJ9u7VAOxVYkUkyBZWRvNYKbDv5fN+L6wiA+4we9+qQahZ0m63XEA==",
"requires": {
"doctrine": "2.1.0",
"has": "1.0.1",
"jsx-ast-utils": "2.0.1",
"prop-types": "15.6.1"
},
"dependencies": {
"doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"requires": {
"esutils": "2.0.2"
}
},
"jsx-ast-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
"integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
"requires": {
"array-includes": "3.0.3"
}
}
}
},
"jsx-ast-utils": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz",
"integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE="
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"requires": {
"graceful-fs": "4.1.11",
"parse-json": "2.2.0",
"pify": "2.3.0",
"strip-bom": "3.0.0"
}
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"requires": {
"pify": "2.3.0"
}
},
"promise": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.0.1.tgz",
@@ -8923,6 +9170,30 @@
"asap": "2.0.6"
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"requires": {
"load-json-file": "2.0.0",
"normalize-package-data": "2.4.0",
"path-type": "2.0.0"
}
},
"read-pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"requires": {
"find-up": "2.1.0",
"read-pkg": "2.0.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
},
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
@@ -9052,6 +9323,15 @@
}
}
},
"redux": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz",
"integrity": "sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA==",
"requires": {
"loose-envify": "1.3.1",
"symbol-observable": "1.2.0"
}
},
"regenerate": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
@@ -9227,6 +9507,12 @@
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
},
"require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
"dev": true
},
"require-uncached": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
@@ -10067,6 +10353,11 @@
"serviceworker-cache-polyfill": "4.0.0"
}
},
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
"symbol-tree": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
@@ -10313,6 +10604,30 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"typescript": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz",
"integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==",
"dev": true
},
"typescript-eslint-parser": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-11.0.0.tgz",
"integrity": "sha512-/fBHTBRBSorWQGKWOOjeMPkzd3o8cOPtFjTRwU5JLNGgVtmMa3KDkiw0R2n+H6ovo9y3OX30/5usm6YTqY44PQ==",
"dev": true,
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.4.1"
},
"dependencies": {
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
"dev": true
}
}
},
"ua-parser-js": {
"version": "0.7.18",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",

View File

@@ -6,14 +6,25 @@
"@fortawesome/fontawesome-svg-core": "^1.2.0-13",
"@fortawesome/free-solid-svg-icons": "^5.1.0-10",
"@fortawesome/react-fontawesome": "0.1.0-10",
"prop-types": "^15.6.1",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-scripts": "1.1.4"
"react-redux": "^5.0.7",
"react-scripts": "1.1.4",
"redux": "^4.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"prettier-eslint": "^8.8.1"
}
}

View File

@@ -4,103 +4,12 @@ import "./App.css";
import InputContainer from "./containers/InputContainer";
import ItemsContainer from "./containers/ItemsContainer";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
inputBottomBorder: true,
inputFieldValue: "",
items: [
{ id: 1, text: "hello1", crossed: false },
{ id: 2, text: "hello2", crossed: false },
{ id: 3, text: "hello3", crossed: false }
]
};
this.addToList = this.addToList.bind(this);
this.removeFromList = this.removeFromList.bind(this);
this.onInputChange = this.onInputChange.bind(this);
this.addToList = this.addToList.bind(this);
this.onItemClick = this.onItemClick.bind(this);
}
render() {
return (
<div id="container">
<h1>To-Do List</h1>
<InputContainer
value={this.state.inputFieldValue}
onInputChange={this.onInputChange}
inputBottomBorder={this.state.inputBottomBorder}
onClick={this.addToList}
/>
<ItemsContainer
items={this.state.items}
onItemClick={this.onItemClick}
handleDelete={this.removeFromList}
/>
</div>
);
}
addToList() {
if (!this.state.inputFieldValue.trim()) {
return;
}
const { items } = this.state;
items.unshift({
crossed: false,
id: items.length + 1,
text: this.state.inputFieldValue.trim()
});
this.setState(
{
inputFieldValue: "",
items
},
() => {
this.updateInputBorder();
}
);
}
removeFromList(id) {
const { items } = this.state;
const newItems = items.filter(item => item.id !== id);
this.setState(
{
items: newItems
},
() => {
this.updateInputBorder();
}
);
}
onInputChange(inputFieldValue) {
this.setState({ inputFieldValue });
}
updateInputBorder() {
this.state.items.length === 0
? this.setState({ inputBottomBorder: false })
: this.setState({ inputBottomBorder: true });
}
onItemClick(id) {
const { items } = this.state;
items.some((item, i) => {
if (item.id === id) {
const newItem = { ...item };
newItem.crossed = !item.crossed;
items[i] = newItem;
return true;
} else {
return false;
}
});
this.setState({
items
});
}
export default function App() {
return (
<div id="container">
<h1>To-Do List</h1>
<InputContainer />
<ItemsContainer />
</div>
);
}
export default App;

26
src/actions.js Normal file
View File

@@ -0,0 +1,26 @@
export const ADD_ITEM = "ADD_ITEM";
export const REMOVE_ITEM = "REMOVE_ITEM";
export const TOGGLE_ITEM = "TOGGLE_ITEM";
export const SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER";
export const VisibilityFilters = {
SHOW_ALL: "SHOW_ALL",
SHOW_COMPLETED: "SHOW_COMPLETED",
SHOW_ACTIVE: "SHOW_ACTIVE"
};
export function addItem(text) {
return { type: ADD_ITEM, text };
}
export function reomveItem(id) {
return { type: REMOVE_ITEM, id };
}
export function toggleItem(id) {
return { type: TOGGLE_ITEM, id };
}
export function setVisibilityFilter(filter) {
return { type: SET_VISIBILITY_FILTER, filter };
}

View File

@@ -1,35 +1,34 @@
import * as React from "react";
import PropTypes from "prop-types";
class Input extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
function Input(props) {
let input;
const classes = [];
if (!props.inputBottomBorder) {
classes.push("no-border");
}
render() {
const classes = [];
if (!this.props.inputBottomBorder) {
classes.push("no-border");
}
return (
<div id="inputs" className={classes.join(" ")}>
<input
id="input"
type="text"
value={this.props.value}
onChange={this.handleChange}
/>
<button id="add" onClick={this.props.onClick}>
add
</button>
</div>
);
}
handleChange(event) {
this.props.onInputChange(event.target.value);
}
return (
<div id="inputs" className={classes.join(" ")}>
<input
ref={node => {
input = node;
}}
id="input"
type="text"
/>
<button id="add" onClick={() => props.onClick(input.value)}>
add
</button>
</div>
);
}
Input.propTypes = {
inputBottomBorder: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
};
export default Input;

View File

@@ -1,6 +1,7 @@
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as React from "react";
import PropTypes from "prop-types";
class Item extends React.Component {
constructor(props) {
@@ -10,43 +11,6 @@ class Item extends React.Component {
};
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.onClick = this.onClick.bind(this);
}
render() {
const deleteClasses = ["delete"];
if (!this.state.hover) {
deleteClasses.push("disabled");
}
const itemClasses = ["item"];
if (this.props.item.crossed) {
itemClasses.push("done");
}
return (
<li
onMouseOver={this.onMouseOver}
onFocus={this.onMouseOver}
onMouseOut={this.onMouseOut}
onBlur={this.onMouseOut}
>
<div className={deleteClasses.join(" ")} onClick={this.handleDelete}>
<FontAwesomeIcon icon={faTrash} />
</div>
<div className={itemClasses.join(" ")} onClick={this.onClick}>
{this.props.item.text}
</div>
</li>
);
}
handleDelete(event) {
this.props.handleDelete(this.props.item.id);
}
onClick(event) {
this.props.onClick(this.props.item.id);
}
onMouseOver() {
this.setState({
@@ -59,6 +23,46 @@ class Item extends React.Component {
hover: false
});
}
render() {
const deleteClasses = ["delete"];
if (!this.state.hover) {
deleteClasses.push("disabled");
}
const itemClasses = ["item"];
if (this.props.item.completed) {
itemClasses.push("done");
}
return (
<li
onMouseOver={this.onMouseOver}
onFocus={this.onMouseOver}
onMouseOut={this.onMouseOut}
onBlur={this.onMouseOut}
>
<div
className={deleteClasses.join(" ")}
onClick={this.props.handleDelete}
>
<FontAwesomeIcon icon={faTrash} />
</div>
<div className={itemClasses.join(" ")} onClick={this.props.onClick}>
{this.props.item.text}
</div>
</li>
);
}
}
Item.propTypes = {
item: PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired
}).isRequired,
handleDelete: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired
};
export default Item;

View File

@@ -0,0 +1,28 @@
import * as React from "react";
import PropTypes from "prop-types";
import Item from "./Item";
export default function ItemsContainer(props) {
const items = props.items.map(item => (
<Item
key={item.id}
item={item}
onClick={() => props.onItemClick(item.id)}
handleDelete={() => props.handleDelete(item.id)}
/>
));
return <ul id="list">{items}</ul>;
}
ItemsContainer.propTypes = {
items: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired
})
).isRequired,
handleDelete: PropTypes.func.isRequired,
onItemClick: PropTypes.func.isRequired
};

View File

@@ -1,14 +1,28 @@
import * as React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Input from "../components/Input";
import { addItem } from "../actions";
export default function InputContainer(props) {
return (
<Input
value={props.value}
onInputChange={props.onInputChange}
inputBottomBorder={props.inputBottomBorder}
onClick={props.onClick}
/>
);
const InputContainer = props => (
<Input
inputBottomBorder={props.inputBottomBorder}
onClick={text => props.dispatch(addItem(text))}
/>
);
InputContainer.propTypes = {
inputBottomBorder: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
inputBottomBorder: state.items.length !== 0
};
}
const InputContainerConnected = connect(mapStateToProps)(InputContainer);
export default InputContainerConnected;

View File

@@ -1,21 +1,18 @@
import * as React from "react";
import { connect } from "react-redux";
import TodoList from "../components/TodoList";
import { toggleItem, reomveItem } from "../actions";
import Item from "../components/Item";
export default class ItemsContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
const items = this.props.items.map(item => (
<Item
key={item.id}
item={item}
onClick={this.props.onItemClick}
handleDelete={this.props.handleDelete}
/>
));
return <ul id="list">{items}</ul>;
}
function mapStateToProps(state) {
return state;
}
function mapDispatchToProps(dispatch) {
return {
onItemClick: id => dispatch(toggleItem(id)),
handleDelete: id => dispatch(reomveItem(id))
};
}
const ItemsContainer = connect(mapStateToProps, mapDispatchToProps)(TodoList);
export default ItemsContainer;

View File

@@ -1,8 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import todoApp from "./reducers";
ReactDOM.render(<App />, document.getElementById('root'));
const store = createStore(todoApp);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
registerServiceWorker();

11
src/reducers/index.js Normal file
View File

@@ -0,0 +1,11 @@
import { combineReducers } from "redux";
import items from "./items";
import visibilityFilter from "./visibilityFilter";
const todoApp = combineReducers({
items,
visibilityFilter
});
export default todoApp;

32
src/reducers/items.js Normal file
View File

@@ -0,0 +1,32 @@
import { ADD_ITEM, REMOVE_ITEM, TOGGLE_ITEM } from "../actions";
export default function items(state = [], action) {
switch (action.type) {
case ADD_ITEM:
return [
...state,
{
id: Math.floor(Math.random() * 100),
text: action.text,
completed: false
}
];
case REMOVE_ITEM:
return state.filter(item => item.id !== action.id);
case TOGGLE_ITEM: {
const itemsArray = [...state];
itemsArray.some((item, i) => {
if (item.id === action.id) {
const newItem = { ...item };
newItem.completed = !item.completed;
itemsArray[i] = newItem;
return true;
}
return false;
});
return itemsArray;
}
default:
return state;
}
}

View File

@@ -0,0 +1,12 @@
import { VisibilityFilters, SET_VISIBILITY_FILTER } from "../actions";
const { SHOW_ALL } = VisibilityFilters;
export default function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return { ...state, visibilityFilter: action.filter };
default:
return state;
}
}