Browse Source

ДЗ: Functional OOP done

Vladimir 2 years ago
parent
commit
2a443915dc
3 changed files with 285 additions and 0 deletions
  1. 15 0
      HW 10/index.html
  2. 254 0
      HW 10/main.js
  3. 16 0
      HW 10/style.css

+ 15 - 0
HW 10/index.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="style.css">
+    <title>Document</title>
+</head>
+<body>
+    <div class="wrapper"></div>
+    <div class="form-container"></div>
+    <script src="main.js"></script>
+</body>
+</html>

+ 254 - 0
HW 10/main.js

@@ -0,0 +1,254 @@
+//ДЗ: Functional OOP
+//Password
+//LoginForm
+//LoginForm Constructor
+//Password Verify
+let wrapper = document.querySelector(".wrapper");     // Сюда буду создавать поле ввода для пароля
+
+function Password(parent, open){                      // Функция конструктор
+    let container = document.createElement("div");    // Создаю контейнер для инпутов
+    let button    = document.createElement("button"); // Создание кнопки
+        button.disabled = true;                       // Отключаю кнопку т.к. при создании формы инпут логина по умолчанию пустой
+    let openCheck = open;                             // Переменная с которой я буду сравнивать открытость текста в поле ввода
+
+    let buildInputs = function(type, innerText) {     // Функция для создания одних и тех же инпутов с разными типами
+        let input = document.createElement("input");
+        let label = document.createElement("label");
+
+        input.type = type;
+        label.innerText = innerText;
+        label.appendChild(input);
+
+        return label;
+    }
+
+    let toggleHiddenPassword = function(type, open) { // Функция для переключения состояния инпута с паролем с видимого на невидимый и переключение чекбокса
+        inputPassword.type = type;
+        inputCheckbox.checked = open;
+    };
+
+    let toggleHiddenCheckPassword = function(check) { // Функция для скрытия повтора пароля если Password в открытом режиме
+        if(check) {
+            container.removeChild(checkPasswordLabel);
+        } else {
+            container.insertBefore(checkPasswordLabel, container.children[2]);
+        }
+    };
+
+    let verifyPassword = function(additional = true) { // Проверка на наличие символов в логине и пароле, если в одном из полей пусто то кнопка disabled
+        if(inputLogin.value.length > 0 && inputPassword.value.length > 0 && additional) {
+            button.disabled = false;
+        } else {
+            button.disabled = true;
+        }
+    }
+
+    container.appendChild(buildInputs("login", "Login")); // Собираю форму ввода
+    container.appendChild(buildInputs("password", "Password"));
+    container.appendChild(buildInputs("password", "Повторите пароль")).classList.add("check-password"); // Тут добавил класс что бы различать два инпута, type = password, друг от друга
+    container.appendChild(buildInputs("checkbox", "Показать пароль"));
+    container.appendChild(button).innerText = "Отправить";
+    parent.appendChild(container);
+
+    let inputLogin         = container.querySelector("input[type='login']"); // Нахожу все инпуты и присваиваю их в переменные для дальнейшей работы с ними
+    let inputPassword      = container.querySelector("input[type='password']");
+    let inputCheckPassword = container.querySelector(".check-password input");
+    let checkPasswordLabel = inputCheckPassword.parentElement;
+    let inputCheckbox      = container.querySelector("input[type='checkbox']");
+
+    open == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false); // Один раз запускаю функцию toggleHiddenPassword для корректной отрисовки инпутов в зависимости от переданных параметров в конструкторе
+    if(open == true) {   // Скрываю inputCheckPassword если конструктор вызвали с параметром open = true, то есть когда Password в открытом режиме 
+        container.removeChild(container.querySelector(".check-password"));
+    }
+
+    this.setValue = function(value) {
+        inputPassword.value = value;
+    };
+
+    this.getValue = function() {
+        return inputPassword.value;
+    };
+
+    this.setOpen = function(hidden) {
+        hidden == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false);
+        openCheck = hidden;
+        toggleHiddenCheckPassword(hidden);
+    };
+
+    this.getOpen = function() {
+        return openCheck;
+    };
+
+    container.oninput = (evt) => {                                         // Навешиваю слушатель события на весь контейнер, а не на каждый инпут в отдельности
+        if(evt.target.type == "text" || evt.target.type == "password") {   // Тут идет проверка что изменения происходят на инпутах типа text или password для передачи value в onChange
+            this.onChange(evt.target.value);
+        } else {                                                           // Если эти изменения в инпуте с другим типом (Остается только чекбокс) то я запускаю setOpen с противоположным значением openCheck, как бы переключаю значение на противоположное
+            this.setOpen(!openCheck);
+            this.onOpenChange(openCheck);
+        }
+
+        if(openCheck == true) {   // Тут идет проверка, если Password в открытом режиме то мне не нужно проверять на совпадение value у inputPassword и inputCheckPassword
+            verifyPassword();
+        } else {
+            verifyPassword(inputPassword.value == inputCheckPassword.value);
+        }
+    };
+};
+
+let p = new Password(wrapper, true);
+
+p.onChange = data => console.log(data);
+p.onOpenChange = open => console.log(open);
+
+p.setValue('qwerty');
+console.log(p.getValue());
+
+p.setOpen(false);
+console.log(p.getOpen());
+
+
+//Form
+//Отображение
+//Sync
+//Validators
+//Many Inputs
+//Validators messages
+//OkButton
+//CancelButton
+let formContainer = document.querySelector(".form-container"); // Контейнер для формы
+
+function Form(el, data, okCallback, cancelCallback){
+    let formBody = document.createElement('div')
+    let okButton = document.createElement('button')
+    let table    = document.createElement("table"); // Создаю таблицу, в ней буду отрисовывать key и value из data
+    let copyData = data;                            // Копия объекта data для возможности перейти к дефолтным значениям
+    okButton.innerHTML = 'OK'
+
+    let cancelButton = document.createElement('button')
+    cancelButton.innerHTML = 'Cancel'
+
+    formBody.innerHTML = '<h1>тут будет форма после перервы</h1>'
+    if (typeof okCallback === 'function'){
+        formBody.appendChild(okButton);
+        okButton.onclick = (e) => {
+            console.log(this)
+            this.okCallback(this.data) // Отдаю текущий поредактированный объект data первым параметром в okCallback
+        }
+    }
+
+    if (typeof cancelCallback === 'function'){
+        formBody.appendChild(cancelButton);
+        cancelButton.onclick = cancelCallback
+    }
+
+    let trCreator = function(key, value, trElement, labelElement, inputElement, inputType) { // Функция для корректной сборки таблицы и расположению в ней key и value
+        for(let i = 0; i < 2; i++) {                
+            let td = document.createElement("td");
+
+            if(i == 0) {                            // В tr нужно поместить два элемента tb, в первом должен быть label с key внутри, а во втором input, собсна тут я этим и занимаюсь
+                labelElement.innerText = key;
+                labelElement.htmlFor   = key;
+                td.appendChild(labelElement);
+            } else {
+                inputElement.value = value;
+                inputElement.id    = key;
+                inputElement.type  = inputType;
+                td.appendChild(inputElement);
+            }
+
+            trElement.appendChild(td);
+        }
+
+        return trElement;
+    };
+
+    let inputCreators = {
+        String(key, value, oninput){
+            let tr    = document.createElement("tr");   
+            let label = document.createElement("label");
+            let input = document.createElement("input");
+
+            input.oninput = () => oninput(input.value);
+
+            return trCreator(key, value, tr, label, input, "text");
+        },
+        Boolean(key, value, oninput){
+            let tr    = document.createElement("tr");   
+            let label = document.createElement("label");
+            let input = document.createElement("input");
+
+            if(value == true) {
+                input.checked = true;
+            }
+
+            input.oninput = () => oninput(input.checked);
+
+            return trCreator(key, value, tr, label, input, "checkbox");
+        },
+        Date(key, value, oninput){
+            let tr    = document.createElement("tr");   
+            let label = document.createElement("label");
+            let input = document.createElement("input");
+
+            let timespan = value.getTime();
+            let dateValue = ((new Date(timespan - value.getTimezoneOffset() * 60 * 1000)).toISOString()).slice(0, -14);
+
+            input.oninput = () => oninput(new Date(input.value));
+
+            return trCreator(key, dateValue, tr, label, input, "date");
+        }
+    }
+
+    for(let [key, value] of Object.entries(data)) {                        // Итерирую объект data и создаю для каждой пары ключ-значение input
+        let inputCreatorFunction = inputCreators[value.constructor.name];
+
+        let tr = inputCreatorFunction(key, value, (newValue) => {
+            if(this.validators[key] != undefined) {                 // Если this.validator[key] не равен undefined для этого input предусмотрена валидация и поэтому ее нужно запустить
+                if(typeof(this.validators[key](newValue, key, data, tr.querySelector(`#${key}`))) == "string") { // Если все это чудо вернуло тип данных который равен "String" то значит поле не прошло валидацию
+                    let span = document.createElement("span");                                                   // Тут будет лежать текст ошибки
+                        span.innerText = this.validators[key](newValue, key, data, tr.querySelector(`#${key}`)); // Собсна пишу сам текст ошибки в span
+                        span.style.position = "absolute";                                                        // Задаю спану absolute что бы табличка не скакала когда я его добавлю в tr
+
+                    tr.style.backgroundColor = "red";
+                    tr.appendChild(span);
+
+                    okButton.disabled = true; // Отключаю кнопку okButton если форма не прошла валидацию
+                } else {
+                    tr.style.backgroundColor = "inherit";
+                    data[key] = newValue;
+                    if(tr.querySelector("span") != null) {                                                        // Тут идет проверка на наличие span с ошибкой, если его нет то удалять не нужно
+                        tr.removeChild(tr.querySelector("span"));
+
+                        okButton.disabled = false; // Включаю кнопку okButton если форма прошла валидацию
+                    }
+                };
+            } else {
+                data[key] = newValue;
+            }
+        });
+        
+        table.appendChild(tr);
+    }
+
+    formBody.insertBefore(table, formBody.children[1]);
+    el.appendChild(formBody);
+
+    this.okCallback     = okCallback
+    this.cancelCallback = cancelCallback
+
+    this.data           = data
+    this.validators     = {}
+}
+
+let form = new Form(formContainer, {
+    name: 'Anakin',
+    surname: 'Skywalker',
+    married: true,
+    birthday: new Date((new Date).getTime() - 86400000 * 30*365)
+}, () => console.log('ok'),() => console.log('cancel') )
+form.okCallback = (e) => console.log(e);
+
+form.validators.surname = (value, key, data, input) => value.length > 2 && 
+                                                     value[0].toUpperCase() == value[0] &&
+                                                    !value.includes(' ') ? true : 'Wrong name'
+console.log(form)

+ 16 - 0
HW 10/style.css

@@ -0,0 +1,16 @@
+.wrapper div {
+    width: 200px;
+    align-items: center;
+    margin: 0 auto;
+    padding: 30px 0;
+}
+
+.form-container {
+    text-align: center;
+}
+
+table {
+    text-align: left;
+    margin: 0 auto;
+    margin-bottom: 20px;
+}