HM-79. Добавлена работа формы с ручкой, валидация формы. HM-88. Добавлены пункты меню с js. HM-76. Добавлена подсветка элементов меню (#40)

This commit is contained in:
Nikolay
2020-08-01 17:32:59 +03:00
committed by GitHub
parent 281ec56288
commit 8c1daa5771
18 changed files with 252 additions and 137 deletions

View File

@ -10,6 +10,7 @@ class FormControl extends Component {
placeholder = '',
initValue = '',
className = '',
required = false,
} = {}) {
super('#form-control', parentNode);
@ -19,6 +20,10 @@ class FormControl extends Component {
tagName: this.getInputTagName(type),
options: {
className: `form-control ${className}`,
},
args: {
type: type === FORM_TYPES.PASSWORD ? 'password' : 'text',
...(required ? {required: ''} : {}),
}
});
this.input.placeholder = placeholder;
@ -28,6 +33,10 @@ class FormControl extends Component {
this.label.insertAdjacentElement('afterend', this.input);
this.label.textContent = label;
this.label.setAttribute('for', id);
this.addEventListener(this.input, 'focus', this.clearError);
this.addEventListener(this.input, 'click', this.clearError);
this.addEventListener(this.input, 'keydown', this.clearError);
}
disabled = (value) => {
@ -42,6 +51,14 @@ class FormControl extends Component {
this.input.value = value;
}
setError = (errorMessage) => {
this.errorText.textContent = errorMessage;
}
clearError = () => {
this.errorText.textContent = '';
}
getInputTagName (type) {
switch (type) {
case FORM_TYPES.TEXT:

View File

@ -0,0 +1,73 @@
import Component from '../component';
import Image from '../../img/logo.svg';
import FormControl from '../form-control';
import {FORM_TYPES} from '../../consts';
class LoginForm extends Component {
constructor (parentNode) {
super('#login-page', parentNode);
this.logoBox = this.mainNode.querySelector('.Login__logo-box');
this.form = this.mainNode.querySelector('.Login__form');
this.inputContainer = this.mainNode.querySelector('.Login__inputContainer');
this.submitButton = this.mainNode.querySelector('.Login__submit');
this.checkboxSystem = this.mainNode.querySelector('.form-check-input');
this.logoImage = this.mainNode.querySelector('.Login__logo');
this.logoImage.src = Image;
this.loginControl = new FormControl(this.inputContainer, {
label: 'Логин:',
id: 'login-form-user-login',
placeholder: 'Введите логин',
required: true,
});
this.passwordControl = new FormControl(this.inputContainer, {
label: 'Пароль:',
id: 'login-form-user-password',
placeholder: 'Введите пароль',
type: FORM_TYPES.PASSWORD,
required: true,
});
this.addEventListener(this.form, 'submit', this.submit);
}
disabled = (value) => {
const elements = [
this.loginControl.input,
this.passwordControl.input,
this.submitButton,
this.checkboxSystem,
];
elements.forEach((element) => {
element.disabled = value;
});
}
validateInputs = () => {
this.form.classList.add('was-validated');
const login = this.loginControl.getValue();
const password = this.passwordControl.getValue();
if (!login) {
this.loginControl.setError('Заполните логин');
}
if (!password) {
this.passwordControl.setError('Заполните пароль');
}
return this.form.checkValidity();
}
submit = (event) => {
event.preventDefault();
if (this.validateInputs()) {
this.next('submit', {
login: this.loginControl.getValue(),
password: this.passwordControl.getValue(),
});
}
}
}
export default LoginForm;

View File

@ -0,0 +1,3 @@
import LoginForm from './LoginForm';
export default LoginForm;

View File

@ -1,8 +1,4 @@
.Login__page {
position: fixed;
top: 0;
left: 0;
width: 100%;
.Login__page {
height: 100%;
background-color: white;
display: flex;
@ -40,4 +36,4 @@
.Login__logo-box {
display: flex;
align-items: center;
}
}

View File

@ -0,0 +1,29 @@
import Component from '../component/index';
import './LoginPage.css';
import LoginForm from '../login-form';
import authServiceApi from '../../api/AuthServiceAPI';
import routeService from '../../services/RouteService';
class LoginPage extends Component {
constructor (mainNodeSelector, parentNode) {
super(mainNodeSelector, parentNode);
this.form = new LoginForm(this.mainNode);
this.addSubscribe(this.form, 'submit', ({login, password}) => {
this.form.disabled(true);
authServiceApi.auth(login, password)
.then(() => {
this.form.disabled(false);
routeService.goTo('/');
})
.catch((e) => {
// TODO: Времено используется alert, потом прикрутим систему нотификаций
// eslint-disable-next-line no-alert
alert(e?.response?.data?.message || 'Неизвестная ошибка');
this.form.disabled(false);
});
});
}
}
export default LoginPage;

View File

@ -0,0 +1,3 @@
import LoginPage from './LoginPage';
export default LoginPage;

View File

@ -1,38 +0,0 @@
import Component from '../component/index';
import './LoginPage.css';
import {createElement} from '../../utils/elementUtils';
import Image from '../../img/logo.svg';
class LoginPage extends Component {
constructor () {
super('#login-page', document.body);
this.logoBox = this.mainNode.querySelector('.Login__logo-box');
this.form = this.mainNode.querySelector('.Login__form');
this.submitBtn = this.mainNode.querySelector('.Login__submit');
this.logoImg = createElement({
tagName: 'img',
parentNode: this.logoBox,
options: {
className: 'Login__logo',
},
args: {
src: Image,
alt: 'logo'
}
});
this.addEventListener(this.form, 'submit', (evt) => {
this.send(evt);
});
this.addEventListener(this.submitBtn, 'click', (evt) => {
this.send(evt);
});
}
send = (evt) => {
evt.preventDefault();
}
}
export default LoginPage;

View File

@ -7,6 +7,10 @@
margin: 5px 10px;
}
.nav-item {
cursor: pointer;
}
@media (max-width: 900px){
.Buttons__container {
flex-direction: column;

View File

@ -0,0 +1,89 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import Image from '../../img/logo.svg';
import './MainMenu.css';
import {createElement} from '../../utils/elementUtils';
import {EVENTS} from '../../consts';
export const NAV_MENU = [
{
title: 'Главная',
url: '/'
},
{
title: 'Список хранилищ',
url: '/api'
},
{
title: 'Журнал',
url: '/logs'
},
];
class MainMenu extends Component {
menuItems = [];
constructor () {
super('#main-menu', document.body);
this.buttonsContainer = this.mainNode.querySelector('.navbar-nav');
this.logoImg = document.createElement('img');
this.logoImg.src = Image;
this.logoImg.alt = 'logo';
this.logoImg.className = 'Logo mr-2';
this.logoBox = this.mainNode.querySelector('.Logo__box');
this.logoBox.appendChild(this.logoImg);
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, (route) => {
this.menuItems.forEach(({url, link}) => {
if (route.url === url) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
});
}
render = () => {
this.menuItems = NAV_MENU.map(({url, title}) => {
const li = createElement({
tagName: 'li',
parentNode: this.buttonsContainer,
options: {
className: 'nav-item',
},
});
const link = createElement({
tagName: 'a',
parentNode: li,
options: {
className: 'nav-link',
textContent: title,
},
});
this.addEventListener(li, 'click', () => {
routeService.goTo(url);
});
return {url, link};
});
}
isHide = () => {
return this.mainNode.parentNode;
}
hideMenu = () => {
this.mainNode.remove();
}
showMenu = () => {
document.body.prepend(this.mainNode);
}
}
const navMenuButtons = new MainMenu();
export default navMenuButtons;

View File

@ -0,0 +1,3 @@
import MainMenu from './MainMenu';
export default MainMenu;

View File

@ -1,33 +0,0 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import ButtonComponent from '../button-component/ButtonComponent';
import Image from '../../img/logo.svg';
import './NavButtonComponent.css';
class NavButtonComponent extends Component {
constructor () {
super('#main-menu', document.body);
this.buttonsContainer = this.mainNode.querySelector('.Buttons__container');
this.logoImg = document.createElement('img');
this.logoImg.src = Image;
this.logoImg.alt = 'logo';
this.logoImg.className = 'Logo mr-2';
this.logoBox = this.mainNode.querySelector('.Logo__box');
this.logoBox.appendChild(this.logoImg);
}
render = (menu) => {
menu.forEach((element) => {
this.button = new ButtonComponent(this.buttonsContainer, element.title, 'btn btn-outline-primary NavButton');
this.button.subscribe('click', () => {
routeService.goTo(element.url);
});
});
}
}
const navMenuButtons = new NavButtonComponent();
export default navMenuButtons;

View File

@ -1,18 +0,0 @@
export const NAV_MENU = [
{
title: 'Главная',
url: '/'
},
{
title: 'Список хранилищ',
url: '/api'
},
{
title: 'Журнал',
url: '/logs'
},
{
title: 'Контакты',
url: '/'
},
];

View File

@ -1,3 +0,0 @@
import NavButtonComponent from './NavButtonComponent';
export default NavButtonComponent;

View File

@ -33,7 +33,7 @@ class RouterPagesContainer extends Component {
*/
url;
constructor () {
constructor (mainMenu) {
super('#page-container', document.body);
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, ({url}) => {
@ -42,6 +42,13 @@ class RouterPagesContainer extends Component {
return route.url === url;
}) || {};
// Показывает или прячет меню в зависимости от роут
if (['/login'].includes(url)) {
mainMenu.hideMenu();
} else {
mainMenu.showMenu();
}
// Рендерит новую страницу, если url изменился
if (url !== this.currentUrl) {
// Удаляет предыдущую страницу