App.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import React, {useState, useEffect, useRef} from 'react'
  2. import './App.scss';
  3. import clockFace from './img/ClockFace.png';
  4. import clockH from './img/ClockFace_H.png';
  5. import clockM from './img/ClockFace_M.png';
  6. import clockS from './img/ClockFace_S.png';
  7. const Spoiler = ({header="+", open=true, children}) => {
  8. const [isOpen, setOpen] = useState(open)
  9. return (
  10. <div>
  11. <div onClick = {e => setOpen(!isOpen)}>{header}</div>
  12. {isOpen && children}
  13. </div>
  14. )
  15. }
  16. const RangeInput = ({min, max}) => {
  17. const [text, setText] = useState('')
  18. return (
  19. <input style={ {backgroundColor: (text.length >= min && text.length <= max) ? '#fff' : '#f00'} }
  20. value={text} onChange={(e) => setText(e.target.value)}/>
  21. )
  22. }
  23. const PasswordConfirm = ({min, char=false, bigChar=false, number=false}) => {
  24. const [pass, setPass] = useState('')
  25. const [pass2, setPass2] = useState('')
  26. const checkPass = (password) => {
  27. if ( password.length >= min &&
  28. (char ? password.match(/[a-zA-ZА-ЯЁа-яё]/) : true) &&
  29. (bigChar ? password.match(/[A-ZА-ЯЁ]/) : true) &&
  30. (number ? password.match(/[\d]/) : true) ) {
  31. return true
  32. } else {
  33. return false
  34. }
  35. }
  36. const printPassReq = (password) => {
  37. let str = ''
  38. if (!checkPass(password)) {
  39. str += 'Пароль должен содержать: '
  40. } else {
  41. str += 'Пароль подходит '
  42. }
  43. if (password.length < min) {
  44. str += ` минимум ${min} символ${((min === 1) ? '' : (min > 1 && min < 5) ? 'а' : 'ов')},`
  45. }
  46. if (!(char ? password.match(/[a-zA-ZА-ЯЁа-яё]/) : true)) {
  47. str += ' буквы,'
  48. }
  49. if (!(bigChar ? password.match(/[A-ZА-ЯЁ]/) : true)) {
  50. str += ' прописные буквы,'
  51. }
  52. if (!(number ? password.match(/[\d]/) : true)) {
  53. str += ' цифры,'
  54. }
  55. return str.slice(0, -1)
  56. }
  57. return (
  58. <div>
  59. <p>
  60. <input type='password' className="p1" value={pass} onChange={(e) => setPass(e.target.value)}
  61. style={ {backgroundColor: ( checkPass(pass) && pass === pass2) ? '#fff' : '#f00'} }/>
  62. <label>
  63. {printPassReq(pass)}
  64. </label>
  65. </p>
  66. <p>
  67. <input type='password' className="p2" value={pass2} onChange={(e) => setPass2(e.target.value)}
  68. style={ {backgroundColor: ( checkPass(pass2) && pass === pass2) ? '#fff' : '#f00'} }/>
  69. <label>
  70. {printPassReq(pass2)}
  71. </label>
  72. </p>
  73. <div>{(pass === pass2 ? '' : 'пароли не совпадают')}</div>
  74. </div>
  75. )
  76. }
  77. const Timer = ({seconds}) => {
  78. const [counter, setCounter] = useState(seconds.s)
  79. const [paused, setPause] = useState(false)
  80. useEffect(() => {
  81. setCounter(seconds.s)
  82. }, [seconds])
  83. useEffect(() => {
  84. const interval = setInterval(() => {
  85. if (counter > 0 && !paused) {
  86. setCounter(counter => counter - 1)
  87. } else {
  88. clearInterval(interval)
  89. }
  90. }, 1000);
  91. return () => {
  92. clearInterval(interval)
  93. }
  94. }, [paused, counter])
  95. let h = Math.floor(counter / 3600)
  96. let m = Math.floor(counter % 3600 / 60)
  97. let s = counter % 60
  98. return (
  99. <div>
  100. <h2>{h >= 10 ? h : '0'+h}:{m >= 10 ? m : '0'+m}:{s >= 10 ? s : '0'+s}</h2>
  101. <button onClick={() => setPause(!paused)}>{paused ? 'Го' : 'Пауза'}</button>
  102. </div>
  103. )
  104. }
  105. const TimerControl = ({setSeconds}) => {
  106. const [s, setS] = useState(0)
  107. const [m, setM] = useState(0)
  108. const [h, setH] = useState(0)
  109. return (
  110. <div>
  111. <input value={h} min="0" max="none" type="number" onChange={(e) => setH(e.target.value)}/>
  112. <input value={m} min="0" max="60" type="number" onChange={(e) => setM(e.target.value)}/>
  113. <input value={s} min="0" max="60" type="number" onChange={(e) => setS(e.target.value)}/>
  114. <button onClick={() => setSeconds({s: +h*3600 + +m*60 + +s}) }>Start</button>
  115. </div>
  116. )
  117. }
  118. const SecondsTimer = ({seconds}) => (
  119. <h2>{parseInt(seconds)}</h2>
  120. )
  121. const TimerContainer = ({seconds={s:100}, refresh=100, render}) => {
  122. const [paused, setPause] = useState(false)
  123. const [time, setTime] = useState(seconds.s)
  124. const t0 = useRef(performance.now())
  125. const pausedAt = useRef(0)
  126. useEffect(() => {
  127. t0.current = performance.now()
  128. setTime(seconds.s)
  129. console.log('mount1')
  130. }, [seconds])
  131. useEffect(() => {
  132. console.log('mount2')
  133. let interval
  134. if (paused) {
  135. pausedAt.current = performance.now()
  136. clearInterval(interval)
  137. } else {
  138. // console.log('pause-', pausedAt.current, performance.now())
  139. if (pausedAt.current !== 0) {
  140. t0.current += (performance.now() - pausedAt.current)
  141. }
  142. interval = setInterval(() => {
  143. let t1 = performance.now()
  144. let delta = (t1-t0.current)/1000
  145. if (seconds.s >= delta) {
  146. // console.log('timeset', time, seconds, delta)
  147. setTime(seconds.s - delta)
  148. } else {
  149. console.log('0')
  150. setTime(0)
  151. clearInterval(interval)
  152. }
  153. }, refresh)
  154. }
  155. return () => {
  156. console.log('unMount')
  157. pausedAt.current = 0
  158. setTime(0)
  159. clearInterval(interval)
  160. }
  161. }, [paused, seconds, refresh])
  162. const DisplayEl = render
  163. return (
  164. <>
  165. <DisplayEl seconds={time} setPause={() => setPause(!paused)} paused={paused}/>
  166. </>
  167. )
  168. }
  169. const LCD = ({seconds, setPause, paused}) => {
  170. let h = (Math.floor(seconds / 3600))
  171. let m = (Math.floor(seconds % 3600 / 60))
  172. let s = parseInt(seconds % 60)
  173. return (
  174. <div>
  175. <h2>{h >= 10 ? h : '0'+h}:{m >= 10 ? m : '0'+m}:{s >= 10 ? s : '0'+s}</h2>
  176. <button onClick={setPause}>{paused ? 'Го' : 'Пауза'}</button>
  177. </div>
  178. )
  179. }
  180. const Watch = ({seconds, setPause, paused}) => {
  181. let hDeg = Math.floor(seconds / 720) * 6
  182. let mDeg = Math.floor(seconds % 3600 / 60) * 6
  183. let sDeg = parseInt(seconds % 60) * 6
  184. return (
  185. <div className="staticCont">
  186. <div className="watchContainer">
  187. <img className="watch" src={clockFace} />
  188. <img className="hours" style={{transform: `rotate(${hDeg}deg)`}} src={clockH} />
  189. <img className="minutes" style={{transform: `rotate(${mDeg}deg)`}} src={clockM} />
  190. <img className="seconds" style={{transform: `rotate(${sDeg}deg)`}} src={clockS} />
  191. </div>
  192. <button className="watchBtn" onClick={setPause}>{paused ? 'Го' : 'Пауза'}</button>
  193. </div>
  194. )
  195. }
  196. const Control = () => {
  197. const [seconds, setSeconds] = useState({s: 200})
  198. return (
  199. <div className="controlContainerComb">
  200. <Timer seconds={seconds} />
  201. <TimerControl setSeconds={setSeconds}/>
  202. </div>
  203. )
  204. }
  205. const ControlContainer = () => {
  206. const [secondsCont, setSecondsCont] = useState({s: 200})
  207. return (
  208. <div className="controlContainerComb">
  209. <TimerContainer seconds={secondsCont} refresh={500} render={LCD}/>
  210. <TimerControl setSeconds={setSecondsCont}/>
  211. </div>
  212. )
  213. }
  214. function App() {
  215. return (
  216. <>
  217. <Spoiler header={<h1>Заголовок</h1>} open={false}>
  218. Контент 1
  219. <p>
  220. лорем ипсум траливали и тп.
  221. </p>
  222. </Spoiler>
  223. <RangeInput min={2} max={10} />
  224. <PasswordConfirm min={3} char={true} bigChar={true} number={true}/>
  225. <Control />
  226. <TimerContainer seconds={{s:10}} refresh={2000} render={SecondsTimer}/>
  227. <TimerContainer seconds={{s:65}} refresh={100} render={LCD}/>
  228. <TimerContainer seconds={{s:3660}} refresh={1} render={Watch}/>
  229. <ControlContainer />
  230. </>
  231. );
  232. }
  233. export default App;