yankevych0210 1 rok temu
rodzic
commit
1271da5b0e

+ 51 - 332
react/hw2/src/App.js

@@ -1,342 +1,61 @@
 import './App.css';
 import React, { useState, useEffect, ReactDOM } from 'react';
-const App = ({ seconds }) => {
-  const [time, setTime] = useState(seconds);
-  const [paused, setPaused] = useState(false);
-
-  useEffect(() => {
-    let interval = null;
-    if (!paused && time > 0) {
-      interval = setInterval(() => {
-        setTime((time) => time - 1);
-      }, 1000);
-    }
-    return () => clearInterval(interval);
-  }, [paused, time]);
-
-  const handlePause = () => {
-    setPaused(true);
-  };
-
-  const formatTime = (time) => {
-    const hours = Math.floor(time / 3600);
-    const minutes = Math.floor((time - hours * 3600) / 60);
-    const seconds = time - hours * 3600 - minutes * 60;
-    return (
-      (hours > 0 ? hours + ':' : '') +
-      (hours > 0 && minutes < 10 ? '0' : '') +
-      minutes +
-      ':' +
-      (seconds < 10 ? '0' : '') +
-      seconds
-    );
-  };
-
+import { Spoiler } from './copmonents/Spoiler';
+import { RangeInput } from './copmonents/RengeInput';
+import { LoginForm } from './copmonents/LoginForm';
+import { PasswordConfirm } from './copmonents/PasswordConfirm';
+import { Timer } from './copmonents/FormatTime';
+import { TimerControl } from './copmonents/TimerControl';
+import { TimerControl2 } from './copmonents/TimerControl2';
+import { TimerContainer } from './copmonents/TimerContainer';
+import { LCD } from './copmonents/LCD';
+import { Watch } from './copmonents/Watch/Watch';
+
+const App = () => {
   return (
     <div>
-      <h1>{formatTime(time)}</h1>
-      <button onClick={handlePause}>Pause</button>
-    </div>
-  );
-};
-
-{
-  const Spoiler = ({ header = '+', open = true, children }) => {
-    // создаем состояние isContentVisible и функцию setIsContentVisible для изменения состояния.
-    const [isContentVisible, setIsContentVisible] = useState(open);
-
-    // создаем функцию для обработки клика на заголовке
-    const handleHeaderClick = () => {
-      setIsContentVisible(!isContentVisible);
-    };
-
-    // возвращаем JSX-разметку, в которой обрабатываем состояние isContentVisible
-    return (
-      <div>
-        {/* выводим заголовок и при клике на него вызываем функцию handleHeaderClick */}
-        <div onClick={handleHeaderClick}>
-          {header}
-          {isContentVisible ? '-' : '+'}
-        </div>
-        {/* если isContentVisible равно true, то выводим children, иначе - ничего не выводим */}
-        {isContentVisible ? children : null}
-      </div>
-    );
-  };
-
-  // примеры использования компонента Spoiler
-
-  <div>
-    <Spoiler header={<h1>Заголовок</h1>} open>
-      Контент 1<p>лорем ипсум траливали и тп.</p>
-    </Spoiler>
-
-    <Spoiler>
-      <h2>Контент 2</h2>
-      <p>лорем ипсум траливали и тп.</p>
-    </Spoiler>
-  </div>;
-}
-
-{
-  const RangeInput = ({ min, max }) => {
-    // создаем состояние inputText, которое отслеживает значение инпута
-    const [inputText, setInputText] = useState('');
-
-    // создаем функцию-обработчик для изменения значения inputText
-    const handleInputChange = (event) => {
-      setInputText(event.target.value);
-    };
-
-    // создаем переменные для стилей инпута, которые зависят от его длины
-    const inputStyle =
-      inputText.length < min || inputText.length > max ? { border: '1px solid red' } : {};
-
-    // возвращаем JSX-разметку, в которой выводим инпут и применяем стили
-    return <input type="text" value={inputText} onChange={handleInputChange} style={inputStyle} />;
-  };
-
-  // пример использования компонента RangeInput
-  <RangeInput min={2} max={10} />;
-}
-
-{
-  const LoginForm = ({ onLogin }) => {
-    const [login, setLogin] = useState('');
-    const [password, setPassword] = useState('');
-
-    const handleLoginChange = (event) => {
-      setLogin(event.target.value);
-    };
-
-    const handlePasswordChange = (event) => {
-      setPassword(event.target.value);
-    };
-
-    const handleSubmit = (event) => {
-      event.preventDefault();
-      onLogin(login, password);
-    };
-
-    const isFormValid = login.trim().length > 0 && password.trim().length > 0;
-
-    return (
-      <form onSubmit={handleSubmit}>
-        <div>
-          <label htmlFor="login-input">Login:</label>
-          <input type="text" id="login-input" value={login} onChange={handleLoginChange} />
-        </div>
-        <div>
-          <label htmlFor="password-input">Password:</label>
-          <input
-            type="password"
-            id="password-input"
-            value={password}
-            onChange={handlePasswordChange}
-          />
-        </div>
-        <button type="submit" disabled={!isFormValid}>
-          Login
-        </button>
-      </form>
-    );
-  };
-}
-
-{
-  const PasswordConfirm = ({ min }) => {
-    const [password, setPassword] = useState('');
-    const [confirmPassword, setConfirmPassword] = useState('');
-    const [valid, setValid] = useState(false);
-
-    useEffect(() => {
-      setValid(password.length >= min && password === confirmPassword);
-    }, [password, confirmPassword, min]);
-
-    const handleChangePassword = (event) => {
-      setPassword(event.target.value);
-    };
-
-    const handleChangeConfirmPassword = (event) => {
-      setConfirmPassword(event.target.value);
-    };
-
-    return (
-      <div>
-        <label>Password:</label>
-        <input type="password" value={password} onChange={handleChangePassword} />
-        <br />
-        <label>Confirm password:</label>
-        <input type="password" value={confirmPassword} onChange={handleChangeConfirmPassword} />
-        <br />
-        <button disabled={!valid}>Submit</button>
-      </div>
-    );
-  };
-}
-
-{
-}
-
-{
-  const TimerControl = ({ onStart }) => {
-    const [hours, setHours] = useState(0);
-    const [minutes, setMinutes] = useState(0);
-    const [seconds, setSeconds] = useState(0);
-
-    const handleStartClick = () => {
-      const totalSeconds = hours * 3600 + minutes * 60 + seconds;
-      onStart(totalSeconds);
-    };
-
-    return (
+      <h3>Task 1: Spoiler </h3>
       <div>
-        <input
-          type="number"
-          value={hours}
-          min={0}
-          max={23}
-          onChange={(e) => setHours(parseInt(e.target.value))}
-        />
-        <input
-          type="number"
-          value={minutes}
-          min={0}
-          max={59}
-          onChange={(e) => setMinutes(parseInt(e.target.value))}
-        />
-        <input
-          type="number"
-          value={seconds}
-          min={0}
-          max={59}
-          onChange={(e) => setSeconds(parseInt(e.target.value))}
-        />
-        <button onClick={handleStartClick}>Start</button>
+        <Spoiler header={<h1>Заголовок</h1>}>
+          Контент 1<p>лорем ипсум траливали и тп.</p>
+        </Spoiler>
+
+        <Spoiler>
+          <h2>Контент 2</h2>
+          <p>лорем ипсум траливали и тп.</p>
+        </Spoiler>
       </div>
-    );
-  };
-}
-
-{
-  const TimerContainer = ({ seconds, refresh, render: TimerComponent }) => {
-    const [time, setTime] = useState(seconds);
-
-    useEffect(() => {
-      const intervalId = setInterval(() => {
-        setTime((prevTime) => {
-          const newTime = prevTime - refresh / 1000;
-          return newTime < 0 ? 0 : newTime;
-        });
-      }, refresh);
-
-      return () => clearInterval(intervalId);
-    }, [refresh]);
-
-    const hours = Math.floor(time / 3600);
-    const minutes = Math.floor((time % 3600) / 60);
-    const secondsRemaining = Math.floor(time % 60);
-
-    return <TimerComponent hours={hours} minutes={minutes} seconds={secondsRemaining} />;
-  };
-}
-
-{
-  const LCD = ({ hours, minutes, seconds }) => (
-    <div>
-      <span>{hours < 10 ? `0${hours}` : hours}</span>:
-      <span>{minutes < 10 ? `0${minutes}` : minutes}</span>:
-      <span>{seconds < 10 ? `0${seconds}` : seconds}</span>
+      <hr />
+      <h3>Task 2: RangeInput </h3>
+      <RangeInput min={2} max={10} />;
+      <hr />
+      <h3>Task 3: LoginForm </h3>
+      <LoginForm onLogin={console.log} />
+      <hr />
+      <h3>Task 4: PasswordConfirm </h3>
+      <PasswordConfirm min={3} />
+      <hr />
+      <h3>Task 5: Timer </h3>
+      <Timer seconds={3600} />
+      <hr />
+      <h3>Task 6: TimerControl </h3>
+      <TimerControl render={Timer} />
+      <hr />
+      <h3>Task 7: TimerContainer </h3>
+      <TimerContainer seconds={1800} refresh={1000} render={SecondsTimer} />
+      <hr />
+      <h3>Task 8: LCD </h3>
+      <TimerContainer seconds={1800} refresh={1000} render={LCD} />
+      <hr />
+      <h3>Task 9: Watch </h3>
+      <Watch seconds={40} minutes={15} hours={4} />
+      <hr />
+      <h3>Task 10: TimerControl + TimerContainer </h3>
+      <TimerControl2 />
+      <hr />
     </div>
   );
-
-  const TimerContainer = ({ seconds, refresh, render }) => {
-    const [time, setTime] = React.useState(seconds);
-
-    React.useEffect(() => {
-      const interval = setInterval(() => {
-        setTime((prevTime) => prevTime - 1);
-      }, refresh);
-
-      return () => clearInterval(interval);
-    }, [refresh]);
-
-    const hours = Math.floor(time / 3600);
-    const minutes = Math.floor((time % 3600) / 60);
-    const secondsLeft = time % 60;
-
-    return <div>{render({ hours, minutes, seconds: secondsLeft })}</div>;
-  };
-
-  // const App = () => (
-  //   <div>
-  //     <TimerContainer seconds={1800} refresh={1000} render={LCD} />
-  //   </div>
-  // );
-
-  // ReactDOM.render(<App />, document.getElementById('root'));
-}
-
-{
-  // import './Watch.css';
-
-  const Watch = ({ hours, minutes, seconds }) => {
-    const hourDegrees = (hours / 12) * 360 + (minutes / 60) * 30;
-    const minuteDegrees = (minutes / 60) * 360 + (seconds / 60) * 6;
-    const secondDegrees = (seconds / 60) * 360;
-
-    return (
-      <div className="watch-container">
-        <div className="watch-face">
-          <div className="watch-hour-hand" style={{ transform: `rotate(${hourDegrees}deg)` }}></div>
-          <div
-            className="watch-minute-hand"
-            style={{ transform: `rotate(${minuteDegrees}deg)` }}></div>
-          <div
-            className="watch-second-hand"
-            style={{ transform: `rotate(${secondDegrees}deg)` }}></div>
-        </div>
-      </div>
-    );
-  };
-  // export default Watch;
-}
-
-// {
-//   // import TimerContainer from './TimerContainer';
-
-//   function TimerControl() {
-//     const [hours, setHours] = useState(0);
-//     const [minutes, setMinutes] = useState(0);
-//     const [seconds, setSeconds] = useState(0);
-
-//     const startTimer = () => {
-//       const totalSeconds = hours * 3600 + minutes * 60 + seconds;
-//       ReactDOM.render(
-//         <TimerContainer seconds={totalSeconds} refresh={1000} render={Timer} />,
-//         document.getElementById('timer-container'),
-//       );
-//     };
-
-//     return (
-//       <div>
-//         <div>
-//           <label>Hours:</label>
-//           <input type="number" value={hours} onChange={(e) => setHours(e.target.value)} />
-//         </div>
-//         <div>
-//           <label>Minutes:</label>
-//           <input type="number" value={minutes} onChange={(e) => setMinutes(e.target.value)} />
-//         </div>
-//         <div>
-//           <label>Seconds:</label>
-//           <input type="number" value={seconds} onChange={(e) => setSeconds(e.target.value)} />
-//         </div>
-//         <button onClick={startTimer}>Start</button>
-//         <div id="timer-container"></div>
-//       </div>
-//     );
-//   }
-// }
+};
+const SecondsTimer = ({ seconds }) => <h2>{seconds}</h2>;
 
 export default App;

+ 0 - 45
react/hw2/src/Watch.css

@@ -1,45 +0,0 @@
-.watch-container {
-  width: 200px;
-  height: 200px;
-  position: relative;
-  margin: 0 auto;
-  border: 1px solid #999;
-  border-radius: 50%;
-}
-
-.watch-face {
-  position: relative;
-  width: 100%;
-  height: 100%;
-  transform: rotate(30deg);
-}
-
-.watch-hour-hand {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform-origin: 50% 100%;
-  width: 5px;
-  height: 50px;
-  background-color: #333;
-}
-
-.watch-minute-hand {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform-origin: 50% 100%;
-  width: 4px;
-  height: 80px;
-  background-color: #333;
-}
-
-.watch-second-hand {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform-origin: 50% 100%;
-  width: 2px;
-  height: 90px;
-  background-color: red;
-}

+ 38 - 0
react/hw2/src/copmonents/FormatTime.jsx

@@ -0,0 +1,38 @@
+import { useState, useEffect } from 'react';
+
+export const Timer = ({ seconds }) => {
+  const [time, setTime] = useState(seconds);
+  const [paused, setPaused] = useState(false);
+
+  useEffect(() => {
+    const intervalId = setInterval(() => {
+      if (!paused) {
+        setTime((prevTime) => {
+          if (prevTime === 0) {
+            clearInterval(intervalId);
+            return 0;
+          } else {
+            return prevTime - 1;
+          }
+        });
+      }
+    }, 1000);
+    return () => clearInterval(intervalId);
+  }, [paused]);
+
+  const formatTime = (time) => {
+    const hours = Math.floor(time / 3600);
+    const minutes = Math.floor((time - hours * 3600) / 60);
+    const seconds = time - hours * 3600 - minutes * 60;
+    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds
+      .toString()
+      .padStart(2, '0')}`;
+  };
+
+  return (
+    <div>
+      <div>{formatTime(time)}</div>
+      <button onClick={() => setPaused(!paused)}>{paused ? 'Resume' : 'Pause'}</button>
+    </div>
+  );
+};

+ 9 - 0
react/hw2/src/copmonents/LCD.jsx

@@ -0,0 +1,9 @@
+export const LCD = ({ seconds, minutes, hours }) => {
+  const formatTime = (seconds, minutes, hours) => {
+    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds
+      .toString()
+      .padStart(2, '0')}`;
+  };
+
+  return <span>{formatTime(seconds, minutes, hours)}</span>;
+};

+ 42 - 0
react/hw2/src/copmonents/LoginForm.jsx

@@ -0,0 +1,42 @@
+import { useState } from 'react';
+
+export const LoginForm = ({ onLogin }) => {
+  const [login, setLogin] = useState('');
+  const [password, setPassword] = useState('');
+
+  const handleLoginChange = (event) => {
+    setLogin(event.target.value);
+  };
+
+  const handlePasswordChange = (event) => {
+    setPassword(event.target.value);
+  };
+
+  const handleSubmit = (event) => {
+    event.preventDefault();
+    onLogin(login, password);
+  };
+
+  const isFormValid = login.trim().length > 0 && password.trim().length > 0;
+
+  return (
+    <form onSubmit={handleSubmit}>
+      <div>
+        <label htmlFor="login-input">Login:</label>
+        <input type="text" id="login-input" value={login} onChange={handleLoginChange} />
+      </div>
+      <div>
+        <label htmlFor="password-input">Password:</label>
+        <input
+          type="password"
+          id="password-input"
+          value={password}
+          onChange={handlePasswordChange}
+        />
+      </div>
+      <button type="submit" disabled={!isFormValid}>
+        Login
+      </button>
+    </form>
+  );
+};

+ 31 - 0
react/hw2/src/copmonents/PasswordConfirm.jsx

@@ -0,0 +1,31 @@
+import { useState, useEffect } from 'react';
+
+export const PasswordConfirm = ({ min }) => {
+  const [password, setPassword] = useState('');
+  const [confirmPassword, setConfirmPassword] = useState('');
+  const [valid, setValid] = useState(false);
+
+  useEffect(() => {
+    setValid(password.length >= min && password === confirmPassword);
+  }, [password, confirmPassword, min]);
+
+  const handleChangePassword = (event) => {
+    setPassword(event.target.value);
+  };
+
+  const handleChangeConfirmPassword = (event) => {
+    setConfirmPassword(event.target.value);
+  };
+
+  return (
+    <div>
+      <label>Password:</label>
+      <input type="password" value={password} onChange={handleChangePassword} />
+      <br />
+      <label>Confirm password:</label>
+      <input type="password" value={confirmPassword} onChange={handleChangeConfirmPassword} />
+      <br />
+      <button disabled={!valid}>Submit</button>
+    </div>
+  );
+};

+ 14 - 0
react/hw2/src/copmonents/RengeInput.jsx

@@ -0,0 +1,14 @@
+import { useState } from 'react';
+
+export const RangeInput = ({ min, max }) => {
+  const [inputText, setInputText] = useState('');
+
+  const handleInputChange = (event) => {
+    setInputText(event.target.value);
+  };
+
+  const inputStyle =
+    inputText.length < min || inputText.length > max ? { border: '1px solid red' } : {};
+
+  return <input type="text" value={inputText} onChange={handleInputChange} style={inputStyle} />;
+};

+ 19 - 0
react/hw2/src/copmonents/Spoiler.jsx

@@ -0,0 +1,19 @@
+import { useState } from 'react';
+
+export const Spoiler = ({ header = null, open = true, children }) => {
+  const [isContentVisible, setIsContentVisible] = useState(open);
+
+  const handleHeaderClick = () => {
+    setIsContentVisible(!isContentVisible);
+  };
+
+  return (
+    <div>
+      <div onClick={handleHeaderClick}>
+        {header}
+        <button>{isContentVisible ? '-' : '+'}</button>
+      </div>
+      {isContentVisible ? children : null}
+    </div>
+  );
+};

+ 26 - 0
react/hw2/src/copmonents/TimerContainer.jsx

@@ -0,0 +1,26 @@
+import { useState, useEffect } from 'react';
+
+export const TimerContainer = ({ seconds, refresh, render: RenderComponent }) => {
+  const [timeLeft, setTimeLeft] = useState(seconds);
+
+  useEffect(() => {
+    const timerId = setInterval(() => {
+      setTimeLeft((prevTimeLeft) => {
+        const newTimeLeft = prevTimeLeft - refresh / 1000;
+        return newTimeLeft < 0 ? 0 : newTimeLeft;
+      });
+    }, refresh);
+
+    return () => clearInterval(timerId);
+  }, [refresh]);
+
+  const hours = Math.floor(timeLeft / 3600);
+  const minutes = Math.floor((timeLeft % 3600) / 60);
+  const secondsLeft = Math.floor(timeLeft % 60);
+
+  return (
+    <>
+      <RenderComponent hours={hours} minutes={minutes} seconds={secondsLeft} />
+    </>
+  );
+};

+ 49 - 0
react/hw2/src/copmonents/TimerControl.jsx

@@ -0,0 +1,49 @@
+import { useState } from 'react';
+
+export const TimerControl = ({ render: RenderComponent }) => {
+  const [hours, setHours] = useState(0);
+  const [minutes, setMinutes] = useState(0);
+  const [seconds, setSeconds] = useState(0);
+  const [timerStarted, setTimerStarted] = useState(false);
+
+  const startTimer = () => {
+    setTimerStarted(true);
+  };
+
+  return (
+    <div>
+      {!timerStarted && (
+        <div>
+          <label htmlFor="hours">Hours:</label>
+          <input
+            type="number"
+            id="hours"
+            min={0}
+            value={hours}
+            onChange={(e) => setHours(e.target.value)}
+          />
+          <label htmlFor="minutes">Minutes:</label>
+          <input
+            type="number"
+            id="minutes"
+            min={0}
+            max={59}
+            value={minutes}
+            onChange={(e) => setMinutes(e.target.value)}
+          />
+          <label htmlFor="seconds">Seconds:</label>
+          <input
+            type="number"
+            id="seconds"
+            min={0}
+            max={59}
+            value={seconds}
+            onChange={(e) => setSeconds(e.target.value)}
+          />
+          <button onClick={startTimer}>Start</button>
+        </div>
+      )}
+      {timerStarted && <RenderComponent seconds={+hours * 3600 + +minutes * 60 + +seconds} />}
+    </div>
+  );
+};

+ 57 - 0
react/hw2/src/copmonents/TimerControl2.jsx

@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import { TimerContainer } from './TimerContainer';
+import { Watch } from './Watch/Watch';
+
+export const TimerControl2 = () => {
+  const [hours, setHours] = useState(0);
+  const [minutes, setMinutes] = useState(0);
+  const [seconds, setSeconds] = useState(0);
+  const [timerStarted, setTimerStarted] = useState(false);
+
+  const startTimer = () => {
+    setTimerStarted(true);
+  };
+
+  return (
+    <div>
+      {!timerStarted && (
+        <div>
+          <label htmlFor="hours">Hours:</label>
+          <input
+            type="number"
+            id="hours"
+            min={0}
+            value={hours}
+            onChange={(e) => setHours(e.target.value)}
+          />
+          <label htmlFor="minutes">Minutes:</label>
+          <input
+            type="number"
+            id="minutes"
+            min={0}
+            max={59}
+            value={minutes}
+            onChange={(e) => setMinutes(e.target.value)}
+          />
+          <label htmlFor="seconds">Seconds:</label>
+          <input
+            type="number"
+            id="seconds"
+            min={0}
+            max={59}
+            value={seconds}
+            onChange={(e) => setSeconds(e.target.value)}
+          />
+          <button onClick={startTimer}>Start</button>
+        </div>
+      )}
+      {timerStarted && (
+        <TimerContainer
+          seconds={+hours * 3600 + +minutes * 60 + +seconds}
+          refresh={1000}
+          render={Watch}
+        />
+      )}
+    </div>
+  );
+};

+ 85 - 0
react/hw2/src/copmonents/Watch/Watch.css

@@ -0,0 +1,85 @@
+.clock {
+  width: 350px;
+  height: 350px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: url('../../img/ClockFace.png') center / cover;
+  border-radius: 50%;
+}
+
+.clock::before {
+  content: '';
+  position: absolute;
+  width: 13px;
+  height: 13px;
+  border-radius: 50%;
+  z-index: 10;
+  background-color: #ee151d;
+}
+
+.hour {
+  position: absolute;
+}
+
+.hourArrow {
+  width: 160px;
+  height: 160px;
+  display: flex;
+  justify-content: center;
+  position: relative;
+  border-radius: 50%;
+}
+
+.hourArrow::before {
+  content: '';
+  position: absolute;
+  width: 10px;
+  height: 85px;
+  background: #2e1e2c;
+}
+
+.minute {
+  position: absolute;
+}
+
+.minuteArrow {
+  width: 190px;
+  height: 190px;
+  display: flex;
+  justify-content: center;
+  position: relative;
+  border-radius: 50%;
+}
+
+.minuteArrow:before {
+  content: '';
+  position: absolute;
+  width: 5px;
+  height: 95px;
+  background: #2e1e2c;
+  border: 1px solid #a7a5a7;
+}
+
+.second {
+  position: absolute;
+}
+
+.secondArrow {
+  width: 230px;
+  height: 230px;
+  display: flex;
+  justify-content: center;
+  position: relative;
+  border-radius: 50%;
+}
+
+.secondArrow::before {
+  content: '';
+  position: absolute;
+  width: 3px;
+  height: 150px;
+  background-color: #ee151d;
+  border-radius: 6px 6px 0 0;
+  z-index: 2;
+}

+ 23 - 0
react/hw2/src/copmonents/Watch/Watch.jsx

@@ -0,0 +1,23 @@
+import './Watch.css';
+
+export const Watch = ({ seconds, minutes, hours }) => {
+  const deg = 6;
+
+  const hoursDeg = hours * 30;
+  const minutesDeg = minutes * deg;
+  const secondsDeg = seconds * deg;
+
+  return (
+    <div className="clock">
+      <div className="hour">
+        <div className="hourArrow" style={{ transform: `rotateZ(${hoursDeg}deg)` }}></div>
+      </div>
+      <div className="minute">
+        <div style={{ transform: `rotateZ(${minutesDeg}deg)` }} className="minuteArrow"></div>
+      </div>
+      <div className="second">
+        <div style={{ transform: `rotateZ(${secondsDeg}deg)` }} className="secondArrow"></div>
+      </div>
+    </div>
+  );
+};

BIN
react/hw2/src/img/ClockFace.png