Alyona Brytvina před 2 roky
rodič
revize
6f40839fde

+ 0 - 36
src/App.css

@@ -1,38 +1,2 @@
-.App {
-  text-align: center;
-}
 
-.App-logo {
-  height: 40vmin;
-  pointer-events: none;
-}
 
-@media (prefers-reduced-motion: no-preference) {
-  .App-logo {
-    animation: App-logo-spin infinite 20s linear;
-  }
-}
-
-.App-header {
-  background-color: #282c34;
-  min-height: 100vh;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  font-size: calc(10px + 2vmin);
-  color: white;
-}
-
-.App-link {
-  color: #61dafb;
-}
-
-@keyframes App-logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}

+ 37 - 19
src/App.js

@@ -1,25 +1,43 @@
-import logo from './logo.svg';
 import './App.css';
+import React from 'react';
+import SpoilerParent from './Task1- spoiler/SpoilerParent/SpoilerParent';
+import RangeInputParent from './Task2-RangeInput/RangeInputParent/RangeInputParent';
+import PasswordConfirmParent from './Task3-PasswordConfirm/PasswordConfirmParent/PasswordConfirmParent';
+import TimerParent from './Task4-Timer/TimerParent/TimerParent';
+import TimerControlParent from './Task5-TimerControl/TimerControlParent/TimerControlParent';
+import TimerContainerParent from './Task6-TimerContainer/TimerContainerParent/TimerContainerParent';
+import {BrowserRouter, Route, Routes, Link} from 'react-router-dom';
 
 function App() {
-  return (
-    <div className="App">
-      <header className="App-header">
-        <img src={logo} className="App-logo" alt="logo" />
-        <p>
-          Edit <code>src/App.js</code> and save to reload.
-        </p>
-        <a
-          className="App-link"
-          href="https://reactjs.org"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          Learn React
-        </a>
-      </header>
-    </div>
-  );
+
+    return (
+        <div className="wrapper">
+            <h1>Название дз</h1>
+            <BrowserRouter>
+                <nav>
+                    <ul>
+                        {/*<li><Link to="/">App</Link></li>??*/}
+                        <li><Link to="/spoiler">Spoiler</Link></li>
+                        <li><Link to="/rangeInput">rangeInput</Link></li>
+                        <li><Link to="/passwordConfirm">PasswordConfirm</Link></li>
+                        <li><Link to="/timer">Timer</Link></li>
+                        <li><Link to="/timerControl">timerControl</Link></li>
+                        <li><Link to="/timerContainer">timerContainer</Link></li>
+                    </ul>
+                </nav>
+                <Routes>
+                    {/*<Route path="/" element={<App />}/>*/}
+                    <Route path="/spoiler" element={<SpoilerParent />}/>
+                    <Route path="/rangeInput" element={<RangeInputParent />}/>
+                    <Route path="/passwordConfirm" element={<PasswordConfirmParent />}/>
+                    <Route path="/timer" element={<TimerParent />}/>
+                    <Route path="/timerControl" element={<TimerControlParent />}/>
+                    <Route path="/timerContainer" element={<TimerContainerParent />}/>
+                </Routes>
+            </BrowserRouter>
+        </div>
+
+    );
 }
 
 export default App;

+ 0 - 8
src/App.test.js

@@ -1,8 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
-  render(<App />);
-  const linkElement = screen.getByText(/learn react/i);
-  expect(linkElement).toBeInTheDocument();
-});

binární
src/Img/clockFace.png


binární
src/Img/сlockFace_H.png


binární
src/Img/сlockFace_M.png


binární
src/Img/сlockFace_S.png


+ 19 - 0
src/Task1- spoiler/Spoiler/Spoiler.css

@@ -0,0 +1,19 @@
+.Spoiler  {
+  text-align: center;
+}
+
+
+.Spoiler {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.Spoiler {
+  color: #61dafb;
+}

+ 23 - 0
src/Task1- spoiler/Spoiler/Spoiler.js

@@ -0,0 +1,23 @@
+import {useState} from 'react';
+import './Spoiler.css';
+
+const Spoiler = ({header = '+', open, children}) => {
+    const [isOpen, setIsOpen] = useState(open);
+
+    return (
+        <div>
+            <div onClick={() => {
+                setIsOpen(!isOpen);
+            }}>
+                <span>{header}</span>
+                {isOpen && (
+                    <div>
+                        {children}
+                    </div>
+                )}
+            </div>
+        </div>
+    );
+};
+
+export default Spoiler;

+ 45 - 0
src/Task1- spoiler/SpoilerParent/SpoilerParent.js

@@ -0,0 +1,45 @@
+import Spoiler from '../Spoiler/Spoiler';
+import React from 'react';
+
+const SpoilerParent = () => {
+
+    return (
+        <div>
+            <Spoiler header={<h1>Заголовок</h1>} open>
+                Контент 1
+                <p>
+                    Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является
+                    стандартной "рыбой" для текстов на латинице с начала XVI века. В то время некий безымянный
+                    печатник
+                    создал большую коллекцию размеров и форм шрифтов, используя Lorem Ipsum для распечатки образцов.
+                    Lorem Ipsum не только успешно пережил без заметных изменений пять веков, но и перешагнул в
+                    электронный дизайн. Его популяризации в новое время послужили публикация листов Letraset с
+                    образцами
+                    Lorem Ipsum в 60-х годах и, в более недавнее время, программы электронной вёрстки типа Aldus
+                    PageMaker, в шаблонах которых используется Lorem Ipsum.
+                </p>
+            </Spoiler>
+
+            <Spoiler>
+                <h2>Контент 2</h2>
+                <p>
+                    Многие думают, что Lorem Ipsum - взятый с потолка псевдо-латинский набор слов, но это не совсем
+                    так.
+                    Его корни уходят в один фрагмент классической латыни 45 года н.э., то есть более двух
+                    тысячелетий
+                    назад. Ричард МакКлинток, профессор латыни из колледжа Hampden-Sydney, штат Вирджиния, взял одно
+                    из
+                    самых странных слов в Lorem Ipsum, "consectetur", и занялся его поисками в классической
+                    латинской
+                    литературе. В результате он нашёл неоспоримый первоисточник Lorem Ipsum в разделах 1.10.32 и
+                    1.10.33
+                    книги "de Finibus Bonorum et Malorum" ("О пределах добра и зла"), написанной Цицероном в 45 году
+                    н.э. Этот трактат по теории этики был очень популярен в эпоху Возрождения. Первая строка Lorem
+                    Ipsum, "Lorem ipsum dolor sit amet..", происходит от одной из строк в разделе 1.10.32
+                </p>
+            </Spoiler>
+        </div>
+    );
+};
+
+export default SpoilerParent;

+ 7 - 0
src/Task2-RangeInput/RangeInput/RangeInput.css

@@ -0,0 +1,7 @@
+.error{
+    border: 1px solid red;
+}
+
+input{
+    outline: none;
+}

+ 27 - 0
src/Task2-RangeInput/RangeInput/RangeInput.js

@@ -0,0 +1,27 @@
+import React from 'react';
+import './RangeInput.css';
+
+class RangeInput extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            value: '',
+        };
+    }
+
+    inputChange = (e) => {
+        this.setState({value: e.target.value});
+    };
+
+    render() {
+        const {min, max} = this.props;
+        const hasError = this.state.value.length > max || this.state.value.length < min;
+        return (
+            <div>
+                <input onChange={this.inputChange} className={hasError ? 'error' : ''}/>
+            </div>
+        );
+    }
+}
+
+export default RangeInput;

+ 9 - 0
src/Task2-RangeInput/RangeInputParent/RangeInputParent.js

@@ -0,0 +1,9 @@
+import RangeInput from '../RangeInput/RangeInput';
+
+const RangeInputParent = () => {
+    return(
+        <RangeInput min={2} max={10} />
+    )
+}
+
+export default RangeInputParent;

+ 13 - 0
src/Task3-PasswordConfirm/PasswordConfirm/PasswordConfirm.css

@@ -0,0 +1,13 @@
+.PasswordConfirmWrapper{
+    display: flex;
+    flex-direction: column;
+    width: 25%;
+}
+
+.error{
+    border: 1px solid red;
+}
+
+input{
+    outline: none;
+}

+ 41 - 0
src/Task3-PasswordConfirm/PasswordConfirm/PasswordConfirm.js

@@ -0,0 +1,41 @@
+import React from 'react';
+import './PasswordConfirm.css';
+
+class PasswordConfirm extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            password: '',
+            passwordConfirm: '',
+        };
+    }
+
+    inputChange = (e) => {
+        this.setState({[e.target.name]: e.target.value})
+    };
+
+    render() {
+        const {min} = this.props;
+        const arePasswordsEqual = this.state.password === this.state.passwordConfirm;
+        const hasErrorPsw = this.state.password.length < min || !arePasswordsEqual;
+        const hasErrorPswConfirm = this.state.passwordConfirm.length < min || !arePasswordsEqual;
+
+        return (
+            <div className="PasswordConfirmWrapper">
+                <input placeholder={'password'}
+                       name={'password'}
+                       type={'password'}
+                       className={hasErrorPsw ? 'error' : ''}
+                       onChange={this.inputChange}
+                />
+                <input placeholder={'confirm password'}
+                       name={'passwordConfirm'}
+                       type={'password'}
+                       className={hasErrorPswConfirm ? 'error' : ''}
+                       onChange={this.inputChange}/>
+            </div>
+        );
+    }
+}
+
+export default PasswordConfirm;

+ 14 - 0
src/Task3-PasswordConfirm/PasswordConfirmParent/PasswordConfirmParent.js

@@ -0,0 +1,14 @@
+import React from 'react';
+import PasswordConfirm from '../PasswordConfirm/PasswordConfirm';
+
+class PasswordConfirmParent extends React.Component{
+    render() {
+        return(
+            <div>
+                <PasswordConfirm min={2} />
+            </div>
+        )
+    }
+}
+
+export default PasswordConfirmParent;

+ 43 - 0
src/Task4-Timer/Timer/Timer.js

@@ -0,0 +1,43 @@
+import {useState, useEffect} from 'react';
+
+function Timer({sec}) {
+    const [count, setCount] = useState(sec);
+    const [isPaused, setIsPaused] = useState(false);
+
+    useEffect(() => {
+        let timer;
+
+        if (count > 0 && isPaused === false) {
+            timer = setTimeout(() => setCount(count - 1), 1000);
+        }
+
+        return () => {
+            clearTimeout(timer);
+        };
+    }, [count, isPaused]);
+
+    useEffect(() => {
+        setCount(sec);
+    }, [sec]);
+
+    const onBtnPauseClick = () => {
+        setIsPaused(!isPaused);
+    };
+
+    const h = Math.floor(count / 3600);
+    const s = count % 60;
+    const min = Math.floor(count % 3600 / 60);
+
+    return (
+        <div>
+            <div>{h < 10 ? '0' + h : h}:{min < 10 ? '0' + min : min}:{s < 10 ? '0' + s : s}</div>
+            <button
+                className="buttonForTimer"
+                onClick={onBtnPauseClick}>
+                {isPaused ? 'Resume' : 'Pause'}
+            </button>
+        </div>
+    );
+}
+
+export default Timer;

+ 12 - 0
src/Task4-Timer/TimerParent/TimerParent.js

@@ -0,0 +1,12 @@
+import Timer from '../Timer/Timer';
+
+
+const TimerParent = () => {
+    return(
+        <div>
+            <Timer sec={60}/>
+        </div>
+    )
+}
+
+export default TimerParent;

+ 20 - 0
src/Task5-TimerControl/TimerControlParent/TimerControlParent.js

@@ -0,0 +1,20 @@
+import Timer from '../../Task4-Timer/Timer/Timer';
+import {useState} from 'react';
+
+
+function TimerControlParent() {
+    const [sec, setSec] = useState(0);
+    const [min, setMin] = useState(0);
+    const [hours, setHours] = useState(0);
+
+    return (
+        <div>
+            <input name="hours" type="number" placeholder="Часы" onChange={(e) => setHours(+e.target.value)}/>
+            <input name="minutes" type="number" placeholder="Минуты" onChange={(e) => setMin(+e.target.value)}/>
+            <input name="sec" type="number" placeholder="Секунды" onChange={(e) => setSec(+e.target.value)}/>
+            <Timer sec={hours * 3600 + min * 60 + sec}/>
+        </div>
+    );
+}
+
+export default TimerControlParent;

+ 18 - 0
src/Task6-TimerContainer/SecondsTimer/SecondsTimer.js

@@ -0,0 +1,18 @@
+
+
+const SecondsTimer = (sec) => {
+    const transformMSToSecCurrent = Math.floor(sec.currentSec / 1000);
+    const transformMSToSecInitial = sec.initialSec / 1000;
+
+    const seconds = (transformMSToSecInitial - transformMSToSecCurrent) % 60;
+    const minutes = Math.floor((transformMSToSecInitial - transformMSToSecCurrent) % 3600 / 60);
+
+    return (
+        <div>
+            <h2>{sec.currentSec}</h2>
+            <div>SecondsTimer: {minutes < 10 ? '0' + minutes : minutes}:{seconds < 10 ? '0' + seconds : seconds}</div>
+        </div>
+    );
+};
+
+export default SecondsTimer;

+ 42 - 0
src/Task6-TimerContainer/TimerContainer/TimerContainer.js

@@ -0,0 +1,42 @@
+import {useEffect, useState} from 'react';
+
+const TimerContainer = (props) => {
+    const [mseconds, setMSeconds] = useState(props.seconds * 1000);
+    const [isPaused, setIsPaused] = useState(false);
+
+    const onBtnPauseClick = () => {
+        setIsPaused(!isPaused);
+    };
+
+    useEffect(() => {
+        let timer;
+        if (mseconds > 0 && isPaused === false) {
+            timer = setTimeout(() => setMSeconds(mseconds - props.refresh), props.refresh);
+        }
+        return () => {
+            clearTimeout(timer);
+        };
+    }, [mseconds, isPaused, props.refresh]);
+
+    return (
+        <div>
+            <div>
+                {props.renderComponents.map((Component) => {
+                    return (
+                        <Component
+                            currentSec={mseconds}
+                            initialSec={props.seconds * 1000}>
+                            {Component}
+                        </Component>);
+                })}
+            </div>
+            <button
+                className="buttonForTimer"
+                onClick={onBtnPauseClick}>
+                {isPaused ? 'Resume' : 'Pause'}
+            </button>
+        </div>
+    );
+};
+
+export default TimerContainer;

+ 18 - 0
src/Task6-TimerContainer/TimerContainerParent/TimerContainerParent.js

@@ -0,0 +1,18 @@
+import SecondsTimer from '../SecondsTimer/SecondsTimer';
+import TimerContainer from '../TimerContainer/TimerContainer';
+import Lcd from '../../Task7-LCD/Lcd/Lcd';
+import Watch from '../../Task8-Watch/Watch/Watch';
+
+const TimerContainerParent = () => {
+    return(
+        <div>
+            <TimerContainer
+                seconds={1800}
+                refresh={100}
+                renderComponents = {[SecondsTimer, Lcd, Watch]}
+            />
+        </div>
+    )
+}
+
+export default TimerContainerParent;

+ 16 - 0
src/Task7-LCD/Lcd/Lcd.js

@@ -0,0 +1,16 @@
+function Lcd(sec) {
+    const transformMSToSecCurrent = Math.floor(sec.currentSec / 1000);
+    const transformMSToSecInitial = sec.initialSec / 1000;
+
+    const h = Math.floor((transformMSToSecInitial - transformMSToSecCurrent) / 3600);
+    const s = (transformMSToSecInitial - transformMSToSecCurrent) % 60;
+    const min = Math.floor((transformMSToSecInitial - transformMSToSecCurrent) % 3600 / 60);
+
+    return (
+        <div>
+            <div>LCD: {h < 10 ? '0' + h : h}:{min < 10 ? '0' + min : min}:{s < 10 ? '0' + s : s}</div>
+        </div>
+    );
+}
+
+export default Lcd;

+ 11 - 0
src/Task8-Watch/Watch/Watch.css

@@ -0,0 +1,11 @@
+
+.Timer{
+    position: relative;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.arrowHours, .arrowMinutes, .arrowSeconds, .baseTimer{
+    position: absolute;
+}

+ 44 - 0
src/Task8-Watch/Watch/Watch.js

@@ -0,0 +1,44 @@
+import clockFace from './../../Img/clockFace.png';
+import clockFace_S from '../../Img/сlockFace_S.png';
+import clockFace_M from '../../Img/сlockFace_M.png';
+import clockFace_H from '../../Img/сlockFace_H.png';
+import './Watch.css';
+
+const Watch = (props) => {
+    const transformMSToSecCurrent = Math.floor(props.currentSec / 1000);
+    const transformMSToSecInitial = props.initialSec / 1000;
+
+    const h = Math.floor((transformMSToSecInitial - transformMSToSecCurrent) / 3600);
+    const s = (transformMSToSecInitial - transformMSToSecCurrent) % 60;
+    const min = Math.floor((transformMSToSecInitial - transformMSToSecCurrent) % 3600 / 60);
+
+    return(
+        <div className="Timer">
+            <img
+                src={clockFace}
+                alt="таймер"
+                className="baseTimer"
+            />
+            <img
+                style={{transform: `rotate(${360/h}deg)`}}
+                src={clockFace_H}
+                alt="стрелочкаЧасовая"
+                className="arrowHours"
+            />
+            <img
+                style={{transform: `rotate(${360/60*min}deg)`}}
+                src={clockFace_M}
+                alt="стрелочкаМинутная"
+                className="arrowMinutes"
+            />
+            <img
+                style={{transform: `rotate(${s*6}deg)`} }
+                src={clockFace_S}
+                alt="стрелочкаСекундная"
+                className="arrowSeconds"
+            />
+        </div>
+    )
+}
+
+export default Watch;

+ 2 - 9
src/index.js

@@ -2,16 +2,9 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import './index.css';
 import App from './App';
-import reportWebVitals from './reportWebVitals';
+
 
 ReactDOM.render(
-  <React.StrictMode>
-    <App />
-  </React.StrictMode>,
+    <App />,
   document.getElementById('root')
 );
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 1
src/logo.svg


+ 0 - 5
src/setupTests.js

@@ -1,5 +0,0 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';