From a0f846947b3a11ad75377dd4d9aeb18ed784f75e Mon Sep 17 00:00:00 2001 From: YarikMix <43493788+YarikMix@users.noreply.github.com> Date: Sun, 25 Feb 2024 13:48:49 +0300 Subject: [PATCH 1/7] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=D0=B0=D0=BD=D1=83?= =?UTF-8?q?=D0=BB=20=D1=80=D0=BE=D1=83=D1=82=D0=B8=D0=BD=D0=B3,=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BA=D0=BE=D0=BC=D0=BF?= =?UTF-8?q?=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20=D0=B8=D0=BD=D0=BF=D1=83=D1=82?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 ++ public/config.js | 15 ++++++++- public/index.css | 2 ++ public/index.js | 9 +++--- public/src/assets/eye-close.svg | 7 +++++ public/src/assets/eye-open.svg | 27 +++++++++++++++++ public/src/components/input/input.css | 23 ++++++++++++++ public/src/components/input/input.hbs | 1 + public/src/components/input/input.js | 42 ++++++++++++++++++++++++++ public/src/pages/register/register.css | 6 ++++ public/src/pages/register/register.js | 12 ++++++++ server/index.js | 14 +++++++-- 12 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 public/src/assets/eye-close.svg create mode 100644 public/src/assets/eye-open.svg create mode 100644 public/src/components/input/input.css create mode 100644 public/src/components/input/input.hbs create mode 100644 public/src/components/input/input.js diff --git a/build.sh b/build.sh index d8a793c8..27995e9e 100644 --- a/build.sh +++ b/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +mkdir public/build handlebars -m public/src/pages/main-page/main-page.hbs -f public/build/main-page.js handlebars -m public/src/pages/login/login.hbs -f public/build/login.js handlebars -m public/src/pages/register/register.hbs -f public/build/register.js @@ -9,3 +10,4 @@ handlebars -m public/src/components/note-editor/note-editor.hbs -f public/build/ handlebars -m public/src/components/avatar/avatar.hbs -f public/build/avatar.js handlebars -m public/src/components/link/link.hbs -f public/build/link.js handlebars -m public/src/components/home/home.hbs -f public/build/home.js +handlebars -m public/src/components/input/input.hbs -f public/build/input.js diff --git a/public/config.js b/public/config.js index 554b60bb..541fd8d5 100644 --- a/public/config.js +++ b/public/config.js @@ -38,7 +38,20 @@ const loginPage = { } const registerPage = { - + inputs: { + login: { + type: "text", + placeholder: "Придумайте логин" + }, + password: { + type: "password", + placeholder: "Придумайте пароль" + }, + repeatPassword: { + type: "password", + placeholder: "Повторите пароль" + } + } } const notes = { diff --git a/public/index.css b/public/index.css index 45ce2bec..4c02b5c5 100644 --- a/public/index.css +++ b/public/index.css @@ -1,4 +1,5 @@ @import './src/pages/main-page/main-page.css'; +@import './src/pages/register/register.css'; @import './src/components/header/header.css'; @import './src/components/notes/notes.css'; @import './src/components/note/note.css'; @@ -6,6 +7,7 @@ @import './src/components/avatar/avatar.css'; @import './src/components/link/link.css'; @import './src/components/home/home.css'; +@import './src/components/input/input.css'; *, *::before, *::after { box-sizing: border-box; diff --git a/public/index.js b/public/index.js index 3c012e17..b88335ad 100644 --- a/public/index.js +++ b/public/index.js @@ -14,10 +14,12 @@ root.appendChild(wrapper) export const userInfo = { login: '', username: '', - isAuthorized: true, + isAuthorized: false, }; -let page = 'main'; +const currentUrl = window.location.href.split("/").slice(-1)[0]; + +let page = ''; function renderHeader() { const header = new Header(root, config, userInfo.isAuthorized); @@ -84,7 +86,6 @@ const listenClick = (e) => { window.addEventListener('click', listenClick); - renderHeader(); -renderMainPage(); +changePage(currentUrl); diff --git a/public/src/assets/eye-close.svg b/public/src/assets/eye-close.svg new file mode 100644 index 00000000..605207fb --- /dev/null +++ b/public/src/assets/eye-close.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/src/assets/eye-open.svg b/public/src/assets/eye-open.svg new file mode 100644 index 00000000..1e2eb01c --- /dev/null +++ b/public/src/assets/eye-open.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/src/components/input/input.css b/public/src/components/input/input.css new file mode 100644 index 00000000..710ca201 --- /dev/null +++ b/public/src/components/input/input.css @@ -0,0 +1,23 @@ +.input-container { + width: 240px; + position: relative; +} + +.input-container input { + width: 100%; + background: #F2F2F2; + color: #000; + border: none; + outline: none; + padding: 8px 16px; + border-radius: 15px; +} + +.input-container .show-password-btn { + position: absolute; + width: 24px; + height: 24px; + top: 8px; + right: 16px; + cursor: pointer; +} \ No newline at end of file diff --git a/public/src/components/input/input.hbs b/public/src/components/input/input.hbs new file mode 100644 index 00000000..511c6336 --- /dev/null +++ b/public/src/components/input/input.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/src/components/input/input.js b/public/src/components/input/input.js new file mode 100644 index 00000000..2abb691e --- /dev/null +++ b/public/src/components/input/input.js @@ -0,0 +1,42 @@ +import "../../../build/input.js" + +export class Input { + #parent; + #config; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + render() { + const div = document.createElement('div'); + div.className = "input-container" + + const template = Handlebars.templates["input.hbs"]; + div.innerHTML = template(this.#config); + + const input = div.querySelector("input"); + + if (this.#config.type === "password") { + const image = document.createElement("img"); + image.src = "/src/assets/eye-close.svg"; + image.className = "show-password-btn"; + + image.addEventListener("click", function () { + if (input.type === "password") { + input.type = "text"; + image.src = "/src/assets/eye-open.svg"; + } else { + input.type = "password"; + image.src = "/src/assets/eye-close.svg"; + } + }) + + div.appendChild(image); + } + + this.#parent.appendChild(div); + + } +} diff --git a/public/src/pages/register/register.css b/public/src/pages/register/register.css index e69de29b..63747594 100644 --- a/public/src/pages/register/register.css +++ b/public/src/pages/register/register.css @@ -0,0 +1,6 @@ +#register-page { + display: flex; + flex-direction: column; + gap: 15px; + align-items: center; +} \ No newline at end of file diff --git a/public/src/pages/register/register.js b/public/src/pages/register/register.js index 7d5d031f..d7f2941e 100644 --- a/public/src/pages/register/register.js +++ b/public/src/pages/register/register.js @@ -1,4 +1,5 @@ import "../../../build/register.js" +import {Input} from "../../components/input/input.js"; export default class RegisterPage { #parent; @@ -13,5 +14,16 @@ export default class RegisterPage { this.#parent.innerHTML = ''; this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['register.hbs'](this.#config.registerPage)); + + const self = document.getElementById("register-page") + + const input1 = new Input(self, this.#config.registerPage.inputs.login) + input1.render() + + const input2 = new Input(self, this.#config.registerPage.inputs.password) + input2.render() + + const input3 = new Input(self, this.#config.registerPage.inputs.repeatPassword) + input3.render() } } diff --git a/server/index.js b/server/index.js index 2ca00dfc..422ff1bd 100644 --- a/server/index.js +++ b/server/index.js @@ -63,13 +63,23 @@ const staticFile = (res, filePath, ext) => { const server = http.createServer((req, res) => { const {url} = req - console.log(url) - switch (url) { case "/": + console.log("home page") + staticFile(res, "/index.html", ".html") + break; + case "/main": console.log("main page") staticFile(res, "/index.html", ".html") break; + case "/login": + console.log("login page") + staticFile(res, "/index.html", ".html") + break; + case "/register": + console.log("register page") + staticFile(res, "/index.html", ".html") + break; default: const extname = String(path.extname(url)).toLocaleLowerCase() if (extname in mimeTypes) { From 467be1de590fbc5fce5ea51e07e472180ebe92d4 Mon Sep 17 00:00:00 2001 From: YarikMix <43493788+YarikMix@users.noreply.github.com> Date: Mon, 26 Feb 2024 01:01:30 +0300 Subject: [PATCH 2/7] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D1=80=D0=B4=D0=B6?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=81=20=D0=B2=D0=B5=D1=82=D0=BA=D0=BE=D0=B9=20?= =?UTF-8?q?vls-34?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/build/header.js | 1 + public/build/home.js | 1 + public/build/image.js | 1 + public/build/input.js | 1 + public/build/link.js | 1 + public/build/login.js | 1 + public/build/main-page.js | 1 + public/build/note-editor.js | 1 + public/build/note.js | 1 + public/build/notes.js | 1 + public/build/register.js | 1 + public/build/submitButton.js | 1 + public/config.js | 49 +++++--- public/index.css | 13 +- public/index.js | 78 ++---------- public/src/assets/avatar.png | Bin 0 -> 16277 bytes public/src/components/avatar/avatar.css | 6 - public/src/components/avatar/avatar.hbs | 3 - public/src/components/avatar/avatar.js | 20 --- public/src/components/header/header.css | 7 ++ public/src/components/header/header.js | 79 +++++++++--- public/src/components/image/image.css | 0 public/src/components/image/image.hbs | 1 + public/src/components/image/image.js | 26 ++++ public/src/components/input/events.js | 3 + public/src/components/input/input.hbs | 7 +- public/src/components/input/input.js | 119 ++++++++++++++---- public/src/components/link/link.hbs | 4 +- public/src/components/link/link.js | 20 ++- public/src/components/submit-button/events.js | 3 + .../components/submit-button/submitButton.css | 18 +++ .../components/submit-button/submitButton.hbs | 1 + .../components/submit-button/submitButton.js | 54 ++++++++ public/src/modules/dispathcer.js | 20 +++ public/src/modules/eventMaker.js | 31 +++++ public/src/modules/router.js | 58 +++++++++ public/src/pages/login/login.css | 21 ++++ public/src/pages/login/login.hbs | 10 +- public/src/pages/login/login.js | 99 ++++++++++++++- public/src/pages/main-page/main-page.js | 33 +++-- public/src/pages/register/register.js | 15 ++- public/src/stores/user/events.js | 3 + public/src/stores/user/userStore.js | 45 +++++++ 43 files changed, 665 insertions(+), 193 deletions(-) create mode 100644 public/build/header.js create mode 100644 public/build/home.js create mode 100644 public/build/image.js create mode 100644 public/build/input.js create mode 100644 public/build/link.js create mode 100644 public/build/login.js create mode 100644 public/build/main-page.js create mode 100644 public/build/note-editor.js create mode 100644 public/build/note.js create mode 100644 public/build/notes.js create mode 100644 public/build/register.js create mode 100644 public/build/submitButton.js create mode 100644 public/src/assets/avatar.png delete mode 100644 public/src/components/avatar/avatar.css delete mode 100644 public/src/components/avatar/avatar.hbs delete mode 100644 public/src/components/avatar/avatar.js create mode 100644 public/src/components/image/image.css create mode 100644 public/src/components/image/image.hbs create mode 100644 public/src/components/image/image.js create mode 100644 public/src/components/input/events.js create mode 100644 public/src/components/submit-button/events.js create mode 100644 public/src/components/submit-button/submitButton.css create mode 100644 public/src/components/submit-button/submitButton.hbs create mode 100644 public/src/components/submit-button/submitButton.js create mode 100644 public/src/modules/dispathcer.js create mode 100644 public/src/modules/eventMaker.js create mode 100644 public/src/modules/router.js create mode 100644 public/src/stores/user/events.js create mode 100644 public/src/stores/user/userStore.js diff --git a/public/build/header.js b/public/build/header.js new file mode 100644 index 00000000..b0d621a0 --- /dev/null +++ b/public/build/header.js @@ -0,0 +1 @@ +!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["header.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,e,a,t,l){var r=n.lookupProperty||function(n,e){if(Object.prototype.hasOwnProperty.call(n,e))return n[e]};return''},useData:!0})}(); \ No newline at end of file diff --git a/public/build/home.js b/public/build/home.js new file mode 100644 index 00000000..4888e240 --- /dev/null +++ b/public/build/home.js @@ -0,0 +1 @@ +!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["home.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,e,n,t,s){return'
\n\t

Страница с приветствием пользователя

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/image.js b/public/build/image.js new file mode 100644 index 00000000..24241429 --- /dev/null +++ b/public/build/image.js @@ -0,0 +1 @@ +!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["image.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,a,n,l,t){var o=e.lookupProperty||function(e,a){if(Object.prototype.hasOwnProperty.call(e,a))return e[a]};return''},useData:!0})}(); \ No newline at end of file diff --git a/public/build/input.js b/public/build/input.js new file mode 100644 index 00000000..2d6a8233 --- /dev/null +++ b/public/build/input.js @@ -0,0 +1 @@ +!function(){var l=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["input.hbs"]=l({1:function(l,n,e,a,t){return' \n'},compiler:[8,">= 4.3.0"],main:function(l,n,e,a,t){var o,s=null!=n?n:l.nullContext||{},c=l.hooks.helperMissing,i="function",r=l.escapeExpression,u=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'
\n \n'+(null!=(r=u(e,"if").call(s,null!=n?u(n,"isPassword"):n,{name:"if",hash:{},fn:l.program(1,t,0),inverse:l.noop,data:t,loc:{start:{line:3,column:4},end:{line:5,column:11}}}))?r:"")+"
\n"},useData:!0})}(); \ No newline at end of file diff --git a/public/build/link.js b/public/build/link.js new file mode 100644 index 00000000..6d5a77be --- /dev/null +++ b/public/build/link.js @@ -0,0 +1 @@ +!function(){var l=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["link.hbs"]=l({compiler:[8,">= 4.3.0"],main:function(l,n,e,t,a){var o,r=null!=n?n:l.nullContext||{},u=l.hooks.helperMissing,i="function",c=l.escapeExpression,l=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'\r\n\t'+c(typeof(o=null!=(o=l(e,"text")||(null!=n?l(n,"text"):n))?o:u)==i?o.call(r,{name:"text",hash:{},data:a,loc:{start:{line:2,column:1},end:{line:2,column:9}}}):o)+"\r\n"},useData:!0})}(); \ No newline at end of file diff --git a/public/build/login.js b/public/build/login.js new file mode 100644 index 00000000..c5babf87 --- /dev/null +++ b/public/build/login.js @@ -0,0 +1 @@ +!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["login.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,n,e,i,s){return'
\n
\n

Страница входа

\n
\n
\n Уже зарегистрированы?\n
\n
\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/main-page.js b/public/build/main-page.js new file mode 100644 index 00000000..24fe096b --- /dev/null +++ b/public/build/main-page.js @@ -0,0 +1 @@ +!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["main-page.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,e,n,t,i){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/note-editor.js b/public/build/note-editor.js new file mode 100644 index 00000000..310f9109 --- /dev/null +++ b/public/build/note-editor.js @@ -0,0 +1 @@ +!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["note-editor.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,t,a,n,i){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/note.js b/public/build/note.js new file mode 100644 index 00000000..d7d6330d --- /dev/null +++ b/public/build/note.js @@ -0,0 +1 @@ +!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["note.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,a,e,t,s){return'
\n\t

Заголовок

\n\t

Первая строчка заметки

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/notes.js b/public/build/notes.js new file mode 100644 index 00000000..ab20e30b --- /dev/null +++ b/public/build/notes.js @@ -0,0 +1 @@ +!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["notes.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,n,a,t,s){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/register.js b/public/build/register.js new file mode 100644 index 00000000..e9b931c7 --- /dev/null +++ b/public/build/register.js @@ -0,0 +1 @@ +!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["register.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,a,t,n,r){return'
\n\t

Страница регистрации

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/submitButton.js b/public/build/submitButton.js new file mode 100644 index 00000000..12dbda55 --- /dev/null +++ b/public/build/submitButton.js @@ -0,0 +1 @@ +!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["submitButton.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,t,l,e,a){var o,u=null!=t?t:n.nullContext||{},s=n.hooks.helperMissing,i="function",r=n.escapeExpression,n=n.lookupProperty||function(n,t){if(Object.prototype.hasOwnProperty.call(n,t))return n[t]};return'"},useData:!0})}(); \ No newline at end of file diff --git a/public/config.js b/public/config.js index 541fd8d5..ef37504c 100644 --- a/public/config.js +++ b/public/config.js @@ -1,43 +1,58 @@ -const button = { - "text": "toast", - "id": "hehe123" -} - const mainPage = { - button: button + href: "/", + needAuth: true } const header = { name: "YouNote", - menu: [ - { - href: "", + avatar: { + id: "user-avatar" + }, + menu: { + home: { + href: "/", text: "Главная", needAuth: false }, - { - href: "main", + main: { + href: "/main", text: "Мои заметки", needAuth: true }, - { - href: "login", + auth: { + href: "/login", text: "Вход", needAuth: false }, - { - href: "register", + register: { + href: "/register", text: "Регистрация", needAuth: false } - ] + } } const loginPage = { - + href: "login", + inputs: { + login: { + type: 'text', + placeholder: 'Введите логин' + }, + password: { + type: "password", + placeholder: "Придумайте пароль" + } + }, + buttons: { + submitBtn: { + btnLabel: "Войти" + } + } } const registerPage = { + href: "register", inputs: { login: { type: "text", diff --git a/public/index.css b/public/index.css index 4c02b5c5..c1fa6255 100644 --- a/public/index.css +++ b/public/index.css @@ -1,13 +1,14 @@ @import './src/pages/main-page/main-page.css'; -@import './src/pages/register/register.css'; @import './src/components/header/header.css'; @import './src/components/notes/notes.css'; @import './src/components/note/note.css'; @import './src/components/note-editor/note-editor.css'; -@import './src/components/avatar/avatar.css'; +@import 'src/components/image/image.css'; @import './src/components/link/link.css'; @import './src/components/home/home.css'; @import './src/components/input/input.css'; +@import "./src/components/submit-button/submitButton.css"; +@import "src/pages/login/login.css"; *, *::before, *::after { box-sizing: border-box; @@ -38,5 +39,11 @@ p, h1, h2, h3, h4, h5, h6 { } #root { - display: grid; + display: flex; + flex-direction: column; +} + +#wrapper{ + display: flex; + flex: 1 0 auto; } \ No newline at end of file diff --git a/public/index.js b/public/index.js index b88335ad..01d3b26e 100644 --- a/public/index.js +++ b/public/index.js @@ -1,91 +1,29 @@ import {config} from '/config.js'; -import MainPage from './src/pages/main-page/main-page.js'; import {Header} from "./src/components/header/header.js"; -import LoginPage from "./src/pages/login/login.js"; -import RegisterPage from "./src/pages/register/register.js"; +import {AppUserStore} from "./src/stores/user/userStore.js"; +import {router} from "./src/modules/router.js"; const root = document.getElementById('root'); console.log('root'); const wrapper = document.createElement("div") wrapper.id = "wrapper" + root.appendChild(wrapper) -export const userInfo = { - login: '', - username: '', - isAuthorized: false, -}; -const currentUrl = window.location.href.split("/").slice(-1)[0]; +AppUserStore.registerEvents(); + -let page = ''; function renderHeader() { - const header = new Header(root, config, userInfo.isAuthorized); + const header = new Header(root, config.header); header.render(); } -const renderMainPage = () => { - const main = new MainPage(wrapper, config, userInfo); - main.render(); -}; - -const renderLoginPage = () => { - const login = new LoginPage(wrapper, config); - login.render(); -}; - -const renderRegisterPage = () => { - const register = new RegisterPage(wrapper, config); - register.render() -} - -const changePage = (href) => { - switch (href) { - case '': - if (page !== 'main') { - renderMainPage(); - page = 'main'; - history.pushState(null, null, '/') - } - break; - case 'main': - if (page !== 'main') { - renderMainPage(); - page = 'main'; - history.pushState(null, null, '/' + page) - } - break; - case 'login': - if (page !== 'login') { - renderLoginPage(); - page = 'login'; - history.pushState(null, null, '/' + page) - } - break; - case 'register': - if (page !== 'register') { - renderRegisterPage(); - page = 'register'; - history.pushState(null, null, '/' + page) - } - break; - default: - console.log('undefined click'); - } -}; +renderHeader(); -const listenClick = (e) => { - e.preventDefault(); - const anchor = e.target.closest('a'); - if (!anchor) return; - const href = anchor.getAttribute('href').replace('/', ''); - changePage(href); -}; -window.addEventListener('click', listenClick); +router.init(wrapper, config) -renderHeader(); -changePage(currentUrl); diff --git a/public/src/assets/avatar.png b/public/src/assets/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..c3fe2ccaafd6128e47bf61297963b492900aa513 GIT binary patch literal 16277 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 T@mn`Z6VXu7QZD_^GW`DlMZW%v literal 0 HcmV?d00001 diff --git a/public/src/components/avatar/avatar.css b/public/src/components/avatar/avatar.css deleted file mode 100644 index e1b38eb9..00000000 --- a/public/src/components/avatar/avatar.css +++ /dev/null @@ -1,6 +0,0 @@ -.avatar { - width: 50px; - height: 50px; - background: gray; - border-radius: 50%; -} \ No newline at end of file diff --git a/public/src/components/avatar/avatar.hbs b/public/src/components/avatar/avatar.hbs deleted file mode 100644 index ebc2cf62..00000000 --- a/public/src/components/avatar/avatar.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/public/src/components/avatar/avatar.js b/public/src/components/avatar/avatar.js deleted file mode 100644 index 4ee5dd9a..00000000 --- a/public/src/components/avatar/avatar.js +++ /dev/null @@ -1,20 +0,0 @@ -import "../../../build/avatar.js" - -export class Avatar { - #parent; - #config; - - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - } - - render() { - console.log("render avatar") - - const tmp = document.createElement('div'); - const template = Handlebars.templates["avatar.hbs"]; - tmp.innerHTML = template(this.#config.avatar); - this.#parent.appendChild(tmp.firstElementChild); - } -} diff --git a/public/src/components/header/header.css b/public/src/components/header/header.css index bf99bf63..c9ddd5a8 100644 --- a/public/src/components/header/header.css +++ b/public/src/components/header/header.css @@ -1,6 +1,7 @@ header { width: 100%; padding: 10px 25px; + height: 80px; background: #fff; display: flex; justify-content: space-between; @@ -28,4 +29,10 @@ header .menu-container { header .menu-container .menu-item a { text-decoration: none; +} + +header .right-container .avatar { + width: 50px; + height: 50px; + border-radius: 50%; } \ No newline at end of file diff --git a/public/src/components/header/header.js b/public/src/components/header/header.js index 7202f964..48903d02 100644 --- a/public/src/components/header/header.js +++ b/public/src/components/header/header.js @@ -1,41 +1,86 @@ -import {Avatar} from "../avatar/avatar.js"; +import {Image} from "../image/image.js"; import {Link} from "../link/link.js"; import '../../../build/header.js'; +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {UserStoreEvents} from "../../stores/user/events.js"; +import {AppUserStore} from "../../stores/user/userStore.js"; +import {router} from "../../modules/router.js"; export class Header { #parent; #config; - #isAuth; - constructor(parent, config, isAuth) { + #menu; + + #homePageLink; + #mainPageLink; + #authPageLink; + #registerPageLink; + + #avatar; + + constructor(parent, config) { this.#parent = parent; this.#config = config; - this.#isAuth = isAuth; + } + + get self () { + return document.getElementById("header") + } + + #addEventListeners(){ + AppEventMaker.subscribe(UserStoreEvents.SUCCSSESFUL_LOGIN, () => { + console.log("log hueg") + this.#avatar = new Image(document.querySelector(".right-container"), this.#config.avatar); + this.#avatar.render(); + this.#avatar.updateImage(AppUserStore.avatar) + + this.#authPageLink.self.hidden = true; + + this.#authPageLink.self.hidden = true; + this.#registerPageLink.self.hidden = true; + + this.#homePageLink.self.hidden = true; + + this.#mainPageLink = new Link(this.#menu, this.#config.menu.main) + this.#mainPageLink.render() + + router.changePage("/") + }) } render() { - this.#parent.insertAdjacentHTML('beforebegin', window.Handlebars.templates['header.hbs'](this.#config.header)); + console.log("header render") - const self = document.getElementById("header") + this.#parent.insertAdjacentHTML('afterbegin', window.Handlebars.templates['header.hbs'](this.#config)); const rightContainer = document.querySelector(".right-container") - const menu = document.createElement("div") - menu.className = "menu-container" + this.#menu = document.createElement("div") + this.#menu.className = "menu-container" + + rightContainer.appendChild(this.#menu) + + if (this.#homePageLink === undefined){ + this.#homePageLink = new Link(this.#menu, this.#config.menu.home) + this.#homePageLink.render() + } + + if (this.#authPageLink === undefined) { + this.#authPageLink = new Link(this.#menu, this.#config.menu.auth) + this.#authPageLink.render() + } + + if (this.#registerPageLink === undefined) { + this.#registerPageLink = new Link(this.#menu, this.#config.menu.register) + this.#registerPageLink.render() + } - this.#config.header.menu.forEach(item => { - const link = new Link(menu, item, this.#isAuth) - link.render() - }) // TODO // Сделать logout - rightContainer.appendChild(menu) + this.#addEventListeners(); - if (this.#isAuth) { - const avatar = new Avatar(rightContainer, this.#config.avatar); - avatar.render(); - } } } diff --git a/public/src/components/image/image.css b/public/src/components/image/image.css new file mode 100644 index 00000000..e69de29b diff --git a/public/src/components/image/image.hbs b/public/src/components/image/image.hbs new file mode 100644 index 00000000..78b0e86c --- /dev/null +++ b/public/src/components/image/image.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/src/components/image/image.js b/public/src/components/image/image.js new file mode 100644 index 00000000..546d1c10 --- /dev/null +++ b/public/src/components/image/image.js @@ -0,0 +1,26 @@ +import "../../../build/image.js" + +export class Image { + #parent; + #config; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get self() { + return document.getElementById(this.#config.id) + } + + updateImage(path) { + this.self.src = path + } + + render() { + const tmp = document.createElement('div'); + const template = Handlebars.templates["image.hbs"]; + tmp.innerHTML = template(this.#config); + this.#parent.appendChild(tmp.firstElementChild); + } +} diff --git a/public/src/components/input/events.js b/public/src/components/input/events.js new file mode 100644 index 00000000..d3eac25e --- /dev/null +++ b/public/src/components/input/events.js @@ -0,0 +1,3 @@ +export const inputEvents = { + INPUT_CHANGE: 'INPUT_CHANGE' +} \ No newline at end of file diff --git a/public/src/components/input/input.hbs b/public/src/components/input/input.hbs index 511c6336..c91c698e 100644 --- a/public/src/components/input/input.hbs +++ b/public/src/components/input/input.hbs @@ -1 +1,6 @@ - \ No newline at end of file +
+ + {{#if isPassword}} + + {{/if}} +
diff --git a/public/src/components/input/input.js b/public/src/components/input/input.js index 2abb691e..da408339 100644 --- a/public/src/components/input/input.js +++ b/public/src/components/input/input.js @@ -1,42 +1,113 @@ import "../../../build/input.js" +import {inputEvents} from "./events.js"; +import {AppEventMaker} from "../../modules/eventMaker.js"; export class Input { #parent; #config; + #listeners = { + showPassword: '', + change: '' + } + #image; + #input; + + id; constructor(parent, config) { this.#parent = parent; this.#config = config; + this.#config.id = crypto.randomUUID(); + + this.id = this.#config.id; + + this.#listeners.showPassword = this.#showPassword.bind(this); + this.#listeners.change = this.#change.bind(this); + + this.#config.isPassword = this.#config.type === 'password'; } - render() { - const div = document.createElement('div'); - div.className = "input-container" + get self() { + return document.querySelector(`#input-${this.#config.id}`); + } + get value() { + return this.#input.value; + } + + #change(){ + AppEventMaker.notify(inputEvents.INPUT_CHANGE, this.#config.id); + } + + #showPassword() { + if (this.#input.type === "password") { + this.#input.type = "text"; + this.#image.src = "/src/assets/eye-open.svg"; + } else { + this.#input.type = "password"; + this.#image.src = "/src/assets/eye-close.svg"; + } + } + + #addEventListeners() { + if(this.#config.isPassword){ + this.#image.addEventListener('click', this.#listeners.showPassword); + } + this.#input.addEventListener('change', this.#listeners.change); + } + + #removeEventListeners() { + if (this.#config.isPassword){ + this.#image.removeEventListener('click', this.#listeners.showPassword); + } + this.#input.removeEventListener('change', this.#listeners.change); + } + + remove(){ + this.#removeEventListeners(); + } + + render() { const template = Handlebars.templates["input.hbs"]; - div.innerHTML = template(this.#config); - - const input = div.querySelector("input"); - - if (this.#config.type === "password") { - const image = document.createElement("img"); - image.src = "/src/assets/eye-close.svg"; - image.className = "show-password-btn"; - - image.addEventListener("click", function () { - if (input.type === "password") { - input.type = "text"; - image.src = "/src/assets/eye-open.svg"; - } else { - input.type = "password"; - image.src = "/src/assets/eye-close.svg"; - } - }) - - div.appendChild(image); + + if(this.self === null){ + this.#parent.insertAdjacentHTML( + 'beforeend', + template(this.#config) + ); + this.#image = document.querySelector(`#input-${this.#config.id} > img`); + this.#input = document.querySelector(`#input-${this.#config.id} > input`); + this.#addEventListeners(); } - this.#parent.appendChild(div); + + // const div = document.createElement('div'); + // div.className = "input-container" + // + // // const template = Handlebars.templates["input.hbs"]; + // div.innerHTML = template(this.#config); + // + // const input = div.querySelector("input"); + // + // if (this.#config.type === "password") { + // this.#image = document.createElement("img"); + // image.src = "/src/assets/eye-close.svg"; + // image.className = "show-password-btn"; + // + // image.addEventListener("click", function () { + // if (input.type === "password") { + // input.type = "text"; + // image.src = "/src/assets/eye-open.svg"; + // } else { + // input.type = "password"; + // image.src = "/src/assets/eye-close.svg"; + // } + // }) + // + // div.appendChild(image); + // } + // + // this.#parent.appendChild(div); } } diff --git a/public/src/components/link/link.hbs b/public/src/components/link/link.hbs index d78e61c9..d66ac22c 100644 --- a/public/src/components/link/link.hbs +++ b/public/src/components/link/link.hbs @@ -1 +1,3 @@ -
{{text}} \ No newline at end of file + + {{text}} + \ No newline at end of file diff --git a/public/src/components/link/link.js b/public/src/components/link/link.js index 3d01b75c..f413a634 100644 --- a/public/src/components/link/link.js +++ b/public/src/components/link/link.js @@ -3,23 +3,21 @@ import "../../../build/link.js" export class Link { #parent; #config; - #isAuth; - constructor(parent, config, isAuth) { + id; + + constructor(parent, config) { + this.id = crypto.randomUUID(); this.#parent = parent; this.#config = config; - this.#isAuth = isAuth; + this.#config.id = this.id; } - render() { - if (this.#config.needAuth && !this.#isAuth) { - return - } - - if (!this.#config.needAuth && this.#isAuth) { - return - } + get self(){ + return document.getElementById(`${this.id}`); + } + render() { const tmp = document.createElement('div'); const template = Handlebars.templates["link.hbs"]; tmp.innerHTML = template(this.#config); diff --git a/public/src/components/submit-button/events.js b/public/src/components/submit-button/events.js new file mode 100644 index 00000000..711ccc72 --- /dev/null +++ b/public/src/components/submit-button/events.js @@ -0,0 +1,3 @@ +export const SubmitButtonEvents = { + BUTTON_SUBMIT: 'BUTTON_SUBMIT' +} \ No newline at end of file diff --git a/public/src/components/submit-button/submitButton.css b/public/src/components/submit-button/submitButton.css new file mode 100644 index 00000000..f2972e89 --- /dev/null +++ b/public/src/components/submit-button/submitButton.css @@ -0,0 +1,18 @@ +.submit-btn{ + width: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: #56A6F0; + border-radius: 20px; + border: none; + color: #ffffff; + text-align: center; + padding: 8px; + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25); +} + +.submit-btn:active{ + + box-shadow: inset 0 4px 4px rgba(0, 0, 0, 0.25); +} \ No newline at end of file diff --git a/public/src/components/submit-button/submitButton.hbs b/public/src/components/submit-button/submitButton.hbs new file mode 100644 index 00000000..e9486370 --- /dev/null +++ b/public/src/components/submit-button/submitButton.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/src/components/submit-button/submitButton.js b/public/src/components/submit-button/submitButton.js new file mode 100644 index 00000000..4a036dff --- /dev/null +++ b/public/src/components/submit-button/submitButton.js @@ -0,0 +1,54 @@ +import '../../../build/submitButton.js' +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {SubmitButtonEvents} from "./events.js"; + +export class SubmitButton{ + #parent; + #props = { + btnLabel: '' + }; + id; + + #listeners = { + submit: this.#submit.bind(this) + }; + + constructor(parent, {btnLabel}) { + this.id = crypto.randomUUID(); + this.#parent = parent; + this.#props.btnLabel = btnLabel; + this.#props.id = this.id; + // this.#listeners.submit = this.#submit.bind(this); + } + + get self(){ + return document.getElementById(`submit-btn-${this.id}`); + } + + #submit(event){ + event.preventDefault(); + AppEventMaker.notify(SubmitButtonEvents.BUTTON_SUBMIT, this.id); + } + + #addEventListeners(){ + this.self.addEventListener('click', this.#listeners.submit); + } + + #removeEventListeners(){ + this.self.removeEventListener('click', this.#listeners.submit); + } + + remove(){ + this.#removeEventListeners(); + } + + render(){ + + this.#parent.insertAdjacentHTML( + 'beforeend', + Handlebars.templates['submitButton.hbs'](this.#props) + ) + + this.#addEventListeners(); + } +} \ No newline at end of file diff --git a/public/src/modules/dispathcer.js b/public/src/modules/dispathcer.js new file mode 100644 index 00000000..cf369634 --- /dev/null +++ b/public/src/modules/dispathcer.js @@ -0,0 +1,20 @@ +class Dispatcher{ + #callbacks; + + constructor() { + this.#callbacks = []; + } + + register(callback){ + this.#callbacks.push(callback); + } + + dispatch(action){ + console.log(action) + this.#callbacks.forEach((callback) => { + callback(action); + }) + } +} + +export const AppDispatcher = new Dispatcher(); \ No newline at end of file diff --git a/public/src/modules/eventMaker.js b/public/src/modules/eventMaker.js new file mode 100644 index 00000000..d9d369cb --- /dev/null +++ b/public/src/modules/eventMaker.js @@ -0,0 +1,31 @@ +class EventMaker{ + #eventsMap; + + constructor() { + this.#eventsMap = new Map(); + } + + subscribe(event, callback){ + if (event in this.#eventsMap){ + this.#eventsMap[event].add(callback); + return; + } + this.#eventsMap[event] = new Set([callback]); + } + + unsubscribe(event, callback){ + this.#eventsMap[event].delete(callback); + } + + notify(event, ...args){ + if(!(event in this.#eventsMap)){ + return; + } + + this.#eventsMap[event].forEach((listener) => { + listener.apply(this, args); + }) + } +} + +export const AppEventMaker = new EventMaker(); \ No newline at end of file diff --git a/public/src/modules/router.js b/public/src/modules/router.js new file mode 100644 index 00000000..ea9976f4 --- /dev/null +++ b/public/src/modules/router.js @@ -0,0 +1,58 @@ +import MainPage from "../pages/main-page/main-page.js"; +import LoginPage from "../pages/login/login.js"; +import RegisterPage from "../pages/register/register.js"; + +class Router { + #currentUrl; + #currentPage; + #pages; + + constructor() { + this.#currentUrl = window.location.href.split("/").slice(-1)[0]; + this.#currentPage = undefined; + this.#pages = []; + + this.registerEvents(); + } + + init(root, config){ + const mainPage = new MainPage(root, config.mainPage) + this.#pages.push(mainPage) + + const loginPage = new LoginPage(root, config.loginPage) + this.#pages.push(loginPage) + + const registerPage = new RegisterPage(root, config.registerPage) + this.#pages.push(registerPage) + + + this.changePage(this.#currentUrl) + } + + changePage(href) { + if (href === "") href = "/" + + this.#pages.forEach(page => { + if (this.#currentPage !== page && page.href === href) { + this.#currentPage?.remove() + page.render() + this.#currentPage = page + history.pushState(null, null, page.href) + } + }) + } + + registerEvents() { + window.addEventListener('click', (e) => this.listenClick(e)); + } + + listenClick (e) { + e.preventDefault(); + const anchor = e.target.closest('a'); + if (!anchor) return; + const href = anchor.getAttribute('href').replace('/', ''); + this.changePage(href); + } +} + +export const router = new Router() diff --git a/public/src/pages/login/login.css b/public/src/pages/login/login.css index e69de29b..50721262 100644 --- a/public/src/pages/login/login.css +++ b/public/src/pages/login/login.css @@ -0,0 +1,21 @@ +.centered-widget{ + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.login-window { + padding: 20px; + border-radius: 20px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #ffffff; +} + +.login-window > div { + margin: 10px; +} \ No newline at end of file diff --git a/public/src/pages/login/login.hbs b/public/src/pages/login/login.hbs index 6d4e59d8..ee077b39 100644 --- a/public/src/pages/login/login.hbs +++ b/public/src/pages/login/login.hbs @@ -1,3 +1,9 @@ -
-

Страница входа

+
+
\ No newline at end of file diff --git a/public/src/pages/login/login.js b/public/src/pages/login/login.js index d9d96f99..431c0507 100644 --- a/public/src/pages/login/login.js +++ b/public/src/pages/login/login.js @@ -1,17 +1,112 @@ import "../../../build/login.js" +import {Input} from "../../components/input/input.js"; +import {SubmitButton} from "../../components/submit-button/submitButton.js"; +import {inputEvents} from "../../components/input/events.js"; +import {SubmitButtonEvents} from "../../components/submit-button/events.js"; +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {AppDispatcher} from "../../modules/dispathcer.js"; +import {AppUserStore, UserActions} from "../../stores/user/userStore.js"; export default class LoginPage { #parent; #config; + #loginInput; + #passwordInput; + #submitBtn; + + #subscribed; // Костыль + constructor(parent, config) { this.#parent = parent; this.#config = config; + this.#subscribed = false; // Костыль } - render() { + get href () { + return this.#config.href; + } + + #subscribeToEvents(){ + AppEventMaker.subscribe(inputEvents.INPUT_CHANGE, (id) => { + if(id === this.#passwordInput.id){ + this.#validatePassword() + } else{ + this.#validateLogin(); + } + }); + + AppEventMaker.subscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { + if(id === this.#submitBtn.id){ + AppDispatcher.dispatch({ + type: UserActions.LOGIN, + payload: this.#loginInput.value + }) + } + }) + + this.#subscribed = true; // Костыль + } + + #unsubscribeToEvents(){ + console.log("unsubscribeToEvents") + + // TODO + // Не работает + + AppEventMaker.unsubscribe(inputEvents.INPUT_CHANGE, (id) => { + if(id === this.#passwordInput.id){ + this.#validatePassword() + } else{ + this.#validateLogin(); + } + }); + + AppEventMaker.unsubscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { + console.log("fasfdfasd") + if(id === this.#submitBtn.id){ + console.log("fffff") + AppDispatcher.dispatch({ + type: UserActions.LOGIN + }) + } + }) + + } + + #validatePassword(){ + console.log("password validated") + } + + #validateLogin(){ + console.log("login validated") + } + + remove(){ + this.#unsubscribeToEvents(); + this.#submitBtn.remove(); + this.#passwordInput.remove(); + this.#loginInput.remove(); this.#parent.innerHTML = ''; + } + + render() { + console.log("loginPage render") + + this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['login.hbs'](this.#config.loginPage)); + this.#loginInput = new Input(document.querySelector('.username-input-place'), this.#config.inputs.login); + this.#loginInput.render(); + + this.#passwordInput = new Input(document.querySelector('.password-input-place'), this.#config.inputs.password); + this.#passwordInput.render(); + + this.#submitBtn = new SubmitButton(document.querySelector('.submit-btn-place'), this.#config.buttons.submitBtn); + this.#submitBtn.render(); + + // Костыль + if (!this.#subscribed) { + this.#subscribeToEvents(); + } - this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['login.hbs'](this.#config.login)); } } diff --git a/public/src/pages/main-page/main-page.js b/public/src/pages/main-page/main-page.js index 964887e2..ca3cdd85 100644 --- a/public/src/pages/main-page/main-page.js +++ b/public/src/pages/main-page/main-page.js @@ -1,38 +1,47 @@ +import "../../../build/main-page.js" import {NotesContainer} from "../../components/notes/notes.js"; import {NoteEditor} from "../../components/note-editor/note-editor.js"; +import {AppUserStore} from "../../stores/user/userStore.js"; import {Home} from "../../components/home/home.js"; -import "../../../build/main-page.js" export default class MainPage { #parent; #config; - #user; - constructor(parent, config, user) { + constructor(parent, config) { this.#parent = parent; this.#config = config; - this.#user = user; } - render() { + get href () { + return this.#config.href; + } + + get self () { + return document.getElementById('main-page'); + } + + remove(){ + console.log("MainPage remove") this.#parent.innerHTML = ''; + } + + render() { + console.log("MainPage render") const tmp = document.createElement('div'); const template = Handlebars.templates["main-page.hbs"]; tmp.innerHTML = template(this.#config.mainPage); this.#parent.appendChild(tmp.firstElementChild); - const self = document.getElementById('main-page'); - - - if (this.#user.isAuthorized) { - const notesContainer = new NotesContainer(self, this.#config) + if (AppUserStore.IsAuthenticated()) { + const notesContainer = new NotesContainer(this.self, this.#config) notesContainer.render() - const noteEditor = new NoteEditor(self, this.#config) + const noteEditor = new NoteEditor(this.self, this.#config) noteEditor.render() } else { - const home = new Home(self, this.#config) + const home = new Home(this.self, this.#config) home.render() } } diff --git a/public/src/pages/register/register.js b/public/src/pages/register/register.js index d7f2941e..a9b9f419 100644 --- a/public/src/pages/register/register.js +++ b/public/src/pages/register/register.js @@ -10,20 +10,27 @@ export default class RegisterPage { this.#config = config; } - render() { + get href () { + return this.#config.href; + } + + remove(){ this.#parent.innerHTML = ''; + } + + render() { this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['register.hbs'](this.#config.registerPage)); const self = document.getElementById("register-page") - const input1 = new Input(self, this.#config.registerPage.inputs.login) + const input1 = new Input(self, this.#config.inputs.login) input1.render() - const input2 = new Input(self, this.#config.registerPage.inputs.password) + const input2 = new Input(self, this.#config.inputs.password) input2.render() - const input3 = new Input(self, this.#config.registerPage.inputs.repeatPassword) + const input3 = new Input(self, this.#config.inputs.repeatPassword) input3.render() } } diff --git a/public/src/stores/user/events.js b/public/src/stores/user/events.js new file mode 100644 index 00000000..e535e34a --- /dev/null +++ b/public/src/stores/user/events.js @@ -0,0 +1,3 @@ +export const UserStoreEvents = { + SUCCSSESFUL_LOGIN: 'SUCCSSESFUL_LOGIN' +} \ No newline at end of file diff --git a/public/src/stores/user/userStore.js b/public/src/stores/user/userStore.js new file mode 100644 index 00000000..4e1600db --- /dev/null +++ b/public/src/stores/user/userStore.js @@ -0,0 +1,45 @@ +import {AppDispatcher} from "../../modules/dispathcer.js"; +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {UserStoreEvents} from "./events.js"; + +class UserStore { + #state = { + username: '', + avatarUrl: '/src/assets/avatar.png', + isAuth: false + } + + registerEvents(){ + AppDispatcher.register((action) => { + switch (action.type){ + case UserActions.LOGIN: + this.login(action.payload); + } + }) + } + + get username() { + return this.#state.username; + } + + get avatar() { + return this.#state.avatarUrl; + } + + IsAuthenticated() { + return this.#state.isAuth; + } + + login(username){ + console.log("login successfull"); + this.#state.isAuth = true; + this.#state.username = username; + AppEventMaker.notify(UserStoreEvents.SUCCSSESFUL_LOGIN); + } +} + +export const AppUserStore = new UserStore(); + +export const UserActions = { + LOGIN: "LOGIN" +} \ No newline at end of file From 4be1d6631696d3c3226937a531bf73038c226adc Mon Sep 17 00:00:00 2001 From: YarikMix <43493788+YarikMix@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:31:34 +0300 Subject: [PATCH 3/7] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8F=20=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 10 +- public/build/header.js | 1 - public/build/home.js | 1 - public/build/image.js | 1 - public/build/input.js | 1 - public/build/link.js | 1 - public/build/login.js | 1 - public/build/main-page.js | 1 - public/build/note-editor.js | 1 - public/build/note.js | 1 - public/build/notes.js | 1 - public/build/register.js | 1 - public/build/submitButton.js | 1 - public/config.js | 95 ++++++++++++++----- public/index.css | 17 +++- public/index.js | 26 +++-- .../submitButton.css => button/button.css} | 8 +- .../submitButton.hbs => button/button.hbs} | 0 .../submitButton.js => button/button.js} | 6 +- .../{submit-button => button}/events.js | 0 public/src/components/footer/footer.css | 6 ++ public/src/components/footer/footer.hbs | 3 + public/src/components/footer/footer.js | 20 ++++ public/src/components/header/header.css | 5 +- public/src/components/header/header.js | 11 ++- public/src/components/input/input.css | 30 ++++-- public/src/components/input/input.hbs | 1 + public/src/components/input/input.js | 6 ++ public/src/components/link/link.css | 9 ++ public/src/components/note/note.css | 1 + public/src/components/wrapper/wrapper.css | 0 public/src/components/wrapper/wrapper.hbs | 3 + public/src/components/wrapper/wrapper.js | 20 ++++ public/src/modules/router.js | 7 +- public/src/pages/login/login.css | 12 ++- public/src/pages/login/login.hbs | 14 +-- public/src/pages/login/login.js | 77 +++++++++++---- public/src/pages/main-page/main-page.hbs | 3 - .../main-page.css => main/main.css} | 3 +- public/src/pages/main/main.hbs | 3 + .../{main-page/main-page.js => main/main.js} | 18 ++-- public/src/pages/profile/profile.css | 8 ++ public/src/pages/profile/profile.hbs | 3 + public/src/pages/profile/profile.js | 48 ++++++++++ public/src/pages/register/register.css | 20 +++- public/src/pages/register/register.hbs | 8 +- public/src/pages/register/register.js | 35 +++++-- 47 files changed, 419 insertions(+), 129 deletions(-) delete mode 100644 public/build/header.js delete mode 100644 public/build/home.js delete mode 100644 public/build/image.js delete mode 100644 public/build/input.js delete mode 100644 public/build/link.js delete mode 100644 public/build/login.js delete mode 100644 public/build/main-page.js delete mode 100644 public/build/note-editor.js delete mode 100644 public/build/note.js delete mode 100644 public/build/notes.js delete mode 100644 public/build/register.js delete mode 100644 public/build/submitButton.js rename public/src/components/{submit-button/submitButton.css => button/button.css} (76%) rename public/src/components/{submit-button/submitButton.hbs => button/button.hbs} (100%) rename public/src/components/{submit-button/submitButton.js => button/button.js} (89%) rename public/src/components/{submit-button => button}/events.js (100%) create mode 100644 public/src/components/footer/footer.css create mode 100644 public/src/components/footer/footer.hbs create mode 100644 public/src/components/footer/footer.js create mode 100644 public/src/components/wrapper/wrapper.css create mode 100644 public/src/components/wrapper/wrapper.hbs create mode 100644 public/src/components/wrapper/wrapper.js delete mode 100644 public/src/pages/main-page/main-page.hbs rename public/src/pages/{main-page/main-page.css => main/main.css} (61%) create mode 100644 public/src/pages/main/main.hbs rename public/src/pages/{main-page/main-page.js => main/main.js} (62%) create mode 100644 public/src/pages/profile/profile.css create mode 100644 public/src/pages/profile/profile.hbs create mode 100644 public/src/pages/profile/profile.js diff --git a/build.sh b/build.sh index 27995e9e..462943d1 100644 --- a/build.sh +++ b/build.sh @@ -1,13 +1,17 @@ #!/bin/bash -mkdir public/build -handlebars -m public/src/pages/main-page/main-page.hbs -f public/build/main-page.js +mkdir -p public/build +handlebars -m public/src/pages/main/main.hbs -f public/build/main.js handlebars -m public/src/pages/login/login.hbs -f public/build/login.js handlebars -m public/src/pages/register/register.hbs -f public/build/register.js +handlebars -m public/src/pages/profile/profile.hbs -f public/build/profile.js +handlebars -m public/src/components/image/image.hbs -f public/build/image.js handlebars -m public/src/components/header/header.hbs -f public/build/header.js handlebars -m public/src/components/notes/notes.hbs -f public/build/notes.js handlebars -m public/src/components/note/note.hbs -f public/build/note.js handlebars -m public/src/components/note-editor/note-editor.hbs -f public/build/note-editor.js -handlebars -m public/src/components/avatar/avatar.hbs -f public/build/avatar.js handlebars -m public/src/components/link/link.hbs -f public/build/link.js handlebars -m public/src/components/home/home.hbs -f public/build/home.js handlebars -m public/src/components/input/input.hbs -f public/build/input.js +handlebars -m public/src/components/wrapper/wrapper.hbs -f public/build/wrapper.js +handlebars -m public/src/components/footer/footer.hbs -f public/build/footer.js +handlebars -m public/src/components/button/button.hbs -f public/build/button.js diff --git a/public/build/header.js b/public/build/header.js deleted file mode 100644 index b0d621a0..00000000 --- a/public/build/header.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["header.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,e,a,t,l){var r=n.lookupProperty||function(n,e){if(Object.prototype.hasOwnProperty.call(n,e))return n[e]};return''},useData:!0})}(); \ No newline at end of file diff --git a/public/build/home.js b/public/build/home.js deleted file mode 100644 index 4888e240..00000000 --- a/public/build/home.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["home.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,e,n,t,s){return'
\n\t

Страница с приветствием пользователя

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/image.js b/public/build/image.js deleted file mode 100644 index 24241429..00000000 --- a/public/build/image.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["image.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,a,n,l,t){var o=e.lookupProperty||function(e,a){if(Object.prototype.hasOwnProperty.call(e,a))return e[a]};return''},useData:!0})}(); \ No newline at end of file diff --git a/public/build/input.js b/public/build/input.js deleted file mode 100644 index 2d6a8233..00000000 --- a/public/build/input.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var l=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["input.hbs"]=l({1:function(l,n,e,a,t){return' \n'},compiler:[8,">= 4.3.0"],main:function(l,n,e,a,t){var o,s=null!=n?n:l.nullContext||{},c=l.hooks.helperMissing,i="function",r=l.escapeExpression,u=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'
\n \n'+(null!=(r=u(e,"if").call(s,null!=n?u(n,"isPassword"):n,{name:"if",hash:{},fn:l.program(1,t,0),inverse:l.noop,data:t,loc:{start:{line:3,column:4},end:{line:5,column:11}}}))?r:"")+"
\n"},useData:!0})}(); \ No newline at end of file diff --git a/public/build/link.js b/public/build/link.js deleted file mode 100644 index 6d5a77be..00000000 --- a/public/build/link.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var l=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["link.hbs"]=l({compiler:[8,">= 4.3.0"],main:function(l,n,e,t,a){var o,r=null!=n?n:l.nullContext||{},u=l.hooks.helperMissing,i="function",c=l.escapeExpression,l=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'\r\n\t'+c(typeof(o=null!=(o=l(e,"text")||(null!=n?l(n,"text"):n))?o:u)==i?o.call(r,{name:"text",hash:{},data:a,loc:{start:{line:2,column:1},end:{line:2,column:9}}}):o)+"\r\n"},useData:!0})}(); \ No newline at end of file diff --git a/public/build/login.js b/public/build/login.js deleted file mode 100644 index c5babf87..00000000 --- a/public/build/login.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["login.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,n,e,i,s){return'
\n \n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/main-page.js b/public/build/main-page.js deleted file mode 100644 index 24fe096b..00000000 --- a/public/build/main-page.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var a=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["main-page.hbs"]=a({compiler:[8,">= 4.3.0"],main:function(a,e,n,t,i){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/note-editor.js b/public/build/note-editor.js deleted file mode 100644 index 310f9109..00000000 --- a/public/build/note-editor.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["note-editor.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,t,a,n,i){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/note.js b/public/build/note.js deleted file mode 100644 index d7d6330d..00000000 --- a/public/build/note.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["note.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,a,e,t,s){return'
\n\t

Заголовок

\n\t

Первая строчка заметки

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/notes.js b/public/build/notes.js deleted file mode 100644 index ab20e30b..00000000 --- a/public/build/notes.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["notes.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,n,a,t,s){return'
\n\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/register.js b/public/build/register.js deleted file mode 100644 index e9b931c7..00000000 --- a/public/build/register.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["register.hbs"]=e({compiler:[8,">= 4.3.0"],main:function(e,a,t,n,r){return'
\n\t

Страница регистрации

\n
'},useData:!0})}(); \ No newline at end of file diff --git a/public/build/submitButton.js b/public/build/submitButton.js deleted file mode 100644 index 12dbda55..00000000 --- a/public/build/submitButton.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["submitButton.hbs"]=n({compiler:[8,">= 4.3.0"],main:function(n,t,l,e,a){var o,u=null!=t?t:n.nullContext||{},s=n.hooks.helperMissing,i="function",r=n.escapeExpression,n=n.lookupProperty||function(n,t){if(Object.prototype.hasOwnProperty.call(n,t))return n[t]};return'"},useData:!0})}(); \ No newline at end of file diff --git a/public/config.js b/public/config.js index ef37504c..533cd0ed 100644 --- a/public/config.js +++ b/public/config.js @@ -5,6 +5,9 @@ const mainPage = { const header = { name: "YouNote", + avatarLink: { + href: "/profile" + }, avatar: { id: "user-avatar" }, @@ -15,7 +18,7 @@ const header = { needAuth: false }, main: { - href: "/main", + href: "/", text: "Мои заметки", needAuth: true }, @@ -32,41 +35,80 @@ const header = { } } +const footer = { + id: "footer" +} + +const wrapper = { + id: "wrapper" +} + +const profilePage = { + href: "profile", + id: "profile-page", + logoutBtn: { + btnLabel: "Выйти" + } +} + const loginPage = { href: "login", - inputs: { - login: { - type: 'text', - placeholder: 'Введите логин' + form: { + id: "login-form", + inputs: { + login: { + type: 'text', + placeholder: 'Введите логин' + }, + password: { + type: "password", + placeholder: "Придумайте пароль" + } }, - password: { - type: "password", - placeholder: "Придумайте пароль" - } - }, - buttons: { - submitBtn: { - btnLabel: "Войти" + links: { + registerPage: { + href: "/register", + text: "Ещё не зарегистрированы?", + } + }, + buttons: { + submitBtn: { + btnLabel: "Войти" + } } } } const registerPage = { href: "register", - inputs: { - login: { - type: "text", - placeholder: "Придумайте логин" + form: { + id: "register-form", + inputs: { + login: { + type: "text", + placeholder: "Введите логин" + }, + password: { + type: "password", + placeholder: "Введите пароль" + }, + repeatPassword: { + type: "password", + placeholder: "Повторите пароль" + } }, - password: { - type: "password", - placeholder: "Придумайте пароль" + links: { + loginPage: { + href: "/login", + text: "Уже есть аккаунт?", + } }, - repeatPassword: { - type: "password", - placeholder: "Повторите пароль" + buttons: { + submitBtn: { + btnLabel: "Зарегистрироваться" + } } - } + }, } const notes = { @@ -90,9 +132,12 @@ export const config = { mainPage: mainPage, loginPage: loginPage, registerPage: registerPage, + profilePage: profilePage, header: header, notes: notes, noteEditor: noteEditor, note: note, - avatar: avatar + avatar: avatar, + wrapper: wrapper, + footer: footer }; diff --git a/public/index.css b/public/index.css index c1fa6255..45963e90 100644 --- a/public/index.css +++ b/public/index.css @@ -1,4 +1,7 @@ -@import './src/pages/main-page/main-page.css'; +@import 'src/pages/main/main.css'; +@import "src/pages/login/login.css"; +@import "src/pages/register/register.css"; +@import "src/pages/profile/profile.css"; @import './src/components/header/header.css'; @import './src/components/notes/notes.css'; @import './src/components/note/note.css'; @@ -7,8 +10,9 @@ @import './src/components/link/link.css'; @import './src/components/home/home.css'; @import './src/components/input/input.css'; -@import "./src/components/submit-button/submitButton.css"; -@import "src/pages/login/login.css"; +@import "src/components/button/button.css"; +@import "src/components/wrapper/wrapper.css"; +@import "src/components/footer/footer.css"; *, *::before, *::after { box-sizing: border-box; @@ -25,6 +29,11 @@ body { background: #D9D9D9; } +html, +body { + height: 100%; +} + img, picture, video, canvas, svg { display: block; max-width: 100%; @@ -41,9 +50,9 @@ p, h1, h2, h3, h4, h5, h6 { #root { display: flex; flex-direction: column; + height: 100%; } #wrapper{ - display: flex; flex: 1 0 auto; } \ No newline at end of file diff --git a/public/index.js b/public/index.js index 01d3b26e..00cfd411 100644 --- a/public/index.js +++ b/public/index.js @@ -1,29 +1,35 @@ import {config} from '/config.js'; import {Header} from "./src/components/header/header.js"; +import {Wrapper} from "./src/components/wrapper/wrapper.js"; +import {Footer} from "./src/components/footer/footer.js"; import {AppUserStore} from "./src/stores/user/userStore.js"; import {router} from "./src/modules/router.js"; const root = document.getElementById('root'); -console.log('root'); - -const wrapper = document.createElement("div") -wrapper.id = "wrapper" - -root.appendChild(wrapper) - AppUserStore.registerEvents(); - function renderHeader() { const header = new Header(root, config.header); header.render(); } -renderHeader(); +function renderFooter() { + const footer = new Footer(root, config.footer); + footer.render() +} + +function renderWrapper() { + const wrapper = new Wrapper(root, config.wrapper); + wrapper.render(); + router.init(wrapper.self, config); +} -router.init(wrapper, config) + +renderWrapper(); +renderHeader(); +renderFooter(); diff --git a/public/src/components/submit-button/submitButton.css b/public/src/components/button/button.css similarity index 76% rename from public/src/components/submit-button/submitButton.css rename to public/src/components/button/button.css index f2972e89..f129c519 100644 --- a/public/src/components/submit-button/submitButton.css +++ b/public/src/components/button/button.css @@ -6,13 +6,19 @@ background-color: #56A6F0; border-radius: 20px; border: none; + outline: none; color: #ffffff; text-align: center; padding: 8px; box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25); + cursor: pointer; + transition: 0.3s; } -.submit-btn:active{ +.submit-btn:hover{ + transform: scale(1.05) +} +.submit-btn:active{ box-shadow: inset 0 4px 4px rgba(0, 0, 0, 0.25); } \ No newline at end of file diff --git a/public/src/components/submit-button/submitButton.hbs b/public/src/components/button/button.hbs similarity index 100% rename from public/src/components/submit-button/submitButton.hbs rename to public/src/components/button/button.hbs diff --git a/public/src/components/submit-button/submitButton.js b/public/src/components/button/button.js similarity index 89% rename from public/src/components/submit-button/submitButton.js rename to public/src/components/button/button.js index 4a036dff..7eb8fde6 100644 --- a/public/src/components/submit-button/submitButton.js +++ b/public/src/components/button/button.js @@ -1,8 +1,8 @@ -import '../../../build/submitButton.js' +import '../../../build/button.js' import {AppEventMaker} from "../../modules/eventMaker.js"; import {SubmitButtonEvents} from "./events.js"; -export class SubmitButton{ +export class Button { #parent; #props = { btnLabel: '' @@ -46,7 +46,7 @@ export class SubmitButton{ this.#parent.insertAdjacentHTML( 'beforeend', - Handlebars.templates['submitButton.hbs'](this.#props) + Handlebars.templates['button.hbs'](this.#props) ) this.#addEventListeners(); diff --git a/public/src/components/submit-button/events.js b/public/src/components/button/events.js similarity index 100% rename from public/src/components/submit-button/events.js rename to public/src/components/button/events.js diff --git a/public/src/components/footer/footer.css b/public/src/components/footer/footer.css new file mode 100644 index 00000000..abb32196 --- /dev/null +++ b/public/src/components/footer/footer.css @@ -0,0 +1,6 @@ +footer { + padding: 20px; + background: black; + color: #fff; + flex: 0 0 auto; +} \ No newline at end of file diff --git a/public/src/components/footer/footer.hbs b/public/src/components/footer/footer.hbs new file mode 100644 index 00000000..45d1c6d6 --- /dev/null +++ b/public/src/components/footer/footer.hbs @@ -0,0 +1,3 @@ +
+ Scratch senior devs +
\ No newline at end of file diff --git a/public/src/components/footer/footer.js b/public/src/components/footer/footer.js new file mode 100644 index 00000000..4e311095 --- /dev/null +++ b/public/src/components/footer/footer.js @@ -0,0 +1,20 @@ +import "../../../build/footer.js" + +export class Footer { + #parent; + #config; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get self() { + return document.getElementById(`${this.#config.id}`) + } + + render() { + const template = window.Handlebars.templates["footer.hbs"]; + this.#parent.insertAdjacentHTML('beforeend', template(this.#config)); + } +} \ No newline at end of file diff --git a/public/src/components/header/header.css b/public/src/components/header/header.css index c9ddd5a8..18214817 100644 --- a/public/src/components/header/header.css +++ b/public/src/components/header/header.css @@ -1,7 +1,7 @@ header { width: 100%; - padding: 10px 25px; - height: 80px; + height: 60px; + padding: 0 25px; background: #fff; display: flex; justify-content: space-between; @@ -35,4 +35,5 @@ header .right-container .avatar { width: 50px; height: 50px; border-radius: 50%; + cursor: pointer; } \ No newline at end of file diff --git a/public/src/components/header/header.js b/public/src/components/header/header.js index 48903d02..104c8b16 100644 --- a/public/src/components/header/header.js +++ b/public/src/components/header/header.js @@ -31,7 +31,11 @@ export class Header { #addEventListeners(){ AppEventMaker.subscribe(UserStoreEvents.SUCCSSESFUL_LOGIN, () => { console.log("log hueg") - this.#avatar = new Image(document.querySelector(".right-container"), this.#config.avatar); + + const avatarLink = new Link(document.querySelector(".right-container"), this.#config.avatarLink) + avatarLink.render() + + this.#avatar = new Image(avatarLink.self, this.#config.avatar); this.#avatar.render(); this.#avatar.updateImage(AppUserStore.avatar) @@ -52,7 +56,10 @@ export class Header { render() { console.log("header render") - this.#parent.insertAdjacentHTML('afterbegin', window.Handlebars.templates['header.hbs'](this.#config)); + this.#parent.insertAdjacentHTML( + 'afterbegin', + window.Handlebars.templates['header.hbs'](this.#config) + ); const rightContainer = document.querySelector(".right-container") diff --git a/public/src/components/input/input.css b/public/src/components/input/input.css index 710ca201..e5bd5396 100644 --- a/public/src/components/input/input.css +++ b/public/src/components/input/input.css @@ -4,20 +4,38 @@ } .input-container input { + display: block; width: 100%; - background: #F2F2F2; - color: #000; - border: none; + padding: 12px; + border: 1px solid #ddd; outline: none; - padding: 8px 16px; - border-radius: 15px; + border-radius: 5px; + transition: 0.3s; +} + +.input-container input:focus { + border: 1px solid #56A6F0; +} + +.input-container[data-error] input { + border: 1px solid #c92432; + color: #c92432; + background: #fffafa; +} + +.input-container[data-error]::after { + content: attr(data-error); + color: #c92432; + font-size: 0.85em; + display: block; + margin: 5px 0; } .input-container .show-password-btn { position: absolute; width: 24px; height: 24px; - top: 8px; + top: 14px; right: 16px; cursor: pointer; } \ No newline at end of file diff --git a/public/src/components/input/input.hbs b/public/src/components/input/input.hbs index c91c698e..58f5b0af 100644 --- a/public/src/components/input/input.hbs +++ b/public/src/components/input/input.hbs @@ -1,5 +1,6 @@
+ {{#if isPassword}} {{/if}} diff --git a/public/src/components/input/input.js b/public/src/components/input/input.js index da408339..327081b1 100644 --- a/public/src/components/input/input.js +++ b/public/src/components/input/input.js @@ -9,6 +9,7 @@ export class Input { showPassword: '', change: '' } + #image; #input; @@ -35,6 +36,10 @@ export class Input { return this.#input.value; } + throwError(message) { + this.self.dataset.error = message + } + #change(){ AppEventMaker.notify(inputEvents.INPUT_CHANGE, this.#config.id); } @@ -75,6 +80,7 @@ export class Input { 'beforeend', template(this.#config) ); + this.#image = document.querySelector(`#input-${this.#config.id} > img`); this.#input = document.querySelector(`#input-${this.#config.id} > input`); this.#addEventListeners(); diff --git a/public/src/components/link/link.css b/public/src/components/link/link.css index e69de29b..67703134 100644 --- a/public/src/components/link/link.css +++ b/public/src/components/link/link.css @@ -0,0 +1,9 @@ +a { + color: dodgerblue; + text-decoration: none; + transition: 0.3s; +} + +a:hover { + color: blue; +} \ No newline at end of file diff --git a/public/src/components/note/note.css b/public/src/components/note/note.css index 67c76c25..a120ddf9 100644 --- a/public/src/components/note/note.css +++ b/public/src/components/note/note.css @@ -6,4 +6,5 @@ box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; border-radius: 15px; padding: 15px; + width: 300px; } \ No newline at end of file diff --git a/public/src/components/wrapper/wrapper.css b/public/src/components/wrapper/wrapper.css new file mode 100644 index 00000000..e69de29b diff --git a/public/src/components/wrapper/wrapper.hbs b/public/src/components/wrapper/wrapper.hbs new file mode 100644 index 00000000..ba1ff2ed --- /dev/null +++ b/public/src/components/wrapper/wrapper.hbs @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/public/src/components/wrapper/wrapper.js b/public/src/components/wrapper/wrapper.js new file mode 100644 index 00000000..eb2e2e6a --- /dev/null +++ b/public/src/components/wrapper/wrapper.js @@ -0,0 +1,20 @@ +import "../../../build/wrapper.js" + +export class Wrapper { + #parent; + #config; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get self() { + return document.getElementById(`${this.#config.id}`) + } + + render() { + const template = window.Handlebars.templates["wrapper.hbs"]; + this.#parent.insertAdjacentHTML('afterbegin', template(this.#config)); + } +} \ No newline at end of file diff --git a/public/src/modules/router.js b/public/src/modules/router.js index ea9976f4..78ef26db 100644 --- a/public/src/modules/router.js +++ b/public/src/modules/router.js @@ -1,6 +1,7 @@ -import MainPage from "../pages/main-page/main-page.js"; +import Main from "../pages/main/main.js"; import LoginPage from "../pages/login/login.js"; import RegisterPage from "../pages/register/register.js"; +import ProfilePage from "../pages/profile/profile.js"; class Router { #currentUrl; @@ -16,7 +17,7 @@ class Router { } init(root, config){ - const mainPage = new MainPage(root, config.mainPage) + const mainPage = new Main(root, config.mainPage) this.#pages.push(mainPage) const loginPage = new LoginPage(root, config.loginPage) @@ -25,6 +26,8 @@ class Router { const registerPage = new RegisterPage(root, config.registerPage) this.#pages.push(registerPage) + const profilePage = new ProfilePage(root, config.profilePage) + this.#pages.push(profilePage) this.changePage(this.#currentUrl) } diff --git a/public/src/pages/login/login.css b/public/src/pages/login/login.css index 50721262..aae82aec 100644 --- a/public/src/pages/login/login.css +++ b/public/src/pages/login/login.css @@ -1,12 +1,12 @@ -.centered-widget{ - display: flex; - align-items: center; - justify-content: center; +.login-form-wrapper { width: 100%; height: 100%; + display: flex; + justify-content: center; + align-items: center; } -.login-window { +.login-form { padding: 20px; border-radius: 20px; display: flex; @@ -14,6 +14,8 @@ align-items: center; justify-content: center; background: #ffffff; + width: 300px; + gap: 20px; } .login-window > div { diff --git a/public/src/pages/login/login.hbs b/public/src/pages/login/login.hbs index ee077b39..d9cf5e47 100644 --- a/public/src/pages/login/login.hbs +++ b/public/src/pages/login/login.hbs @@ -1,9 +1,5 @@ -
- -
\ No newline at end of file + diff --git a/public/src/pages/login/login.js b/public/src/pages/login/login.js index 431c0507..d340b484 100644 --- a/public/src/pages/login/login.js +++ b/public/src/pages/login/login.js @@ -1,11 +1,12 @@ import "../../../build/login.js" import {Input} from "../../components/input/input.js"; -import {SubmitButton} from "../../components/submit-button/submitButton.js"; +import {Button} from "../../components/button/button.js"; import {inputEvents} from "../../components/input/events.js"; -import {SubmitButtonEvents} from "../../components/submit-button/events.js"; +import {SubmitButtonEvents} from "../../components/button/events.js"; import {AppEventMaker} from "../../modules/eventMaker.js"; import {AppDispatcher} from "../../modules/dispathcer.js"; -import {AppUserStore, UserActions} from "../../stores/user/userStore.js"; +import {UserActions} from "../../stores/user/userStore.js"; +import {Link} from "../../components/link/link.js"; export default class LoginPage { #parent; @@ -13,6 +14,7 @@ export default class LoginPage { #loginInput; #passwordInput; + #link; #submitBtn; #subscribed; // Костыль @@ -27,7 +29,13 @@ export default class LoginPage { return this.#config.href; } + get form () { + return document.getElementById(this.#config.form.id) + } + #subscribeToEvents(){ + console.log("subscribeToEvents") + AppEventMaker.subscribe(inputEvents.INPUT_CHANGE, (id) => { if(id === this.#passwordInput.id){ this.#validatePassword() @@ -38,10 +46,15 @@ export default class LoginPage { AppEventMaker.subscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { if(id === this.#submitBtn.id){ - AppDispatcher.dispatch({ - type: UserActions.LOGIN, - payload: this.#loginInput.value - }) + const validateLogin = this.#validateLogin() + const validatePassword = this.#validatePassword() + if (validateLogin && validatePassword) { + + AppDispatcher.dispatch({ + type: UserActions.LOGIN, + payload: this.#loginInput.value + }) + } } }) @@ -54,6 +67,7 @@ export default class LoginPage { // TODO // Не работает + /* AppEventMaker.unsubscribe(inputEvents.INPUT_CHANGE, (id) => { if(id === this.#passwordInput.id){ this.#validatePassword() @@ -61,25 +75,48 @@ export default class LoginPage { this.#validateLogin(); } }); + */ AppEventMaker.unsubscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { - console.log("fasfdfasd") if(id === this.#submitBtn.id){ - console.log("fffff") AppDispatcher.dispatch({ type: UserActions.LOGIN }) } }) - } #validatePassword(){ - console.log("password validated") + console.log("password validation started") + + const value = this.#passwordInput.value + + if (value === "") + { + this.#passwordInput.throwError("Пароль не может быть пустым!") + return false + } + + return true } #validateLogin(){ - console.log("login validated") + console.log("login validation started") + + console.log(this.#loginInput) + delete this.#loginInput.self.dataset.error + + const value = this.#loginInput.value + + if (value === "") + { + this.#loginInput.throwError("Логин не может быть пустым!") + return false + } + + + + return true } remove(){ @@ -93,20 +130,26 @@ export default class LoginPage { render() { console.log("loginPage render") - this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['login.hbs'](this.#config.loginPage)); - this.#loginInput = new Input(document.querySelector('.username-input-place'), this.#config.inputs.login); + this.#parent.insertAdjacentHTML( + 'beforeend', + window.Handlebars.templates['login.hbs'](this.#config.form) + ); + + this.#loginInput = new Input(this.form, this.#config.form.inputs.login); this.#loginInput.render(); - this.#passwordInput = new Input(document.querySelector('.password-input-place'), this.#config.inputs.password); + this.#passwordInput = new Input(this.form, this.#config.form.inputs.password); this.#passwordInput.render(); - this.#submitBtn = new SubmitButton(document.querySelector('.submit-btn-place'), this.#config.buttons.submitBtn); + this.#link = new Link(this.form, this.#config.form.links.registerPage); + this.#link.render(); + + this.#submitBtn = new Button(this.form, this.#config.form.buttons.submitBtn); this.#submitBtn.render(); // Костыль if (!this.#subscribed) { this.#subscribeToEvents(); } - } } diff --git a/public/src/pages/main-page/main-page.hbs b/public/src/pages/main-page/main-page.hbs deleted file mode 100644 index ab9b2f1c..00000000 --- a/public/src/pages/main-page/main-page.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/public/src/pages/main-page/main-page.css b/public/src/pages/main/main.css similarity index 61% rename from public/src/pages/main-page/main-page.css rename to public/src/pages/main/main.css index f3309779..1dc25baa 100644 --- a/public/src/pages/main-page/main-page.css +++ b/public/src/pages/main/main.css @@ -1,6 +1,5 @@ -#main-page { +#main { display: grid; - grid-template-columns: 300px 500px; justify-content: center; padding: 25px; gap: 20px; diff --git a/public/src/pages/main/main.hbs b/public/src/pages/main/main.hbs new file mode 100644 index 00000000..9ab6dc7b --- /dev/null +++ b/public/src/pages/main/main.hbs @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/public/src/pages/main-page/main-page.js b/public/src/pages/main/main.js similarity index 62% rename from public/src/pages/main-page/main-page.js rename to public/src/pages/main/main.js index ca3cdd85..43c1a141 100644 --- a/public/src/pages/main-page/main-page.js +++ b/public/src/pages/main/main.js @@ -1,10 +1,9 @@ -import "../../../build/main-page.js" +import "../../../build/main.js" import {NotesContainer} from "../../components/notes/notes.js"; -import {NoteEditor} from "../../components/note-editor/note-editor.js"; import {AppUserStore} from "../../stores/user/userStore.js"; import {Home} from "../../components/home/home.js"; -export default class MainPage { +export default class Main { #parent; #config; @@ -18,28 +17,25 @@ export default class MainPage { } get self () { - return document.getElementById('main-page'); + return document.getElementById('main'); } remove(){ - console.log("MainPage remove") + console.log("Main remove") this.#parent.innerHTML = ''; } render() { - console.log("MainPage render") + console.log("Main page render") const tmp = document.createElement('div'); - const template = Handlebars.templates["main-page.hbs"]; - tmp.innerHTML = template(this.#config.mainPage); + const template = Handlebars.templates["main.hbs"]; + tmp.innerHTML = template(this.#config); this.#parent.appendChild(tmp.firstElementChild); if (AppUserStore.IsAuthenticated()) { const notesContainer = new NotesContainer(this.self, this.#config) notesContainer.render() - - const noteEditor = new NoteEditor(this.self, this.#config) - noteEditor.render() } else { const home = new Home(this.self, this.#config) home.render() diff --git a/public/src/pages/profile/profile.css b/public/src/pages/profile/profile.css new file mode 100644 index 00000000..eedf3f46 --- /dev/null +++ b/public/src/pages/profile/profile.css @@ -0,0 +1,8 @@ +.profile-page { + display: flex; + flex-direction: column; +} + +.profile-page button { + width: 200px; +} \ No newline at end of file diff --git a/public/src/pages/profile/profile.hbs b/public/src/pages/profile/profile.hbs new file mode 100644 index 00000000..d19d9a8f --- /dev/null +++ b/public/src/pages/profile/profile.hbs @@ -0,0 +1,3 @@ +
+

Страница профиля

+
\ No newline at end of file diff --git a/public/src/pages/profile/profile.js b/public/src/pages/profile/profile.js new file mode 100644 index 00000000..1959b8b5 --- /dev/null +++ b/public/src/pages/profile/profile.js @@ -0,0 +1,48 @@ +import "../../../build/profile.js" +import {AppUserStore} from "../../stores/user/userStore.js"; +import {Button} from "../../components/button/button.js"; + +export default class ProfilePage { + #parent; + #config; + + #logoutBtn; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get href () { + return this.#config.href; + } + + get self () { + return document.getElementById(this.#config.id); + } + + remove(){ + this.#parent.innerHTML = ''; + } + + render() { + console.log("Profile page render") + + this.#parent.insertAdjacentHTML( + 'afterbegin', + window.Handlebars.templates['profile.hbs'](this.#config) + ); + + console.log(this.self) + + this.#logoutBtn = new Button(this.self, this.#config.logoutBtn) + this.#logoutBtn.render() + + console.log(AppUserStore.username) + + const test = document.createElement("h1") + test.innerText = `Добро пожаловать, ${AppUserStore.username}` + + this.self.appendChild(test) + } +} diff --git a/public/src/pages/register/register.css b/public/src/pages/register/register.css index 63747594..589ae551 100644 --- a/public/src/pages/register/register.css +++ b/public/src/pages/register/register.css @@ -1,6 +1,22 @@ -#register-page { +.register-form-wrapper { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.register-form { + padding: 20px; + border-radius: 20px; display: flex; flex-direction: column; - gap: 15px; align-items: center; + justify-content: center; + background: #ffffff; + gap: 20px; +} + +.register-window > div { + margin: 10px; } \ No newline at end of file diff --git a/public/src/pages/register/register.hbs b/public/src/pages/register/register.hbs index b2a81402..1167e2d7 100644 --- a/public/src/pages/register/register.hbs +++ b/public/src/pages/register/register.hbs @@ -1,3 +1,5 @@ -
-

Страница регистрации

-
\ No newline at end of file +
+
+

Форма регистрации

+
+
diff --git a/public/src/pages/register/register.js b/public/src/pages/register/register.js index a9b9f419..8a568f39 100644 --- a/public/src/pages/register/register.js +++ b/public/src/pages/register/register.js @@ -1,10 +1,18 @@ import "../../../build/register.js" import {Input} from "../../components/input/input.js"; +import {Link} from "../../components/link/link.js"; +import {Button} from "../../components/button/button.js"; export default class RegisterPage { #parent; #config; + #loginInput; + #passwordInput; + #repeatPasswordInput; + #link; + #submitBtn; + constructor(parent, config) { this.#parent = parent; this.#config = config; @@ -14,23 +22,36 @@ export default class RegisterPage { return this.#config.href; } + get form () { + return document.getElementById(this.#config.form.id) + } + remove(){ this.#parent.innerHTML = ''; } render() { - this.#parent.insertAdjacentHTML('beforeend', window.Handlebars.templates['register.hbs'](this.#config.registerPage)); + this.#parent.insertAdjacentHTML( + 'beforeend', + window.Handlebars.templates['register.hbs'](this.#config.form) + ); const self = document.getElementById("register-page") - const input1 = new Input(self, this.#config.inputs.login) - input1.render() + this.#loginInput = new Input(this.form, this.#config.form.inputs.login); + this.#loginInput.render(); + + this.#passwordInput = new Input(this.form, this.#config.form.inputs.password); + this.#passwordInput.render(); + + this.#repeatPasswordInput = new Input(this.form, this.#config.form.inputs.repeatPassword); + this.#repeatPasswordInput.render(); - const input2 = new Input(self, this.#config.inputs.password) - input2.render() + this.#link = new Link(this.form, this.#config.form.links.loginPage); + this.#link.render(); - const input3 = new Input(self, this.#config.inputs.repeatPassword) - input3.render() + this.#submitBtn = new Button(this.form, this.#config.form.buttons.submitBtn); + this.#submitBtn.render(); } } From 5ad2a9d58752386e0b63a4bbc0029774b683c30f Mon Sep 17 00:00:00 2001 From: YarikMix <43493788+YarikMix@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:47:27 +0300 Subject: [PATCH 4/7] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B1=D0=B0=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/src/pages/login/login.js | 53 ++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/public/src/pages/login/login.js b/public/src/pages/login/login.js index d340b484..7975b165 100644 --- a/public/src/pages/login/login.js +++ b/public/src/pages/login/login.js @@ -19,10 +19,20 @@ export default class LoginPage { #subscribed; // Костыль + #callback; + constructor(parent, config) { this.#parent = parent; this.#config = config; this.#subscribed = false; // Костыль + this.#callback = (id) => { + console.log("asdfasdf") + if(id === this.#submitBtn.id){ + AppDispatcher.dispatch({ + type: UserActions.LOGIN + }) + } + } } get href () { @@ -33,6 +43,20 @@ export default class LoginPage { return document.getElementById(this.#config.form.id) } + validateData = (id) => { + if(id === this.#submitBtn.id){ + const validateLogin = this.#validateLogin() + const validatePassword = this.#validatePassword() + if (validateLogin && validatePassword) { + + AppDispatcher.dispatch({ + type: UserActions.LOGIN, + payload: this.#loginInput.value + }) + } + } + } + #subscribeToEvents(){ console.log("subscribeToEvents") @@ -44,25 +68,13 @@ export default class LoginPage { } }); - AppEventMaker.subscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { - if(id === this.#submitBtn.id){ - const validateLogin = this.#validateLogin() - const validatePassword = this.#validatePassword() - if (validateLogin && validatePassword) { - - AppDispatcher.dispatch({ - type: UserActions.LOGIN, - payload: this.#loginInput.value - }) - } - } - }) + AppEventMaker.subscribe(SubmitButtonEvents.BUTTON_SUBMIT, this.validateData) this.#subscribed = true; // Костыль } #unsubscribeToEvents(){ - console.log("unsubscribeToEvents") + console.log("unsubscribeToEvents login") // TODO // Не работает @@ -77,13 +89,7 @@ export default class LoginPage { }); */ - AppEventMaker.unsubscribe(SubmitButtonEvents.BUTTON_SUBMIT, (id) => { - if(id === this.#submitBtn.id){ - AppDispatcher.dispatch({ - type: UserActions.LOGIN - }) - } - }) + AppEventMaker.unsubscribe(SubmitButtonEvents.BUTTON_SUBMIT, this.validateData) } #validatePassword(){ @@ -104,6 +110,7 @@ export default class LoginPage { console.log("login validation started") console.log(this.#loginInput) + console.log(this.#loginInput.self.dataset) delete this.#loginInput.self.dataset.error const value = this.#loginInput.value @@ -148,8 +155,6 @@ export default class LoginPage { this.#submitBtn.render(); // Костыль - if (!this.#subscribed) { - this.#subscribeToEvents(); - } + this.#subscribeToEvents(); } } From f4580dd1c819173f85e21b186fd66c5a223fed26 Mon Sep 17 00:00:00 2001 From: YarikMix <43493788+YarikMix@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:18:48 +0300 Subject: [PATCH 5/7] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B2=D0=B5=D1=80=D1=81=D1=82=D0=BA=D1=83,=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B1=D0=B0=D0=B3?= =?UTF-8?q?=D0=B8,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 + public/config.js | 52 ++++++--- public/index.css | 3 + public/index.js | 26 ++--- public/src/assets/settings.png | Bin 0 -> 1225 bytes public/src/components/button/button.css | 3 +- public/src/components/button/button.hbs | 4 +- public/src/components/button/button.js | 32 +++--- public/src/components/header/header.css | 21 +++- public/src/components/header/header.hbs | 2 +- public/src/components/header/header.js | 61 +++++----- public/src/components/home/home.css | 19 ++++ public/src/components/home/home.hbs | 16 ++- public/src/components/home/home.js | 17 ++- public/src/components/input/input.css | 15 +++ .../components/link-button/link-button.css | 27 +++++ .../components/link-button/link-button.hbs | 3 + .../src/components/link-button/link-button.js | 43 ++++++++ public/src/components/link/link.hbs | 14 ++- public/src/components/link/link.js | 34 ++++-- public/src/modules/requests.js | 0 public/src/modules/router.js | 61 +++++----- public/src/pages/login/login.css | 1 + public/src/pages/login/login.js | 104 +++++++----------- public/src/pages/main/main.css | 3 +- public/src/pages/main/main.js | 10 +- public/src/pages/notFound/not-found.css | 13 +++ public/src/pages/notFound/not-found.hbs | 3 + public/src/pages/notFound/not-found.js | 37 +++++++ public/src/pages/profile/profile.css | 5 + public/src/pages/profile/profile.js | 26 +++-- public/src/pages/register/register.css | 1 + public/src/pages/register/register.hbs | 2 +- public/src/pages/register/register.js | 68 +++++++++++- public/src/stores/user/events.js | 3 +- public/src/stores/user/userStore.js | 19 +++- server/index.js | 47 +------- 37 files changed, 536 insertions(+), 261 deletions(-) create mode 100644 public/src/assets/settings.png create mode 100644 public/src/components/link-button/link-button.css create mode 100644 public/src/components/link-button/link-button.hbs create mode 100644 public/src/components/link-button/link-button.js create mode 100644 public/src/modules/requests.js create mode 100644 public/src/pages/notFound/not-found.css create mode 100644 public/src/pages/notFound/not-found.hbs create mode 100644 public/src/pages/notFound/not-found.js diff --git a/build.sh b/build.sh index 462943d1..58d12c44 100644 --- a/build.sh +++ b/build.sh @@ -4,6 +4,7 @@ handlebars -m public/src/pages/main/main.hbs -f public/build/main.js handlebars -m public/src/pages/login/login.hbs -f public/build/login.js handlebars -m public/src/pages/register/register.hbs -f public/build/register.js handlebars -m public/src/pages/profile/profile.hbs -f public/build/profile.js +handlebars -m public/src/pages/notFound/not-found.hbs -f public/build/not-found.js handlebars -m public/src/components/image/image.hbs -f public/build/image.js handlebars -m public/src/components/header/header.hbs -f public/build/header.js handlebars -m public/src/components/notes/notes.hbs -f public/build/notes.js @@ -15,3 +16,4 @@ handlebars -m public/src/components/input/input.hbs -f public/build/input.js handlebars -m public/src/components/wrapper/wrapper.hbs -f public/build/wrapper.js handlebars -m public/src/components/footer/footer.hbs -f public/build/footer.js handlebars -m public/src/components/button/button.hbs -f public/build/button.js +handlebars -m public/src/components/link-button/link-button.hbs -f public/build/link-button.js diff --git a/public/config.js b/public/config.js index 533cd0ed..5bea6757 100644 --- a/public/config.js +++ b/public/config.js @@ -1,10 +1,23 @@ const mainPage = { + id: "main-page", href: "/", - needAuth: true + needAuth: true, + home: { + id: "home", + linkToLogin: { + href: "/login", + text: "Попробовать", + type: "submit" + } + } } const header = { name: "YouNote", + logo: { + href: "/", + text: "YouNote" + }, avatarLink: { href: "/profile" }, @@ -14,23 +27,20 @@ const header = { menu: { home: { href: "/", - text: "Главная", - needAuth: false + text: "Главная" }, main: { href: "/", - text: "Мои заметки", - needAuth: true + text: "Мои заметки" }, auth: { + id: "link-to-login", href: "/login", - text: "Вход", - needAuth: false + text: "Вход" }, register: { href: "/register", - text: "Регистрация", - needAuth: false + text: "Регистрация" } } } @@ -44,23 +54,25 @@ const wrapper = { } const profilePage = { - href: "profile", + href: "/profile", id: "profile-page", logoutBtn: { - btnLabel: "Выйти" + text: "Выйти" } } const loginPage = { - href: "login", + href: "/login", form: { id: "login-form", inputs: { login: { + id: "login", type: 'text', placeholder: 'Введите логин' }, password: { + id: "password", type: "password", placeholder: "Придумайте пароль" } @@ -73,14 +85,14 @@ const loginPage = { }, buttons: { submitBtn: { - btnLabel: "Войти" + text: "Войти" } } } } const registerPage = { - href: "register", + href: "/register", form: { id: "register-form", inputs: { @@ -105,12 +117,21 @@ const registerPage = { }, buttons: { submitBtn: { - btnLabel: "Зарегистрироваться" + text: "Зарегистрироваться" } } }, } +const notFoundPage = { + href: "/404", + id: "not-found", + link: { + href: "/", + text: "Вернуться на главную" + } +} + const notes = { } @@ -133,6 +154,7 @@ export const config = { loginPage: loginPage, registerPage: registerPage, profilePage: profilePage, + notFoundPage: notFoundPage, header: header, notes: notes, noteEditor: noteEditor, diff --git a/public/index.css b/public/index.css index 45963e90..efcc90e8 100644 --- a/public/index.css +++ b/public/index.css @@ -2,6 +2,7 @@ @import "src/pages/login/login.css"; @import "src/pages/register/register.css"; @import "src/pages/profile/profile.css"; +@import "src/pages/notFound/not-found.css"; @import './src/components/header/header.css'; @import './src/components/notes/notes.css'; @import './src/components/note/note.css'; @@ -13,6 +14,7 @@ @import "src/components/button/button.css"; @import "src/components/wrapper/wrapper.css"; @import "src/components/footer/footer.css"; +@import "src/components/link-button/link-button.css"; *, *::before, *::after { box-sizing: border-box; @@ -55,4 +57,5 @@ p, h1, h2, h3, h4, h5, h6 { #wrapper{ flex: 1 0 auto; + padding: 50px 0; } \ No newline at end of file diff --git a/public/index.js b/public/index.js index 00cfd411..e5a25441 100644 --- a/public/index.js +++ b/public/index.js @@ -10,26 +10,14 @@ const root = document.getElementById('root'); AppUserStore.registerEvents(); -function renderHeader() { - const header = new Header(root, config.header); - header.render(); -} +const wrapper = new Wrapper(root, config.wrapper); +wrapper.render(); -function renderFooter() { - const footer = new Footer(root, config.footer); - footer.render() -} +router.init(wrapper.self, config); -function renderWrapper() { - const wrapper = new Wrapper(root, config.wrapper); - wrapper.render(); - - router.init(wrapper.self, config); -} - - -renderWrapper(); -renderHeader(); -renderFooter(); +const header = new Header(root, config.header); +header.render(); +const footer = new Footer(root, config.footer); +footer.render() diff --git a/public/src/assets/settings.png b/public/src/assets/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c89ac4a13acf4d7875d4d59d5e13805b81e7e5 GIT binary patch literal 1225 zcmV;)1UCDLP)31Zem_rxJP(=**uGqbz?q1elG z)xT;FZz7U-SZF4bT%#h|PiZ#G+1lb{=b;Yb_*FoHpdg+zbpZ7w9b;Y5oer85Nw?H6P9pXLU*Dd6p4P- z8bO9|oEXp50eLAo;}U>(bwFH7-iA48+6QcmickX~)E83#zNA1TFF5(qP4Gse(PWY6 z-D)TX>cD}d(rn$U2xqJ({_e{p6TlVV zputv2KQkd`EBjL7O)Mdbkz3s{{XC&B7h3Qm?eiQus2F^&??Y|s^aX_ zzsG@N7Gx{Heqh#OOdPZWpRwDaJ_nuw-?dDkR-hVmp-R^klFz>9EC|}-q5!`I?Q55( zbUW^|?Mj)zZnn2Y3ot3fbq?6J3b+m&5h8dEcp;=K2!Bwe@}mN})&ZMAeX9}?tpW!G zh()0!$oD6L$`(>|+K&QiB9q{}BzZ70EvU?0I8kGq-jes z0PTXhqR=6wNHPFfH8d1}ty0R0$Zc8M5Kciu(2d-&dASI@6jBB%ZB84#g1TOTYDk(x zW?h}6akj#<`0{~H;1)8;(1kQS*SI9WuL9ga&m&>!u$Kz&z%3mo2S#To!Ld=3rzJxQg1ygrQqMBeB$tqYib9h;uad2;tWW z_>C$?cxi1XYFUzQjC{hs&({f;N&ZjdEd#F3H`DRB>9Y%k%hFS?G8pEhDIMMy6`=+~ zs4u1fWL4zM0->$y1oET1(1v@ry_KhP@9}@ zs5YL{WP3mp3x{zJz@Z%YileTt&1D6bYI7zRC$qRW2=LTl%oA}IIiekPq#gI)pQXtz nPlGyvx`R0oW&KQM*XRENxl_Ir`72aj00000NkvXXu0mjfp_V6I literal 0 HcmV?d00001 diff --git a/public/src/components/button/button.css b/public/src/components/button/button.css index f129c519..57f17d5d 100644 --- a/public/src/components/button/button.css +++ b/public/src/components/button/button.css @@ -1,5 +1,6 @@ .submit-btn{ width: 100%; + min-width: 80px; display: flex; align-items: center; justify-content: center; @@ -20,5 +21,5 @@ } .submit-btn:active{ - box-shadow: inset 0 4px 4px rgba(0, 0, 0, 0.25); + transform: scale(0.98) } \ No newline at end of file diff --git a/public/src/components/button/button.hbs b/public/src/components/button/button.hbs index e9486370..57403bed 100644 --- a/public/src/components/button/button.hbs +++ b/public/src/components/button/button.hbs @@ -1 +1,3 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/src/components/button/button.js b/public/src/components/button/button.js index 7eb8fde6..b59539b9 100644 --- a/public/src/components/button/button.js +++ b/public/src/components/button/button.js @@ -1,41 +1,35 @@ import '../../../build/button.js' -import {AppEventMaker} from "../../modules/eventMaker.js"; -import {SubmitButtonEvents} from "./events.js"; export class Button { #parent; #props = { - btnLabel: '' + id: "", + text: "", }; - id; + #onSubmit; - #listeners = { - submit: this.#submit.bind(this) - }; - - constructor(parent, {btnLabel}) { + constructor(parent, config, onSubmit) { this.id = crypto.randomUUID(); this.#parent = parent; - this.#props.btnLabel = btnLabel; this.#props.id = this.id; - // this.#listeners.submit = this.#submit.bind(this); + this.#props.text = config.text; + this.#onSubmit = onSubmit; } get self(){ return document.getElementById(`submit-btn-${this.id}`); } - #submit(event){ - event.preventDefault(); - AppEventMaker.notify(SubmitButtonEvents.BUTTON_SUBMIT, this.id); - } - #addEventListeners(){ - this.self.addEventListener('click', this.#listeners.submit); + this.self.addEventListener('click', (e) => { + console.log(e) + e.preventDefault() + this.#onSubmit() + }); } #removeEventListeners(){ - this.self.removeEventListener('click', this.#listeners.submit); + this.self.removeEventListener('click', this.#onSubmit); } remove(){ @@ -44,11 +38,13 @@ export class Button { render(){ + this.#parent.insertAdjacentHTML( 'beforeend', Handlebars.templates['button.hbs'](this.#props) ) + this.#addEventListeners(); } } \ No newline at end of file diff --git a/public/src/components/header/header.css b/public/src/components/header/header.css index 18214817..c57b0e98 100644 --- a/public/src/components/header/header.css +++ b/public/src/components/header/header.css @@ -1,12 +1,20 @@ header { width: 100%; - height: 60px; - padding: 0 25px; - background: #fff; + padding: 10px 25px; display: flex; justify-content: space-between; align-items: center; - box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + position: fixed; + background: rgba(0,0,0,0); + transition: 0.5s; +} + +header.black { + background: rgba(0,0,0,0.2); +} + +header.black .logo-container a { + color: #fff; } header .logo-container a{ @@ -14,6 +22,7 @@ header .logo-container a{ text-decoration: none; font-weight: bold; font-size: 18px; + transition: 0.5s; } header .right-container { @@ -32,8 +41,8 @@ header .menu-container .menu-item a { } header .right-container .avatar { - width: 50px; - height: 50px; + width: 30px; + height: 30px; border-radius: 50%; cursor: pointer; } \ No newline at end of file diff --git a/public/src/components/header/header.hbs b/public/src/components/header/header.hbs index dbf427eb..e17dc92b 100644 --- a/public/src/components/header/header.hbs +++ b/public/src/components/header/header.hbs @@ -1,7 +1,7 @@