-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Курганов Артемий #36
base: master
Are you sure you want to change the base?
Курганов Артемий #36
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
import { Button, Gapped, Input, Modal, Select } from '@skbkontur/react-ui'; | ||
import React, { useState } from 'react'; | ||
import ReactDom from 'react-dom'; | ||
import './style.css'; | ||
|
||
/** | ||
|
@@ -56,4 +59,148 @@ import './style.css'; | |
* Придумай, как избежать излишнего дублирования. | ||
*/ | ||
|
||
console.log('Hi from script!'); | ||
// Выполнены задания 1-8 | ||
|
||
const fieldRepo : {[key: string] : string}= { | ||
'Имя': '', | ||
'Фамилия': '', | ||
'Город': '', | ||
} | ||
|
||
const Form = () => { | ||
let [isOpened, setOpenedState] = useState(false); | ||
|
||
let [name, setName] = useState(''); | ||
let [surname, setSurname] = useState(''); | ||
let [city, setCity] = useState(''); | ||
|
||
let [nameEmpty, setNameEmpty] = useState(false); | ||
let [surnameEmpty, setSurnameEmpty] = useState(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Эти штуки лучше объявить через const, линтер в моей IDE даже ругается на это) Они же действительно никогда не меняются в обычном джаваскриптовом смысле — если они меняются, то это значит, что компонент ререндерится == функция Form выполняется заново, и это уже новые объекты в памяти, а не старые |
||
|
||
const openModal = () => {setOpenedState(true)}; | ||
const closeModal = () => {setOpenedState(false)}; | ||
|
||
let [changeMessage, setChangeMessage] = useState(Array<string>()); | ||
const saveChanges = () => { | ||
if (!name) | ||
setNameEmpty(true); | ||
if (!surname) | ||
setSurnameEmpty(true); | ||
|
||
if (!(name && surname)) | ||
return; | ||
|
||
const newChangeMessage = [ | ||
add_if_updated('Имя', name), | ||
add_if_updated('Фамилия', surname), | ||
add_if_updated('Город', city), | ||
]; | ||
|
||
setChangeMessage(newChangeMessage); | ||
openModal(); | ||
}; | ||
|
||
const add_if_updated = (field_name: string, new_value: string) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Какой-то python_case проскочил, атата)) |
||
const old_value = fieldRepo[field_name]; | ||
fieldRepo[field_name] = new_value; | ||
if (old_value && old_value !== new_value){ | ||
return `${field_name}: Было ${old_value}, стало ${new_value}`; | ||
} | ||
return '' | ||
}; | ||
|
||
return ( | ||
<div className='form'> | ||
<Gapped vertical gap={20}> | ||
<p className="form-title">Информация о пользователе</p> | ||
<InputRow | ||
title='Имя' | ||
placeholder='Введите имя пользователя' | ||
onInputChange={value => { setName(value); setNameEmpty(false); }} | ||
isError={nameEmpty} | ||
/> | ||
|
||
<InputRow | ||
title='Фамилия' | ||
placeholder='Введите фамилию пользователя' | ||
onInputChange={value => { setSurname(value); setSurnameEmpty(false); }} | ||
isError={surnameEmpty} | ||
/> | ||
|
||
<SelectRow | ||
title='Город' | ||
onInputChange={value => {setCity(value) }} | ||
/> | ||
<Button use="primary" onClick={saveChanges}>Сохранить</Button> | ||
</Gapped> | ||
{isOpened && <ModalOnSave close={closeModal} changeMessage={changeMessage}/>} | ||
</div> | ||
); | ||
} | ||
|
||
const ModalOnSave = ({close, changeMessage} : ModeOnSaveType) => ( | ||
<Modal onClose={close}> | ||
<Modal.Header>Пользователь сохранен</Modal.Header> | ||
<Modal.Body> | ||
{ changeMessage.map(data => <p>{data}</p>) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Во время рендера модалки в консоли ошибки, что у элементов нет свойства key. Лучше всегда его указывать, когда вот так через map рендеришь компоненты |
||
</Modal.Body> | ||
<Modal.Footer> | ||
<Button onClick={close}>Закрыть</Button> | ||
</Modal.Footer> | ||
</Modal> | ||
) | ||
|
||
const InputRow = ({title, placeholder='', onInputChange, isError} : RowType) => { | ||
return ( | ||
<div className='inputRow'> | ||
<Gapped gap={20}> | ||
<p className='title'>{title}</p> | ||
<div> | ||
<Input placeholder={placeholder} onChange={(e) => onInputChange(e.target.value)}></Input> | ||
{isError && <p className='error-message'>Заполните поле</p>} | ||
</div> | ||
</Gapped> | ||
|
||
</div> | ||
) | ||
} | ||
|
||
const SelectRow = ({title, onInputChange} : SelectType) => { | ||
const cities = ['Москва', 'Санкт-Петербург', 'Екатеринбург']; | ||
return ( | ||
<div className='inputRow'> | ||
<Gapped gap={20}> | ||
<p className='title'>{title}</p> | ||
{/* Почему-то здесь ругается IDE, хотя по документации должно быть всё ок. Код запускается и работает*/} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это потому, что в селект можно передать items самых разных типов, и onValueChange работает как раз с тем же типом, что и у items. Да, ты передаёшь массив строк, но тайпскрипт всё равно не справляется с автовыводом типов, и ему надо помочь, явно его указав, вот так: <Select items={cities} ... /> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
<Select items={cities} placeholder='Выберете город' onValueChange={(value: string) => { onInputChange(value)}}></Select> | ||
</Gapped> | ||
</div> | ||
) | ||
} | ||
|
||
type InputType = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Типам, которые описывают параметры компонентов, принято добавлять в конец Props. То есть это должен быть не |
||
title: string, | ||
onInputChange: (value: string) => void, | ||
} | ||
|
||
interface RowType extends InputType { | ||
placeholder?: string, | ||
isError: boolean, | ||
}; | ||
|
||
interface SelectType extends InputType { | ||
title: string, | ||
}; | ||
|
||
type ModeOnSaveType = { | ||
close: () => void, | ||
changeMessage: string[], | ||
}; | ||
|
||
|
||
ReactDom.render( | ||
<Form></Form>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если у компонента или элемента нет детей, то можно сократить, и написать вот так: |
||
document.getElementById('someId') | ||
); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,22 @@ | ||
p { | ||
margin: 0 0; | ||
} | ||
|
||
body { | ||
font-family: "Segoe UI", Helvetica, sans-serif; | ||
} | ||
|
||
.form .inputRow .error-message { | ||
color: red; | ||
font-size: 10px; | ||
font-style: italic; | ||
} | ||
|
||
.form .form-title { | ||
font-size: 28px; | ||
font-weight: bold; | ||
} | ||
|
||
.form .inputRow .title{ | ||
width: 100px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Неплохой, конечно, айдишник, но если бы это был код в реальном проекте, я бы докопалась) Хотя бы универсальным
root
его назвать, или что-то более конкретное, типаformPage