App.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. import logo from './logo.svg';
  2. import './App.css';
  3. import React, {useEffect, useState, useRef} from 'react';
  4. const MyDiv = () => {
  5. return (
  6. <div>
  7. Привет-привет!
  8. <input />
  9. </div>
  10. )
  11. }
  12. const _ = React.createElement
  13. const MyDivNoJSX = () => {
  14. return _('div', {},
  15. "Привет-привет!",
  16. _('input'))
  17. }
  18. const MyDivNoJSX2 = () => {
  19. return _('div', {children: [ "Привет-привет!", _('input')]})
  20. }
  21. console.log(<div id="id1" style={{color: 'red'}}>
  22. потомки
  23. <input/>
  24. </div>)
  25. console.log(<div id="id1" style={{color: 'red'}} children={[
  26. "потомки",<input/>]} /> )
  27. console.log(<MyDiv />)
  28. const TwoDivs = () =>
  29. <div>
  30. <MyDiv />
  31. <MyDiv />
  32. </div>
  33. const Add = ({a=100,b=1000}) =>
  34. <div>
  35. a + b = {a} + {b} = {+a + +b}
  36. </div>
  37. const BlockOfTextWithHeader = ({children,title='No Title'}) =>
  38. <>
  39. <h2>{title}</h2>
  40. <p>
  41. {children}
  42. </p>
  43. </>
  44. const arr = ["Див 1", "Див 2"]
  45. const data = [
  46. {children: '1111',title: 'адын-адын'},
  47. {children: '2222',title: 'два-два'},
  48. {children: '3333',title: 'тры-тры'},
  49. ]
  50. const Counter = () => {
  51. const [count, setCount] = useState(10)
  52. console.log(count)
  53. return (<button onClick={() => setCount(count +1)}>
  54. {count}
  55. </button>)
  56. }
  57. const TextLength = ({text}) => <h1>{text.length}</h1>
  58. const Input = () => {
  59. const [text, setText] = useState('testtest')
  60. return (
  61. <div>
  62. <input value={text}
  63. onChange={e => setText(e.target.value.toUpperCase())}/>
  64. <TextLength text={text} />
  65. </div>
  66. )
  67. }
  68. const LoginForm = ({onLogin}) => {
  69. //тут надо два состояния - для логина и для пароля;
  70. const [stateLogin, setStateLogin] = useState("")
  71. const [statePass, setStatePass] = useState("")
  72. //кнопка логина должна быть disabled если одно из полей пустое
  73. const disabled = stateLogin && statePass ? false : true
  74. //по клику на кнопку запустить onLogin и передать туда
  75. //текущее состояние login и password
  76. return (
  77. //<h1>тут вместо h1 должна быть верстка: два инпута и кнопка login</h1>
  78. <div>
  79. <input
  80. value={stateLogin}
  81. onChange={e => setStateLogin(e.target.value)}
  82. />
  83. <input
  84. value={statePass}
  85. onChange={e => setStatePass(e.target.value)}
  86. />
  87. <button
  88. disabled={disabled}
  89. onClick = {() => onLogin(stateLogin, statePass)}
  90. >
  91. Login</button>
  92. </div>
  93. )
  94. }
  95. /*Spoiler
  96. Реализуйте компонент Spoiler, скрывающий контент и открывающий его по клику. Компонент будет получать 3 пропс:
  97. header, который будет выводится всегда
  98. open, может быть true или false, если написать в JSX без значения, это значит open={true}
  99. вложенный контент, т. е. children, который отображается в открытом состоянии спойлера и не отображается в закрытом
  100. Изначально компонент имеет состояние переданное через пропс open По клику на <div> в котором будет отображаться header должно меняться состояние на противоположное Обеспечьте условие, которое будет показывать или нет children.*/
  101. const Spoiler = ({ header = "+", open = true, children }) => {
  102. //напишите тут код
  103. const [getOpen, setOpen] = useState(open)
  104. return (
  105. <>
  106. <div onClick={() => {
  107. setOpen(!getOpen)
  108. console.log(getOpen)
  109. }}>
  110. {header}
  111. </div>
  112. {getOpen && children}
  113. </>
  114. )
  115. }
  116. class RangeInput extends React.Component {
  117. constructor(props) {
  118. super(props);
  119. this.min = props.min
  120. this.max = props.max
  121. this.state = {
  122. value: ""
  123. };
  124. }
  125. changeValue(e){
  126. this.setState({value: +e.target.value})
  127. }
  128. render() {
  129. if (this.state.value !== "" && this.state.value < this.min || this.state.value > this.max) {
  130. return <input value={this.state.value} onChange={ e => this.changeValue(e)} className= 'red' type='number'/>
  131. }
  132. return <input value={this.state.value} onChange={ e => this.changeValue(e)} type='number'/>
  133. }
  134. }
  135. /*PasswordConfirm
  136. Реализовать компонент PasswordConfirm, отображающий два <input type='password'/> со следующими возможностями:
  137. prop min - минимальная длина пароля
  138. Используйте компонент-класс и setState для отслеживания и валидации совпадения паролей и проверки на длину.Или useState
  139. По желанию добавьте более хитрые валидации типа проверки на размеры буков и наличие цифр в пароле.*/
  140. class PasswordConfirm extends React.Component {
  141. constructor(props){
  142. super(props)
  143. this.min = props.min
  144. this.state = {
  145. inputFirstValue: "",
  146. inpitSecondValue: ""
  147. }
  148. }
  149. changeValue1(e){
  150. this.setState({inputFirstValue: e.target.value})
  151. }
  152. changeValue2(e){
  153. this.setState({inpitSecondValue: e.target.value})
  154. }
  155. render() {
  156. return (
  157. <div>
  158. {this.state.inputFirstValue === "" ? <input value={this.state.inputFirstValue} onChange={e => this.changeValue1(e)} ></input>
  159. : this.state.inputFirstValue.length < this.min ? <input value={this.state.inputFirstValue} onChange={e => this.changeValue1(e)} className="red"></input>
  160. : this.state.inputFirstValue === this.state.inpitSecondValue ? <input value={this.state.inputFirstValue} onChange={e => this.changeValue1(e)} className="green"></input>
  161. : <input value={this.state.inputFirstValue} onChange={e => this.changeValue1(e)} ></input>}
  162. {this.state.inpitSecondValue === "" ? <input value={this.state.inpitSecondValue} onChange={e => this.changeValue2(e)}></input>
  163. : this.state.inpitSecondValue.length < this.min ? <input value={this.state.inpitSecondValue} onChange={e => this.changeValue2(e)} className="red"></input>
  164. : this.state.inputFirstValue === this.state.inpitSecondValue ? <input value={this.state.inpitSecondValue} onChange={e => this.changeValue2(e)} className="green"></input>
  165. : <input value={this.state.inpitSecondValue} onChange={e => this.changeValue2(e)}></input>}
  166. </div>
  167. )
  168. }
  169. }
  170. /*Timer
  171. Напишите компонент, в который передается через props количество секунд, а компонент при этом реализует обратный отсчет раз в секунду уменьшая количество секунд на 1. Останавливается на 0. Добавьте в компонент кнопку паузы.
  172. Компонент должен отображать часы, минуты и секунды.*/
  173. function Timer({pauseState = false, sec}){
  174. const [pause, setPause] = useState(pauseState)
  175. const [time, setTime] = useState(sec)
  176. let timeout
  177. useEffect(() => {
  178. if (time > 0 && pause === false) {
  179. timeout = setTimeout(() => {
  180. console.log(time)
  181. setTime(time - 1000)
  182. }, 1000)
  183. }
  184. })
  185. return (
  186. <div>
  187. <div>{time/1000}</div>
  188. <button onClick={()=>{clearTimeout(timeout); setPause(!pause)}}>пауза</button>
  189. </div>
  190. )
  191. }
  192. /*TimerControl
  193. Напишите компонент, с тремя полями ввода (часы, минуты и секунды) и кнопкой Start, по которой будет стартовать компонент Timer*/
  194. function Timer2({sec}){
  195. const [pause, setPause] = useState(false)
  196. const [time, setTime] = useState(sec)
  197. console.log('Timer2' , time)
  198. useEffect(()=>setTime(sec), [sec])
  199. const timeoutRef = useRef()
  200. useEffect(() => {
  201. if (time.value > 0 && pause === false) {
  202. timeoutRef.current = setTimeout(() => {
  203. console.log(time.value)
  204. setTime({value: time.value - 1000})
  205. }, 1000)
  206. console.log('timeout', timeoutRef.current)
  207. }
  208. }, [time, pause])
  209. let pauseButtonText = pause ? "продолжить отсчёт" : "пауза"
  210. let hours = Math.floor(time.value / (60 * 60 * 1000))
  211. let minutes = Math.floor((time.value - (hours*60*60*1000))/(60*1000))
  212. let seconds = Math.floor((time.value - (hours*60*60*1000) - (minutes*60*1000))/1000)
  213. return (
  214. <div>
  215. <span>{hours}:</span><span>{minutes}:</span><span>{seconds}</span>
  216. <button onClick={()=>{console.log('pause', timeoutRef.current); clearTimeout(timeoutRef.current); setPause(!pause)}}>{pauseButtonText}</button>
  217. </div>
  218. )
  219. }
  220. function TimerControl({f}) {
  221. const [hours, setHours] = useState(0)
  222. const [minutes, setMinutes] = useState(0)
  223. const [seconds, setSeconds] = useState(0)
  224. const setTimer = () => {
  225. let value = seconds * 1000 + minutes * 60 * 1000 + hours * 60 * 60 * 1000
  226. console.log(value)
  227. console.log(f)
  228. f({value})
  229. }
  230. return(
  231. <div>
  232. <span>Hours:</span>
  233. <input type="number" min={0} max={24} value={hours} onChange={(e)=>setHours(e.target.value)}/>
  234. <span>Minutes:</span>
  235. <input type="number" min={0} max={60} value={minutes} onChange={(e)=>setMinutes(e.target.value)}/>
  236. <span>Seconds:</span>
  237. <input type="number" min={0} max={60} value={seconds} onChange={(e)=>setSeconds(e.target.value)}/>
  238. <button onClick={setTimer}>START TIMER</button>
  239. </div>
  240. )
  241. }
  242. /*TimerContainer
  243. const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
  244. SecondsTimer в данном случае играет роль presentation компонента, который сам ничего не умеет делать, а просто является шаблоном для отображения своих props в удобном для пользователя виде.
  245. Реализуйте контейнерный компонент, который будет обеспечивать состояние и логику для любого таймера:
  246. <TimerContainer seconds={1800} refresh={100} render={SecondsTimer}/>
  247. TimerContainer должен:
  248. воспринимать три пропса:
  249. seconds - секунды для обратного отсчета
  250. refresh - периодичность обновления таймера в миллисекундах
  251. render - компонент для отрисовки, которому передается текущее время
  252. Время вычисляется не по количеству обновлений, а по разности между стартовым и текущим моментом. Иначе таймер будет очень неточен
  253. так как JSX понимает переменные с маленькой буквы не как компоненты-функции, а как тэги HTML, переприсвойте props render в переменную с большой буквы и используйте её в JSX, как имя компонента, передавая пропс seconds.
  254. Так как при любом обновлении состояния функция-компонент, как и любая другая функция, запускается целиком используйте setInterval в useEffect*/
  255. const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
  256. function TimerContainer({seconds, refresh, render}) {
  257. const [timeStart, setTimeStart] = useState((new Date()).getTime())
  258. const [timeNow, setTimeNow] = useState((new Date()).getTime())
  259. const [timerIsActiv, settimerActiv] = useState(true)
  260. let time = (seconds - (timeNow - timeStart))/1000
  261. console.log('time' , time)
  262. if (time<0)time=0
  263. useEffect(
  264. ()=>{
  265. if (time <= 0){
  266. console.log("time < 0", timeNow)
  267. settimerActiv(false)
  268. setTimeNow(seconds + timeStart)
  269. }
  270. }, [timeNow])
  271. useEffect(
  272. ()=>{
  273. if(timerIsActiv) {
  274. const intervalId = setInterval(()=>{
  275. console.log(timeNow)
  276. setTimeNow((new Date()).getTime())
  277. }
  278. , refresh)
  279. return ()=>clearInterval(intervalId)
  280. }
  281. }, [timerIsActiv])
  282. const Render = render
  283. return (
  284. <Render seconds={time} timerIsActiv={timerIsActiv} settimerActiv={settimerActiv}/>
  285. )}
  286. /*LCD
  287. Сделайте из компонента Timer presentation компонент без state, прикрутите его к TimerContainer*/
  288. function TimerPresentation({seconds, timerIsActiv = false, settimerActiv}){
  289. return (
  290. <div>
  291. <div>{seconds}</div>
  292. <button onClick={()=>{settimerActiv(!timerIsActiv)}}>пауза</button>
  293. </div>
  294. )
  295. }
  296. /*Watch
  297. Реализуйте часы со стрелками в качестве presentation компонента:
  298. квадратный блок-контейнер
  299. стрелки и, возможно, цифры позиционируются с помощью transform: rotate(УГОЛdeg)
  300. В верстке используйте position absolute для накладывания блоков стрелок и цифр друг на друга (это даст общий центр вращения)
  301. для корректного центра вращения блок со стрелкой или цифрой должен быть шириной с родительский квадратный блок
  302. есть еще всякий css (text-orientation) для вращения цифр внутри повернутого блока*/
  303. function Watch({seconds}) {
  304. let hours = seconds / 3600
  305. console.log('hours' , hours)
  306. let hDeg = hours * 30
  307. console.log('hDeg' , hDeg)
  308. let minutes = (seconds % 3600) / 60
  309. console.log('minutes' , minutes)
  310. let mDeg = minutes * 6
  311. let sec = seconds % 60
  312. let sDeg = sec * 6
  313. return (
  314. <div className='container'>
  315. <img src='./images/ClockFace_H.png' style={{ transform: `rotate(${hDeg}deg)` }} />
  316. <img src='./images/ClockFace_M.png' style={{ transform: `rotate(${mDeg}deg)` }} />
  317. <img src='./images/ClockFace_S.png' style={{ transform: `rotate(${sDeg}deg)` }} />
  318. </div>
  319. )
  320. }
  321. function App() {
  322. const[timer, setTimer] = useState({value: 0})
  323. console.log('app timer state',timer)
  324. const fCallbackTimer = useRef((value) => setTimer(value))
  325. console.log("проверка" , fCallbackTimer.current)
  326. return (
  327. <div className="App">
  328. <header className="App-header" >
  329. {Math.random() > 0.5 && <div children={`
  330. много
  331. много
  332. строк`} />}
  333. <MyDiv />
  334. <MyDivNoJSX />
  335. </header>
  336. <Spoiler header={<h1>Заголовок</h1>} open>
  337. Контент 1
  338. <p>
  339. лорем ипсум траливали и тп.
  340. </p>
  341. </Spoiler>
  342. <Spoiler>
  343. <h2>Контент 2</h2>
  344. <p>
  345. лорем ипсум траливали и тп.
  346. </p>
  347. </Spoiler>
  348. <RangeInput min={2} max={10} />
  349. <LoginForm onLogin={(l,p) => console.log('LOGIN: ', l, ' PASSWORD: ', p)} />
  350. <PasswordConfirm min = {5}/>
  351. <Timer sec={50000}/>
  352. <Timer2 sec={timer}/>
  353. <TimerControl f={fCallbackTimer.current}/>
  354. <TimerContainer seconds={1800} refresh={100} render={SecondsTimer}/>
  355. <TimerContainer seconds={18000} refresh={1000} render={TimerPresentation}/>
  356. <TimerContainer seconds={3600000*5} refresh={1000} render={Watch}/>
  357. </div>
  358. );
  359. }
  360. export default App;