App.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. import ClockFace from "./assets/images/ClockFace.png";
  2. import ClockFace_H from "./assets/images/ClockFace_H.png";
  3. import ClockFace_M from "./assets/images/ClockFace_M.png";
  4. import ClockFace_S from "./assets/images/ClockFace_S.png";
  5. import './assets/scss/_page-styles/rickandmortyapi.scss';
  6. import './assets/scss/_page-styles/common.scss';
  7. import './assets/scss/_page-styles/clock-face.scss';
  8. import React, {useState, useEffect, useRef} from 'react';
  9. // hw19 (22.10.2022)
  10. import dataEpisodes from './assets/json/dataEpisodes.json';
  11. import dataCharacters from './assets/json/dataCharacters.json';
  12. const CharactersItem = ({name, gender, image}) =>
  13. <li className="characters-list__item">
  14. <h3 className="characters-list__name">{name}</h3>
  15. <p>{gender}</p>
  16. <img className="characters-list__image"
  17. alt={name}
  18. src={image}
  19. />
  20. </li>
  21. const EpisodeItem = ({name, air_date, children}) =>
  22. <>
  23. <h1 className="episode__name">{name}</h1>
  24. <time className="episode__air-date">{air_date}</time>
  25. {children}
  26. </>
  27. const Episode = () =>
  28. dataEpisodes.map(item => (
  29. <div className="episode"
  30. key={Math.random()}
  31. >
  32. <EpisodeItem name={item.name}
  33. air_date={item.air_date}
  34. >
  35. <ul className="characters-list">
  36. {dataCharacters.map(character => (
  37. <CharactersItem key={Math.random()}
  38. name={character.name}
  39. gender={character.gender}
  40. image={character.image}
  41. />
  42. ))}
  43. </ul>
  44. </EpisodeItem>
  45. </div>
  46. ))
  47. // React JSX Homework - hw20 (22.10.2022)
  48. // Spoiler
  49. const Spoiler = ({header="+", open, children}) => {
  50. const [isOpen, setIsOpen] = useState(open)
  51. return (
  52. <section>
  53. <div onClick={() => setIsOpen(!isOpen)}>{header}</div>
  54. {isOpen && children}
  55. </section>
  56. )
  57. }
  58. // RangeInput
  59. const RangeInput = ({min, max}) => {
  60. const [text, setText] = useState('');
  61. return (
  62. <input type="text"
  63. value={text}
  64. style={{borderColor: (text.length > max || text.length < min) ? 'red' : 'rgb(118, 118, 118)'}}
  65. onChange={event => setText(event.target.value)}
  66. />
  67. )
  68. }
  69. // LoginForm
  70. const LoginForm = ({onLogin}) => {
  71. const [login, setLogin] = useState('');
  72. const [password, setPassword] = useState('');
  73. return(
  74. <fieldset>
  75. <input placeholder="login"
  76. type="text"
  77. name="login"
  78. value={login}
  79. onChange={e => setLogin(e.target.value)}
  80. /><br/>
  81. <input placeholder="password"
  82. type="password"
  83. name="password"
  84. value={password}
  85. onChange={e => setPassword(e.target.value)}
  86. /><br/>
  87. <button className="login-button"
  88. onClick={() => onLogin(login, password)}
  89. type="button"
  90. disabled={!(login.length > 0 && password.length > 0)}
  91. >Login</button>
  92. </fieldset>
  93. )
  94. }
  95. // PasswordConfirm
  96. const PasswordConfirm = ({min}) => {
  97. const [password, setPassword] = useState('');
  98. const [confirmPassword, setConfirmPassword] = useState('');
  99. return(
  100. <fieldset>
  101. <input placeholder="password"
  102. type="password"
  103. name="password"
  104. value={password}
  105. style={{borderColor: (password.length >= min && password === confirmPassword) ? 'gray' : 'red'}}
  106. onChange={e => setPassword(e.target.value)}
  107. /><br/>
  108. <input placeholder="confirm password"
  109. type="password"
  110. name="confirmPassword"
  111. value={confirmPassword}
  112. style={{borderColor: (confirmPassword.length >= min && confirmPassword === password) ? 'gray' : 'red'}}
  113. onChange={e => setConfirmPassword(e.target.value)}
  114. />
  115. </fieldset>
  116. )
  117. }
  118. // Timer
  119. const Timer = ({seconds}) => {
  120. const [count, setCount] = useState(seconds);
  121. const intervalRef = useRef(null)
  122. useEffect(() => {
  123. intervalRef.current = setInterval(() => {
  124. setCount(count => count - 1)
  125. }, 1000);
  126. return () => {
  127. clearInterval(intervalRef.current);
  128. }
  129. }, [seconds]);
  130. const pause = () => {
  131. clearInterval(intervalRef.current);
  132. }
  133. if(count === 0) pause();
  134. const ISODate = (start, end) => new Date(count * 1000).toISOString().slice(start, end);
  135. return(
  136. <div>
  137. <span>{ISODate(11, 13)}</span>:
  138. <span>{ISODate(14, 16)}</span>:
  139. <span>{ISODate(17, 19)}</span>
  140. <button className="default-button"
  141. type="button"
  142. onClick={pause}
  143. >Pause</button>
  144. </div>
  145. )
  146. }
  147. // TimerControl
  148. const TimerControl = () => {
  149. const [hoursVal, setHoursVal] = useState(10);
  150. const [minutesVal, setMinutesVal] = useState(0);
  151. const [secondsVal, setSecondsVal] = useState(0);
  152. const [isStart, setStart] = useState(false);
  153. const totalSeconds = (hoursVal * 3600) + (minutesVal * 60) + secondsVal;
  154. return(
  155. <fieldset>
  156. <label>
  157. часы
  158. <input type="text"
  159. name="hours"
  160. value={hoursVal}
  161. onChange={event => setHoursVal(+event.target.value)}
  162. />
  163. </label>
  164. <label>
  165. минуты
  166. <input type="text"
  167. name="minutes"
  168. value={minutesVal}
  169. onChange={event => setMinutesVal(+event.target.value)}
  170. />
  171. </label>
  172. <label>
  173. секунды
  174. <input type="text"
  175. name="seconds"
  176. value={secondsVal}
  177. onChange={event => setSecondsVal(+event.target.value)}
  178. />
  179. </label>
  180. <button type="button"
  181. className="default-button"
  182. onClick={() => setStart(!isStart)}
  183. >Start Timer</button>
  184. {isStart && <Timer seconds={totalSeconds}/>}
  185. </fieldset>
  186. )
  187. }
  188. // TimerContainer
  189. const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
  190. const TimerContainer = ({seconds, refresh, render}) => {
  191. const [count, setCount] = useState(seconds);
  192. useEffect(() => {
  193. const intervalID = setInterval(() => {
  194. setCount(count => count - 1)
  195. }, refresh);
  196. return () => {
  197. clearInterval(intervalID);
  198. }
  199. }, [seconds]);
  200. const RenderProps = render;
  201. return(
  202. <RenderProps seconds={count}/>
  203. );
  204. }
  205. // LCD
  206. const TimerPresentation = ({seconds}) => {
  207. const ISODate = (start, end) => new Date(seconds * 1000).toISOString().slice(start, end);
  208. return(
  209. <div>
  210. <span>{ISODate(11, 13)}</span>:
  211. <span>{ISODate(14, 16)}</span>:
  212. <span>{ISODate(17, 19)}</span>
  213. </div>
  214. )
  215. }
  216. // Watch
  217. const Watch = ({seconds}) => {
  218. const ISODate = (start, end) => new Date(seconds * 1000).toISOString().slice(start, end);
  219. return (
  220. <figure className="clock-face">
  221. <img className="clock-face__bg"
  222. src={ClockFace}
  223. width="400"
  224. height="400"
  225. alt="циферблат"
  226. />
  227. <img className="clock-face__element"
  228. src={ClockFace_H}
  229. width="400"
  230. height="400"
  231. alt="стрелка часовая"
  232. style={{rotate: `${ISODate(11, 13)*30}deg`}}
  233. />
  234. <img className="clock-face__element"
  235. src={ClockFace_M}
  236. width="400"
  237. height="400"
  238. alt="стрелка минутная"
  239. style={{rotate: `${ISODate(14, 16)*6}deg`}}
  240. />
  241. <img className="clock-face__element"
  242. src={ClockFace_S}
  243. width="400"
  244. height="400"
  245. alt="стрелка секундная"
  246. style={{rotate: `${ISODate(17, 19)*6}deg`}}
  247. />
  248. </figure>
  249. )
  250. }
  251. // TimerControl + TimerContainer
  252. const TimerControlContainer = ({render}) => {
  253. const [hoursVal, setHoursVal] = useState(0);
  254. const [minutesVal, setMinutesVal] = useState(0);
  255. const [secondsVal, setSecondsVal] = useState(0);
  256. const [isStart, setStart] = useState(false);
  257. const totalSeconds = (hoursVal * 3600) + (minutesVal * 60) + secondsVal;
  258. return(
  259. <fieldset>
  260. <label>
  261. часы
  262. <input type="text"
  263. name="hours"
  264. value={hoursVal}
  265. onChange={event => setHoursVal(+event.target.value)}
  266. />
  267. </label>
  268. <label>
  269. минуты
  270. <input type="text"
  271. name="minutes"
  272. value={minutesVal}
  273. onChange={event => setMinutesVal(+event.target.value)}
  274. />
  275. </label>
  276. <label>
  277. секунды
  278. <input type="text"
  279. name="seconds"
  280. value={secondsVal}
  281. onChange={event => setSecondsVal(+event.target.value)}
  282. />
  283. </label>
  284. <button type="button"
  285. className="default-button"
  286. onClick={() => setStart(!isStart)}
  287. >Start Timer</button>
  288. {isStart && <TimerContainer seconds={totalSeconds} render={render} refresh={150}/>}
  289. </fieldset>
  290. )
  291. }
  292. const App = () => {
  293. return (
  294. <div className="App">
  295. {/*<section className="episodes">
  296. <Episode />
  297. </section>*/}
  298. <Spoiler header={<h1>Заголовок</h1>} open>
  299. Контент 1
  300. <p>
  301. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi, eaque?
  302. </p>
  303. </Spoiler>
  304. <br/>
  305. <Spoiler>
  306. <h2>Контент 2</h2>
  307. <p>
  308. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam consectetur consequatur corporis dolores, laborum minus repellat sequi! Aliquam, aspernatur, ipsam?
  309. </p>
  310. </Spoiler>
  311. <br/>
  312. <RangeInput min={5} max={20} />
  313. <br/><br/>
  314. <LoginForm onLogin={(login, password) => console.log({login, password})}/>
  315. <br/>
  316. <PasswordConfirm min={2} />
  317. <br/>
  318. <Timer seconds={50}/>
  319. <TimerControl />
  320. <TimerContainer seconds={1800}
  321. refresh={1000}
  322. render={SecondsTimer}
  323. />
  324. <TimerContainer seconds={500}
  325. refresh={500}
  326. render={TimerPresentation}
  327. />
  328. <TimerContainer seconds={30800}
  329. refresh={1000}
  330. render={Watch}
  331. />
  332. <TimerControlContainer render={Watch} />
  333. </div>
  334. )
  335. }
  336. export default App;