Browse Source

added input player

Rostyslav Nahornyi 2 years ago
parent
commit
f78b7c52f8

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "prettier.tabWidth": 4
+}

+ 13 - 0
jsconfig.json

@@ -0,0 +1,13 @@
+{
+    "compilerOptions": {
+        "module": "ESNext",
+        "moduleResolution": "Node",
+        "target": "ES2020",
+        "jsx": "preserve",
+        "strictFunctionTypes": true
+    },
+    "exclude": [
+        "node_modules",
+        "**/node_modules/*"
+    ]
+}

+ 724 - 0
package-lock.json

@@ -8,6 +8,10 @@
       "name": "audio-player",
       "version": "0.1.0",
       "dependencies": {
+        "@emotion/react": "^11.9.0",
+        "@emotion/styled": "^11.8.1",
+        "@mui/icons-material": "^5.6.2",
+        "@mui/material": "^5.6.2",
         "@testing-library/jest-dom": "^5.16.4",
         "@testing-library/react": "^13.1.1",
         "@testing-library/user-event": "^13.5.0",
@@ -1973,6 +1977,81 @@
         "postcss": "^8.3"
       }
     },
+    "node_modules/@emotion/babel-plugin": {
+      "version": "11.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz",
+      "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.12.13",
+        "@babel/plugin-syntax-jsx": "^7.12.13",
+        "@babel/runtime": "^7.13.10",
+        "@emotion/hash": "^0.8.0",
+        "@emotion/memoize": "^0.7.5",
+        "@emotion/serialize": "^1.0.2",
+        "babel-plugin-macros": "^2.6.1",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.0.13"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/babel-plugin-macros": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
+      "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "cosmiconfig": "^6.0.0",
+        "resolve": "^1.12.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/cosmiconfig": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+      "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.1.0",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.7.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@emotion/cache": {
+      "version": "11.7.1",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+      "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+      "dependencies": {
+        "@emotion/memoize": "^0.7.4",
+        "@emotion/sheet": "^1.1.0",
+        "@emotion/utils": "^1.0.0",
+        "@emotion/weak-memoize": "^0.2.5",
+        "stylis": "4.0.13"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+      "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
+    },
     "node_modules/@emotion/is-prop-valid": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
@@ -1986,6 +2065,74 @@
       "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
       "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
     },
+    "node_modules/@emotion/react": {
+      "version": "11.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
+      "integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@emotion/babel-plugin": "^11.7.1",
+        "@emotion/cache": "^11.7.1",
+        "@emotion/serialize": "^1.0.3",
+        "@emotion/utils": "^1.1.0",
+        "@emotion/weak-memoize": "^0.2.5",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0",
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/serialize": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+      "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+      "dependencies": {
+        "@emotion/hash": "^0.8.0",
+        "@emotion/memoize": "^0.7.4",
+        "@emotion/unitless": "^0.7.5",
+        "@emotion/utils": "^1.0.0",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@emotion/sheet": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+      "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+    },
+    "node_modules/@emotion/styled": {
+      "version": "11.8.1",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz",
+      "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@emotion/babel-plugin": "^11.7.1",
+        "@emotion/is-prop-valid": "^1.1.2",
+        "@emotion/serialize": "^1.0.2",
+        "@emotion/utils": "^1.1.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0",
+        "@emotion/react": "^11.0.0-rc.0",
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@emotion/stylis": {
       "version": "0.8.5",
       "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
@@ -1996,6 +2143,16 @@
       "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
       "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
     },
+    "node_modules/@emotion/utils": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+      "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+    },
+    "node_modules/@emotion/weak-memoize": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
+      "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
+    },
     "node_modules/@eslint/eslintrc": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz",
@@ -2753,6 +2910,237 @@
       "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
       "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg=="
     },
+    "node_modules/@mui/base": {
+      "version": "5.0.0-alpha.77",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.77.tgz",
+      "integrity": "sha512-Zqm3qlczGViD3lJSYo8ZnQLHJ3PwGYftbDfVuh2Rq5OD88F7H6oDILlqknzty59NDkeSVO2qlymYmHOY1nLodg==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@emotion/is-prop-valid": "^1.1.2",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "@popperjs/core": "^2.11.5",
+        "clsx": "^1.1.1",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0",
+        "react-dom": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/icons-material": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.6.2.tgz",
+      "integrity": "sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@mui/material": "^5.0.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/material": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
+      "integrity": "sha512-bwMvroBrMgUTwUh/BcjhtcJwEw9uH4chV3+ZSj6RckOJtMj8U4yEeD7S4NgHE8Ioj5eObKFzHpih/cTD1sDRpg==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/base": "5.0.0-alpha.77",
+        "@mui/system": "^5.6.2",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "@types/react-transition-group": "^4.4.4",
+        "clsx": "^1.1.1",
+        "csstype": "^3.0.11",
+        "hoist-non-react-statics": "^3.3.2",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2",
+        "react-transition-group": "^4.4.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.5.0",
+        "@emotion/styled": "^11.3.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0",
+        "react-dom": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/private-theming": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.6.2.tgz",
+      "integrity": "sha512-IbrSfFXfiZdyhRMC2bgGTFtb16RBQ5mccmjeh3MtAERWuepiCK7gkW5D9WhEsfTu6iez+TEjeUKSgmMHlsM2mg==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/utils": "^5.6.1",
+        "prop-types": "^15.7.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/styled-engine": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.6.1.tgz",
+      "integrity": "sha512-jEhH6TBY8jc9S8yVncXmoTYTbATjEu44RMFXj6sIYfKr5NArVwTwRo3JexLL0t3BOAiYM4xsFLgfKEIvB9SAeQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@emotion/cache": "^11.7.1",
+        "prop-types": "^15.7.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.4.1",
+        "@emotion/styled": "^11.3.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/system": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.2.tgz",
+      "integrity": "sha512-Wg9TRbvavSwEYk6UdpnoDx+CqJfaAN7AzlmwEx7DtGmx0snFVBST8FVb1Ev1vXosxEnq6/fe7ZDRobFVewvEPQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/private-theming": "^5.6.2",
+        "@mui/styled-engine": "^5.6.1",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "clsx": "^1.1.1",
+        "csstype": "^3.0.11",
+        "prop-types": "^15.7.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.5.0",
+        "@emotion/styled": "^11.3.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/types": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.3.tgz",
+      "integrity": "sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA==",
+      "peerDependencies": {
+        "@types/react": "*"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/utils": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.6.1.tgz",
+      "integrity": "sha512-CPrzrkiBusCZBLWu0Sg5MJvR3fKJyK3gKecLVX012LULyqg2U64Oz04BKhfkbtBrPBbSQxM+DWW9B1c9hmV9nQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@types/prop-types": "^15.7.4",
+        "@types/react-is": "^16.7.1 || ^17.0.0",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "react": "^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2842,6 +3230,15 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.5",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
+      "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
     "node_modules/@rollup/plugin-babel": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -3622,6 +4019,22 @@
         "@types/react": "*"
       }
     },
+    "node_modules/@types/react-is": {
+      "version": "17.0.3",
+      "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
+      "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
+    "node_modules/@types/react-transition-group": {
+      "version": "4.4.4",
+      "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
+      "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
     "node_modules/@types/resolve": {
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -5186,6 +5599,14 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "node_modules/clsx": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+      "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -6146,6 +6567,15 @@
         "utila": "~0.4"
       }
     },
+    "node_modules/dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "dependencies": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
     "node_modules/dom-serializer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -7454,6 +7884,11 @@
         "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
       }
     },
+    "node_modules/find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -13365,6 +13800,21 @@
         }
       }
     },
+    "node_modules/react-transition-group": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+      "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+      "dependencies": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      },
+      "peerDependencies": {
+        "react": ">=16.6.0",
+        "react-dom": ">=16.6.0"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -14477,6 +14927,11 @@
         "postcss": "^8.2.15"
       }
     },
+    "node_modules/stylis": {
+      "version": "4.0.13",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+      "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+    },
     "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -17390,6 +17845,71 @@
         "postcss-value-parser": "^4.2.0"
       }
     },
+    "@emotion/babel-plugin": {
+      "version": "11.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz",
+      "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==",
+      "requires": {
+        "@babel/helper-module-imports": "^7.12.13",
+        "@babel/plugin-syntax-jsx": "^7.12.13",
+        "@babel/runtime": "^7.13.10",
+        "@emotion/hash": "^0.8.0",
+        "@emotion/memoize": "^0.7.5",
+        "@emotion/serialize": "^1.0.2",
+        "babel-plugin-macros": "^2.6.1",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.0.13"
+      },
+      "dependencies": {
+        "babel-plugin-macros": {
+          "version": "2.8.0",
+          "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
+          "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
+          "requires": {
+            "@babel/runtime": "^7.7.2",
+            "cosmiconfig": "^6.0.0",
+            "resolve": "^1.12.0"
+          }
+        },
+        "cosmiconfig": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+          "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+          "requires": {
+            "@types/parse-json": "^4.0.0",
+            "import-fresh": "^3.1.0",
+            "parse-json": "^5.0.0",
+            "path-type": "^4.0.0",
+            "yaml": "^1.7.2"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+        }
+      }
+    },
+    "@emotion/cache": {
+      "version": "11.7.1",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+      "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+      "requires": {
+        "@emotion/memoize": "^0.7.4",
+        "@emotion/sheet": "^1.1.0",
+        "@emotion/utils": "^1.0.0",
+        "@emotion/weak-memoize": "^0.2.5",
+        "stylis": "4.0.13"
+      }
+    },
+    "@emotion/hash": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+      "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
+    },
     "@emotion/is-prop-valid": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
@@ -17403,6 +17923,49 @@
       "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
       "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
     },
+    "@emotion/react": {
+      "version": "11.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
+      "integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
+      "requires": {
+        "@babel/runtime": "^7.13.10",
+        "@emotion/babel-plugin": "^11.7.1",
+        "@emotion/cache": "^11.7.1",
+        "@emotion/serialize": "^1.0.3",
+        "@emotion/utils": "^1.1.0",
+        "@emotion/weak-memoize": "^0.2.5",
+        "hoist-non-react-statics": "^3.3.1"
+      }
+    },
+    "@emotion/serialize": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+      "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+      "requires": {
+        "@emotion/hash": "^0.8.0",
+        "@emotion/memoize": "^0.7.4",
+        "@emotion/unitless": "^0.7.5",
+        "@emotion/utils": "^1.0.0",
+        "csstype": "^3.0.2"
+      }
+    },
+    "@emotion/sheet": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+      "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+    },
+    "@emotion/styled": {
+      "version": "11.8.1",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz",
+      "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==",
+      "requires": {
+        "@babel/runtime": "^7.13.10",
+        "@emotion/babel-plugin": "^11.7.1",
+        "@emotion/is-prop-valid": "^1.1.2",
+        "@emotion/serialize": "^1.0.2",
+        "@emotion/utils": "^1.1.0"
+      }
+    },
     "@emotion/stylis": {
       "version": "0.8.5",
       "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
@@ -17413,6 +17976,16 @@
       "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
       "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
     },
+    "@emotion/utils": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+      "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+    },
+    "@emotion/weak-memoize": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
+      "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
+    },
     "@eslint/eslintrc": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz",
@@ -17975,6 +18548,101 @@
       "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
       "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg=="
     },
+    "@mui/base": {
+      "version": "5.0.0-alpha.77",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.77.tgz",
+      "integrity": "sha512-Zqm3qlczGViD3lJSYo8ZnQLHJ3PwGYftbDfVuh2Rq5OD88F7H6oDILlqknzty59NDkeSVO2qlymYmHOY1nLodg==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@emotion/is-prop-valid": "^1.1.2",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "@popperjs/core": "^2.11.5",
+        "clsx": "^1.1.1",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2"
+      }
+    },
+    "@mui/icons-material": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.6.2.tgz",
+      "integrity": "sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ==",
+      "requires": {
+        "@babel/runtime": "^7.17.2"
+      }
+    },
+    "@mui/material": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
+      "integrity": "sha512-bwMvroBrMgUTwUh/BcjhtcJwEw9uH4chV3+ZSj6RckOJtMj8U4yEeD7S4NgHE8Ioj5eObKFzHpih/cTD1sDRpg==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/base": "5.0.0-alpha.77",
+        "@mui/system": "^5.6.2",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "@types/react-transition-group": "^4.4.4",
+        "clsx": "^1.1.1",
+        "csstype": "^3.0.11",
+        "hoist-non-react-statics": "^3.3.2",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2",
+        "react-transition-group": "^4.4.2"
+      }
+    },
+    "@mui/private-theming": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.6.2.tgz",
+      "integrity": "sha512-IbrSfFXfiZdyhRMC2bgGTFtb16RBQ5mccmjeh3MtAERWuepiCK7gkW5D9WhEsfTu6iez+TEjeUKSgmMHlsM2mg==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/utils": "^5.6.1",
+        "prop-types": "^15.7.2"
+      }
+    },
+    "@mui/styled-engine": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.6.1.tgz",
+      "integrity": "sha512-jEhH6TBY8jc9S8yVncXmoTYTbATjEu44RMFXj6sIYfKr5NArVwTwRo3JexLL0t3BOAiYM4xsFLgfKEIvB9SAeQ==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@emotion/cache": "^11.7.1",
+        "prop-types": "^15.7.2"
+      }
+    },
+    "@mui/system": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.2.tgz",
+      "integrity": "sha512-Wg9TRbvavSwEYk6UdpnoDx+CqJfaAN7AzlmwEx7DtGmx0snFVBST8FVb1Ev1vXosxEnq6/fe7ZDRobFVewvEPQ==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@mui/private-theming": "^5.6.2",
+        "@mui/styled-engine": "^5.6.1",
+        "@mui/types": "^7.1.3",
+        "@mui/utils": "^5.6.1",
+        "clsx": "^1.1.1",
+        "csstype": "^3.0.11",
+        "prop-types": "^15.7.2"
+      }
+    },
+    "@mui/types": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.3.tgz",
+      "integrity": "sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA==",
+      "requires": {}
+    },
+    "@mui/utils": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.6.1.tgz",
+      "integrity": "sha512-CPrzrkiBusCZBLWu0Sg5MJvR3fKJyK3gKecLVX012LULyqg2U64Oz04BKhfkbtBrPBbSQxM+DWW9B1c9hmV9nQ==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "@types/prop-types": "^15.7.4",
+        "@types/react-is": "^16.7.1 || ^17.0.0",
+        "prop-types": "^15.7.2",
+        "react-is": "^17.0.2"
+      }
+    },
     "@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -18021,6 +18689,11 @@
         }
       }
     },
+    "@popperjs/core": {
+      "version": "2.11.5",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
+      "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw=="
+    },
     "@rollup/plugin-babel": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -18608,6 +19281,22 @@
         "@types/react": "*"
       }
     },
+    "@types/react-is": {
+      "version": "17.0.3",
+      "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
+      "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
+      "requires": {
+        "@types/react": "*"
+      }
+    },
+    "@types/react-transition-group": {
+      "version": "4.4.4",
+      "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
+      "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+      "requires": {
+        "@types/react": "*"
+      }
+    },
     "@types/resolve": {
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -19773,6 +20462,11 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "clsx": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+      "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -20472,6 +21166,15 @@
         "utila": "~0.4"
       }
     },
+    "dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "requires": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
     "dom-serializer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -21442,6 +22145,11 @@
         "pkg-dir": "^4.1.0"
       }
     },
+    "find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+    },
     "find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -25546,6 +26254,17 @@
         "workbox-webpack-plugin": "^6.4.1"
       }
     },
+    "react-transition-group": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+      "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+      "requires": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      }
+    },
     "readable-stream": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -26363,6 +27082,11 @@
         "postcss-selector-parser": "^6.0.4"
       }
     },
+    "stylis": {
+      "version": "4.0.13",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+      "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+    },
     "supports-color": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

+ 4 - 0
package.json

@@ -3,6 +3,10 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@emotion/react": "^11.9.0",
+    "@emotion/styled": "^11.8.1",
+    "@mui/icons-material": "^5.6.2",
+    "@mui/material": "^5.6.2",
     "@testing-library/jest-dom": "^5.16.4",
     "@testing-library/react": "^13.1.1",
     "@testing-library/user-event": "^13.5.0",

+ 4 - 21
public/index.html

@@ -10,34 +10,17 @@
       content="Web site created using create-react-app"
     />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
-    <!--
-      manifest.json provides metadata used when your web app is installed on a
-      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-    -->
     <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-    <!--
-      Notice the use of %PUBLIC_URL% in the tags above.
-      It will be replaced with the URL of the `public` folder during the build.
-      Only files inside the `public` folder can be referenced from the HTML.
+    
 
-      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
-      work correctly both with client-side routing and a non-root public URL.
-      Learn how to configure a non-root public URL by running `npm run build`.
-    -->
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap" rel="stylesheet">
     <title>React App</title>
   </head>
   <body>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root"></div>
-    <!--
-      This HTML file is a template.
-      If you open it directly in the browser, you will see an empty page.
 
-      You can add webfonts, meta tags, or analytics to this file.
-      The build step will place the bundled scripts into the <body> tag.
-
-      To begin the development, run `npm start` or `yarn start`.
-      To create a production bundle, use `npm run build` or `yarn build`.
-    -->
   </body>
 </html>

+ 5 - 3
src/App.js

@@ -1,11 +1,13 @@
 import React from "react";
 import { BrowserRouter, Routes, Route } from "react-router-dom";
-import { Home, Playlists, Profile, Queue, Tracks } from "./Pages";
+import { Home, Playlists, Profile, Queue, Tracks } from "./pages";
+import { history } from "./utils/history";
+
 
 const App = () => {
     return (
-        <BrowserRouter>
-            <Routes>
+        <BrowserRouter history={history}>
+            <Routes >
                 <Route path="/" element={<Home />} />
                 <Route path="profile" element={<Profile />} />
                 <Route path="queue" element={<Queue />} />

+ 71 - 6
src/Components/LeftBar/LeftBar.jsx

@@ -1,9 +1,74 @@
-import React from 'react'
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { Wrapper, Navbar, Line, BtnBack, Logo, Image, Text } from "./style";
+import { Tab } from "../../components";
+import { ROUTES } from "../../utils/constants";
+
+import logo from "../../assets/logo.png";
+import IconBack from "@mui/icons-material/KeyboardBackspace";
+import HomeIcon from "../../assets/home_icon.svg";
+import ProfileIcon from "../../assets/profile_icon.svg";
+import QueueIcon from "../../assets/queue_icon.svg";
+import TrackIcon from "../../assets/track_icon.svg";
+import PlaylistIcon from "../../assets/playlist_icon.svg";
+import { push } from "../../utils/history";
+
+const tabs = [
+    {
+        label: "Home",
+        route: ROUTES.HOME,
+        icon: HomeIcon,
+    },
+    {
+        label: "Profile",
+        route: ROUTES.PROFILE,
+        icon: ProfileIcon,
+    },
+    {
+        label: "Play queue",
+        route: ROUTES.QUEUE,
+        icon: QueueIcon,
+    },
+    {
+        label: "Tracks",
+        route: ROUTES.TRACKS,
+        icon: TrackIcon,
+    },
+    {
+        label: "Playlists",
+        route: ROUTES.PLAYLISTS,
+        icon: PlaylistIcon,
+    },
+];
 
 const LeftBar = () => {
-  return (
-    <div>LeftBar</div>
-  )
-}
+    return (
+        <Wrapper>
+            <Navbar>
+                <BtnBack>
+                    <IconBack />
+                </BtnBack>
+                <Link to="/">
+                    <Logo>
+                        <Image src={logo} alt="logo" />
+                        <Text>Audio Player</Text>
+                    </Logo>
+                </Link>
+            </Navbar>
 
-export default LeftBar
+            {tabs.map((tab, index) => (
+                <React.Fragment key={index}>
+                    <Link to={tab.route} onClick={() => push(tab.route)}>
+                        <Tab
+                            label={tab.label}
+                            icon={tab.icon}
+                            url={tab.route}
+                        />
+                    </Link>
+                    {index === 2 ? <Line /> : null}
+                </React.Fragment>
+            ))}
+        </Wrapper>
+    );
+};
+export default LeftBar;

+ 50 - 0
src/Components/LeftBar/style.js

@@ -0,0 +1,50 @@
+import styled from "styled-components";
+
+export const Wrapper = styled.div`
+    width: 20%;
+    display: flex;
+    flex-direction: column;
+    background: #1c2125;
+    padding: 5px 0 0 10px;
+`;
+
+export const Navbar = styled.aside`
+    display: flex;
+    align-items: center;
+    text-decoration: none;
+    margin-bottom: 25px;
+`;
+
+export const BtnBack = styled.div`
+    color: grey;
+    transition: 0.1s;
+
+    &:hover {
+        color: #fff;
+        cursor: pointer;
+    }
+`;
+
+export const Logo = styled.div`
+    display: flex;
+    align-items: center;
+    margin-left: 30px;
+`;
+
+export const Image = styled.img`
+    width: 15px;
+    height: 15px;
+`;
+
+export const Text = styled.p`
+    font-size: 14px;
+    margin-left: 5px;
+`;
+
+export const Line = styled.div`
+    margin: 10px 0;
+    background: rgb(50, 50, 50);
+    height: 1px;
+    margin-top: 14px;
+    margin-left: -10px;
+`;

+ 108 - 0
src/Components/Player/Player.jsx

@@ -0,0 +1,108 @@
+import React, { useState } from "react";
+import {
+    ButtonCollapse,
+    ButtonVolume,
+    Audio,
+    Duration,
+    Footer,
+    Header,
+    MainButtons,
+    Info,
+    PlaylistName,
+    TrackName,
+    Volume,
+    VolumeSettings,
+    Wrapper,
+    StatusButton,
+    NextButton,
+    RepeatButton,
+    PreviousButton,
+    ShuffleButton,
+} from "./style";
+
+import CollapseIcon from "../../assets/collapse_icon.svg";
+import ShuffleIcon from "../../assets/shuffle_icon.svg";
+import PreviousIcon from "../../assets/previous_icon.svg";
+import NextIcon from "../../assets/next_icon.svg";
+import PlayIcon from "../../assets/play_icon.svg";
+import StopIcon from "../../assets/stop_icon.svg";
+import RepeatIcon from "../../assets/repeat_icon.svg";
+import VolumeUpIcon from "../../assets/volume_up_icon.svg";
+import VolumeStopIcon from "../../assets/volume_stop_icon.svg";
+
+const Player = () => {
+    const [isPlaying, setIsPlaying] = useState(false);
+    const [duration, setDuration] = useState(0);
+    const [currentTime, setCurrentTime] = useState(0);
+    const [track, setTrack] = useState({}); // props track
+    const [playlist, setPlaylist] = useState([]); // arr with tracks and their props
+    const [playlistIndex, setPlaylistIndex] = useState([]);
+    const [volume, setVolume] = useState(50);
+
+    const [isCollapsed, setIsCollapsed] = useState(false);
+    const [isRepeated, setIsRepeated] = useState(false);
+
+    return (
+        <Wrapper isCollapsed={isCollapsed}>
+            <Header>
+                <Duration>0:00:00</Duration>
+                <Audio
+                    type={"range"}
+                    min={0}
+                    max={100}
+                    value={currentTime}
+                    onChange={(e) => {
+                        setCurrentTime(e.target.value);
+                    }}
+                />
+                <ButtonCollapse
+                    src={CollapseIcon}
+                    isCollapsed={isCollapsed}
+                    onClick={() => setIsCollapsed(!isCollapsed)} // change to redux
+                />
+            </Header>
+            {!isCollapsed ? (
+                <Footer>
+                    <Info>
+                        <PlaylistName>Here is playlist</PlaylistName>
+                        <TrackName>Here is track</TrackName>
+                    </Info>
+                    <MainButtons>
+                        <ShuffleButton src={ShuffleIcon} />
+                        <PreviousButton src={PreviousIcon} />
+                        <StatusButton
+                            src={isPlaying ? StopIcon : PlayIcon}
+                            isPlaying={isPlaying}
+                            onClick={() => setIsPlaying(!isPlaying)} // redux
+                        />
+                        <NextButton src={NextIcon} />
+                        <RepeatButton
+                            src={RepeatIcon}
+                            isRepeated={isRepeated}
+                            onClick={() => setIsRepeated(!isRepeated)} // change to redux
+                        />
+                    </MainButtons>
+                    <VolumeSettings>
+                        <ButtonVolume
+                            src={volume ? VolumeUpIcon : VolumeStopIcon}
+                            onClick={() =>
+                                volume ? setVolume(0) : setVolume(100) // redux
+                            }
+                        />
+                        <Volume
+                            type={"range"}
+                            min={0}
+                            max={100}
+                            value={volume}
+                            onChange={(e) => {
+                                setVolume(e.target.value);
+                            }}
+                        ></Volume>
+                    </VolumeSettings>
+                </Footer>
+            ) : null}
+        </Wrapper>
+    );
+};
+
+export default Player;

+ 158 - 0
src/Components/Player/style.js

@@ -0,0 +1,158 @@
+import styled from "styled-components";
+
+const whiteFilter = `invert(100%) sepia(0%) saturate(7500%) hue-rotate(116deg)
+brightness(109%) contrast(109%);`;
+const darkFilter = `invert(48%) sepia(3%) saturate(4%) hue-rotate(326deg) brightness(110%) contrast(78%);`;
+
+export const Wrapper = styled.div`
+    height: ${({ isCollapsed }) => (isCollapsed ? "30px" : "75px")};
+    flex-shrink: 0;
+    background: #0f0e0e;
+    transition: 0.4s;
+
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    padding: 5px 20px 0;
+`;
+
+export const Header = styled.div`
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-bottom: 5px;
+`;
+
+export const Duration = styled.p`
+    font-size: 12px;
+`;
+export const Audio = styled.input`
+    appearance: none;
+    -webkit-appearance: none;
+    background: #0f0e0e;
+    width: 95%;
+
+    &:focus {
+        outline: none;
+    }
+
+    &::-webkit-slider-thumb {
+        -webkit-appearance: none;
+
+        width: 10px;
+        height: 10px;
+        border-radius: 5px;
+        background: rgb(100, 100, 100);
+        cursor: pointer;
+        margin-top: -1.8px;
+    }
+
+    &::-webkit-slider-runnable-track {
+        width: 100%;
+        height: 5px;
+        cursor: pointer;
+        background: rgb(50, 50, 50);
+        border-radius: 5px;
+    }
+`;
+export const ButtonCollapse = styled.img`
+    width: 15px;
+    height: 15px;
+
+    filter: ${({ isCollapsed }) => (isCollapsed ? darkFilter : whiteFilter)}
+            
+    &:hover {
+        cursor: pointer;
+    }
+`;
+export const Footer = styled.div`
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+`;
+export const Info = styled.div`
+    width: 150px;
+`;
+export const PlaylistName = styled.p`
+    font-size: 16px;
+`;
+export const TrackName = styled.p`
+    font-size: 12px;
+`;
+export const MainButtons = styled.div`
+    display: flex;
+    align-items: center;
+`;
+export const ShuffleButton = styled.img`
+    width: 18px;
+    height: 18px;
+    transition: 0.3s;
+    filter: ${darkFilter}
+
+    &:hover {
+        cursor: pointer;
+        filter: ${whiteFilter}
+    }
+`;
+export const PreviousButton = styled.img`
+    width: 22px;
+    height: 22px;
+    transition: 0.3s;
+    filter: ${darkFilter}
+
+    &:hover {
+        cursor: pointer;
+        filter: ${whiteFilter}
+    }
+`;
+export const StatusButton = styled.img`
+    width: 40px;
+    height: 40px;
+    filter: invert(98%) sepia(0%) saturate(303%) hue-rotate(143deg) brightness(88%) contrast(84%);
+    transition: 0.6s;
+
+    &:hover {
+        cursor: pointer;
+        filter: ${whiteFilter}
+    }
+`;
+export const NextButton = styled.img`
+    width: 22px;
+    height: 22px;
+    transition: 0.3s;
+    filter: ${darkFilter}
+
+    &:hover {
+        cursor: pointer;
+        filter: ${whiteFilter}
+    }
+`;
+export const RepeatButton = styled.img`
+    width: 18px;
+    height: 18px;
+    transition: 0.3s;
+    filter: ${({ isRepeated }) => (isRepeated ? whiteFilter : darkFilter)}
+
+    &:hover {
+        cursor: pointer;
+    }
+`;
+export const VolumeSettings = styled.div`
+    width: 150px;
+    display: flex;
+    align-items: center;
+`;
+export const ButtonVolume = styled.img`
+    width: 22px;
+    height: 22px;
+    filter: ${darkFilter}
+    transition: 0.3s;
+
+    &:hover {
+        filter: ${whiteFilter}
+        cursor: pointer;
+    }
+`;
+export const Volume = styled.input`
+    width: 100%;
+`;

+ 0 - 8
src/Components/Profile/Profile.jsx

@@ -1,8 +0,0 @@
-import LeftBar from './LeftBar/LeftBar';
-import Profile from './Profile/Profile';
-
-
-export {
-    LeftBar,
-    Profile,
-}

+ 16 - 0
src/Components/Tab/Tab.jsx

@@ -0,0 +1,16 @@
+import React, {useState} from "react";
+import { Wrapper, Text, Image } from "./style";
+import { history } from "../../utils/history";
+
+const Tab = ({label, icon, url}) => {
+    const [isActive, setIsActive] = useState(history.location.pathname === url);
+
+    return (
+        <Wrapper active={isActive}>
+            <Image src={icon} alt="icon" />
+            <Text>{label}</Text>
+        </Wrapper>
+    );
+};
+
+export default Tab;

+ 31 - 0
src/Components/Tab/style.js

@@ -0,0 +1,31 @@
+import styled from "styled-components";
+
+export const Wrapper = styled.div`
+    display: flex;
+    width: 99%;
+    margin: 0 0 5px -10px;
+    padding: 5px 0 5px 10px;
+    display: flex;
+    align-items: center;
+    transition: 0.2s;
+    border-radius: 0 3px 3px 0;
+    background: ${({ active }) =>
+        active ? "rgba(255, 255, 255, 0.1)" : "none"};
+
+    &:hover {
+        cursor: pointer;
+        background: rgba(255, 255, 255, 0.1);
+    }
+`;
+
+export const Text = styled.p`
+    font-size: 14px;
+    margin-left: 10px;
+`;
+
+export const Image = styled.img`
+    filter: invert(100%) sepia(0%) saturate(7500%) hue-rotate(116deg)
+        brightness(109%) contrast(109%);
+    width: 20px;
+    height: 20px;
+`;

+ 9 - 0
src/Components/index.js

@@ -0,0 +1,9 @@
+import LeftBar from './LeftBar/LeftBar';
+import Player from './Player/Player';
+import Tab from './Tab/Tab';
+
+export {
+    LeftBar,
+    Player,
+    Tab,
+}

+ 14 - 6
src/Pages/Home/Home.jsx

@@ -1,9 +1,17 @@
-import React from 'react'
+import React from "react";
+import { LeftBar, Player } from "../../components";
+import { Content, Main, Wrapper } from "./styles";
 
 const Home = () => {
-  return (
-    <div>Home</div>
-  )
-}
+    return (
+        <Wrapper>
+            <Main>
+                <LeftBar />
+                <Content></Content>
+            </Main>
+            <Player />
+        </Wrapper>
+    );
+};
 
-export default Home
+export default Home;

+ 16 - 0
src/Pages/Home/styles.js

@@ -0,0 +1,16 @@
+import styled from "styled-components";
+
+export const Wrapper = styled.div`
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+`;
+
+export const Main = styled.div`
+    display: flex;
+    flex-grow: 1;
+`;
+
+export const Content = styled.main`
+    width: 80%;
+`;

+ 2 - 1
src/Pages/Playlists/Playlists.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
+import { LeftBar } from "../../components";
 
 const Playlists = () => {
   return (
-    <div>Playlists</div>
+    <LeftBar></LeftBar>
   )
 }
 

+ 2 - 1
src/Pages/Profile/Profile.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
+import { LeftBar } from "../../components";
 
 const Profile = () => {
   return (
-    <div>Profile</div>
+    <LeftBar>Profile</LeftBar>
   )
 }
 

+ 2 - 1
src/Pages/Queue/Queue.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
+import { LeftBar } from "../../components";
 
 const Queue = () => {
   return (
-    <div>Queue</div>
+    <LeftBar></LeftBar>
   )
 }
 

+ 2 - 1
src/Pages/Tracks/Tracks.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
+import { LeftBar } from "../../components";
 
 const Tracks = () => {
   return (
-    <div>Tracks</div>
+    <LeftBar></LeftBar>
   )
 }
 

+ 1 - 0
src/assets/collapse_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M24 24V9H27V18.9L41.9 4L44 6.1L29.1 21H39V24ZM6.1 44 4 41.9 18.9 27H9V24H24V39H21V29.1Z"/></svg>

+ 1 - 0
src/assets/home_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M8 42V18L24.1 6L40 18V42H28.3V27.75H19.65V42ZM11 39H16.65V24.75H31.3V39H37V19.5L24.1 9.75L11 19.5ZM24 24.35Z"/></svg>

BIN
src/assets/logo.png


+ 1 - 0
src/assets/next_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M34 36V12H37V36ZM11 36V12L28.3 24ZM14 24ZM14 30.25 23.05 24 14 17.75Z"/></svg>

File diff suppressed because it is too large
+ 1 - 0
src/assets/play_icon.svg


+ 1 - 0
src/assets/playlist_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M6 14.95V11.95H29.65V14.95ZM6 23.2V20.2H29.65V23.2ZM6 31.45V28.45H21.3V31.45ZM32.6 37.95 25.8 31.15 27.9 29 32.6 33.65 41.85 24.4 44 26.55Z"/></svg>

+ 1 - 0
src/assets/previous_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M11 36V12H14V36ZM37 36 19.7 24 37 12ZM34 24ZM34 30.25V17.75L24.95 24Z"/></svg>

File diff suppressed because it is too large
+ 1 - 0
src/assets/profile_icon.svg


+ 1 - 0
src/assets/queue_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M32 40Q29.55 40 27.8 38.325Q26.05 36.65 26.05 34.25Q26.05 31.85 27.725 30.175Q29.4 28.5 31.8 28.5Q32.6 28.5 33.375 28.65Q34.15 28.8 34.9 29.15V12H44V15.55H37.9V34.3Q37.9 36.65 36.175 38.325Q34.45 40 32 40ZM6 15V12H29.65V15ZM6 23.25V20.25H29.65V23.25ZM6 31.5V28.5H21.3V31.5Z"/></svg>

+ 1 - 0
src/assets/repeat_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M14 44 6 36 14 28 16.1 30.2 11.8 34.5H35V26.5H38V37.5H11.8L16.1 41.8ZM10 21.5V10.5H36.2L31.9 6.2L34 4L42 12L34 20L31.9 17.8L36.2 13.5H13V21.5Z"/></svg>

+ 1 - 0
src/assets/shuffle_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M19.75 21.8 7.5 9.6 9.65 7.45 21.9 19.65ZM29.05 40.5V37.5H35.3L26.1 28.35L28.2 26.2L37.5 35.4V29.05H40.5V40.5ZM9.6 40.5 7.5 38.35 35.4 10.45H29.05V7.45H40.5V18.9H37.5V12.6Z"/></svg>

File diff suppressed because it is too large
+ 1 - 0
src/assets/stop_icon.svg


+ 1 - 0
src/assets/track_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M19.65 42Q16.5 42 14.325 39.825Q12.15 37.65 12.15 34.5Q12.15 31.35 14.325 29.175Q16.5 27 19.65 27Q21.05 27 22.175 27.4Q23.3 27.8 24.15 28.5V6H35.85V12.75H27.15V34.5Q27.15 37.65 24.975 39.825Q22.8 42 19.65 42Z"/></svg>

File diff suppressed because it is too large
+ 1 - 0
src/assets/volume_stop_icon.svg


+ 1 - 0
src/assets/volume_up_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M28 41.45V38.35Q32.85 36.95 35.925 32.975Q39 29 39 23.95Q39 18.9 35.95 14.9Q32.9 10.9 28 9.55V6.45Q34.2 7.85 38.1 12.725Q42 17.6 42 23.95Q42 30.3 38.1 35.175Q34.2 40.05 28 41.45ZM6 30V18H14L24 8V40L14 30ZM27 32.4V15.55Q29.7 16.4 31.35 18.75Q33 21.1 33 24Q33 26.95 31.35 29.25Q29.7 31.55 27 32.4ZM21 15.6 15.35 21H9V27H15.35L21 32.45ZM16.3 24Z"/></svg>

+ 1 - 0
src/index.js

@@ -1,5 +1,6 @@
 import ReactDOM from "react-dom/client";
 import App from "./App";
+import './style.css';
 
 const root = ReactDOM.createRoot(document.getElementById("root"));
 

+ 16 - 0
src/style.css

@@ -0,0 +1,16 @@
+* {
+    padding: 0;
+    margin: 0;
+    box-sizing: border-box;
+    color: #fff;
+    text-decoration: inherit;
+    font-family: "Source Sans Pro", sans-serif;
+    font-weight: 400;
+}
+::-webkit-scrollbar {
+    width: 0;
+}
+
+body {
+    background: #28272d;
+}

+ 10 - 1
src/utils/constants.js

@@ -1 +1,10 @@
-const backendURL = "http://player.node.ed.asmer.org.ua";
+export const backendURL = "http://player.node.ed.asmer.org.ua";
+
+export const ROUTES = {
+    "HOME": "/",
+    "PROFILE": "/profile",
+    "QUEUE": "/queue",
+    "TRACKS": "/tracks",
+    "PLAYLISTS": "/playlists",
+}
+

+ 6 - 0
src/utils/history.js

@@ -0,0 +1,6 @@
+import { createBrowserHistory } from "history";
+
+export const history = createBrowserHistory();
+export const push = (location) => history.push(location);
+export const back = () => history.back();
+export const forward = () => history.forward();