init
This commit is contained in:
commit
a458210f0a
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# 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*
|
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### Preview
|
||||
|
||||
You can visit app at [github.io](https://FutureXpo.github.io/gold-point).
|
12051
package-lock.json
generated
Normal file
12051
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
package.json
Normal file
49
package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "gold-point",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://FutureXpo.github.io/gold-point",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.8.2",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.5.1",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "^12.1.4",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "5.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write --use-tabs \"src/**/*.js\"",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"predeploy": "npm run build",
|
||||
"deploy": "gh-pages -d build"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^3.2.3",
|
||||
"prettier": "^2.6.0"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
44
public/index.html
Normal file
44
public/index.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
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" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||
<!--
|
||||
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`.
|
||||
-->
|
||||
<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>
|
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
BIN
public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
27
src/App/App.css
Normal file
27
src/App/App.css
Normal file
@ -0,0 +1,27 @@
|
||||
.App {
|
||||
min-height: 100vh;
|
||||
background-color: #eeeeff;
|
||||
position:relative;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items: center;
|
||||
font-size: calc(1rem + 1.2vmin);
|
||||
}
|
||||
.body {
|
||||
padding-top:7.5rem;
|
||||
border-radius: 2rem;
|
||||
min-height: 30rem;
|
||||
width: 40rem;
|
||||
position:relative;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #282c34;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.body {
|
||||
width: 95%;
|
||||
padding-top:5.5rem;
|
||||
}
|
||||
}
|
121
src/App/App.js
Normal file
121
src/App/App.js
Normal file
@ -0,0 +1,121 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
|
||||
import Header from "../Components/Header/Header";
|
||||
import MoneyList from "../Components/MoneyList/MoneyList";
|
||||
import Loading from "../Components/Loading/Loading";
|
||||
import Footer from "../Components/Footer/Footer";
|
||||
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
|
||||
const [data, setData] = useState({money:{}, isLoading:true, error:false});
|
||||
|
||||
async function getDateInfo(date_url,next_day){
|
||||
try {
|
||||
var response = await fetch(date_url||"https://www.cbr-xml-daily.ru/daily_json.js").then(response => response.json());
|
||||
if (!response.Valute) {
|
||||
throw new Error("Something went wrong!");
|
||||
}
|
||||
const dayData = response.Valute;
|
||||
|
||||
const dailyData = {};
|
||||
for (const key in dayData) {
|
||||
let money_info = {
|
||||
charCode: dayData[key].CharCode,
|
||||
name: names[dayData[key].ID]||dayData[key].Name, //Поскольку сервер возвращает имена не в именительном падеже, необходимо брать имена из массива
|
||||
value: (dayData[key].Value/dayData[key].Nominal).toFixed(4),
|
||||
};
|
||||
dailyData[dayData[key].ID] = money_info;
|
||||
|
||||
if(next_day&&next_day.values[dayData[key].ID]){ //если есть информация про следующий день, вычисляем процентную разницу
|
||||
next_day.values[dayData[key].ID].diff = (((next_day.values[dayData[key].ID].value - money_info.value) / money_info.value) * 100).toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
date:response.Date,
|
||||
next_day:next_day,
|
||||
info:{
|
||||
values: dailyData,
|
||||
previousDate:response.PreviousDate,
|
||||
previousURL:response.PreviousURL
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {error:error}
|
||||
}
|
||||
}
|
||||
|
||||
const getData = useCallback(async () => {
|
||||
var money = {};
|
||||
let date_info = await getDateInfo();
|
||||
if(date_info.error) return setData({money:{}, error:date_info.error});
|
||||
|
||||
let previous_date_info = await getDateInfo(date_info.info.previousURL,date_info.info); //поскольку previousValue предоставляется без previousNominal, то необходимо получать все данные за прошлый день и вычислять diff используя их
|
||||
if(previous_date_info.error) return setData({money:{}, error:date_info.error});
|
||||
|
||||
money[date_info.date] = previous_date_info.next_day;
|
||||
money[previous_date_info.date] = previous_date_info.info;
|
||||
|
||||
setData({money:money, date:date_info.date});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, [getData]);
|
||||
|
||||
const updateData = (money) => setData({...data, money:money});
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<Header />
|
||||
<div className="body">
|
||||
{data.isLoading?
|
||||
<Loading />:
|
||||
<MoneyList money={data.money} date={data.date} updateData={updateData} getDateInfo={getDateInfo}/>
|
||||
}
|
||||
</div>
|
||||
<Footer isLoading={data.isLoading}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
const names = {
|
||||
"R01010":"Австралийский доллар",
|
||||
"R01020A":"Азербайджанский манат",
|
||||
"R01035":"Фунт стерлингов Соединенного королевства",
|
||||
"R01060":"Армянский драм",
|
||||
"R01090B":"Белорусский рубль",
|
||||
"R01100":"Болгарский лев",
|
||||
"R01115":"Бразильский реал",
|
||||
"R01135":"Венгерский форинт",
|
||||
"R01200":"Гонконгский доллар",
|
||||
"R01215":"Датская крона",
|
||||
"R01235":"Доллар США",
|
||||
"R01239":"Евро",
|
||||
"R01270":"Индийская рупия",
|
||||
"R01335":"Казахстанский тенге",
|
||||
"R01350":"Канадский доллар",
|
||||
"R01370":"Киргизский сом",
|
||||
"R01375":"Китайский юань",
|
||||
"R01500":"Молдавский лей",
|
||||
"R01535":"Норвежская крона",
|
||||
"R01565":"Польский злотый",
|
||||
"R01585F":"Румынский лей",
|
||||
"R01589":"СДР (специальные права заимствования)",
|
||||
"R01625":"Сингапурский доллар",
|
||||
"R01670":"Таджикский сомони",
|
||||
"R01700J":"Турецкая лира",
|
||||
"R01710A":"Новый туркменский манат",
|
||||
"R01717":"Узбекский сум",
|
||||
"R01720":"Украинская гривна",
|
||||
"R01760":"Чешская крона",
|
||||
"R01770":"Шведская крона",
|
||||
"R01775":"Швейцарский франк",
|
||||
"R01810":"Южноафриканский рэнд",
|
||||
"R01815":"Южнокорейская вона",
|
||||
"R01820":"Японская иена"
|
||||
}
|
21
src/Components/Footer/Footer.css
Normal file
21
src/Components/Footer/Footer.css
Normal file
@ -0,0 +1,21 @@
|
||||
.footer {
|
||||
font-size:1.1rem;
|
||||
width:100%;
|
||||
/*background-color: #dddddd;*/
|
||||
padding-top:1rem;
|
||||
padding-bottom:1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.fixed{
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
}
|
||||
.footer .link {
|
||||
color:darkblue;
|
||||
margin-left:0.5rem;
|
||||
}
|
||||
@media screen and (max-width: 700px) {
|
||||
.footer {
|
||||
font-size:0.9rem;
|
||||
}
|
||||
}
|
10
src/Components/Footer/Footer.js
Normal file
10
src/Components/Footer/Footer.js
Normal file
@ -0,0 +1,10 @@
|
||||
import './Footer.css';
|
||||
|
||||
export default function Footer(props) {
|
||||
return (
|
||||
<footer className={props.isLoading?"footer fixed":"footer"}>
|
||||
Создано при помощи
|
||||
<a href="https://www.cbr-xml-daily.ru/" className="link">API для курсов ЦБ РФ</a>
|
||||
</footer>
|
||||
);
|
||||
}
|
41
src/Components/Header/Header.css
Normal file
41
src/Components/Header/Header.css
Normal file
@ -0,0 +1,41 @@
|
||||
.header{
|
||||
width:100%;
|
||||
display:flex;
|
||||
justify-content:center;
|
||||
}
|
||||
.header_box {
|
||||
margin-top:1rem;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index:10;
|
||||
width: 41rem;
|
||||
}
|
||||
.header .under_titles {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height:4rem;
|
||||
z-index:9;
|
||||
background-color: #eeeeff;
|
||||
}
|
||||
.header_box .titles {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #8899dd;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.header_box {
|
||||
margin-top:0rem;
|
||||
width: 100%;
|
||||
}
|
||||
.header_box .titles {
|
||||
border-radius: 0rem;
|
||||
|
||||
}
|
||||
}
|
16
src/Components/Header/Header.js
Normal file
16
src/Components/Header/Header.js
Normal file
@ -0,0 +1,16 @@
|
||||
import './Header.css';
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="header_box">
|
||||
<div className="titles">
|
||||
<h4>Код валюты</h4>
|
||||
<h4>Курс (р.)</h4>
|
||||
<h4>Разница</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className="under_titles"></div>
|
||||
</header>
|
||||
);
|
||||
}
|
53
src/Components/Loading/Loading.css
Normal file
53
src/Components/Loading/Loading.css
Normal file
@ -0,0 +1,53 @@
|
||||
.loading{
|
||||
width:100%;
|
||||
height:25rem;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
}
|
||||
.loading .loading_anim{
|
||||
width:100%;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
}
|
||||
.loading .loading_anim .loading_animation{
|
||||
transform:scale(.3);
|
||||
background-color:rgb(255,255,255);
|
||||
width:2rem;
|
||||
height:2rem;
|
||||
margin:0.1rem;
|
||||
border-radius:100%;
|
||||
animation: animation1 infinite 1000ms alternate ease-in-out forwards;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a2{
|
||||
animation-delay:100ms;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a3{
|
||||
animation-delay:200ms;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a4{
|
||||
animation-delay:300ms;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a5{
|
||||
animation-delay:400ms;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a6{
|
||||
animation-delay:500ms;
|
||||
}
|
||||
.loading .loading_anim .loading_animation.a7{
|
||||
animation-delay:600ms;
|
||||
}
|
||||
|
||||
@keyframes animation1{
|
||||
0%{
|
||||
transform:scale(.3);
|
||||
background-color:rgb(255,255,255);
|
||||
}
|
||||
|
||||
100%{
|
||||
transform:scale(1);
|
||||
background-color:rgb(0,0,0);
|
||||
}
|
||||
}
|
17
src/Components/Loading/Loading.js
Normal file
17
src/Components/Loading/Loading.js
Normal file
@ -0,0 +1,17 @@
|
||||
import './Loading.css';
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="loading">
|
||||
<div className="loading_anim">
|
||||
<div className="loading_animation a1"></div>
|
||||
<div className="loading_animation a2"></div>
|
||||
<div className="loading_animation a3"></div>
|
||||
<div className="loading_animation a4"></div>
|
||||
<div className="loading_animation a5"></div>
|
||||
<div className="loading_animation a6"></div>
|
||||
<div className="loading_animation a7"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
32
src/Components/MoneyList/Item/Item.css
Normal file
32
src/Components/MoneyList/Item/Item.css
Normal file
@ -0,0 +1,32 @@
|
||||
.item {
|
||||
display:inline-block;
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
background: #aaaacc;
|
||||
border-radius:1rem;
|
||||
margin-top:0.3rem;
|
||||
padding-top:2rem;
|
||||
padding-bottom:2rem;
|
||||
transition: all 0.3s ease;
|
||||
position:relative;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.item_info{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.item_info .value{
|
||||
}
|
||||
.item_info .diff.plus{
|
||||
color:green;
|
||||
}
|
||||
.item_info .diff.minus{
|
||||
color:darkred;
|
||||
}
|
||||
|
||||
.item:hover{
|
||||
transform: scale(1.02);
|
||||
background: #cacafa;
|
||||
}
|
23
src/Components/MoneyList/Item/Item.js
Normal file
23
src/Components/MoneyList/Item/Item.js
Normal file
@ -0,0 +1,23 @@
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import './Item.css';
|
||||
|
||||
export default function Item(props) {
|
||||
return (
|
||||
<Tooltip title={props.item.name} followCursor arrow placement="bottom-end">
|
||||
<div className="item" onClick={()=>{props.openModal(props.item.id)}}>
|
||||
<Grid container rowSpacing={0} columnSpacing={{ xs: 1, sm: 2, md: 0 }} className="item_info">
|
||||
<Grid item xs={4} className="charCode">
|
||||
{props.item.charCode}
|
||||
</Grid>
|
||||
<Grid item xs={4} className="value">
|
||||
{props.item.value}
|
||||
</Grid>
|
||||
<Grid item xs={4} className={props.item.diff>=0?"diff plus":"diff minus"}>
|
||||
{props.item.diff}%{props.item.diff>=0?"▲":"▼"}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
52
src/Components/MoneyList/Modal/Item/Item.css
Normal file
52
src/Components/MoneyList/Modal/Item/Item.css
Normal file
@ -0,0 +1,52 @@
|
||||
.modal_item {
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
background: #fff;
|
||||
margin-top:0.3rem;
|
||||
padding-top:1rem;
|
||||
padding-bottom:1rem;
|
||||
transition: all 0.3s ease;
|
||||
position:relative;
|
||||
border-bottom:1px solid;
|
||||
}
|
||||
.modal_item.first {
|
||||
border-top:1px solid;
|
||||
}
|
||||
.modal_item .item_info{
|
||||
padding-left:2rem;
|
||||
}
|
||||
.modal_item .item_info .date{
|
||||
font-size:1.5rem;
|
||||
text-align:left;
|
||||
color:#009;
|
||||
}
|
||||
.modal_item .item_info .value{
|
||||
font-size:1.2rem;
|
||||
font-weight:bold;
|
||||
text-align:right;
|
||||
}
|
||||
.modal_item .item_info .diff{
|
||||
text-align:center;
|
||||
}
|
||||
.modal_item .item_info .diff.plus{
|
||||
color:green;
|
||||
}
|
||||
.modal_item .item_info .diff.minus{
|
||||
color:darkred;
|
||||
}
|
||||
@media screen and (max-width: 700px) {
|
||||
.modal_item {
|
||||
margin-top:0.1rem;
|
||||
padding-top:0.7rem;
|
||||
padding-bottom:0.7rem;
|
||||
}
|
||||
.modal_item .item_info .date{
|
||||
font-size:1.3rem;
|
||||
}
|
||||
.modal_item .item_info .value{
|
||||
font-size:1.1rem;
|
||||
}
|
||||
.modal_item .item_info .diff{
|
||||
font-size:0.9rem;
|
||||
}
|
||||
}
|
26
src/Components/MoneyList/Modal/Item/Item.js
Normal file
26
src/Components/MoneyList/Modal/Item/Item.js
Normal file
@ -0,0 +1,26 @@
|
||||
import Grid from "@mui/material/Grid";
|
||||
import './Item.css';
|
||||
|
||||
export default function Item(props) {
|
||||
|
||||
function getDate(date){
|
||||
let d = new Date(date);
|
||||
return [("0" + d.getDate()).slice(-2),("0"+(d.getMonth()+1)).slice(-2),d.getFullYear()].join("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={props.index===0?"modal_item first":"modal_item"} >
|
||||
<Grid container rowSpacing={0} columnSpacing={{ xs: 1, sm: 2, md: 2 }} className="item_info">
|
||||
<Grid item xs={5} className="date">
|
||||
{getDate(props.item.date)}
|
||||
</Grid>
|
||||
<Grid item xs={4} className="value">
|
||||
{props.item.value}₽
|
||||
</Grid>
|
||||
<Grid item xs={3} className={props.item.diff>=0?"diff plus":"diff minus"}>
|
||||
{props.item.diff}%{props.item.diff>=0?"▲":"▼"}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
54
src/Components/MoneyList/Modal/Modal.css
Normal file
54
src/Components/MoneyList/Modal/Modal.css
Normal file
@ -0,0 +1,54 @@
|
||||
.modal{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background:#fff;
|
||||
padding:2rem;
|
||||
border-radius:1rem;
|
||||
width:40rem;
|
||||
}
|
||||
|
||||
.modal-back{
|
||||
display:none;
|
||||
}
|
||||
.modal .modal_header{
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
padding-bottom:2rem;
|
||||
}
|
||||
.modal .modal_body {
|
||||
overflow-y:scroll;
|
||||
max-height:60vh;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.modal {
|
||||
overflow-y:scroll;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding:0;
|
||||
border-radius:0;
|
||||
}
|
||||
.modal .modal-back{
|
||||
padding-top:1.5rem;
|
||||
padding-bottom:1.5rem;
|
||||
padding-left:1rem;
|
||||
display: block;
|
||||
color:#555;
|
||||
}
|
||||
.modal .modal_header{
|
||||
padding-left:1rem;
|
||||
padding-right:1rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight:bold;
|
||||
}
|
||||
.modal .modal_body {
|
||||
overflow-y:hidden;
|
||||
max-height:none;
|
||||
padding-bottom:2rem;
|
||||
padding-left:2.5%;
|
||||
padding-right:2.5%;
|
||||
max-width:95%;
|
||||
}
|
||||
}
|
99
src/Components/MoneyList/Modal/Modal.js
Normal file
99
src/Components/MoneyList/Modal/Modal.js
Normal file
@ -0,0 +1,99 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import Loading from "../../Loading/Loading";
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Fade from '@mui/material/Fade';
|
||||
|
||||
import Item from "./Item/Item";
|
||||
|
||||
import './Modal.css';
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms || 250));
|
||||
}
|
||||
|
||||
export default function ModalMoney(props) {
|
||||
var {open, money, id, date} = props;
|
||||
const [data,setData]=useState({isLoading:true});
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, [props.open]);
|
||||
|
||||
if(!id) return null;
|
||||
|
||||
async function getData(){
|
||||
if(data.isLoading&&id){
|
||||
let day_date = date;
|
||||
let day_url = money[date].previousURL;
|
||||
let next_day_date;
|
||||
|
||||
let money_={...money}
|
||||
let data_ = [];
|
||||
|
||||
while (data_.length<11){
|
||||
if(!money_[day_date]){ //проверка что существует день
|
||||
let response = await props.getDateInfo(day_url,money_[next_day_date]||null); //получаем информацию за день и обновляем разцницу за следующий
|
||||
money_[day_date] = response.info;
|
||||
if(response.next_day){
|
||||
money_[next_day_date] = response.next_day;
|
||||
data_[data_.length-1].diff = response.next_day.values[id].diff
|
||||
}
|
||||
await sleep();
|
||||
}
|
||||
let date_info = {
|
||||
date:day_date,
|
||||
value:money_[day_date].values[id].value,
|
||||
diff:money_[day_date].values[id].diff
|
||||
}
|
||||
data_.push(date_info);
|
||||
|
||||
next_day_date = day_date;
|
||||
day_url = money_[day_date].previousURL;
|
||||
day_date = money_[day_date].previousDate;
|
||||
|
||||
}
|
||||
props.updateData(money_);
|
||||
setData({values:data_});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const closeModal = () => {
|
||||
props.closeModal();
|
||||
setTimeout(()=>{setData({isLoading:true})},300);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={closeModal}
|
||||
closeAfterTransition
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 300,
|
||||
}}
|
||||
>
|
||||
<Fade in={open} timeout={300}>
|
||||
<div className="modal">
|
||||
<div className="modal-back" onClick={()=>closeModal()}>
|
||||
← Вернуться к списку валют
|
||||
</div >
|
||||
<div className="modal_header">
|
||||
{money[date].values[id].name} ({money[date].values[id].charCode})
|
||||
</div>
|
||||
<div className="modal_body">
|
||||
{data.isLoading?<Loading/>:
|
||||
data.values.map((item, index) => {
|
||||
if(index===data.values.length-1) return null;
|
||||
return <Item item={item} index={index}/>;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
</Modal>
|
||||
);
|
||||
}
|
5
src/Components/MoneyList/MoneyList.css
Normal file
5
src/Components/MoneyList/MoneyList.css
Normal file
@ -0,0 +1,5 @@
|
||||
.money-list {
|
||||
width:100%;
|
||||
position:relative;
|
||||
display:block;
|
||||
}
|
26
src/Components/MoneyList/MoneyList.js
Normal file
26
src/Components/MoneyList/MoneyList.js
Normal file
@ -0,0 +1,26 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import './MoneyList.css';
|
||||
|
||||
import Item from "./Item/Item";
|
||||
import Modal from "./Modal/Modal";
|
||||
|
||||
export default function MoneyList(props) {
|
||||
const [modal, setModal] = useState({open:false});
|
||||
|
||||
const openModal = (id) => setModal({id:id,open:true});
|
||||
const closeModal = () => setModal({...modal,open:false});
|
||||
|
||||
let values = [];
|
||||
|
||||
for (let [id, info] of Object.entries(props.money[props.date].values)) values.push({...info, id:id}); //получаем массив для вывода
|
||||
|
||||
return (
|
||||
<div className="money_list">
|
||||
{values.map((item, index) => { {
|
||||
return <Item item={item} openModal={openModal}/>;
|
||||
}})}
|
||||
<Modal id={modal.id} open={modal.open} money={props.money} date={props.date} closeModal={closeModal} updateData={props.updateData} getDateInfo={props.getDateInfo}/>
|
||||
</div>
|
||||
);
|
||||
}
|
13
src/index.css
Normal file
13
src/index.css
Normal file
@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
11
src/index.js
Normal file
11
src/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App/App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
Reference in New Issue
Block a user