diff --git a/.eslintrc.js b/.eslintrc.js index fe0f890..88c68a4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,5 +15,8 @@ module.exports = { "ecmaVersion": 2018, "sourceType": "module" }, + "rules": { + "no-param-reassign": [2, { "props": false }] + } }; diff --git a/debug.log b/debug.log index 571b8a3..ae6bd58 100644 --- a/debug.log +++ b/debug.log @@ -5,3 +5,6 @@ [0112/191140.288:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [0113/181410.359:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [0114/181756.347:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) +[0118/180712.859:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) +[0121/174104.700:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) +[0125/183609.977:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) diff --git a/package-lock.json b/package-lock.json index 140608e..dab33e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9168,6 +9168,11 @@ "mimic-fn": "^2.1.0" } }, + "openweather-apis": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/openweather-apis/-/openweather-apis-4.4.1.tgz", + "integrity": "sha512-/t3IVSEwLFQN2xBdu4iAib9OifLzdAbea/OCokchZgvMOgwMEmr8g/pRz2HjuYQpHp6HWaiS2dMf5h48LJPGug==" + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", diff --git a/package.json b/package.json index 18bbc67..4915573 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "dependencies": { "chart.js": "^2.9.3", "core-js": "^3.4.7", + "openweather-apis": "^4.4.1", "regenerator-runtime": "^0.13.3" } } diff --git a/src/assets/audio/ambient.mp3 b/src/assets/audio/ambient.mp3 new file mode 100644 index 0000000..b11c7aa Binary files /dev/null and b/src/assets/audio/ambient.mp3 differ diff --git a/src/assets/audio/birds-waves.mp3 b/src/assets/audio/birds-waves.mp3 new file mode 100644 index 0000000..4f13460 Binary files /dev/null and b/src/assets/audio/birds-waves.mp3 differ diff --git a/src/assets/audio/birds.mp3 b/src/assets/audio/birds.mp3 new file mode 100644 index 0000000..fc41555 Binary files /dev/null and b/src/assets/audio/birds.mp3 differ diff --git a/src/assets/audio/cafe.mp3 b/src/assets/audio/cafe.mp3 new file mode 100644 index 0000000..ea51963 Binary files /dev/null and b/src/assets/audio/cafe.mp3 differ diff --git a/src/assets/audio/thunder.mp3 b/src/assets/audio/thunder.mp3 new file mode 100644 index 0000000..03f8845 Binary files /dev/null and b/src/assets/audio/thunder.mp3 differ diff --git a/src/assets/audio/waves.mp3 b/src/assets/audio/waves.mp3 new file mode 100644 index 0000000..193bb34 Binary files /dev/null and b/src/assets/audio/waves.mp3 differ diff --git a/src/assets/fonts/fonts-calculator/Crystal.ttf b/src/assets/fonts/fonts-calculator/Crystal.ttf new file mode 100644 index 0000000..3c6f5fd Binary files /dev/null and b/src/assets/fonts/fonts-calculator/Crystal.ttf differ diff --git a/src/assets/fonts/fonts-calculator/Roboto-Bold.ttf b/src/assets/fonts/fonts-calculator/Roboto-Bold.ttf new file mode 100644 index 0000000..d3f01ad Binary files /dev/null and b/src/assets/fonts/fonts-calculator/Roboto-Bold.ttf differ diff --git a/src/assets/fonts/fonts-calculator/Roboto-Regular.ttf b/src/assets/fonts/fonts-calculator/Roboto-Regular.ttf new file mode 100644 index 0000000..2c97eea Binary files /dev/null and b/src/assets/fonts/fonts-calculator/Roboto-Regular.ttf differ diff --git a/src/assets/fonts/fonts-calculator/symbol.ttf b/src/assets/fonts/fonts-calculator/symbol.ttf new file mode 100644 index 0000000..fd1c033 Binary files /dev/null and b/src/assets/fonts/fonts-calculator/symbol.ttf differ diff --git a/src/assets/fonts/fonts-weather/owfont-regular.eot b/src/assets/fonts/fonts-weather/owfont-regular.eot new file mode 100644 index 0000000..d536096 Binary files /dev/null and b/src/assets/fonts/fonts-weather/owfont-regular.eot differ diff --git a/src/assets/fonts/fonts-weather/owfont-regular.otf b/src/assets/fonts/fonts-weather/owfont-regular.otf new file mode 100644 index 0000000..42e6ffa Binary files /dev/null and b/src/assets/fonts/fonts-weather/owfont-regular.otf differ diff --git a/src/assets/fonts/fonts-weather/owfont-regular.svg b/src/assets/fonts/fonts-weather/owfont-regular.svg new file mode 100644 index 0000000..6969ffb --- /dev/null +++ b/src/assets/fonts/fonts-weather/owfont-regular.svg @@ -0,0 +1,394 @@ + + + + +Created by FontForge 20110222 at Wed Feb 11 17:55:33 2015 + By www-data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/fonts/fonts-weather/owfont-regular.ttf b/src/assets/fonts/fonts-weather/owfont-regular.ttf new file mode 100644 index 0000000..2b1683c Binary files /dev/null and b/src/assets/fonts/fonts-weather/owfont-regular.ttf differ diff --git a/src/assets/fonts/fonts-weather/owfont-regular.woff b/src/assets/fonts/fonts-weather/owfont-regular.woff new file mode 100644 index 0000000..591f3d0 Binary files /dev/null and b/src/assets/fonts/fonts-weather/owfont-regular.woff differ diff --git a/src/assets/img/metal.jpg b/src/assets/img/metal.jpg new file mode 100644 index 0000000..f110b03 Binary files /dev/null and b/src/assets/img/metal.jpg differ diff --git a/src/assets/img/weather.jpg b/src/assets/img/weather.jpg new file mode 100644 index 0000000..da1f426 Binary files /dev/null and b/src/assets/img/weather.jpg differ diff --git a/src/css/style.css b/src/css/style.css index 57253ac..bf8399e 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -3,6 +3,7 @@ --accent-color-contrast: #120C0E; --error-color: #f00; --text-color: #000; + --input-outline-color: #ccc; --app-background-color: #fff; --header-footer-text-color: #FDF6A1; } diff --git a/src/index.html b/src/index.html index 1a45397..cfc5301 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,8 @@ - + + Start Page diff --git a/src/js/components/auth/auth.css b/src/js/components/auth/auth.css new file mode 100644 index 0000000..89918b0 --- /dev/null +++ b/src/js/components/auth/auth.css @@ -0,0 +1,105 @@ +.modal-shadow { + position: fixed; + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.5); + top: 0; +} + +.modal { + width: 300px; + padding: 10px 0; + background-color: var(--modal-background-color); + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 9px; +} + +.auth { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + border-radius: 10px; + width: 300px; + background-color: var(--accent-color-contrast); +} + +.auth-header { + margin-top: 15px; + font-size: 33px; + line-height: 33px; + color: var(--header-footer-text-color); +} + +.auth-description { + margin: 10px 10px 20px 10px; + font-size: 16px; + color: var(--header-footer-text-color); +} + +.auth-form { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.auth-inputs { + display: flex; + flex-direction: column; + align-items: center; + padding: 10px; + background-color: var(--header-footer-text-color); + width: 100%; +} + +.auth-form-input { + max-width: 260px; + width: calc(100% - 20px); + font-size: 16px; + padding: 12px; + margin: 10px; + border: 1px solid var(--input-outline-color); + border-radius: 5px; +} + +.auth-form-error { + max-width: 260px; + color: var(--error-color); + margin-bottom: 20px; +} + +.auth-button { + font-weight: bold; + margin: 10px; + padding: 10px 20px; + font-size: 18px; + background-color: var(--header-footer-text-color); + border: none; + border-radius: 5px; + cursor: pointer; +} + +.button-register { + background-color: transparent; + color: var(--header-footer-text-color); +} + +.auth-button:focus { + outline:none; + box-shadow: none; +} + +.button-log-in:hover { + background-color: var(--app-background-color); +} + +.button-register:hover { + color: var(--app-background-color); +} diff --git a/src/js/components/auth/auth.js b/src/js/components/auth/auth.js new file mode 100644 index 0000000..0c427d7 --- /dev/null +++ b/src/js/components/auth/auth.js @@ -0,0 +1,176 @@ +import './auth.css'; +import * as Constants from '../../data/constants'; +import create from '../../utils/create'; +import { validatePassword, validateEmail } from './validation'; +import * as RemoteAuth from '../../services/auth'; + +export class Auth { + constructor(register) { + this.isLogin = register; + this.modelContainer = create('div', 'modal-shadow'); + this.modal = create('div', 'auth'); + this.modelContainer.appendChild(this.modal); + this.modelContainer.addEventListener('click', this.clickShadow.bind(this)); + document.body.appendChild(this.modelContainer); + this.render(); + this.addListeners(); + } + + render() { + const formCaption = (this.isLogin) ? 'Login' : 'Register'; + const emailInput = (this.isLogin) ? '' : `; + `; + const buttonLogin = (this.isLogin) ? 'Login' : 'Register'; + const buttonRegister = (this.isLogin) ? 'Register' : 'Login'; + const template = ` +

${formCaption}

+ Insert your e-mail and password. +
+
+ + ${emailInput} + + +
+
+ + +
+
+ `; + this.modal.innerHTML = template; + } + + addListeners() { + this.regBtn = document.querySelector('.button-register'); + this.loginBtn = document.querySelector('.button-log-in'); + if (this.isLogin) { + this.regBtn.addEventListener('click', this.switchForm.bind(this)); + this.loginBtn.addEventListener('click', this.login.bind(this)); + } else { + this.regBtn.addEventListener('click', this.switchForm.bind(this)); + this.loginBtn.addEventListener('click', this.register.bind(this)); + const password = document.querySelector('#auth-password'); + password.addEventListener('change', this.validatePassword.bind(this)); + const email = document.querySelector('#auth-email'); + email.addEventListener('change', this.validateEmail.bind(this)); + } + } + + register(evt) { + evt.preventDefault(); + const username = document.querySelector('#auth-username'); + const email = document.querySelector('#auth-email'); + const password = document.querySelector('#auth-password'); + this.registerUser(username.value, email.value, password.value); + } + + login(evt) { + evt.preventDefault(); + const username = document.querySelector('#auth-username'); + const password = document.querySelector('#auth-password'); + this.loginUser(username.value, password.value); + } + + switchForm() { + this.isLogin = !this.isLogin; + this.modal.innerHTML = ''; + this.render(); + this.addListeners(); + } + + validatePassword(evt) { + const mess = document.querySelector('.auth-form-error'); + if (!validatePassword(evt.target.value)) { + mess.innerText = 'password must contain digits, uppercase and lovercase letters, special symbols'; + } else { + mess.innerText =''; + } + } + + validateEmail(evt) { + const mess = document.querySelector('.auth-form-error'); + if (!validateEmail(evt.target.value)) { + mess.innerText = 'incorrect email'; + } else { + mess.innerText =''; + } + } + + clickShadow(evt) { + if(evt.target === this.modelContainer) { + this.closeModal(); + } + } + + closeModal() { + document.body.removeChild(this.modelContainer); + window.myapp.header.deleteLoginForm(); + } + + async registerUser(username, email, password) { + try { + const res = await RemoteAuth.registerUser(username, email, password); + if (res.statusCode === 200) { + localStorage.setItem(Constants.userItemLocalStorage, JSON.stringify(res.token)); + window.myapp.header.setLogged(); + this.closeModal(); + } + if (res.statusCode === 400) { + const mess = document.querySelector('.auth-form-error'); + mess.innerText = `${res.reason}`; + } + } catch (err) { + const mess = document.querySelector('.auth-form-error'); + mess.innerText = `${err.name}: ${err.message}`; + } + } + + async loginUser(username, password) { + try { + const res = await RemoteAuth.loginUser(username, password); + if (res.statusCode === 200) { + localStorage.setItem(Constants.userItemLocalStorage, JSON.stringify(res.token)); + window.myapp.header.setLogged(); + this.closeModal(); + } + if (res.statusCode === 403) { + const mess = document.querySelector('.auth-form-error'); + mess.innerText = `${res.reason}`; + } + } catch (err) { + const mess = document.querySelector('.auth-form-error'); + mess.innerText = `${err.name}: ${err.message}`; + } + } +} + +export default Auth; diff --git a/src/js/components/auth/validation.js b/src/js/components/auth/validation.js new file mode 100644 index 0000000..8d15ccd --- /dev/null +++ b/src/js/components/auth/validation.js @@ -0,0 +1,10 @@ +export const validatePassword = function validatePassword(password) { + const re = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?!.*\s).{8,15}$/; + return re.test(password); +}; + +export const validateEmail = function validateEmail(email) { + // eslint-disable-next-line no-useless-escape,max-len + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +}; diff --git a/src/js/components/base-menu/baseMenu.css b/src/js/components/base-menu/baseMenu.css index 5cfb3be..07cee15 100644 --- a/src/js/components/base-menu/baseMenu.css +++ b/src/js/components/base-menu/baseMenu.css @@ -36,6 +36,9 @@ -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; padding: 40px; background-color: #ffffff; } @@ -51,7 +54,9 @@ .cross { width: 25px; height: 25px; + padding: 14px; background: url(../../../assets/img/Group.png); + background-repeat: no-repeat; } .cross:hover { @@ -103,4 +108,15 @@ -webkit-transform: translate(400px); transform: translate(400px); } + +@media (max-width: 1700px) { + .baseMenu { + width: 320px; + -webkit-transform: translate(320px); + transform: translate(320px); + } + .header-block { + padding: 30px; + } +} /*# sourceMappingURL=baseMenu.css.map */ \ No newline at end of file diff --git a/src/js/components/base-menu/baseMenu.scss b/src/js/components/base-menu/baseMenu.scss index a53c5e3..ece1d33 100644 --- a/src/js/components/base-menu/baseMenu.scss +++ b/src/js/components/base-menu/baseMenu.scss @@ -29,10 +29,11 @@ $width: 400px; .header-block { display: flex; justify-content: space-between; + align-items: center; padding: 40px; background-color: #ffffff; - .header-caption{ + .header-caption { background-color: #ffffff; } } @@ -44,7 +45,9 @@ $width: 400px; .cross { width: 25px; height: 25px; + padding: 14px; background: url(../../../assets/img/Group.png); + background-repeat: no-repeat; &:hover { cursor: pointer; @@ -89,3 +92,17 @@ $width: 400px; .baseMenu.hide { transform: translate($width); } + + +@media (max-width: 1700px) { + $width: 320px; + + .baseMenu { + width: $width; + transform: translate($width); + } + + .header-block { + padding: 30px; + } +} \ No newline at end of file diff --git a/src/js/components/calculator/css/fonts.css b/src/js/components/calculator/css/fonts.css new file mode 100644 index 0000000..7744f61 --- /dev/null +++ b/src/js/components/calculator/css/fonts.css @@ -0,0 +1,31 @@ +@font-face { + font-family: 'Crystal'; + src: url('../../../../assets/fonts/fonts-calculator/Crystal.ttf') + format('truetype'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: 'Roboto'; + src: url('../../../../assets/fonts/fonts-calculator/Roboto-Regular.ttf') + format('truetype'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: 'Roboto'; + src: url('../../../../assets/fonts/fonts-calculator/Roboto-Bold.ttf') + format('truetype'); + font-weight: 700; + font-style: normal; +} + +@font-face { + font-family: 'Symbol'; + src: url('../../../../assets/fonts/fonts-calculator/symbol.ttf') + format('truetype'); + font-weight: normal; + font-style: normal; +} diff --git a/src/js/components/calculator/css/normalize.css b/src/js/components/calculator/css/normalize.css new file mode 100644 index 0000000..cbe9a2e --- /dev/null +++ b/src/js/components/calculator/css/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +/* main { + display: block; +} */ + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/src/js/components/calculator/css/style.css b/src/js/components/calculator/css/style.css new file mode 100644 index 0000000..e13e3d0 --- /dev/null +++ b/src/js/components/calculator/css/style.css @@ -0,0 +1,170 @@ +* { + box-sizing: border-box; + font-family: Roboto, sans-serif; +} + +.block.calc { + background-color: transparent; +} + +.calculator { + /* width: 400px; */ + min-height: 550px; + padding: 30px; + border-radius: 20px; + background-image: url('../../../../assets/img/metal.jpg'); +} + +@media only screen and (max-width: 540px) { + .calculator { + width: 320px; + height: 550px; + padding: 20px; + } +} + +.output { + width: 100%; + margin-bottom: 30px; + background-color: rgba(0, 0, 0, 0.75); +} + +.output .result, +.output .expression { + height: 50px; + display: flex; + justify-content: flex-end; + align-items: center; + padding-right: 10px; + overflow: hidden; +} + +@media only screen and (max-width: 540px) { + .output .result, + .output .expression { + height: 50px; + } +} + +.output .result p { + font-family: Crystal, sans-serif; + font-size: 35px; + line-height: 36px; + text-align: right; + word-wrap: break-word; + word-break: break-all; + color: rgba(255, 255, 255, 0.75); +} + +@media only screen and (max-width: 540px) { + .output .result p { + font-size: 22px; + } +} + +.output .expression p { + font-family: monospace; + font-size: 24px; + line-height: 140%; + text-align: right; + color: #b1ffb1; +} + +@media only screen and (max-width: 540px) { + .output .expression p { + font-size: 18px; + } +} + +.calculator-grid { + width: 100%; + height: 100%; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: stretch; +} + +.calculator-grid > button { + cursor: pointer; + width: 70px; + height: 44px; + border-radius: 10px; + font-size: 30px; + border: none; + outline: none; + background-color: #ccc; + margin-right: 6px; + box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.5); +} + +@media only screen and (max-width: 540px) { + .calculator-grid > button { + width: 55px; + height: 45px; + border-radius: 7px; + font-size: 30px; + } +} + +.calculator-grid > button:nth-child(4n + 1) { + margin-right: 0; +} + +.calculator-grid > button:hover { + background-color: rgba(255, 255, 255, 0.9); +} + +.note { + width: 500px; + color: #fff; + background-color: rgba(0, 0, 0, 0.5); + padding: 20px; + margin: 0 auto; +} + +@media only screen and (max-width: 540px) { + .note { + width: 320px; + } +} + +.note h2 { + text-align: center; +} + +@media only screen and (max-width: 540px) { + .note h2 { + font-size: 30px; + } +} + +@media (max-width: 1699px) { + .calculator-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-gap: 10px; + } + + .output { + grid-column: 1/5; + } + + .calculator-grid > button { + margin-right: 0; + width: 50px; + height: 44px; + border-radius: 10px; + font-size: 24px; + } +} + +@media (max-width: 359px) { + .calculator { + width: 300px; + } + + .note { + width: 300px; + } +} diff --git a/src/js/components/calculator/js/calculator.js b/src/js/components/calculator/js/calculator.js new file mode 100644 index 0000000..1a44ff9 --- /dev/null +++ b/src/js/components/calculator/js/calculator.js @@ -0,0 +1,123 @@ +import { expressionCalculator, opPriorities } from './expression-calculator'; +import '../css/normalize.css'; +import '../css/fonts.css'; +import '../css/style.css'; + +export class Calculator { + constructor(parentNode) { + this.parentNode = parentNode; + this.render(); + this.result = 0; + this.resultElem = document.querySelector('[data-result]'); + this.expression = ''; + this.expressionElem = document.querySelector('[data-expression]'); + this.addEventListeners(); + } + + handleBackspace() { + const len = this.expression.length; + this.expression = this.expression.substr(0, len - 1); + this.expressionElem.innerHTML = this.fixExpression(this.expression); + } + + handleClearAll() { + this.result = 0; + this.expression = ''; + this.expressionElem.innerText = this.expression; + this.resultElem.innerText = this.result; + } + + handlePlusMinus() { + const length = this.expression.length; + const str = this.expression; + this.expression = + str[length - 1] === '!' ? str.substr(0, length - 1) : str + '!'; + this.expressionElem.innerHTML = this.fixExpression(this.expression); + } + + handleEqual() { + const isOp = opPriorities[this.expression[0]] ? true : false; + if (isOp) { + if (!isNaN(this.result)) { + this.result = expressionCalculator(this.result + this.expression); + } + } else { + this.result = expressionCalculator(this.expression); + } + this.resultElem.innerText = isNaN(this.result) ? 'Error' : this.result; + this.expression = ''; + this.expressionElem.innerText = ''; + } + + fixExpression(expr) { + expr = expr.replaceAll('r', '√'); + expr = expr.replaceAll('-', '—'); + expr = expr.replaceAll('!', '‐'); + return expr; + } + + addEventListeners() { + const btns = document.querySelectorAll('[data-code]'); + btns.forEach((elem) => { + elem.addEventListener('click', (evt) => { + const code = evt.target.dataset.code; + this.expression += code; + this.expressionElem.innerHTML = this.fixExpression(this.expression); + }); + }); + + const backspaceButton = document.querySelector('[data-backspace]'); + backspaceButton.addEventListener('click', () => this.handleBackspace()); + + const clearallButton = document.querySelector('[data-all-clear]'); + clearallButton.addEventListener('click', () => this.handleClearAll()); + + const plusminusButton = document.querySelector('[data-plus-minus]'); + plusminusButton.addEventListener('click', () => this.handlePlusMinus()); + + const equalsButton = document.querySelector('[data-equals]'); + equalsButton.addEventListener('click', () => this.handleEqual()); + } + + render() { + this.parentNode.innerHTML = ` +
+
+
+

0

+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ `; + } +} diff --git a/src/js/components/calculator/js/expression-calculator.js b/src/js/components/calculator/js/expression-calculator.js new file mode 100644 index 0000000..4dc11d5 --- /dev/null +++ b/src/js/components/calculator/js/expression-calculator.js @@ -0,0 +1,121 @@ +export const opPriorities = {"+":1, "-":1, "/":2, "*":2, "r":3, "^":3}; +let num_stack = []; +let op_stack = []; + +export function expressionCalculator(expr) { + let ret; + try { + if (checkBrackets(expr) == false) throw new Error('ExpressionError: Brackets must be paired'); + let parsedExpr = parseString(expr); + let len = parsedExpr.length; + + for(let i = 0; i < len; i++) { + if (typeof(parsedExpr[i]) == "number") num_stack.push(parsedExpr[i]); + else if (parsedExpr[i] == "(") op_stack.push(parsedExpr[i]); + else if (parsedExpr[i] == ")") { + while(op_stack[op_stack.length - 1] != '(') doOperations(); + op_stack.pop(); + } + else { + while(true) { + if(op_stack.length == 0 || op_stack[op_stack.length - 1] == '(' || opPriorities[parsedExpr[i]] > opPriorities[op_stack[op_stack.length - 1]]) { + op_stack.push(parsedExpr[i]); + break; + } + else doOperations(); + } + } + } + while(op_stack.length != 0) doOperations(); + ret = toFixed(num_stack.pop()); + } + catch(err) { + console.log(err.message); + return NaN; + } + finally { + num_stack = []; + op_stack = []; + } + return ret; +} + +function checkBrackets(expr) { + let open_brackets = 0; + let closed_brackets = 0; + let len = expr.length; + for(let i = 0; i < len; i++) { + if(expr[i] == '(') open_brackets++; + else if(expr[i] == ')') closed_brackets++; + } + if(open_brackets == closed_brackets) return true; + else return false; +} + +function parseString(expr) { + let arr = []; + let len = expr.length; + let number = ''; + for(let i = 0; i < len; i++) { + let code = expr.charCodeAt(i); + if(code >= 48 && code <=57 || code === '.'.charCodeAt() || code === '!'.charCodeAt()) number += expr[i]; + else { + if(number != '') { + number = number.replace('!','-'); + arr.push(parseFloat(number)); + number = ''; + } + if(expr[i] != ' ') arr.push(expr[i]); + } + } + if(number != '') { + number = number.replace('!','-'); + arr.push(parseFloat(number)); + } + return arr; +} + +function doOperations() { + const cur_op = op_stack.pop(); + + let val; + if (cur_op === 'r') { + const x = num_stack.pop(); + if(x < 0) { + throw new Error("Square root from negative number"); + } + val = Math.sqrt(x); + } else { + const y = num_stack.pop(); + const x = num_stack.pop(); + val = calculateOperation(x, y, cur_op); + } + num_stack.push(val); +} + +function calculateOperation(arg1, arg2, op) { + let res = 0; + switch (op) { + case '+': + res = arg1 + arg2; break; + case '-': + res = arg1 - arg2; break; + case '*': + res = arg1 * arg2; break; + case '/': + if(arg2 == 0) { + throw new Error("Division by zero"); + } + res = arg1 / arg2; break; + case '%': + res = arg1 * arg2 / 100; break; + case '^': + res = Math.pow(arg1, arg2); + } + return res; +} + +function toFixed(value) { + var power = Math.pow(10, 14); + return String(Math.round(value * power) / power); +} diff --git a/src/js/components/command-menu/command-menu.css b/src/js/components/command-menu/command-menu.css index 7d4e9af..2c79ab5 100644 --- a/src/js/components/command-menu/command-menu.css +++ b/src/js/components/command-menu/command-menu.css @@ -1 +1,49 @@ -/* Стили списка команд */ \ No newline at end of file +.menu-manager .menu-item:hover, .other-items .options:hover { + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + background-color: rgba(78, 167, 166, 0.6); + border-radius: 9em; + -webkit-box-shadow: inset 1px 1px 10px #f3faf7; + box-shadow: inset 1px 1px 10px #f3faf7; +} + +.menu-manager { + padding: 10px; +} + +.menu-manager .menu-item { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin: 15px 10px; + padding: 6px; +} + +.menu-manager .menu-item p { + margin: 0 15px; +} + +.menu-manager .submit { + padding: 5px 10px; +} + +.menu-manager .submit.active-item { + background-color: #dc3545; +} + +.other-items { + padding: 10px; +} + +.other-items .options { + cursor: pointer; + margin: 0px 10px; + padding: 6px 20px; +} +/*# sourceMappingURL=command-menu.css.map */ \ No newline at end of file diff --git a/src/js/components/command-menu/command-menu.js b/src/js/components/command-menu/command-menu.js index 4901190..02f804f 100644 --- a/src/js/components/command-menu/command-menu.js +++ b/src/js/components/command-menu/command-menu.js @@ -1,10 +1,105 @@ -import "./command-menu.css"; +import './command-menu.css'; import Menu from '../base-menu/baseMenu'; +import { classListBlocks } from '../../data/constants'; +import create from '../../utils/create'; +import OptionsMenu from '../options-menu/options-menu'; + +const blockNames = [ + 'Finance', + 'News (RSS)', + 'Popular likns', + 'Shops', + 'Travels', + 'Google', + 'Weather', + 'To Do', + 'Calculator', +]; + +const getClassListBlocks = () => { + let blockList = []; + + const localBlockList = JSON.parse(localStorage.getItem('classListBlocks')); + + if (localBlockList) { + blockList = localBlockList; + } else { + localStorage.setItem('classListBlocks', JSON.stringify(classListBlocks)); + } + return blockList; +}; class CommandMenu extends Menu { - constructor(clickedElement, caption, obj) { + constructor(clickedElement, caption) { super(clickedElement, caption); + this.managerBlock = create('div', 'menu-manager'); + this.renderContent(); + } + + fillManagerBlock() { + this.managerBlock.innerHTML = ''; + + const localClassList = JSON.parse(localStorage.getItem('classListBlocks')); + localClassList.forEach((name, index) => { + const html = `

${blockNames[index]}

+ `; + const item = create( + 'div', + `menu-item item-${name.class}`, + html, + this.managerBlock + ); + + const block = this.parentNode.querySelector(`.block.${name.class}`); + + if (!name.active) block.classList.add('hidden-block'); + if (name.active) { + block.classList.remove('hidden-block'); + item.lastElementChild.textContent = 'hide'; + item.lastElementChild.classList.add('active-item'); + } + }); + } + + changePosition(e) { + const classId = e.target.dataset.btn; + + const localClassList = JSON.parse(localStorage.getItem('classListBlocks')); + + const newClassList = localClassList.map((item) => { + if (item.class === classId && item.active) + return { ...item, active: false }; + if (item.class === classId && !item.active) + return { ...item, active: true }; + return item; + }); + + localStorage.setItem('classListBlocks', JSON.stringify(newClassList)); + + this.fillManagerBlock(); + this.addBtnListener(); + } + + addBtnListener() { + const buttons = this.parentNode.querySelectorAll('.menu-item .submit'); + buttons.forEach((btn) => { + btn.onclick = this.changePosition.bind(this); + return btn; + }); + } + + renderContent() { + const contentBlock = document.querySelector('.menu-content.Main'); + contentBlock.appendChild(this.managerBlock); + + getClassListBlocks(); + this.fillManagerBlock(); + this.addBtnListener(); + + const otherMenuItems = create('div', 'other-items', '', contentBlock); + const options = create('div', 'options', 'Options', otherMenuItems); + this.optionsMenu = new OptionsMenu(options, 'Options'); } } diff --git a/src/js/components/command-menu/command-menu.scss b/src/js/components/command-menu/command-menu.scss new file mode 100644 index 0000000..f949c76 --- /dev/null +++ b/src/js/components/command-menu/command-menu.scss @@ -0,0 +1,48 @@ +%hoverItem { + transition: all 0.2s ease-in-out; + background-color: rgba(78, 167, 166, 0.6); + border-radius: 9em; + box-shadow: inset 1px 1px 10px #f3faf7; +} + +.menu-manager { + padding: 10px; + + .menu-item { + display: flex; + justify-content: space-between; + align-items: center; + margin: 15px 10px; + padding: 6px; + + p { + margin: 0 15px; + } + + &:hover { + @extend %hoverItem; + } + } + + .submit { + padding: 5px 10px; + } + + .submit.active-item { + background-color: #dc3545; + } +} + +.other-items { + padding: 10px; + + .options { + cursor: pointer; + margin: 0px 10px; + padding: 6px 20px; + + &:hover { + @extend %hoverItem; + } + } +} diff --git a/src/js/components/finance/finance-menu.js b/src/js/components/finance/finance-menu.js new file mode 100644 index 0000000..8ac1f41 --- /dev/null +++ b/src/js/components/finance/finance-menu.js @@ -0,0 +1,38 @@ +// import './rss-menu.css'; +import Menu from '../base-menu/baseMenu'; +import create from '../../utils/create'; + +class FinanceMenu extends Menu { + constructor(clickedElement, caption) { + super(clickedElement, caption); + this.renderContent(); + } + + addDangerBtn() { + const menuContent = this.parentNode.querySelector('.menu-content.Fina'); + + create( + 'div', + 'danger-block', + ``, + menuContent + ); + } + + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.finance'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector(`[data-btn="finance"]`); + mainMenuBtn.click(); + this.hide.bind(this)(); + }); + } + + renderContent() { + this.addDangerBtn(); + this.addDangerBtnListener(); + } +} + +export default FinanceMenu; diff --git a/src/js/components/finance/finance.css b/src/js/components/finance/finance.css index 61d41ff..ac960da 100644 --- a/src/js/components/finance/finance.css +++ b/src/js/components/finance/finance.css @@ -3,7 +3,7 @@ h3 { font-weight: bold; padding: 10px 22px; height: 45px; - background-color: #EEEEEE; + background-color: #eeeeee; } .currencyList { @@ -20,15 +20,24 @@ h3 { margin: 5px; } -.currencyChoiceBlock { +.wrapCurrencyChoiceBlock { display: -webkit-box; display: -ms-flexbox; display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; -ms-flex-pack: distribute; justify-content: space-around; + margin: 0 5px; +} + +.currencyChoiceBlock select:last-child { + margin-left: 10px; } #line-chart { height: 300px; + width: 100%; + padding: 5px; } /*# sourceMappingURL=finance.css.map */ \ No newline at end of file diff --git a/src/js/components/finance/finance.js b/src/js/components/finance/finance.js index 5ff56a8..5947245 100644 --- a/src/js/components/finance/finance.js +++ b/src/js/components/finance/finance.js @@ -1,35 +1,36 @@ -import "./finance.css"; -import Chart from "chart.js"; +import './finance.css'; +import Chart from 'chart.js'; +import FinanceMenu from './finance-menu'; -const nbrbURL = "https://www.nbrb.by/api/exrates/rates?periodicity=0"; +const nbrbURL = 'https://www.nbrb.by/api/exrates/rates?periodicity=0'; const extraCurrencyData = [ - { Cur_Abbreviation: "AUD", Cur_ID: 170, Cur_Scale: 1 }, - { Cur_Abbreviation: "BGN", Cur_ID: 191, Cur_Scale: 1 }, - { Cur_Abbreviation: "UAH", Cur_ID: 290, Cur_Scale: 100 }, - { Cur_Abbreviation: "DKK", Cur_ID: 291, Cur_Scale: 10 }, - { Cur_Abbreviation: "USD", Cur_ID: 145, Cur_Scale: 1 }, - { Cur_Abbreviation: "EUR", Cur_ID: 292, Cur_Scale: 1 }, - { Cur_Abbreviation: "PLN", Cur_ID: 293, Cur_Scale: 10 }, - { Cur_Abbreviation: "JPY", Cur_ID: 355, Cur_Scale: 100 }, - { Cur_Abbreviation: "IRR", Cur_ID: 303, Cur_Scale: 100000 }, - { Cur_Abbreviation: "ISK", Cur_ID: 294, Cur_Scale: 100 }, - { Cur_Abbreviation: "CAD", Cur_ID: 23, Cur_Scale: 1 }, - { Cur_Abbreviation: "CNY", Cur_ID: 304, Cur_Scale: 10 }, - { Cur_Abbreviation: "KWD", Cur_ID: 72, Cur_Scale: 1 }, - { Cur_Abbreviation: "MDL", Cur_ID: 296, Cur_Scale: 10 }, - { Cur_Abbreviation: "NZD", Cur_ID: 286, Cur_Scale: 1 }, - { Cur_Abbreviation: "NOK", Cur_ID: 297, Cur_Scale: 10 }, - { Cur_Abbreviation: "RUB", Cur_ID: 298, Cur_Scale: 100 }, - { Cur_Abbreviation: "XDR", Cur_ID: 299, Cur_Scale: 1 }, - { Cur_Abbreviation: "SGD", Cur_ID: 119, Cur_Scale: 1 }, - { Cur_Abbreviation: "KGS", Cur_ID: 300, Cur_Scale: 100 }, - { Cur_Abbreviation: "KZT", Cur_ID: 301, Cur_Scale: 1000 }, - { Cur_Abbreviation: "TRY", Cur_ID: 302, Cur_Scale: 10 }, - { Cur_Abbreviation: "GBP", Cur_ID: 143, Cur_Scale: 1 }, - { Cur_Abbreviation: "CZK", Cur_ID: 305, Cur_Scale: 100 }, - { Cur_Abbreviation: "SEK", Cur_ID: 306, Cur_Scale: 10 }, - { Cur_Abbreviation: "CHF", Cur_ID: 130, Cur_Scale: 1 }, + { Cur_Abbreviation: 'AUD', Cur_ID: 170, Cur_Scale: 1 }, + { Cur_Abbreviation: 'BGN', Cur_ID: 191, Cur_Scale: 1 }, + { Cur_Abbreviation: 'UAH', Cur_ID: 290, Cur_Scale: 100 }, + { Cur_Abbreviation: 'DKK', Cur_ID: 291, Cur_Scale: 10 }, + { Cur_Abbreviation: 'USD', Cur_ID: 145, Cur_Scale: 1 }, + { Cur_Abbreviation: 'EUR', Cur_ID: 292, Cur_Scale: 1 }, + { Cur_Abbreviation: 'PLN', Cur_ID: 293, Cur_Scale: 10 }, + { Cur_Abbreviation: 'JPY', Cur_ID: 355, Cur_Scale: 100 }, + { Cur_Abbreviation: 'IRR', Cur_ID: 303, Cur_Scale: 100000 }, + { Cur_Abbreviation: 'ISK', Cur_ID: 294, Cur_Scale: 100 }, + { Cur_Abbreviation: 'CAD', Cur_ID: 23, Cur_Scale: 1 }, + { Cur_Abbreviation: 'CNY', Cur_ID: 304, Cur_Scale: 10 }, + { Cur_Abbreviation: 'KWD', Cur_ID: 72, Cur_Scale: 1 }, + { Cur_Abbreviation: 'MDL', Cur_ID: 296, Cur_Scale: 10 }, + { Cur_Abbreviation: 'NZD', Cur_ID: 286, Cur_Scale: 1 }, + { Cur_Abbreviation: 'NOK', Cur_ID: 297, Cur_Scale: 10 }, + { Cur_Abbreviation: 'RUB', Cur_ID: 298, Cur_Scale: 100 }, + { Cur_Abbreviation: 'XDR', Cur_ID: 299, Cur_Scale: 1 }, + { Cur_Abbreviation: 'SGD', Cur_ID: 119, Cur_Scale: 1 }, + { Cur_Abbreviation: 'KGS', Cur_ID: 300, Cur_Scale: 100 }, + { Cur_Abbreviation: 'KZT', Cur_ID: 301, Cur_Scale: 1000 }, + { Cur_Abbreviation: 'TRY', Cur_ID: 302, Cur_Scale: 10 }, + { Cur_Abbreviation: 'GBP', Cur_ID: 143, Cur_Scale: 1 }, + { Cur_Abbreviation: 'CZK', Cur_ID: 305, Cur_Scale: 100 }, + { Cur_Abbreviation: 'SEK', Cur_ID: 306, Cur_Scale: 10 }, + { Cur_Abbreviation: 'CHF', Cur_ID: 130, Cur_Scale: 1 }, ]; const getRateForDayRequest = async () => { @@ -48,9 +49,9 @@ const getRateForDayRequest = async () => { const getCurrencyPairData = async () => { const data = await getRateForDayRequest(); - const eur = data.find((currency) => currency.Cur_Abbreviation === "EUR"); - const usd = data.find((currency) => currency.Cur_Abbreviation === "USD"); - const rub = data.find((currency) => currency.Cur_Abbreviation === "RUB"); + const eur = data.find((currency) => currency.Cur_Abbreviation === 'EUR'); + const usd = data.find((currency) => currency.Cur_Abbreviation === 'USD'); + const rub = data.find((currency) => currency.Cur_Abbreviation === 'RUB'); return { eur, usd, @@ -60,7 +61,7 @@ const getCurrencyPairData = async () => { const getDataForChart = async (currencyId) => { const currentDate = new Date(); - const dataDate = currentDate.toString().split(" "); + const dataDate = currentDate.toString().split(' '); const day = dataDate[0]; const month = dataDate[1]; const date = dataDate[2]; @@ -68,7 +69,7 @@ const getDataForChart = async (currencyId) => { const responseForYear = await fetch( `https://www.nbrb.by/API/ExRates/Rates/Dynamics/${currencyId} - ?startDate=Tue%2C+31+Dec+2019+21%3A00%3A00+GMT&endDate=${day} + ?startDate=Tue%2C+3+Mar+2020+21%3A00%3A00+GMT&endDate=${day} %2C+${date}+${month}+${year}+21%3A00%3A00+GMT` ); @@ -77,8 +78,8 @@ const getDataForChart = async (currencyId) => { class Finance { constructor(parentNode) { this.parentNode = parentNode; - this.currencyList = document.createElement("div"); - this.currencyChoiceBlock = document.createElement("div"); + this.currencyList = document.createElement('div'); + this.currencyChoiceBlock = document.createElement('div'); this.chart = false; this.render(); } @@ -86,10 +87,10 @@ class Finance { async createPairEurUsd() { const { eur, usd } = await getCurrencyPairData(); - const eurUsd = document.createElement("p"); + const eurUsd = document.createElement('p'); - this.currencyList.classList.add("currencyList"); - eurUsd.classList.add("eurUsd"); + this.currencyList.classList.add('currencyList'); + eurUsd.classList.add('eurUsd'); eurUsd.textContent = `EUR/USD: ${( eur.Cur_OfficialRate / @@ -102,9 +103,9 @@ class Finance { async createPairEurRub() { const { eur, rub } = await getCurrencyPairData(); - const eurRub = document.createElement("p"); + const eurRub = document.createElement('p'); - eurRub.classList.add("eurRub"); + eurRub.classList.add('eurRub'); eurRub.textContent = `EUR/RUB: ${( eur.Cur_OfficialRate / @@ -117,9 +118,9 @@ class Finance { async createPairBlrUsd() { const { usd } = await getCurrencyPairData(); - const blrUsd = document.createElement("p"); + const blrUsd = document.createElement('p'); - blrUsd.classList.add("blrUsd"); + blrUsd.classList.add('blrUsd'); blrUsd.textContent = `USD/BLR: ${( usd.Cur_OfficialRate / usd.Cur_Scale @@ -129,13 +130,13 @@ class Finance { } createSelect(selectName, selectedItem) { - const select = document.createElement("select"); + const select = document.createElement('select'); select.name = selectName; const fragment = document.createDocumentFragment(); extraCurrencyData.forEach((currency) => { - const option = document.createElement("option"); + const option = document.createElement('option'); option.value = `${currency.Cur_ID}`; if (currency.Cur_Abbreviation === selectedItem) option.selected = true; option.textContent = `${currency.Cur_Abbreviation}`; @@ -149,25 +150,32 @@ class Finance { } renderCurrencyChoiceBlock() { - this.currencyChoiceBlock.classList.add("currencyChoiceBlock"); - this.currencyChoiceBlock.textContent = "Select currency pair "; - this.parentNode.appendChild(this.currencyChoiceBlock); + const wrapCurrencyChoiceBlock = document.createElement('div'); + const titleCurrencyChoiceBlock = document.createElement('div'); + + titleCurrencyChoiceBlock.textContent = 'Select currency pair '; + wrapCurrencyChoiceBlock.classList.add('wrapCurrencyChoiceBlock'); + this.currencyChoiceBlock.classList.add('currencyChoiceBlock'); + + wrapCurrencyChoiceBlock.appendChild(titleCurrencyChoiceBlock); + wrapCurrencyChoiceBlock.appendChild(this.currencyChoiceBlock); + this.parentNode.appendChild(wrapCurrencyChoiceBlock); } axesLinearChart(updatedDate, currencyDynamic, rateCurrencyPair) { if (this.chart) { - const previousChart = document.querySelector(".chartBlock"); + const previousChart = document.querySelector('.chartBlock'); previousChart.parentElement.removeChild(previousChart); } - const chartBlock = document.createElement("div"); - chartBlock.classList.add("chartBlock"); - chartBlock.innerHTML = ``; + const chartBlock = document.createElement('div'); + chartBlock.classList.add('chartBlock'); + chartBlock.innerHTML = ``; this.parentNode.appendChild(chartBlock); - const ctx = document.getElementById("line-chart").getContext("2d"); + const ctx = document.getElementById('line-chart').getContext('2d'); this.chart = new Chart(ctx, { - type: "line", + type: 'line', data: { datasets: [ { @@ -176,8 +184,8 @@ class Finance { } Rate: ${rateCurrencyPair}`, data: currencyDynamic, fill: false, - borderColor: "red", - backgroundColor: "red", + borderColor: 'red', + backgroundColor: 'red', radius: 1, borderWidth: 1, }, @@ -211,7 +219,7 @@ class Finance { dates.forEach((dateItem) => shortDates.push(dateItem.slice(0, 10))); - if (select === "selectLeft") { + if (select === 'selectLeft') { currencyDynamicsLeft = []; yearData.forEach((val) => currencyDynamicsLeft.push( @@ -219,7 +227,7 @@ class Finance { ) ); } - if (select === "selectRight") { + if (select === 'selectRight') { currencyDynamicsRight = []; yearData.forEach((val) => currencyDynamicsRight.push( @@ -240,9 +248,9 @@ class Finance { ); }; - getYearCurrencyData(292, "selectLeft"); + getYearCurrencyData(292, 'selectLeft'); setTimeout(() => { - getYearCurrencyData(145, "selectRight"); + getYearCurrencyData(145, 'selectRight'); }, 0); let flag = false; @@ -255,23 +263,33 @@ class Finance { } } - const selectLeft = await this.createSelect("selectLeft", "EUR"); - const selectRight = await this.createSelect("selectRight", "USD"); + const selectLeft = await this.createSelect('selectLeft', 'EUR'); + const selectRight = await this.createSelect('selectRight', 'USD'); - selectLeft.addEventListener("click", addClickedCurrency); - selectRight.addEventListener("click", addClickedCurrency); + selectLeft.addEventListener('click', addClickedCurrency); + selectRight.addEventListener('click', addClickedCurrency); } render() { - const caption = document.createElement("h3"); - caption.textContent = "Finance"; - this.parentNode.appendChild(caption); + this.parentNode.innerHTML = ` + + `; this.createPairEurUsd(); this.createPairEurRub(); this.createPairBlrUsd(); this.parentNode.appendChild(this.currencyList); this.renderCurrencyChoiceBlock(); this.getFinanceData(); + + this.btnMenu = this.parentNode.querySelector('.dot-menu'); + this.rssMenu = new FinanceMenu(this.btnMenu, 'Finance'); } } diff --git a/src/js/components/finance/finance.scss b/src/js/components/finance/finance.scss index 44aa675..03a0460 100644 --- a/src/js/components/finance/finance.scss +++ b/src/js/components/finance/finance.scss @@ -3,24 +3,36 @@ h3 { font-weight: bold; padding: 10px 22px; height: 45px; - background-color: #EEEEEE; + background-color: #eeeeee; } -.currencyList{ +.currencyList { display: flex; justify-content: space-between; } -.currencyList p{ +.currencyList p { font-size: 0.7rem; margin: 5px; } -.currencyChoiceBlock{ +.wrapCurrencyChoiceBlock { display: flex; + flex-wrap: wrap; justify-content: space-around; + margin: 0 5px; } -#line-chart{ +.currencyChoiceBlock { + select { + &:last-child { + margin-left: 10px; + } + } +} + +#line-chart { height: 300px; -} \ No newline at end of file + width: 100%; + padding: 5px; +} diff --git a/src/js/components/footer/footer.css b/src/js/components/footer/footer.css index aaff4c0..002b683 100644 --- a/src/js/components/footer/footer.css +++ b/src/js/components/footer/footer.css @@ -12,7 +12,7 @@ footer { -webkit-box-align: center; -ms-flex-align: center; align-items: center; - height: 50px; + min-height: 50px; padding: 0 40px; background-color: var(--accent-color-contrast); color: var(--header-footer-text-color); @@ -49,4 +49,28 @@ footer span { height: 35px; margin-right: 10px; } + +@media (max-width: 767px) { + footer { + padding: 0 20px 10px 20px; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + } + footer .rsschool-logo { + -webkit-transform: scale(0.8); + transform: scale(0.8); + } + footer .github-logo { + -webkit-transform: scale(0.8); + transform: scale(0.8); + } + footer a { + font-size: 0.8rem; + margin: 0 5px; + text-align: center; + } +} /*# sourceMappingURL=footer.css.map */ \ No newline at end of file diff --git a/src/js/components/footer/footer.scss b/src/js/components/footer/footer.scss index 33d4a96..2ddba42 100644 --- a/src/js/components/footer/footer.scss +++ b/src/js/components/footer/footer.scss @@ -4,7 +4,7 @@ footer { flex: 0 0 auto; justify-content: space-between; align-items: center; - height: 50px; + min-height: 50px; padding: 0 40px; background-color: var(--accent-color-contrast); color: var(--header-footer-text-color); @@ -35,3 +35,25 @@ footer span { height: 35px; margin-right: 10px; } + +@media (max-width: 767px) { + footer { + padding: 0 20px 10px 20px; + flex-wrap: wrap; + justify-content: center; + + .rsschool-logo { + transform: scale(0.8); + } + + .github-logo { + transform: scale(0.8); + } + + a { + font-size: 0.8rem; + margin: 0 5px; + text-align: center; + } + } +} diff --git a/src/js/components/google-menu/google-menu.js b/src/js/components/google-menu/google-menu.js index eeddf97..a3b1d84 100644 --- a/src/js/components/google-menu/google-menu.js +++ b/src/js/components/google-menu/google-menu.js @@ -1,19 +1,29 @@ // import "./google-menu.css"; -import { fullGoogleLinks, faviconUrl } from "../../data/constants"; -import Menu from "../base-menu/baseMenu"; -import Google from "../google/google"; +import { fullGoogleLinks, faviconUrl } from '../../data/constants'; +import Menu from '../base-menu/baseMenu'; +import Google from '../google/google'; +import { getOptionItems } from '../options-menu/options-menu'; + +const getImage = (website) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = ``; + } else { + img = ''; + } + return img; +}; const getFullLinks = () => { let fullLinks = []; - const localGoogleLinks = JSON.parse( - localStorage.getItem("fullGoogleLinks") - ); + const localGoogleLinks = JSON.parse(localStorage.getItem('fullGoogleLinks')); if (localGoogleLinks) { fullLinks = localGoogleLinks; } else { fullLinks = fullGoogleLinks; - localStorage.setItem("fullGoogleLinks", JSON.stringify(fullGoogleLinks)); + localStorage.setItem('fullGoogleLinks', JSON.stringify(fullGoogleLinks)); } return fullLinks; }; @@ -21,13 +31,13 @@ const getFullLinks = () => { const getGoogleLinks = () => { let googleLinks = []; - const localGoogleLinks = JSON.parse(localStorage.getItem("googleLinks")); + const localGoogleLinks = JSON.parse(localStorage.getItem('googleLinks')); if (localGoogleLinks) { googleLinks = localGoogleLinks; } else { googleLinks = fullGoogleLinks.slice(0, 7); - localStorage.setItem("googleLinks", JSON.stringify(googleLinks)); + localStorage.setItem('googleLinks', JSON.stringify(googleLinks)); } return googleLinks; }; @@ -45,8 +55,8 @@ class GoogleMenu extends Menu { (website) => website.title === title ); - let check = ""; - if (activeLinks) check = "checked"; + let check = ''; + if (activeLinks) check = 'checked'; return check; } @@ -67,36 +77,36 @@ class GoogleMenu extends Menu { localGoogleLinks.push(necessaryService); } - localStorage.setItem("googleLinks", JSON.stringify(localGoogleLinks)); + localStorage.setItem('googleLinks', JSON.stringify(localGoogleLinks)); } clearMenuContent() { const websites = document.querySelectorAll(`.website.google`); websites.forEach((link) => link.parentElement.removeChild(link)); - const nameWebsite = document.querySelector(".name-input.google"); - const urlWebsite = document.querySelector(".url-input.google"); + const nameWebsite = document.querySelector('.name-input.google'); + const urlWebsite = document.querySelector('.url-input.google'); - if (nameWebsite) nameWebsite.value = ""; - if (urlWebsite) urlWebsite.value = ""; + if (nameWebsite) nameWebsite.value = ''; + if (urlWebsite) urlWebsite.value = ''; } fillMenuContent() { this.clearMenuContent(); - const menuContent = document.querySelector(".menu-content.Goog"); + const menuContent = document.querySelector('.menu-content.Goog'); const fragment = document.createDocumentFragment(); const fullLocalLinks = getFullLinks(); fullLocalLinks.forEach((website) => { const check = this.findActiveWebsite(website.title); - const web = document.createElement("div"); - web.classList.add("website", "google"); + const web = document.createElement('div'); + web.classList.add('website', 'google'); web.innerHTML = ` `; @@ -106,9 +116,9 @@ class GoogleMenu extends Menu { } createForm() { - const menuContent = document.querySelector(".menu-content.Goog"); - const form = document.createElement("div"); - form.classList.add("form"); + const menuContent = document.querySelector('.menu-content.Goog'); + const form = document.createElement('div'); + form.classList.add('form'); form.innerHTML = `
@@ -121,14 +131,17 @@ class GoogleMenu extends Menu {
+
+ +
`; menuContent.appendChild(form); } createObjForSet() { - const nameWebsite = document.querySelector(".name-input.google"); - const urlWebsite = document.querySelector(".url-input.google"); + const nameWebsite = document.querySelector('.name-input.google'); + const urlWebsite = document.querySelector('.url-input.google'); const title = nameWebsite.value; const url = urlWebsite.value; @@ -148,31 +161,30 @@ class GoogleMenu extends Menu { const data = this.createObjForSet(); if (data) { const fullLocalLinks = JSON.parse( - localStorage.getItem("fullGoogleLinks") + localStorage.getItem('fullGoogleLinks') ); fullLocalLinks.push(data); - localStorage.setItem("fullGoogleLinks", JSON.stringify(fullLocalLinks)); + localStorage.setItem('fullGoogleLinks', JSON.stringify(fullLocalLinks)); } } cleanLocalLinks() { - localStorage.removeItem("fullGoogleLinks"); - localStorage.removeItem("googleLinks"); - Google.prototype.fillContentBlock(this.privateClass, "googleLinks"); + localStorage.removeItem('fullGoogleLinks'); + localStorage.removeItem('googleLinks'); + Google.prototype.fillContentBlock(this.privateClass, 'googleLinks'); } changeLinks(e) { const websiteClickedCheckbox = e.target.dataset.google; const activeWebsite = this.findActiveWebsite(websiteClickedCheckbox); - console.log(activeWebsite, websiteClickedCheckbox); this.changeWebsiteArray(activeWebsite, websiteClickedCheckbox); - Google.prototype.fillContentBlock(this.privateClass, "googleLinks"); + Google.prototype.fillContentBlock(this.privateClass, 'googleLinks'); } addListenerToBtn() { const btnSub = document.querySelector(`.submit.google`); - btnSub.addEventListener("click", () => { + btnSub.addEventListener('click', () => { this.createObjForSet.bind(this)(); this.setObjData.bind(this)(); this.fillMenuContent.bind(this)(); @@ -182,7 +194,7 @@ class GoogleMenu extends Menu { addListenerToDelBtn() { const btnDel = document.querySelector(`.delete.google`); - btnDel.addEventListener("click", () => { + btnDel.addEventListener('click', () => { this.cleanLocalLinks(); this.fillMenuContent.bind(this)(); this.addListenerToLabel.bind(this)(); @@ -192,16 +204,31 @@ class GoogleMenu extends Menu { addListenerToLabel() { const labels = this.parentNode.querySelectorAll(`.input-google`); labels.forEach((label) => - label.addEventListener("click", this.changeLinks.bind(this)) + label.addEventListener('click', this.changeLinks.bind(this)) ); } + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.google'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector('[data-btn="google"]'); + const btnDel = document.querySelector('.delete.google'); + + mainMenuBtn.click(); + btnDel.click(); + + this.hide.bind(this)(); + }); + } + renderContent() { this.fillMenuContent(); this.createForm(); this.addListenerToLabel(); this.addListenerToBtn(); this.addListenerToDelBtn(); + this.addDangerBtnListener(); } } diff --git a/src/js/components/google/google.js b/src/js/components/google/google.js index 4456c97..f9ce6a0 100644 --- a/src/js/components/google/google.js +++ b/src/js/components/google/google.js @@ -2,7 +2,29 @@ import "./google.css"; import { fullGoogleLinks } from "../../data/constants"; import GoogleMenu from "../google-menu/google-menu"; +import { getOptionItems } from "../options-menu/options-menu"; +const getImage = (url) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = `website` + } else { + img = ''; + } + return img; +} + +const getTargetBlank = () => { + let targetBlank; + const optionTarget = getOptionItems()[1]; + if (optionTarget.checked) { + targetBlank = '_blank' + } else { + targetBlank = '' + } + return targetBlank; +} class Google { constructor(obj) { this.parentNode = obj.parentNode; @@ -43,8 +65,8 @@ class Google { website.classList.add("websites", `${myClass}`); website.innerHTML = ` - - website + + ${getImage(web.favicon)} ${web.title} `; diff --git a/src/js/components/header/header.css b/src/js/components/header/header.css index e28019d..0046fd0 100644 --- a/src/js/components/header/header.css +++ b/src/js/components/header/header.css @@ -21,4 +21,12 @@ h1 { font-size: 30px; color: var(--header-footer-text-color); } + +.login-btn { + background: transparent; + color: var(--header-footer-text-color); + cursor: pointer; + outline: none; + border: none; +} /*# sourceMappingURL=header.css.map */ \ No newline at end of file diff --git a/src/js/components/header/header.js b/src/js/components/header/header.js index 2523c4e..0d28584 100644 --- a/src/js/components/header/header.js +++ b/src/js/components/header/header.js @@ -2,6 +2,9 @@ import './header.css'; import * as Constants from '../../data/constants'; import create from '../../utils/create'; import Pages from '../pages/pages'; +import {Auth} from '../auth/auth'; +import * as RemoteAuth from '../../services/auth'; + import CommandMenu from '../command-menu/command-menu'; class Header { @@ -12,16 +15,45 @@ class Header { render() { const img = document.createElement('img'); - img.src = "./img/sp.jpg"; + img.src = "./img/sp.jpg"; this.pages = new Pages(); + const isLogged = RemoteAuth.isLogged(); + const btnLoginCaption = (isLogged) ? `

Logout

` : `

Login

`; + this.btnLogin = create('button', 'login-btn', btnLoginCaption); this.btnMenu = create('button', 'menu-btn', ``); - this.header = create('header', 'header', [img, this.pages.getMenu(), this.btnMenu], this.parentNode); + this.header = create('header', 'header', [img, this.pages.getMenu(), this.btnLogin, this.btnMenu], this.parentNode); + const handler = (isLogged) ? this.logout.bind(this) : this.showLoginForm.bind(this); + this.btnLogin.addEventListener('click', handler); this.showMenu(); } showMenu() { - this.sideMenu = new CommandMenu(this.btnMenu, 'Main Menu'); - } + setTimeout(() => { + this.sideMenu = new CommandMenu(this.btnMenu, 'Main Menu'); + }, 0); + } + + showLoginForm() { + this.auth = new Auth(true); + } + + deleteLoginForm() { + this.auth = null; + console.log('delete'); + } + + logout() { +// localStorage.removeItem(Constants.userItemLocalStorage); + this.btnLogin.removeEventListener('click', this.logout.bind(this)); + this.btnLogin.addEventListener('click', this.showLoginForm.bind(this)); + this.btnLogin.innerText = 'Login'; + } + + setLogged() { + this.btnLogin.addEventListener('click', this.logout.bind(this)); + this.btnLogin.removeEventListener('click', this.showLoginForm.bind(this)); + this.btnLogin.innerText = 'Logout'; + } } export default Header; diff --git a/src/js/components/main/main.css b/src/js/components/main/main.css index 2b42e5f..0189c10 100644 --- a/src/js/components/main/main.css +++ b/src/js/components/main/main.css @@ -1,13 +1,24 @@ main { - flex: 1 0 auto; + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto; padding: 20px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; } .container { float: left; - column-width: auto; - column-count: 4; - column-gap: 20px; + -webkit-column-width: auto; + column-width: auto; + -webkit-column-count: 4; + column-count: 4; + -webkit-column-gap: 20px; + column-gap: 20px; } .block { @@ -19,41 +30,89 @@ main { } .masonry { - margin: 0; + margin: 0; + display: -ms-grid; display: grid; - grid-template-rows: 1fr auto; + -ms-grid-rows: 1fr auto; + grid-template-rows: 1fr auto; margin-bottom: 20px; - break-inside: avoid; + -webkit-column-break-inside: avoid; + break-inside: avoid; } .bl1 { - height: 400px; + height: 400px; } + .bl2 { - height: 500px; + height: 500px; } + .bl3 { - height: 600px; + height: 600px; } + .bl4 { - height: 350px; + height: 350px; } + .bl5 { - height: 300px; + height: 300px; } + .bl6 { - height: 250px; + height: 250px; } + .bl7 { - height: 600px; + height: 600px; } + .bl8 { - height: 450px; + height: 450px; } + .bl9 { - height: 350px; + height: 350px; } +.block.hidden-block { + display: none; +} +@media (max-width: 1699px) { + .block { + width: 320px; + } +} + +@media (max-width: 1379px) { + .container { + -webkit-column-count: 3; + column-count: 3; + } +} + +@media (max-width: 1079px) { + .container { + -webkit-column-count: 2; + column-count: 2; + } +} + +@media (max-width: 767px) { + main { + padding: 20px 0; + } + .container { + -webkit-column-count: 1; + column-count: 1; + } +} +@media (max-width: 359px) { + .block { + width: 300px; + } +} /*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/src/js/components/main/main.js b/src/js/components/main/main.js index 4b3ddd9..c05ab01 100644 --- a/src/js/components/main/main.js +++ b/src/js/components/main/main.js @@ -1,11 +1,14 @@ -import "./main.css"; +import './main.css'; import create from '../../utils/create'; -import Finance from "../finance/finance"; -import Rss from "../rss-news/rss"; -import Popular from "../popular-links/popular"; -import Shops from "../shops/shops"; -import Travel from "../travel/travel"; -import Google from "../google/google"; +import Finance from '../finance/finance'; +import Rss from '../rss-news/rss'; +import Popular from '../popular-links/popular'; +import Shops from '../shops/shops'; +import Travel from '../travel/travel'; +import Google from '../google/google'; +import Weather from '../weather/weather'; +import ToDo from '../todo/todo'; +import { Calculator } from '../calculator/js/calculator'; class Main { constructor(parentNode) { @@ -14,58 +17,73 @@ class Main { } render() { - const main = create("main", "", null, this.parentNode); - const container = create("div", "container", null, main); + const main = create('main', '', null, this.parentNode); + const container = create('div', 'container', null, main); - const financeContainer = document.createElement("div"); - financeContainer.classList.add("block", "finance", "masonry"); + const financeContainer = document.createElement('div'); + financeContainer.classList.add('block', 'finance', 'masonry'); container.appendChild(financeContainer); this.fin = new Finance(financeContainer); - const rssNewsContainer = document.createElement("div"); - rssNewsContainer.classList.add("block", "rss", "masonry"); + const rssNewsContainer = document.createElement('div'); + rssNewsContainer.classList.add('block', 'rss', 'masonry'); container.appendChild(rssNewsContainer); this.rss = new Rss(rssNewsContainer); - const popularContainer = document.createElement("div"); - popularContainer.classList.add("block", "popular", "masonry"); + const popularContainer = document.createElement('div'); + popularContainer.classList.add('block', 'popular', 'masonry'); container.appendChild(popularContainer); this.popularContainer = new Popular({ parentNode: popularContainer, - privateClass: "popular", - caption: "Popular links", - arrayDataName: "popularLinks", + privateClass: 'popular', + caption: 'Popular links', + arrayDataName: 'popularLinks', }); - const shopsContainer = document.createElement("div"); - shopsContainer.classList.add("block", "shops", "masonry"); + const shopsContainer = document.createElement('div'); + shopsContainer.classList.add('block', 'shops', 'masonry'); container.appendChild(shopsContainer); this.shopsContainer = new Shops({ parentNode: shopsContainer, - privateClass: "shops", - caption: "Shops", - arrayDataName: "shopsLinks", + privateClass: 'shops', + caption: 'Shops', + arrayDataName: 'shopsLinks', }); - const travelContainer = document.createElement("div"); - travelContainer.classList.add("block", "travel", "masonry"); + const travelContainer = document.createElement('div'); + travelContainer.classList.add('block', 'travel', 'masonry'); container.appendChild(travelContainer); this.travelContainer = new Travel({ parentNode: travelContainer, - privateClass: "travel", - caption: "Travels", - arrayDataName: "travelLinks", + privateClass: 'travel', + caption: 'Travels', + arrayDataName: 'travelLinks', }); - const googleContainer = document.createElement("div"); - googleContainer.classList.add("block", "google", "masonry"); + const googleContainer = document.createElement('div'); + googleContainer.classList.add('block', 'google', 'masonry'); container.appendChild(googleContainer); this.googleContainer = new Google({ parentNode: googleContainer, - privateClass: "google", - caption: "Google", - arrayDataName: "googleLinks", + privateClass: 'google', + caption: 'Google', + arrayDataName: 'googleLinks', }); + + const weatherContainer = document.createElement('div'); + weatherContainer.classList.add('block', 'weather', 'masonry'); + container.appendChild(weatherContainer); + this.weatherContainer = new Weather(weatherContainer); + + const toDoContainer = document.createElement('div'); + toDoContainer.classList.add('block', 'todo', 'masonry'); + container.appendChild(toDoContainer); + this.toDoContainer = new ToDo(toDoContainer); + + const calc = document.createElement('div'); + calc.classList.add('block', 'calc', 'masonry'); + container.appendChild(calc); + this.calc = new Calculator(calc); } } diff --git a/src/js/components/main/main.scss b/src/js/components/main/main.scss index 19d261e..5475f87 100644 --- a/src/js/components/main/main.scss +++ b/src/js/components/main/main.scss @@ -1,15 +1,94 @@ main { - width: 100%; - height: calc(100vh - 120px); - background-color: var(--accent-color-light); + flex: 1 0 auto; padding: 20px; display: flex; - flex-wrap: wrap; + justify-content: center; +} + +.container { + float: left; + column-width: auto; + column-count: 4; + column-gap: 20px; } .block { width: 400px; background-color: var(--app-background-color); - margin: 5px; + height: -webkit-fit-content; + height: -moz-fit-content; height: fit-content; } + +.masonry { + margin: 0; + display: grid; + grid-template-rows: 1fr auto; + margin-bottom: 20px; + break-inside: avoid; +} + +.bl1 { + height: 400px; +} +.bl2 { + height: 500px; +} +.bl3 { + height: 600px; +} +.bl4 { + height: 350px; +} +.bl5 { + height: 300px; +} +.bl6 { + height: 250px; +} +.bl7 { + height: 600px; +} +.bl8 { + height: 450px; +} +.bl9 { + height: 350px; +} + +.block.hidden-block { + display: none; +} + +@media (max-width: 1699px) { + .block { + width: 320px; + } +} + +@media (max-width: 1379px) { + .container { + column-count: 3; + } +} + +@media (max-width: 1079px) { + .container { + column-count: 2; + } +} + +@media (max-width: 767px) { + main { + padding: 20px 0; + } + .container { + column-count: 1; + } +} + +@media (max-width: 359px) { + .block { + width: 300px; + } +} diff --git a/src/js/components/options-menu/options-menu.css b/src/js/components/options-menu/options-menu.css new file mode 100644 index 0000000..029c450 --- /dev/null +++ b/src/js/components/options-menu/options-menu.css @@ -0,0 +1,98 @@ +.menu-content.Opti { + padding: 15px; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.2s; + transition: 0.2s; +} + +.slider:before { + position: absolute; + content: ''; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.2s; + transition: 0.2s; +} + +input:checked + .slider { + background-color: #2196f3; +} + +input:focus + .slider { + -webkit-box-shadow: 0 0 1px #2196f3; + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +.items-block { + display: -ms-grid; + display: grid; + grid-gap: 20px; +} + +.option { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.select-block { + margin: 15px 0; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.select-block .select-sound { + padding: 10px 30px 10px 10px; + outline: none; + border-radius: 5px; +} +/*# sourceMappingURL=options-menu.css.map */ \ No newline at end of file diff --git a/src/js/components/options-menu/options-menu.js b/src/js/components/options-menu/options-menu.js new file mode 100644 index 0000000..e92b49f --- /dev/null +++ b/src/js/components/options-menu/options-menu.js @@ -0,0 +1,279 @@ +import './options-menu.css'; +import Menu from '../base-menu/baseMenu'; +import create from '../../utils/create'; + +const optionItems = [ + { + title: 'Show images for websites', + checked: true, + myClass: 'favicon-item', + }, + { + title: 'Open link in new tab', + checked: true, + myClass: 'target-blank', + }, + { + title: 'Turn on background sound', + checked: false, + myClass: 'bg-sound', + }, +]; + +const sounds = [ + { + name: 'ambient', + src: 'audio/ambient.mp3', + selected: false, + }, + { + name: 'birds-waves', + src: 'audio/birds-waves.mp3', + selected: false, + }, + { + name: 'birds', + src: 'audio/birds.mp3', + selected: true, + }, + { + name: 'cafe', + src: 'audio/cafe.mp3', + selected: false, + }, + { + name: 'thunder', + src: 'audio/thunder.mp3', + selected: false, + }, + { + name: 'waves', + src: 'audio/waves.mp3', + selected: false, + }, +]; + +export const getOptionItems = () => { + let tempOptionItems; + + const localOptionItems = JSON.parse(localStorage.getItem('optionItems')); + + if (localOptionItems) { + tempOptionItems = localOptionItems; + } else { + tempOptionItems = optionItems; + localStorage.setItem('optionItems', JSON.stringify(optionItems)); + } + return tempOptionItems; +}; + +export const getOptionFavicon = () => { + let optionFavicon; + + const localOptionFavicon = JSON.parse(localStorage.getItem('optionFavicon')); + + if (localOptionFavicon) { + optionFavicon = localOptionFavicon; + } else { + optionFavicon = optionItems[0]; + localStorage.setItem('optionFavicon', JSON.stringify(optionItems[0])); + } + return optionFavicon; +}; + +const getSoundList = () => { + let soundList = []; + + const localSoundList = JSON.parse(localStorage.getItem('soundList')); + + if (localSoundList) { + soundList = localSoundList; + } else { + soundList = sounds; + localStorage.setItem('soundList', JSON.stringify(sounds)); + } + return soundList; +}; + +class OptionsMenu extends Menu { + constructor(clickedElement, caption, privateClass) { + super(clickedElement, caption); + this.contentBlock = this.parentNode.querySelector('.menu-content.Opti'); + this.renderContent(); + this.privateClass = privateClass; + } + + fillItemsBlock() { + const itemsBlock = create('div', 'items-block', '', this.contentBlock); + const localOptionItems = getOptionItems(); + + localOptionItems.forEach((item) => { + const option = create('div', `option ${item.myClass}`, ``, itemsBlock); + + create('div', `title-${item.myClass}`, `${item.title}`, option); + + const check = item.checked && 'checked'; + const toggleHtml = ` + + `; + create('div', `toggle-switch`, toggleHtml, option); + }); + } + + addSelectListener(value) { + const localSoundList = JSON.parse(localStorage.getItem('soundList')); + const newList = localSoundList.map((sound) => { + if (sound.name === value) { + return { ...sound, selected: true }; + } else { + return { ...sound, selected: false }; + } + }); + + localStorage.setItem('soundList', JSON.stringify(newList)); + this.playSound(); + } + + createSelectSound() { + const selectBlock = create( + 'div', + 'select-block', + 'Choose sound', + this.contentBlock + ); + const select = create('select', 'select-sound', '', selectBlock); + + const localSoundList = getSoundList(); + + localSoundList.forEach((sound) => { + const option = create('option', 'option-sound', `${sound.name}`, select); + option.value = `${sound.name}`; + if (sound.selected) option.selected = true; + }); + + select.onclick = (e) => this.addSelectListener.bind(this)(e.target.value); + } + + creteAudioElements() { + sounds.forEach((item) => { + const audio = document.createElement('audio'); + audio.classList.add(`audio-${item.name}`); + audio.src = item.src; + audio.loop = true; + this.contentBlock.appendChild(audio); + }); + } + + stopCurrentSound(selectedSound) { + const currentSound = JSON.parse(localStorage.getItem('currentSound')); + + if (!currentSound) return; + if (currentSound.name === selectedSound.name) return; + + const audio = this.contentBlock.querySelector( + `.audio-${currentSound.name}` + ); + + audio.pause(); + audio.currentTime = 0; + } + + playSound() { + const toggle = this.contentBlock.querySelector('.checkbox-bg-sound'); + + const localSoundList = getSoundList(); + const selectedSound = localSoundList.find( + (sound) => sound.selected === true + ); + this.stopCurrentSound(selectedSound); + + localStorage.setItem('currentSound', JSON.stringify(selectedSound)); + + const audio = this.contentBlock.querySelector( + `.audio-${selectedSound.name}` + ); + + if (toggle.checked) audio.play(); + if (!toggle.checked) { + audio.pause(); + audio.currentTime = 0; + } + } + + toggleCheckboxSound() { + const toggle = this.contentBlock.querySelector('.checkbox-bg-sound'); + toggle.onclick = () => this.playSound.bind(this)(); + } + + toggleFaviconRequest() { + const currentRequest = getOptionItems(); + + if (currentRequest[0].checked) { + localStorage.setItem( + 'optionItems', + JSON.stringify([ + { ...currentRequest[0], checked: false }, + currentRequest[1], + currentRequest[2], + ]) + ); + } else { + localStorage.setItem( + 'optionItems', + JSON.stringify([ + { ...currentRequest[0], checked: true }, + currentRequest[1], + currentRequest[2], + ]) + ); + } + } + + toggleCheckboxFavicon() { + const toggle = this.contentBlock.querySelector('.checkbox-favicon-item'); + toggle.onclick = () => this.toggleFaviconRequest.bind(this)(); + } + + toggleTargetBlank() { + const currentRequest = getOptionItems(); + + if (currentRequest[1].checked) { + localStorage.setItem( + 'optionItems', + JSON.stringify([ + currentRequest[0], + { ...currentRequest[1], checked: false }, + currentRequest[2], + ]) + ); + } else { + localStorage.setItem( + 'optionItems', + JSON.stringify([ + currentRequest[0], + { ...currentRequest[1], checked: true }, + currentRequest[2], + ]) + ); + } + } + + toggleCheckboxTargetBlank() { + const toggle = this.contentBlock.querySelector('.checkbox-target-blank'); + toggle.onclick = () => this.toggleTargetBlank.bind(this)(); + } + + renderContent() { + this.fillItemsBlock(); + this.createSelectSound(); + this.creteAudioElements(); + this.toggleCheckboxSound(); + this.toggleCheckboxFavicon(); + this.toggleCheckboxTargetBlank(); + } +} + +export default OptionsMenu; diff --git a/src/js/components/options-menu/options-menu.scss b/src/js/components/options-menu/options-menu.scss new file mode 100644 index 0000000..cf41302 --- /dev/null +++ b/src/js/components/options-menu/options-menu.scss @@ -0,0 +1,84 @@ +.menu-content.Opti{ + padding: 15px; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.2s; + transition: 0.2s; +} + +.slider:before { + position: absolute; + content: ''; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.2s; + transition: 0.2s; +} + +input:checked + .slider { + background-color: #2196f3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +.items-block { + display: grid; + grid-gap: 20px; +} + +.option { + display: flex; + justify-content: space-between; + align-items: center; +} + +.select-block { + margin: 15px 0; + display: flex; + justify-content: space-between; + align-items: center; + + .select-sound { + padding: 10px 30px 10px 10px; + outline: none; + border-radius: 5px; + } +} diff --git a/src/js/components/pages/pages.js b/src/js/components/pages/pages.js index d9d563b..eb7c65b 100644 --- a/src/js/components/pages/pages.js +++ b/src/js/components/pages/pages.js @@ -56,7 +56,7 @@ class Pages { } addPage(name, color = Constants.DEFAULT_COLOR) { - this.pages.push({ name: name, color: color }); + this.pages.push({ name, color }); this.curPage = this.pages.length - 1; const li = this.createLiElem(name); location.hash = encodeURI(name); @@ -65,7 +65,7 @@ class Pages { deletePage(name) { if (this.pages.length > 1) { - let i = undefined; + let i; this.pages.forEach((elem, idx) => { if(elem.name === name) { i = idx; diff --git a/src/js/components/popular-links/popular.js b/src/js/components/popular-links/popular.js index fe3e469..d30416d 100644 --- a/src/js/components/popular-links/popular.js +++ b/src/js/components/popular-links/popular.js @@ -1,7 +1,29 @@ import PopMenu from "../popular-menu/popular-menu"; import "./popular.css"; import { fullPopularLinks } from "../../data/constants"; +import { getOptionItems } from "../options-menu/options-menu"; +const getImage = (url) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = `website` + } else { + img = ''; + } + return img; +} + +const getTargetBlank = () => { + let targetBlank; + const optionTarget = getOptionItems()[1]; + if (optionTarget.checked) { + targetBlank = '_blank' + } else { + targetBlank = '' + } + return targetBlank; +} class Popular { constructor(obj) { this.parentNode = obj.parentNode; @@ -34,6 +56,7 @@ class Popular { myClass = this.privateClass, arrayDataName = this.arrayDataName ) { + this.clearLinks(myClass); const localPopularLinks = this.getPopularLinks(arrayDataName || this.arrayDataName); const content = document.querySelector(`.${myClass}.popular-content`); @@ -42,8 +65,8 @@ class Popular { website.classList.add("websites", `${myClass}`); website.innerHTML = ` - - website + + ${getImage(web.favicon)} ${web.title} `; diff --git a/src/js/components/popular-menu/popular-menu.css b/src/js/components/popular-menu/popular-menu.css index 116bdd0..dcc4f58 100644 --- a/src/js/components/popular-menu/popular-menu.css +++ b/src/js/components/popular-menu/popular-menu.css @@ -42,6 +42,7 @@ .website .website-logo { width: 24px; + margin-right: 15px; } .form { @@ -83,8 +84,8 @@ padding: 10px; } -.btn-block .delete, -.btn-block .submit { +.delete { + background-color: gray; margin: 0 15px; padding: 10px 15px; border-radius: 5px; @@ -94,21 +95,96 @@ font-size: 0.8rem; } -.btn-block .delete:hover, -.btn-block .submit:hover { +.delete:hover { -webkit-transform: scale(1.1); transform: scale(1.1); - -webkit-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; border-radius: 5px; cursor: pointer; } -.btn-block .delete { - background-color: #dc3545; +.delete:active { + -webkit-transform: scale(0.9); + transform: scale(0.9); } -.btn-block .submit { +.submit { background-color: #007bff; + margin: 0 15px; + padding: 10px 15px; + border-radius: 5px; + border: none; + outline: none; + color: #f6f6f6; + font-size: 0.8rem; +} + +.submit:hover { + -webkit-transform: scale(1.1); + transform: scale(1.1); + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; + border-radius: 5px; + cursor: pointer; +} + +.submit:active { + -webkit-transform: scale(0.9); + transform: scale(0.9); +} + +.danger-block { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + margin-top: 50px; +} + +.danger { + background-color: #dc3545; + margin: 0 15px; + padding: 10px 15px; + border-radius: 5px; + border: none; + outline: none; + color: #f6f6f6; + font-size: 0.8rem; +} + +.danger:hover { + -webkit-transform: scale(1.1); + transform: scale(1.1); + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; + border-radius: 5px; + cursor: pointer; +} + +.danger:active { + -webkit-transform: scale(0.9); + transform: scale(0.9); +} + +@media (max-width: 1700px) { + .btn-block { + display: -ms-grid; + display: grid; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + grid-gap: 20px; + } + .submit { + -ms-grid-row: 1; + -ms-grid-row-span: 1; + grid-row: 1/2; + } + .danger-block { + margin-top: 10px; + } } /*# sourceMappingURL=popular-menu.css.map */ \ No newline at end of file diff --git a/src/js/components/popular-menu/popular-menu.js b/src/js/components/popular-menu/popular-menu.js index ee3919b..64e0a19 100644 --- a/src/js/components/popular-menu/popular-menu.js +++ b/src/js/components/popular-menu/popular-menu.js @@ -1,19 +1,31 @@ -import "./popular-menu.css"; -import { fullPopularLinks, faviconUrl } from "../../data/constants"; -import Menu from "../base-menu/baseMenu"; -import Popular from "../popular-links/popular"; +import './popular-menu.css'; +import { fullPopularLinks, faviconUrl } from '../../data/constants'; +import Menu from '../base-menu/baseMenu'; +import Popular from '../popular-links/popular'; +import { getOptionItems } from '../options-menu/options-menu'; + +const getImage = (website) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = ``; + } else { + img = ''; + } + return img; +}; const getFullLinks = () => { let fullLinks = []; const localPopularLinks = JSON.parse( - localStorage.getItem("fullPopularLinks") + localStorage.getItem('fullPopularLinks') ); if (localPopularLinks) { fullLinks = localPopularLinks; } else { fullLinks = fullPopularLinks; - localStorage.setItem("fullPopularLinks", JSON.stringify(fullPopularLinks)); + localStorage.setItem('fullPopularLinks', JSON.stringify(fullPopularLinks)); } return fullLinks; }; @@ -21,13 +33,13 @@ const getFullLinks = () => { const getPopularLinks = () => { let popularLinks = []; - const localPopularLinks = JSON.parse(localStorage.getItem("popularLinks")); + const localPopularLinks = JSON.parse(localStorage.getItem('popularLinks')); if (localPopularLinks) { popularLinks = localPopularLinks; } else { popularLinks = fullPopularLinks.slice(0, 6); - localStorage.setItem("popularLinks", JSON.stringify(popularLinks)); + localStorage.setItem('popularLinks', JSON.stringify(popularLinks)); } return popularLinks; }; @@ -45,8 +57,8 @@ class PopMenu extends Menu { (website) => website.title === title ); - let check = ""; - if (activeLinks) check = "checked"; + let check = ''; + if (activeLinks) check = 'checked'; return check; } @@ -67,36 +79,36 @@ class PopMenu extends Menu { localPopularLinks.push(necessaryService); } - localStorage.setItem("popularLinks", JSON.stringify(localPopularLinks)); + localStorage.setItem('popularLinks', JSON.stringify(localPopularLinks)); } clearMenuContent() { const websites = document.querySelectorAll(`.website.popular`); websites.forEach((link) => link.parentElement.removeChild(link)); - const nameWebsite = document.querySelector(".name-input.popular"); - const urlWebsite = document.querySelector(".url-input.popular"); + const nameWebsite = document.querySelector('.name-input.popular'); + const urlWebsite = document.querySelector('.url-input.popular'); - if (nameWebsite) nameWebsite.value = ""; - if (urlWebsite) urlWebsite.value = ""; + if (nameWebsite) nameWebsite.value = ''; + if (urlWebsite) urlWebsite.value = ''; } fillMenuContent() { this.clearMenuContent(); - const menuContent = document.querySelector(".menu-content.Popu"); + const menuContent = document.querySelector('.menu-content.Popu'); const fragment = document.createDocumentFragment(); const fullLocalLinks = getFullLinks(); fullLocalLinks.forEach((website) => { const check = this.findActiveWebsite(website.title); - const web = document.createElement("div"); - web.classList.add("website", "popular"); + const web = document.createElement('div'); + web.classList.add('website', 'popular'); web.innerHTML = ` `; @@ -106,9 +118,9 @@ class PopMenu extends Menu { } createForm() { - const menuContent = document.querySelector(".menu-content.Popu"); - const form = document.createElement("div"); - form.classList.add("form"); + const menuContent = document.querySelector('.menu-content.Popu'); + const form = document.createElement('div'); + form.classList.add('form'); form.innerHTML = `
@@ -121,14 +133,17 @@ class PopMenu extends Menu {
+
+ +
`; menuContent.appendChild(form); } createObjForSet() { - const nameWebsite = document.querySelector(".name-input.popular"); - const urlWebsite = document.querySelector(".url-input.popular"); + const nameWebsite = document.querySelector('.name-input.popular'); + const urlWebsite = document.querySelector('.url-input.popular'); const title = nameWebsite.value; const url = urlWebsite.value; @@ -148,31 +163,30 @@ class PopMenu extends Menu { const data = this.createObjForSet(); if (data) { const fullLocalLinks = JSON.parse( - localStorage.getItem("fullPopularLinks") + localStorage.getItem('fullPopularLinks') ); fullLocalLinks.push(data); - localStorage.setItem("fullPopularLinks", JSON.stringify(fullLocalLinks)); + localStorage.setItem('fullPopularLinks', JSON.stringify(fullLocalLinks)); } } cleanLocalLinks() { - localStorage.removeItem("fullPopularLinks"); - localStorage.removeItem("popularLinks"); - Popular.prototype.fillContentBlock(this.privateClass, "popularLinks"); + localStorage.removeItem('fullPopularLinks'); + localStorage.removeItem('popularLinks'); + Popular.prototype.fillContentBlock(this.privateClass, 'popularLinks'); } changeLinks(e) { const websiteClickedCheckbox = e.target.dataset.popular; const activeWebsite = this.findActiveWebsite(websiteClickedCheckbox); - console.log(activeWebsite, websiteClickedCheckbox); this.changeWebsiteArray(activeWebsite, websiteClickedCheckbox); - Popular.prototype.fillContentBlock(this.privateClass, "popularLinks"); + Popular.prototype.fillContentBlock(this.privateClass, 'popularLinks'); } addListenerToBtn() { const btnSub = document.querySelector(`.submit.popular`); - btnSub.addEventListener("click", () => { + btnSub.addEventListener('click', () => { this.createObjForSet.bind(this)(); this.setObjData.bind(this)(); this.fillMenuContent.bind(this)(); @@ -182,7 +196,7 @@ class PopMenu extends Menu { addListenerToDelBtn() { const btnDel = document.querySelector(`.delete.popular`); - btnDel.addEventListener("click", () => { + btnDel.addEventListener('click', () => { this.cleanLocalLinks(); this.fillMenuContent.bind(this)(); this.addListenerToLabel.bind(this)(); @@ -192,16 +206,31 @@ class PopMenu extends Menu { addListenerToLabel() { const labels = this.parentNode.querySelectorAll(`.input-popular`); labels.forEach((label) => - label.addEventListener("click", this.changeLinks.bind(this)) + label.addEventListener('click', this.changeLinks.bind(this)) ); } + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.popular'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector('[data-btn="popular"]'); + const btnDel = document.querySelector('.delete.popular'); + + mainMenuBtn.click(); + btnDel.click(); + + this.hide.bind(this)(); + }); + } + renderContent() { this.fillMenuContent(); this.createForm(); this.addListenerToLabel(); this.addListenerToBtn(); this.addListenerToDelBtn(); + this.addDangerBtnListener(); } } diff --git a/src/js/components/popular-menu/popular-menu.scss b/src/js/components/popular-menu/popular-menu.scss index d471d7b..f2f9e24 100644 --- a/src/js/components/popular-menu/popular-menu.scss +++ b/src/js/components/popular-menu/popular-menu.scss @@ -33,6 +33,7 @@ .website-logo { width: 24px; + margin-right: 15px; } } @@ -61,34 +62,85 @@ } } +%button { + margin: 0 15px; + padding: 10px 15px; + border-radius: 5px; + border: none; + outline: none; + color: #f6f6f6; + font-size: 0.8rem; + + &:hover { + transform: scale(1.1); + transition: all 0.1s ease-in-out; + border-radius: 5px; + cursor: pointer; + } + + &:active { + transform: scale(0.9); + } +} + +@mixin button($color) { + background-color: $color; + margin: 0 15px; + padding: 10px 15px; + border-radius: 5px; + border: none; + outline: none; + color: #f6f6f6; + font-size: 0.8rem; + + &:hover { + transform: scale(1.1); + transition: all 0.1s ease-in-out; + border-radius: 5px; + cursor: pointer; + } + + &:active { + transform: scale(0.9); + } +} + .btn-block { display: flex; justify-content: space-between; padding: 10px; +} +.delete { + @include button(gray); +} - .delete, - .submit { - margin: 0 15px; - padding: 10px 15px; - border-radius: 5px; - border: none; - outline: none; - color: #f6f6f6; - font-size: 0.8rem; - - &:hover { - transform: scale(1.1); - transition: all 0.2s ease-in-out; - border-radius: 5px; - cursor: pointer; - } +.submit { + @include button(#007bff); +} + +.danger-block { + display: flex; + justify-content: center; + margin-top: 50px; +} + +.danger { + @include button(#dc3545); +} + + +@media (max-width: 1700px) { + .btn-block { + display: grid; + justify-content: center; + grid-gap: 20px; } - .delete { - background-color: #dc3545; + .submit{ + grid-row: 1/2; } - .submit { - background-color: #007bff; + .danger-block { + margin-top: 10px; } -} +} \ No newline at end of file diff --git a/src/js/components/rss-menu/rss-menu.css b/src/js/components/rss-menu/rss-menu.css index b94bc4f..4e0500b 100644 --- a/src/js/components/rss-menu/rss-menu.css +++ b/src/js/components/rss-menu/rss-menu.css @@ -26,7 +26,7 @@ -ms-flex-align: center; align-items: center; cursor: pointer; - margin-left: 10px; + margin-left: 15px; } .bookmark-logo { diff --git a/src/js/components/rss-menu/rss-menu.js b/src/js/components/rss-menu/rss-menu.js index 29b5dc4..3456c70 100644 --- a/src/js/components/rss-menu/rss-menu.js +++ b/src/js/components/rss-menu/rss-menu.js @@ -1,28 +1,29 @@ -import "./rss-menu.css"; -import Menu from "../base-menu/baseMenu"; -import Rss, { fullUrlArray } from "../rss-news/rss"; +import './rss-menu.css'; +import Menu from '../base-menu/baseMenu'; +import Rss, { fullUrlArray } from '../rss-news/rss'; +import create from '../../utils/create'; const findActiveBookmark = (service) => { - const localUrlArray = JSON.parse(localStorage.getItem("urlArray")); + const localUrlArray = JSON.parse(localStorage.getItem('urlArray')); const activeBookmark = localUrlArray.some( (bookmark) => bookmark.service === service ); - let check = ""; - if (activeBookmark) check = "checked"; + let check = ''; + if (activeBookmark) check = 'checked'; return check; }; const fillMenuContent = () => { - const menuContent = document.querySelector(".menu-content.Book"); + const menuContent = document.querySelector('.menu-content.Book'); const fragment = document.createDocumentFragment(); fullUrlArray.forEach((service) => { const check = findActiveBookmark(service.service); - const bookmark = document.createElement("div"); - bookmark.classList.add("bookmark"); + const bookmark = document.createElement('div'); + bookmark.classList.add('bookmark'); bookmark.innerHTML = ` @@ -36,8 +37,19 @@ const fillMenuContent = () => { menuContent.appendChild(fragment); }; +const addDangerBtn = () => { + const menuContent = document.querySelector('.menu-content.Book'); + + create( + 'div', + 'danger-block', + ``, + menuContent + ); +}; + const changeUrlArray = (activeService, serviceClickedCheckbox) => { - const localUrlArray = JSON.parse(localStorage.getItem("urlArray")); + const localUrlArray = JSON.parse(localStorage.getItem('urlArray')); if (activeService) { const index = localUrlArray.findIndex( @@ -51,7 +63,7 @@ const changeUrlArray = (activeService, serviceClickedCheckbox) => { localUrlArray.push(necessaryService); } - localStorage.setItem("urlArray", JSON.stringify(localUrlArray)); + localStorage.setItem('urlArray', JSON.stringify(localUrlArray)); }; const changeBookmarks = (e) => { @@ -70,12 +82,25 @@ class RssMenu extends Menu { } addListenerToLabel() { - const labels = this.parentNode.querySelectorAll(".input"); - labels.forEach((label) => label.addEventListener("click", changeBookmarks)); + const labels = this.parentNode.querySelectorAll('.input'); + labels.forEach((label) => label.addEventListener('click', changeBookmarks)); + } + + addDangerBtnListener() { + const dangerBtn = document.querySelector('.danger.rss'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector(`[data-btn="rss"]`); + mainMenuBtn.click(); + this.hide.bind(this)(); + localStorage.removeItem('urlArray'); + }); } renderContent() { fillMenuContent(); + addDangerBtn(); + this.addDangerBtnListener(); this.addListenerToLabel(); } } diff --git a/src/js/components/rss-menu/rss-menu.scss b/src/js/components/rss-menu/rss-menu.scss index 068eec4..8c0eceb 100644 --- a/src/js/components/rss-menu/rss-menu.scss +++ b/src/js/components/rss-menu/rss-menu.scss @@ -16,7 +16,7 @@ display: flex; align-items: center; cursor: pointer; - margin-left: 10px; + margin-left: 15px; } .bookmark-logo { diff --git a/src/js/components/rss-news/rss.css b/src/js/components/rss-news/rss.css index 542248e..a6f6b73 100644 --- a/src/js/components/rss-news/rss.css +++ b/src/js/components/rss-news/rss.css @@ -87,20 +87,19 @@ } .topic { - display: -ms-grid; - display: grid; + margin: 10px 0; + display: -webkit-box; + display: -ms-flexbox; + display: flex; -webkit-box-align: center; -ms-flex-align: center; align-items: center; - -ms-grid-columns: 1fr 5fr; - grid-template-columns: 1fr 5fr; - grid-gap: 10px; - margin: 5px 0; } .topic .topic-img { width: 60px; max-height: 40px; + margin-right: 10px; } .topic .topic-link { diff --git a/src/js/components/rss-news/rss.js b/src/js/components/rss-news/rss.js index b9aee15..be034f7 100644 --- a/src/js/components/rss-news/rss.js +++ b/src/js/components/rss-news/rss.js @@ -1,71 +1,72 @@ -import "./rss.css"; -import RssMenu from "../rss-menu/rss-menu"; +import './rss.css'; +import RssMenu from '../rss-menu/rss-menu'; +import { getOptionItems } from '../options-menu/options-menu'; export const fullUrlArray = [ { - service: "Lenta-ru", - logo: "https://lenta.ru/images/small_logo.png", + service: 'Lenta-ru', + logo: 'https://lenta.ru/images/small_logo.png', links: [ - "https://lenta.ru/rss/news", - "https://lenta.ru/rss/top7", - "https://lenta.ru/rss/last24", - "https://lenta.ru/rss/articles", - "https://lenta.ru/rss/news/russia", - "https://lenta.ru/rss/photo", + 'https://lenta.ru/rss/news', + 'https://lenta.ru/rss/top7', + 'https://lenta.ru/rss/last24', + 'https://lenta.ru/rss/articles', + 'https://lenta.ru/rss/news/russia', + 'https://lenta.ru/rss/photo', ], }, { - service: "Газета-Ru", - logo: "https://img.gazeta.ru/files3/677/4728677/gazeta_logo.jpg", + service: 'Газета-Ru', + logo: 'https://img.gazeta.ru/files3/677/4728677/gazeta_logo.jpg', links: [ - "https://www.gazeta.ru/export/rss/first.xml", - "https://www.gazeta.ru/export/rss/lenta.xml", - "https://www.gazeta.ru/export/rss/lastnews.xml", - "https://www.gazeta.ru/export/rss/politics.xml", - "https://www.gazeta.ru/export/rss/business.xml", - "https://www.gazeta.ru/export/rss/social.xml", + 'https://www.gazeta.ru/export/rss/first.xml', + 'https://www.gazeta.ru/export/rss/lenta.xml', + 'https://www.gazeta.ru/export/rss/lastnews.xml', + 'https://www.gazeta.ru/export/rss/politics.xml', + 'https://www.gazeta.ru/export/rss/business.xml', + 'https://www.gazeta.ru/export/rss/social.xml', ], }, { - service: "TUT-BY", - logo: "https://img.tyt.by/i/rss/news/logo.gif", + service: 'TUT-BY', + logo: 'https://img.tyt.by/i/rss/news/logo.gif', links: [ - "https://news.tut.by/rss/index.rss", - "https://news.tut.by/rss/economics.rss", - "https://news.tut.by/rss/society.rss", + 'https://news.tut.by/rss/index.rss', + 'https://news.tut.by/rss/economics.rss', + 'https://news.tut.by/rss/society.rss', ], }, { - service: "Yahoo", - logo: "http://l.yimg.com/rz/d/yahoo_news_en-US_s_f_p_168x21_news.png", + service: 'Yahoo', + logo: 'http://l.yimg.com/rz/d/yahoo_news_en-US_s_f_p_168x21_news.png', links: [ - "https://www.yahoo.com/news/rss", - "https://finance.yahoo.com/news/rssindex", + 'https://www.yahoo.com/news/rss', + 'https://finance.yahoo.com/news/rssindex', ], }, { - service: "cnn", - logo: "http://i2.cdn.turner.com/cnn/2015/images/09/24/cnn.digital.png", + service: 'cnn', + logo: 'http://i2.cdn.turner.com/cnn/2015/images/09/24/cnn.digital.png', links: [ - "http://rss.cnn.com/rss/edition.rss", - "http://rss.cnn.com/rss/edition_world.rss", + 'http://rss.cnn.com/rss/edition.rss', + 'http://rss.cnn.com/rss/edition_world.rss', ], }, { - service: "Un-org", + service: 'Un-org', logo: - "https://news.un.org/en/sites/all/themes/bootstrap_un_news/images/un-emblem-for-rss.png", + 'https://news.un.org/en/sites/all/themes/bootstrap_un_news/images/un-emblem-for-rss.png', links: [ - "https://news.un.org/feed/subscribe/en/news/all/rss.xml", - "https://news.un.org/feed/subscribe/en/news/topic/health/feed/rss.xml", + 'https://news.un.org/feed/subscribe/en/news/all/rss.xml', + 'https://news.un.org/feed/subscribe/en/news/topic/health/feed/rss.xml', ], }, { - service: "bbc", - logo: "http://news.bbcimg.co.uk/nol/shared/img/bbc_news_120x60.gif", + service: 'bbc', + logo: 'http://news.bbcimg.co.uk/nol/shared/img/bbc_news_120x60.gif', links: [ - "http://feeds.bbci.co.uk/news/world/rss.xml", - "http://feeds.bbci.co.uk/news/politics/rss.xml", + 'http://feeds.bbci.co.uk/news/world/rss.xml', + 'http://feeds.bbci.co.uk/news/politics/rss.xml', ], }, ]; @@ -73,47 +74,47 @@ export const fullUrlArray = [ let urlArray = []; const getUrlArray = () => { - const localUrlArray = JSON.parse(localStorage.getItem("urlArray")); + const localUrlArray = JSON.parse(localStorage.getItem('urlArray')); if (localUrlArray) { urlArray = localUrlArray; } else { urlArray.push(fullUrlArray[0]); - localStorage.setItem("urlArray", JSON.stringify(urlArray)); + localStorage.setItem('urlArray', JSON.stringify(urlArray)); } return urlArray; }; const clearBookmarks = () => { - const bookmarks = document.querySelectorAll(".logo-bookmark"); + const bookmarks = document.querySelectorAll('.logo-bookmark'); bookmarks.forEach((bookmark) => bookmark.parentElement.removeChild(bookmark)); }; const setNewsTitle = (text) => { - const title = document.querySelector(".title"); + const title = document.querySelector('.title'); title.textContent = text; }; const setNumberPage = (currentNumber, amountNumbers) => { - const currentPage = document.querySelector(".current-page"); - const amountPages = document.querySelector(".amount-pages"); + const currentPage = document.querySelector('.current-page'); + const amountPages = document.querySelector('.amount-pages'); currentPage.textContent = currentNumber; amountPages.textContent = amountNumbers; }; const showErrorApi = (text) => { - const body = document.querySelector("body"); - const block = document.createElement("div"); + const body = document.querySelector('body'); + const block = document.createElement('div'); block.textContent = text; - block.classList.add("error"); + block.classList.add('error'); body.appendChild(block); setTimeout(() => { - const hiddenBlock = document.createElement("div"); + const hiddenBlock = document.createElement('div'); hiddenBlock.textContent = text; - hiddenBlock.classList.add("hiddenBlock", "hide"); + hiddenBlock.classList.add('hiddenBlock', 'hide'); body.appendChild(hiddenBlock); block.parentElement.removeChild(block); setTimeout(() => { @@ -122,16 +123,16 @@ const showErrorApi = (text) => { }, 3000); }; -const jsonApi = "https://api.rss2json.com/v1/api.json?rss_url="; -const apiKey = "&api_key=j0hwglpcodohzh4p4j8pdhejx1kdhkgtxmxgba6n"; +const jsonApi = 'https://api.rss2json.com/v1/api.json?rss_url='; +const apiKey = '&api_key=j0hwglpcodohzh4p4j8pdhejx1kdhkgtxmxgba6n'; -const getRss = async (url = "https://lenta.ru/rss/news") => { +const getRss = async (url = 'https://lenta.ru/rss/news') => { const request = await fetch(`${jsonApi}${url}${apiKey}`); const data = await request.json(); - const status = data.status === "error"; + const status = data.status === 'error'; if (status) { - showErrorApi("You are converting new feeds in a very short period"); + showErrorApi('You are converting new feeds in a very short period'); throw new Error("Api doesn't provides with information"); } @@ -139,10 +140,34 @@ const getRss = async (url = "https://lenta.ru/rss/news") => { }; const clearContentBlock = () => { - const topics = document.querySelectorAll(".topic"); + const topics = document.querySelectorAll('.topic'); topics.forEach((topic) => topic.parentElement.removeChild(topic)); }; +const getImage = (url) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = `topic`; + } else { + img = ''; + } + return img; +}; + +const getTargetBlank = () => { + let targetBlank; + const optionTarget = getOptionItems()[1]; + if (optionTarget.checked) { + targetBlank = '_blank'; + } else { + targetBlank = ''; + } + return targetBlank; +}; + class Rss { constructor(parentNode) { this.parentNode = parentNode; @@ -154,14 +179,14 @@ class Rss { fillNewsBookmarks() { clearBookmarks(); const localUrlArray = getUrlArray(); - const bookmarksContainer = document.querySelector(".bookmarks"); + const bookmarksContainer = document.querySelector('.bookmarks'); localUrlArray.forEach((service) => { - const bookmark = document.createElement("img"); - bookmark.classList.add("logo-bookmark", `${service.service}`); + const bookmark = document.createElement('img'); + bookmark.classList.add('logo-bookmark', `${service.service}`); bookmark.src = service.logo; bookmarksContainer.appendChild(bookmark); - bookmark.addEventListener("click", () => { + bookmark.addEventListener('click', () => { this.listenerBookmark(service); }); }); @@ -184,24 +209,21 @@ class Rss { const url = service.links[this.numberLink]; const rssNews = await getRss(url); - const content = document.querySelector(".content"); + const content = document.querySelector('.content'); clearContentBlock(); rssNews.items.forEach((news) => { const correctTitle = - news.description.includes(" + ${getImage(news.enclosure.link)} ${news.title} + }" target="${getTargetBlank()}">${news.title} `; content.appendChild(topic); }); @@ -211,8 +233,8 @@ class Rss { } changePage(service) { - const prevPage = document.querySelector(".prev-page"); - const nextPage = document.querySelector(".next-page"); + const prevPage = document.querySelector('.prev-page'); + const nextPage = document.querySelector('.next-page'); prevPage.onclick = () => { this.fillContentBlock(service, -1); @@ -249,8 +271,8 @@ class Rss { this.fillNewsBookmarks.bind(this)(); this.fillContentBlock(urlArray[0], 0); this.changePage(urlArray[0]); - this.btnMenu = this.parentNode.querySelector(".rss-menu"); - this.rssMenu = new RssMenu(this.btnMenu, "Bookmark manager"); + this.btnMenu = this.parentNode.querySelector('.rss-menu'); + this.rssMenu = new RssMenu(this.btnMenu, 'Bookmark manager'); } } diff --git a/src/js/components/rss-news/rss.scss b/src/js/components/rss-news/rss.scss index 6568c25..d828b72 100644 --- a/src/js/components/rss-news/rss.scss +++ b/src/js/components/rss-news/rss.scss @@ -72,15 +72,15 @@ } .topic { - display: grid; + margin: 10px 0; + display: flex; align-items: center; - grid-template-columns: 1fr 5fr; - grid-gap: 10px; - margin: 5px 0; .topic-img { width: 60px; max-height: 40px; + + margin-right: 10px; } .topic-link { diff --git a/src/js/components/shops-menu/shops-menu.js b/src/js/components/shops-menu/shops-menu.js index d9b3147..d03bcbf 100644 --- a/src/js/components/shops-menu/shops-menu.js +++ b/src/js/components/shops-menu/shops-menu.js @@ -1,17 +1,29 @@ -import "./shops-menu.css"; -import { fullShopsLinks, faviconUrl } from "../../data/constants"; -import Menu from "../base-menu/baseMenu"; -import Shops from "../shops/shops"; +import './shops-menu.css'; +import { fullShopsLinks, faviconUrl } from '../../data/constants'; +import Menu from '../base-menu/baseMenu'; +import Shops from '../shops/shops'; +import { getOptionItems } from '../options-menu/options-menu'; + +const getImage = (website) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = ``; + } else { + img = ''; + } + return img; +}; const getFullLinks = () => { let fullLinks = []; - const localShopsLinks = JSON.parse(localStorage.getItem("fullShopsLinks")); + const localShopsLinks = JSON.parse(localStorage.getItem('fullShopsLinks')); if (localShopsLinks) { fullLinks = localShopsLinks; } else { fullLinks = fullShopsLinks; - localStorage.setItem("fullShopsLinks", JSON.stringify(fullShopsLinks)); + localStorage.setItem('fullShopsLinks', JSON.stringify(fullShopsLinks)); } return fullLinks; }; @@ -19,13 +31,13 @@ const getFullLinks = () => { const getShopsLinks = () => { let shopsLinks = []; - const localShopsLinks = JSON.parse(localStorage.getItem("shopsLinks")); + const localShopsLinks = JSON.parse(localStorage.getItem('shopsLinks')); if (localShopsLinks) { shopsLinks = localShopsLinks; } else { shopsLinks = fullShopsLinks.slice(0, 6); - localStorage.setItem("shopsLinks", JSON.stringify(shopsLinks)); + localStorage.setItem('shopsLinks', JSON.stringify(shopsLinks)); } return shopsLinks; }; @@ -43,8 +55,8 @@ class ShopsMenu extends Menu { (website) => website.title === title ); - let check = ""; - if (activeLinks) check = "checked"; + let check = ''; + if (activeLinks) check = 'checked'; return check; } @@ -65,36 +77,36 @@ class ShopsMenu extends Menu { localShopsLinks.push(necessaryService); } - localStorage.setItem("shopsLinks", JSON.stringify(localShopsLinks)); + localStorage.setItem('shopsLinks', JSON.stringify(localShopsLinks)); } clearMenuContent() { const websites = document.querySelectorAll(`.website.shops`); websites.forEach((link) => link.parentElement.removeChild(link)); - const nameWebsite = document.querySelector(".name-input.shops"); - const urlWebsite = document.querySelector(".url-input.shops"); + const nameWebsite = document.querySelector('.name-input.shops'); + const urlWebsite = document.querySelector('.url-input.shops'); - if (nameWebsite) nameWebsite.value = ""; - if (urlWebsite) urlWebsite.value = ""; + if (nameWebsite) nameWebsite.value = ''; + if (urlWebsite) urlWebsite.value = ''; } fillMenuContent() { this.clearMenuContent(); - const menuContent = document.querySelector(".menu-content.Shop"); + const menuContent = document.querySelector('.menu-content.Shop'); const fragment = document.createDocumentFragment(); const fullLocalLinks = getFullLinks(); fullLocalLinks.forEach((website) => { const check = this.findActiveWebsite(website.title); - const web = document.createElement("div"); - web.classList.add("website", "shops"); + const web = document.createElement('div'); + web.classList.add('website', 'shops'); web.innerHTML = ` `; @@ -104,10 +116,10 @@ class ShopsMenu extends Menu { } createForm() { - const menuContent = document.querySelector(".menu-content.Shop"); + const menuContent = document.querySelector('.menu-content.Shop'); - const form = document.createElement("div"); - form.classList.add("form"); + const form = document.createElement('div'); + form.classList.add('form'); form.innerHTML = `
@@ -120,14 +132,17 @@ class ShopsMenu extends Menu {
+
+ +
`; menuContent.appendChild(form); } createObjForSet() { - const nameWebsite = document.querySelector(".name-input.shops"); - const urlWebsite = document.querySelector(".url-input.shops"); + const nameWebsite = document.querySelector('.name-input.shops'); + const urlWebsite = document.querySelector('.url-input.shops'); const title = nameWebsite.value; const url = urlWebsite.value; // const faviconUrl = "https://www.google.com/s2/favicons?domain="; @@ -147,16 +162,16 @@ class ShopsMenu extends Menu { setObjData() { const data = this.createObjForSet(); if (data) { - const fullLocalLinks = JSON.parse(localStorage.getItem("fullShopsLinks")); + const fullLocalLinks = JSON.parse(localStorage.getItem('fullShopsLinks')); fullLocalLinks.push(data); - localStorage.setItem("fullShopsLinks", JSON.stringify(fullLocalLinks)); + localStorage.setItem('fullShopsLinks', JSON.stringify(fullLocalLinks)); } } cleanLocalLinks() { - localStorage.removeItem("fullShopsLinks"); - localStorage.removeItem("shopsLinks"); - Shops.prototype.fillContentBlock(this.privateClass, "shopsLinks"); + localStorage.removeItem('fullShopsLinks'); + localStorage.removeItem('shopsLinks'); + Shops.prototype.fillContentBlock(this.privateClass, 'shopsLinks'); } changeLinks(e) { @@ -164,13 +179,12 @@ class ShopsMenu extends Menu { const activeWebsite = this.findActiveWebsite(websiteClickedCheckbox); this.changeWebsiteArray(activeWebsite, websiteClickedCheckbox); - console.log("Ok"); - Shops.prototype.fillContentBlock(this.privateClass, "shopsLinks"); + Shops.prototype.fillContentBlock(this.privateClass, 'shopsLinks'); } addListenerToBtn() { const btnSub = document.querySelector(`.submit.shops`); - btnSub.addEventListener("click", () => { + btnSub.addEventListener('click', () => { this.createObjForSet.bind(this)(); this.setObjData.bind(this)(); this.fillMenuContent.bind(this)(); @@ -180,7 +194,7 @@ class ShopsMenu extends Menu { addListenerToDelBtn() { const btnDel = document.querySelector(`.delete.shops`); - btnDel.addEventListener("click", () => { + btnDel.addEventListener('click', () => { this.cleanLocalLinks(); this.fillMenuContent.bind(this)(); this.addListenerToLabel.bind(this)(); @@ -190,16 +204,31 @@ class ShopsMenu extends Menu { addListenerToLabel() { const labels = this.parentNode.querySelectorAll(`.input-shops`); labels.forEach((label) => - label.addEventListener("click", this.changeLinks.bind(this)) + label.addEventListener('click', this.changeLinks.bind(this)) ); } + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.shops'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector('[data-btn="shops"]'); + const btnDel = document.querySelector('.delete.shops'); + + mainMenuBtn.click(); + btnDel.click(); + + this.hide.bind(this)(); + }); + } + renderContent() { this.fillMenuContent(); this.createForm(); this.addListenerToLabel(); this.addListenerToBtn(); this.addListenerToDelBtn(); + this.addDangerBtnListener(); } } diff --git a/src/js/components/shops/shops.js b/src/js/components/shops/shops.js index 0aa1b37..91b334c 100644 --- a/src/js/components/shops/shops.js +++ b/src/js/components/shops/shops.js @@ -2,7 +2,29 @@ import { fullShopsLinks } from "../../data/constants"; import ShopsMenu from "../shops-menu/shops-menu"; import "./shops.css"; +import { getOptionItems } from "../options-menu/options-menu"; +const getImage = (url) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = `website` + } else { + img = ''; + } + return img; +} + +const getTargetBlank = () => { + let targetBlank; + const optionTarget = getOptionItems()[1]; + if (optionTarget.checked) { + targetBlank = '_blank' + } else { + targetBlank = '' + } + return targetBlank; +} class Shops { constructor(obj) { this.parentNode = obj.parentNode; @@ -45,8 +67,8 @@ class Shops { website.classList.add("websites", `${myClass}`); website.innerHTML = ` - - website + + ${getImage(web.favicon)} ${web.title} `; diff --git a/src/js/components/todo/todo-menu.js b/src/js/components/todo/todo-menu.js new file mode 100644 index 0000000..5750165 --- /dev/null +++ b/src/js/components/todo/todo-menu.js @@ -0,0 +1,39 @@ +// import './rss-menu.css'; +import Menu from '../base-menu/baseMenu'; +import create from '../../utils/create'; + +class TodoMenu extends Menu { + constructor(clickedElement, caption) { + super(clickedElement, caption); + this.renderContent(); + } + + addDangerBtn() { + const menuContent = this.parentNode.querySelector('.menu-content.ToDo'); + + create( + 'div', + 'danger-block', + ``, + menuContent + ); + } + + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.todo'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector(`[data-btn="todo"]`); + mainMenuBtn.click(); + this.hide.bind(this)(); + localStorage.removeItem('todo'); + }); + } + + renderContent() { + this.addDangerBtn(); + this.addDangerBtnListener(); + } +} + +export default TodoMenu; diff --git a/src/js/components/todo/todo.css b/src/js/components/todo/todo.css new file mode 100644 index 0000000..ad59cc5 --- /dev/null +++ b/src/js/components/todo/todo.css @@ -0,0 +1,94 @@ +.todo-content { + padding: 10px 15px; +} + +.add-item { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 5px; +} + +.input-task { + width: 85%; + border-radius: 25px; + padding-left: 10px; +} + +.input-task:hover, .input-task:focus { + border-radius: 25px; + outline: none; + -webkit-box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); + box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); +} + +.input-task.new-task { + width: 82%; + border: 2px solid #6b6b6b; +} + +.add-task { + margin-left: -45px; + background-color: dodgerblue; + color: white; + border-radius: 25px; + padding: 10px 12px; + border: none; + outline: none; +} + +.add-task:hover { + -webkit-transform: scale(1.1); + transform: scale(1.1); + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; + cursor: pointer; +} + +.task { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 5px; +} + +.task .resolved { + text-decoration: line-through; + opacity: 0.6; +} + +.task .input-task { + width: 75%; + margin: 5px; + font-size: 0.9rem; + border: none; +} + +.delete-task { + background-color: tomato; + margin-right: 5px; + color: white; + border: 0px; + border-radius: 25px; + padding: 5px 7px; + border: none; + outline: none; +} + +.delete-task:hover { + -webkit-transform: scale(1.1); + transform: scale(1.1); + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; + cursor: pointer; +} +/*# sourceMappingURL=todo.css.map */ \ No newline at end of file diff --git a/src/js/components/todo/todo.js b/src/js/components/todo/todo.js new file mode 100644 index 0000000..8b87457 --- /dev/null +++ b/src/js/components/todo/todo.js @@ -0,0 +1,272 @@ +import TodoMenu from './todo-menu'; +import './todo.css'; + +const tasks = { + started: ['First task', 'Second task'], + completed: ['Solved task'], +}; + +const getTasks = () => { + let allTasks = {}; + + const localTasks = JSON.parse(localStorage.getItem('todo')); + + if (localTasks) { + allTasks = localTasks; + } else { + allTasks = { ...tasks }; + localStorage.setItem('todo', JSON.stringify(allTasks)); + } + return allTasks; +}; + +class ToDo { + constructor(parentNode) { + this.parentNode = parentNode; + this.flag = true; + this.render(); + } + + fillActiveTaskBlock() { + const localTasks = getTasks(); + const startBlock = this.parentNode.querySelector('.started'); + + startBlock.innerHTML = ''; + + localTasks.started.forEach((task, index) => { + const startedTask = document.createElement('div'); + startedTask.classList.add('active-task', 'task'); + startedTask.dataset.task = `task${index}`; + startedTask.innerHTML = ` + + + + `; + startBlock.appendChild(startedTask); + this.addDelBtnListener(); + this.addCheckListener(); + this.addInputListeners(); + }); + } + + fillCompletedTaskBlock() { + const localTasks = getTasks(); + const completeBlock = this.parentNode.querySelector('.completed'); + const title = document.createElement('div'); + title.classList.add('complete-title'); + + completeBlock.innerHTML = ''; + + localTasks.completed.forEach((task, index) => { + const completed = document.createElement('div'); + completed.classList.add('complete-task', 'task'); + completed.dataset.task = `task${index}${index}`; + completed.innerHTML = ` + + + + `; + completeBlock.appendChild(completed); + this.addDelBtnListener(); + this.addCheckListener(); + this.addInputListeners(); + }); + } + + addNewTask() { + const localTasks = getTasks(); + const input = this.parentNode.querySelector('.new-task'); + + if (input.value) localTasks.started.unshift(input.value); + localStorage.setItem('todo', JSON.stringify(localTasks)); + + input.value = ''; + this.fillActiveTaskBlock(); + } + + addAddBtnListener() { + const addBtn = this.parentNode.querySelector('.add-task'); + addBtn.addEventListener('click', this.addNewTask.bind(this)); + } + + addMainInputListener() { + const input = this.parentNode.querySelector('.new-task'); + input.addEventListener('keypress', (e) => { + if (e.keyCode === 13) { + this.addNewTask.bind(this)(); + input.blur(); + } + }); + } + + delete(id) { + const localTasks = getTasks(); + const deletedTask = this.parentNode.querySelector(`.input${id}`); + + const startedTasks = localTasks.started.filter( + (task) => task !== deletedTask.value + ); + const completedTasks = localTasks.completed.filter( + (task) => task !== deletedTask.value + ); + + localStorage.setItem( + 'todo', + JSON.stringify({ + started: startedTasks, + completed: completedTasks, + }) + ); + + this.fillActiveTaskBlock(); + this.fillCompletedTaskBlock(); + } + + addDelBtnListener() { + const delBtn = this.parentNode.querySelectorAll('.delete-task'); + delBtn.forEach((btn) => { + btn.onclick = (e) => { + const { id } = e.target.dataset; + this.delete.bind(this)(id); + }; + }); + } + + addCheckListener() { + const checkboxes = this.parentNode.querySelectorAll('.checkbox-task'); + checkboxes.forEach((checkbox) => { + checkbox.onclick = (e) => { + const { id } = e.target.dataset; + this.checkTask.bind(this)(id); + }; + }); + } + + replaceStartedTask(id) { + const { started, completed } = getTasks(); + const checkedTask = this.parentNode.querySelector(`.input${id}`); + + const checked = started.find((task) => task === checkedTask.value); + completed.unshift(checked); + const startedTasks = started.filter((task) => task !== checkedTask.value); + + localStorage.setItem( + 'todo', + JSON.stringify({ + started: startedTasks, + completed, + }) + ); + + this.fillActiveTaskBlock(); + this.fillCompletedTaskBlock(); + } + + replaceCompletedTask(id) { + const { started, completed } = getTasks(); + const checkedTask = this.parentNode.querySelector(`.input${id}`); + + const checked = completed.find((task) => task === checkedTask.value); + started.push(checked); + const completedTasks = completed.filter( + (task) => task !== checkedTask.value + ); + + localStorage.setItem( + 'todo', + JSON.stringify({ + started, + completed: completedTasks, + }) + ); + + this.fillActiveTaskBlock(); + this.fillCompletedTaskBlock(); + } + + checkTask(id) { + const checkbox = this.parentNode.querySelector(`.checkbox${id}`); + + if (checkbox.checked) this.replaceStartedTask(id); + if (!checkbox.checked) this.replaceCompletedTask(id); + } + + memorizeInputText(e) { + if (!this.flag) return; + this.flag = false; + const { id } = e.target.dataset; + const input = this.parentNode.querySelector(`.input${id}`); + localStorage.setItem('inputText', JSON.stringify(input.value)); + } + + setInputText(memorizedText, currentText) { + const { started, completed } = getTasks(); + const indStartedTask = started.findIndex((text) => text === memorizedText); + const indCompletedTask = completed.findIndex( + (text) => text === memorizedText + ); + + if (indStartedTask !== -1) started[indStartedTask] = currentText; + if (indCompletedTask !== -1) completed[indCompletedTask] = currentText; + + localStorage.setItem('todo', JSON.stringify({ started, completed })); + + this.fillActiveTaskBlock(); + this.fillCompletedTaskBlock(); + } + + getInputText(e) { + const memorizedText = JSON.parse(localStorage.getItem('inputText')); + const { id } = e.target.dataset; + + const input = this.parentNode.querySelector(`.input${id}`); + const currentText = input.value; + + this.setInputText(memorizedText, currentText); + this.flag = true; + } + + addInputListeners() { + const textInputs = this.parentNode.querySelectorAll('.input-text'); + textInputs.forEach((input) => { + input.onclick = (e) => this.memorizeInputText.bind(this)(e); + input.onblur = (e) => this.getInputText.bind(this)(e); + input.onkeypress = (e) => { + if (e.keyCode === 13) input.onblur(e); + }; + }); + } + + render() { + this.parentNode.innerHTML = ` + +
+
+ + +
+
Active:
+
+
Completed:
+
+
+ `; + this.fillActiveTaskBlock(); + this.fillCompletedTaskBlock(); + this.addAddBtnListener(); + this.addMainInputListener(); + this.addInputListeners(); + + this.btnMenu = this.parentNode.querySelector('.dot-menu'); + this.rssMenu = new TodoMenu(this.btnMenu, 'ToDo'); + } +} + +export default ToDo; diff --git a/src/js/components/todo/todo.scss b/src/js/components/todo/todo.scss new file mode 100644 index 0000000..f996555 --- /dev/null +++ b/src/js/components/todo/todo.scss @@ -0,0 +1,80 @@ +.todo-content { + padding: 10px 15px; +} + +.add-item { + display: flex; + justify-content: space-between; + padding: 5px; +} + +.input-task { + width: 85%; + border-radius: 25px; + padding-left: 10px; + + &:hover, + &:focus { + border-radius: 25px; + outline: none; + box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); + } +} + +.input-task.new-task { + width: 82%; + border: 2px solid rgb(107, 107, 107); +} + +.add-task { + margin-left: -45px; + background-color: dodgerblue; + color: white; + border-radius: 25px; + padding: 10px 12px; + border: none; + outline: none; + + &:hover { + transform: scale(1.1); + transition: all 0.1s ease-in-out; + cursor: pointer; + } +} + +.task { + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px; + + .resolved { + text-decoration: line-through; + opacity: 0.6; + } +} + +.task .input-task { + width: 75%; + margin: 5px; + font-size: 0.9rem; + border: none; +} + +.delete-task { + background-color: tomato; + margin-right: 5px; + color: white; + border: 0px; + border-radius: 25px; + // padding: 5px 6px 2px 6px; + padding: 5px 7px; + border: none; + outline: none; + + &:hover { + transform: scale(1.1); + transition: all 0.1s ease-in-out; + cursor: pointer; + } +} diff --git a/src/js/components/travel-menu/travel-menu.js b/src/js/components/travel-menu/travel-menu.js index 423a2c0..eb39d1f 100644 --- a/src/js/components/travel-menu/travel-menu.js +++ b/src/js/components/travel-menu/travel-menu.js @@ -1,19 +1,29 @@ -import "./travel-menu.css"; -import { fullTravelLinks, faviconUrl } from "../../data/constants"; -import Menu from "../base-menu/baseMenu"; -import Travel from "../travel/travel"; +import './travel-menu.css'; +import { fullTravelLinks, faviconUrl } from '../../data/constants'; +import Menu from '../base-menu/baseMenu'; +import Travel from '../travel/travel'; +import { getOptionItems } from '../options-menu/options-menu'; + +const getImage = (website) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = ``; + } else { + img = ''; + } + return img; +}; const getFullLinks = () => { let fullLinks = []; - const localTravelLinks = JSON.parse( - localStorage.getItem("fullTravelLinks") - ); + const localTravelLinks = JSON.parse(localStorage.getItem('fullTravelLinks')); if (localTravelLinks) { fullLinks = localTravelLinks; } else { fullLinks = fullTravelLinks; - localStorage.setItem("fullTravelLinks", JSON.stringify(fullTravelLinks)); + localStorage.setItem('fullTravelLinks', JSON.stringify(fullTravelLinks)); } return fullLinks; }; @@ -21,13 +31,13 @@ const getFullLinks = () => { const getTravelLinks = () => { let travelLinks = []; - const localTravelLinks = JSON.parse(localStorage.getItem("travelLinks")); + const localTravelLinks = JSON.parse(localStorage.getItem('travelLinks')); if (localTravelLinks) { travelLinks = localTravelLinks; } else { travelLinks = fullTravelLinks.slice(0, 6); - localStorage.setItem("travelLinks", JSON.stringify(travelLinks)); + localStorage.setItem('travelLinks', JSON.stringify(travelLinks)); } return travelLinks; }; @@ -45,8 +55,8 @@ class TravelMenu extends Menu { (website) => website.title === title ); - let check = ""; - if (activeLinks) check = "checked"; + let check = ''; + if (activeLinks) check = 'checked'; return check; } @@ -67,36 +77,36 @@ class TravelMenu extends Menu { localTravelLinks.push(necessaryService); } - localStorage.setItem("travelLinks", JSON.stringify(localTravelLinks)); + localStorage.setItem('travelLinks', JSON.stringify(localTravelLinks)); } clearMenuContent() { const websites = document.querySelectorAll(`.website.travel`); websites.forEach((link) => link.parentElement.removeChild(link)); - const nameWebsite = document.querySelector(".name-input.travel"); - const urlWebsite = document.querySelector(".url-input.travel"); + const nameWebsite = document.querySelector('.name-input.travel'); + const urlWebsite = document.querySelector('.url-input.travel'); - if (nameWebsite) nameWebsite.value = ""; - if (urlWebsite) urlWebsite.value = ""; + if (nameWebsite) nameWebsite.value = ''; + if (urlWebsite) urlWebsite.value = ''; } fillMenuContent() { this.clearMenuContent(); - const menuContent = document.querySelector(".menu-content.Trav"); + const menuContent = document.querySelector('.menu-content.Trav'); const fragment = document.createDocumentFragment(); const fullLocalLinks = getFullLinks(); fullLocalLinks.forEach((website) => { const check = this.findActiveWebsite(website.title); - const web = document.createElement("div"); - web.classList.add("website", "travel"); + const web = document.createElement('div'); + web.classList.add('website', 'travel'); web.innerHTML = ` `; @@ -106,9 +116,9 @@ class TravelMenu extends Menu { } createForm() { - const menuContent = document.querySelector(".menu-content.Trav"); - const form = document.createElement("div"); - form.classList.add("form"); + const menuContent = document.querySelector('.menu-content.Trav'); + const form = document.createElement('div'); + form.classList.add('form'); form.innerHTML = `
@@ -121,14 +131,17 @@ class TravelMenu extends Menu {
+
+ +
`; menuContent.appendChild(form); } createObjForSet() { - const nameWebsite = document.querySelector(".name-input.travel"); - const urlWebsite = document.querySelector(".url-input.travel"); + const nameWebsite = document.querySelector('.name-input.travel'); + const urlWebsite = document.querySelector('.url-input.travel'); const title = nameWebsite.value; const url = urlWebsite.value; @@ -148,17 +161,17 @@ class TravelMenu extends Menu { const data = this.createObjForSet(); if (data) { const fullLocalLinks = JSON.parse( - localStorage.getItem("fullTravelLinks") + localStorage.getItem('fullTravelLinks') ); fullLocalLinks.push(data); - localStorage.setItem("fullTravelLinks", JSON.stringify(fullLocalLinks)); + localStorage.setItem('fullTravelLinks', JSON.stringify(fullLocalLinks)); } } cleanLocalLinks() { - localStorage.removeItem("fullTravelLinks"); - localStorage.removeItem("travelLinks"); - Travel.prototype.fillContentBlock(this.privateClass, "travelLinks"); + localStorage.removeItem('fullTravelLinks'); + localStorage.removeItem('travelLinks'); + Travel.prototype.fillContentBlock(this.privateClass, 'travelLinks'); } changeLinks(e) { @@ -166,12 +179,12 @@ class TravelMenu extends Menu { const activeWebsite = this.findActiveWebsite(websiteClickedCheckbox); this.changeWebsiteArray(activeWebsite, websiteClickedCheckbox); - Travel.prototype.fillContentBlock(this.privateClass, "travelLinks"); + Travel.prototype.fillContentBlock(this.privateClass, 'travelLinks'); } addListenerToBtn() { const btnSub = document.querySelector(`.submit.travel`); - btnSub.addEventListener("click", () => { + btnSub.addEventListener('click', () => { this.createObjForSet.bind(this)(); this.setObjData.bind(this)(); this.fillMenuContent.bind(this)(); @@ -181,7 +194,7 @@ class TravelMenu extends Menu { addListenerToDelBtn() { const btnDel = document.querySelector(`.delete.travel`); - btnDel.addEventListener("click", () => { + btnDel.addEventListener('click', () => { this.cleanLocalLinks(); this.fillMenuContent.bind(this)(); this.addListenerToLabel.bind(this)(); @@ -191,16 +204,31 @@ class TravelMenu extends Menu { addListenerToLabel() { const labels = this.parentNode.querySelectorAll(`.input-travel`); labels.forEach((label) => - label.addEventListener("click", this.changeLinks.bind(this)) + label.addEventListener('click', this.changeLinks.bind(this)) ); } + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.travel'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector('[data-btn="travel"]'); + const btnDel = document.querySelector('.delete.travel'); + + mainMenuBtn.click(); + btnDel.click(); + + this.hide.bind(this)(); + }); + } + renderContent() { this.fillMenuContent(); this.createForm(); this.addListenerToLabel(); this.addListenerToBtn(); this.addListenerToDelBtn(); + this.addDangerBtnListener(); } } diff --git a/src/js/components/travel/travel.js b/src/js/components/travel/travel.js index 10fbc35..bfc6173 100644 --- a/src/js/components/travel/travel.js +++ b/src/js/components/travel/travel.js @@ -2,7 +2,29 @@ import "./travel.css"; import { fullTravelLinks } from "../../data/constants"; import TravelMenu from "../travel-menu/travel-menu"; +import { getOptionItems } from "../options-menu/options-menu"; +const getImage = (url) => { + const optionFavicon = getOptionItems()[0]; + let img; + if (optionFavicon.checked) { + img = `website` + } else { + img = ''; + } + return img; +} + +const getTargetBlank = () => { + let targetBlank; + const optionTarget = getOptionItems()[1]; + if (optionTarget.checked) { + targetBlank = '_blank' + } else { + targetBlank = '' + } + return targetBlank; +} class Travel { constructor(obj) { this.parentNode = obj.parentNode; @@ -43,8 +65,8 @@ class Travel { website.classList.add("websites", `${myClass}`); website.innerHTML = ` - - website + + ${getImage(web.favicon)} ${web.title} `; diff --git a/src/js/components/weather/owfont-regular.css b/src/js/components/weather/owfont-regular.css new file mode 100644 index 0000000..bc17040 --- /dev/null +++ b/src/js/components/weather/owfont-regular.css @@ -0,0 +1,559 @@ +/*! + * owfont-regular 1.0.0 by Deniz Fuchidzhiev - http://websygen.com + * License - font: SIL OFL 1.1, css: MIT License + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'owfont'; + /* src: url('fonts/owfont-regular.eot?v=1.0.0'); */ + src: url('../../../assets/fonts/fonts-weather/owfont-regular.eot?v=1.0.0'); + src: url('../../../assets/fonts/fonts-weather/owfont-regular.eot?#iefix&v=1.0.0') format('embedded-opentype'), + url('../../../assets/fonts/fonts-weather/owfont-regular.woff') format('woff'), + url('../../../assets/fonts/fonts-weather/owfont-regular.ttf') format('truetype'), + url('../../../assets/fonts/fonts-weather/owfont-regular.svg#owf-regular') format('svg'); + font-weight: normal; + font-style: normal; +} +.owf { + display: inline-block; + font: normal normal normal 14px/1 owfont; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transform: translate(0, 0); +} +/* makes the font 33% larger relative to the icon container */ +.owf-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.owf-2x { + font-size: 2em; +} +.owf-3x { + font-size: 3em; +} +.owf-4x { + font-size: 4em; +} +.owf-5x { + font-size: 5em; +} +.owf-fw { + width: 1.28571429em; + text-align: center; +} +.owf-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.owf-ul > li { + position: relative; +} +.owf-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.owf-li.owf-lg { + left: -1.85714286em; +} +.owf-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.owf-pull-right { + float: right; +} +.owf-pull-left { + float: left; +} +.owf.owf-pull-left { + margin-right: .3em; +} +.owf.owf-pull-right { + margin-left: .3em; +} + +/* owfont uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ + +/* Weather Condition Codes */ + +/* Thunderstorm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* thunderstorm with light rain */ +.owf-200:before, +.owf-200-d:before, +.owf-200-n:before { + content: "\EB28"; +} +/* thunderstorm with rain */ +.owf-201:before, +.owf-201-d:before, +.owf-201-n:before { + content: "\EB29"; +} +/* thunderstorm with heavy rain */ +.owf-202:before, +.owf-202-d:before, +.owf-202-n:before { + content: "\EB2A"; +} +/* light thunderstorm */ +.owf-210:before, +.owf-210-d:before, +.owf-210-n:before { + content: "\EB32"; +} +/* thunderstorm */ +.owf-211:before, +.owf-211-d:before, +.owf-211-n:before { + content: "\EB33"; +} +/* heavy thunderstorm */ +.owf-212:before, +.owf-212-d:before, +.owf-212-n:before { + content: "\EB34"; +} +/* ragged thunderstorm */ +.owf-221:before, +.owf-221-d:before, +.owf-221-n:before { + content: "\EB3D"; +} +/* thunderstorm with light drizzle */ +.owf-230:before, +.owf-230-d:before, +.owf-230-n:before { + content: "\EB46"; +} +/* thunderstorm with drizzle */ +.owf-231:before, +.owf-231-d:before, +.owf-231-n:before { + content: "\EB47"; +} +/* thunderstorm with heavy drizzle */ +.owf-232:before, +.owf-232-d:before, +.owf-232-n:before { + content: "\EB48"; +} + +/* Drizzle - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* light intensity drizzle */ +.owf-300:before, +.owf-300-d:before, +.owf-300-n:before { + content: "\EB8C"; +} +/* drizzle */ +.owf-301:before, +.owf-301-d:before, +.owf-301-n:before { + content: "\EB8D"; +} +/* heavy intensity drizzle */ +.owf-302:before, +.owf-302-d:before, +.owf-302-n:before { + content: "\EB8E"; +} +/* light intensity drizzle rain */ +.owf-310:before, +.owf-310-d:before, +.owf-310-n:before { + content: "\EB96"; +} +/* drizzle rain */ +.owf-311:before, +.owf-311-d:before, +.owf-311-n:before { + content: "\EB97"; +} +/* heavy intensity drizzle rain */ +.owf-312:before, +.owf-312-d:before, +.owf-312-n:before { + content: "\EB98"; +} +/* shower rain and drizzle */ +.owf-313:before, +.owf-313-d:before, +.owf-313-n:before { + content: "\EB99"; +} +/* heavy shower rain and drizzle*/ +.owf-314:before, +.owf-314-d:before, +.owf-314-n:before { + content: "\EB9A"; +} +/* shower drizzle */ +.owf-321:before, +.owf-321-d:before, +.owf-321-n:before { + content: "\EBA1"; +} + +/* Rain - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* light rain */ +.owf-500:before, +.owf-500-d:before, +.owf-500-n:before { + content: "\EC54"; +} +/* moderate rain */ +.owf-501:before, +.owf-501-d:before, +.owf-501-n:before { + content: "\EC55"; +} +/* heavy intensity rain */ +.owf-502:before, +.owf-502-d:before, +.owf-502-n:before { + content: "\EC56"; +} +/* very heavy rain */ +.owf-503:before, +.owf-503-d:before, +.owf-503-n:before { + content: "\EC57"; +} +/* extreme rain */ +.owf-504:before, +.owf-504-d:before, +.owf-504-n:before { + content: "\EC58"; +} +/* freezing rain */ +.owf-511:before, +.owf-511-d:before, +.owf-511-n:before { + content: "\EC5F"; +} +/* light intensity shower rain */ +.owf-520:before, +.owf-520-d:before, +.owf-520-n:before { + content: "\EC68"; +} +/* shower rain */ +.owf-521:before, +.owf-521-d:before, +.owf-521-n:before { + content: "\EC69"; +} +/* heavy intensity shower rain */ +.owf-522:before, +.owf-522-d:before, +.owf-522-n:before { + content: "\EC6A"; +} +/* ragged shower rain */ +.owf-531:before, +.owf-531-d:before, +.owf-531-n:before { + content: "\EC73"; +} + +/* Snow - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* light snow */ +.owf-600:before, +.owf-600-d:before, +.owf-600-n:before { + content: "\ECB8"; +} +/* snow */ +.owf-601:before, +.owf-601-d:before, +.owf-601-n:before { + content: "\ECB9"; +} +/* heavy snow */ +.owf-602:before, +.owf-602-d:before, +.owf-602-n:before { + content: "\ECBA"; +} +/* sleet */ +.owf-611:before, +.owf-611-d:before, +.owf-611-n:before { + content: "\ECC3"; +} +/* shower sleet */ +.owf-612:before, +.owf-612-d:before, +.owf-612-n:before { + content: "\ECC4"; +} +/* light rain and snow */ +.owf-615:before, +.owf-615-d:before, +.owf-615-n:before { + content: "\ECC7"; +} +/* rain and snow */ +.owf-616:before, +.owf-616-d:before, +.owf-616-n:before { + content: "\ECC8"; +} +/* light shower snow */ +.owf-620:before, +.owf-620-d:before, +.owf-620-n:before { + content: "\ECCC"; +} +/* shower snow */ +.owf-621:before, +.owf-621-d:before, +.owf-621-n:before { + content: "\ECCD"; +} +/* heavy shower snow */ +.owf-622:before, +.owf-622-d:before, +.owf-622-n:before { + content: "\ECCE"; +} + +/* Atmosphere - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* mist */ +.owf-701:before, +.owf-701-d:before, +.owf-701-n:before { + content: "\ED1D"; +} +/* smoke */ +.owf-711:before, +.owf-711-d:before, +.owf-711-n:before { + content: "\ED27"; +} +/* haze */ +.owf-721:before, +.owf-721-d:before, +.owf-721-n:before { + content: "\ED31"; +} +/* Sand/Dust Whirls */ +.owf-731:before, +.owf-731-d:before, +.owf-731-n:before { + content: "\ED3B"; +} +/* Fog */ +.owf-741:before, +.owf-741-d:before, +.owf-741-n:before { + content: "\ED45"; +} +/* sand */ +.owf-751:before, +.owf-751-d:before, +.owf-751-n:before { + content: "\ED4F"; +} +/* dust */ +.owf-761:before, +.owf-761-d:before, +.owf-761-n:before { + content: "\ED59"; +} +/* VOLCANIC ASH */ +.owf-762:before, +.owf-762-d:before, +.owf-762-n:before { + content: "\ED5A"; +} +/* SQUALLS */ +.owf-771:before, +.owf-771-d:before, +.owf-771-n:before { + content: "\ED63"; +} +/* TORNADO */ +.owf-781:before, +.owf-781-d:before, +.owf-781-n:before { + content: "\ED6D"; +} + +/* Clouds - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* sky is clear */ /* Calm */ +.owf-800:before, +.owf-800-d:before, +.owf-951:before, +.owf-951-d:before { + content: "\ED80"; +} +.owf-800-n:before, +.owf-951-n:before { + content: "\F168"; +} +/* few clouds */ +.owf-801:before, +.owf-801-d:before { + content: "\ED81"; +} +.owf-801-n:before { + content: "\F169"; +} +/* scattered clouds */ +.owf-802:before, +.owf-802-d:before { + content: "\ED82"; +} +.owf-802-n:before { + content: "\F16A"; +} +/* broken clouds */ +.owf-803:before, +.owf-803-d:before, +.owf-803-n:before { + content: "\ED83"; +} +/* overcast clouds */ +.owf-804:before, +.owf-804-d:before, +.owf-804-n:before { + content: "\ED84"; +} + +/* Extreme - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* tornado */ +.owf-900:before, +.owf-900-d:before, +.owf-900-n:before { + content: "\EDE4"; +} +/* tropical storm */ +.owf-901:before, +.owf-901-d:before, +.owf-901-n:before { + content: "\EDE5"; +} +/* hurricane */ +.owf-902:before, +.owf-902-d:before, +.owf-902-n:before { + content: "\EDE6"; +} +/* cold */ +.owf-903:before, +.owf-903-d:before, +.owf-903-n:before { + content: "\EDE7"; +} +/* hot */ +.owf-904:before, +.owf-904-d:before, +.owf-904-n:before { + content: "\EDE8"; +} +/* windy */ +.owf-905:before, +.owf-905-d:before, +.owf-905-n:before { + content: "\EDE9"; +} +/* hail */ +.owf-906:before, +.owf-906-d:before, +.owf-906-n:before { + content: "\EDEA"; +} + +/* Additional - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Setting */ +.owf-950:before, +.owf-950-d:before, +.owf-950-n:before { + content: "\EE16"; +} +/* Light breeze */ +.owf-952:before, +.owf-952-d:before, +.owf-952-n:before { + content: "\EE18"; +} +/* Gentle Breeze */ +.owf-953:before, +.owf-953-d:before, +.owf-953-n:before { + content: "\EE19"; +} +/* Moderate breeze */ +.owf-954:before, +.owf-954-d:before, +.owf-954-n:before { + content: "\EE1A"; +} +/* Fresh Breeze */ +.owf-955:before, +.owf-955-d:before, +.owf-955-n:before { + content: "\EE1B"; +} +/* Strong Breeze */ +.owf-956:before, +.owf-956-d:before, +.owf-956-n:before { + content: "\EE1C"; +} +/* High wind, near gale */ +.owf-957:before, +.owf-957-d:before, +.owf-957-n:before { + content: "\EE1D"; +} +/* Gale */ +.owf-958:before, +.owf-958-d:before, +.owf-958-n:before { + content: "\EE1E"; +} +/* Severe Gale */ +.owf-959:before, +.owf-959-d:before, +.owf-959-n:before { + content: "\EE1F"; +} +/* Storm */ +.owf-960:before, +.owf-960-d:before, +.owf-960-n:before { + content: "\EE20"; +} +/* Violent Storm */ +.owf-961:before, +.owf-961-d:before, +.owf-961-n:before { + content: "\EE21"; +} +/* Hurricane */ +.owf-962:before, +.owf-962-d:before, +.owf-962-n:before { + content: "\EE22"; +} \ No newline at end of file diff --git a/src/js/components/weather/weather-menu.js b/src/js/components/weather/weather-menu.js new file mode 100644 index 0000000..7c618ba --- /dev/null +++ b/src/js/components/weather/weather-menu.js @@ -0,0 +1,39 @@ +// import './rss-menu.css'; +import Menu from '../base-menu/baseMenu'; +import create from '../../utils/create'; + +class WeatherMenu extends Menu { + constructor(clickedElement, caption) { + super(clickedElement, caption); + this.renderContent(); + } + + addDangerBtn() { + const menuContent = this.parentNode.querySelector('.menu-content.Weat'); + + create( + 'div', + 'danger-block', + ``, + menuContent + ); + } + + addDangerBtnListener() { + const dangerBtn = this.parentNode.querySelector('.danger.weather'); + + dangerBtn.addEventListener('click', () => { + const mainMenuBtn = this.parentNode.querySelector(`[data-btn="weather"]`); + mainMenuBtn.click(); + this.hide.bind(this)(); + localStorage.removeItem('city'); + }); + } + + renderContent() { + this.addDangerBtn(); + this.addDangerBtnListener(); + } +} + +export default WeatherMenu; diff --git a/src/js/components/weather/weather.css b/src/js/components/weather/weather.css new file mode 100644 index 0000000..d6e20b5 --- /dev/null +++ b/src/js/components/weather/weather.css @@ -0,0 +1,19 @@ +.weather-content { + text-align: center; + background-image: url(../../../assets/img/weather.jpg); + background-size: cover; + display: -ms-grid; + display: grid; + grid-gap: 5px; + padding: 10px; + text-shadow: 0px 0px 10px #e4b18c; +} + +.city:focus, +.city:hover { + border-radius: 25px; + outline: none; + -webkit-box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); + box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); +} +/*# sourceMappingURL=weather.css.map */ \ No newline at end of file diff --git a/src/js/components/weather/weather.js b/src/js/components/weather/weather.js new file mode 100644 index 0000000..bb80169 --- /dev/null +++ b/src/js/components/weather/weather.js @@ -0,0 +1,122 @@ +import './weather.css'; +import './owfont-regular.css'; +import WeatherMenu from './weather-menu'; + +class Weather { + constructor(parentNode) { + this.parentNode = parentNode; + this.render(); + } + + getDomElements() { + const weatherIcon = this.parentNode.querySelector('.weather-icon'); + const temperature = this.parentNode.querySelector('.temperature'); + const humidity = this.parentNode.querySelector('.humidity'); + const wind = this.parentNode.querySelector('.wind'); + const weatherDescription = this.parentNode.querySelector( + '.weather-description' + ); + const city = this.parentNode.querySelector('.city'); + + return { + weatherIcon, + temperature, + humidity, + wind, + weatherDescription, + city, + }; + } + + addListeners() { + const { city } = this.getDomElements(); + city.addEventListener('keypress', this.setCity.bind(this)); + city.addEventListener('blur', this.setCity.bind(this)); + city.addEventListener('click', () => (city.textContent = '')); + } + + async getWeatherData() { + const { city } = this.getDomElements(); + + if (localStorage.getItem('city') === null) { + city.textContent = 'Minsk'; + } else { + city.textContent = localStorage.getItem('city'); + } + const url = `https://api.openweathermap.org/data/2.5/weather?q=${city.textContent}&lang=en&appid=543c9bb3c115ee39476506208d454b41&units=metric`; + const res = await fetch(url); + const data = await res.json(); + if (!res.ok) city.textContent = 'Enter correct city'; + + return data; + } + + async fillWeatherBlock() { + const data = await this.getWeatherData(); + const { + weatherIcon, + temperature, + humidity, + wind, + weatherDescription, + } = this.getDomElements(); + + weatherIcon.className = 'weather-icon owf'; + weatherIcon.classList.add(`owf-${data.weather[0].id}`); + temperature.textContent = `${data.main.temp}°C`; + humidity.textContent = `Humidity ${data.main.humidity} %`; + wind.textContent = `Wind speed ${data.wind.speed} m/s`; + weatherDescription.textContent = data.weather[0].description; + } + + setCity(e) { + const { city } = this.getDomElements(); + const pressedEnter = e.which === 13 || e.keyCode === 13; + const cityEntered = + e.target.innerText !== '' && e.target.innerText !== 'Minsk'; + this.fillWeatherBlock.bind(this); + if (e.type === 'keypress') { + if (pressedEnter) { + city.blur(); + if (cityEntered) { + localStorage.setItem('city', e.target.innerText); + city.blur(); + this.fillWeatherBlock(); + } + } + } else if (e.target.innerText === '') { + city.textContent = localStorage.getItem('city') || 'Minsk'; + } else { + localStorage.setItem('city', e.target.innerText); + this.fillWeatherBlock(); + } + } + + render() { + this.parentNode.innerHTML = ` + +
+
Minsk
+ +
+
+
+
+
+ `; + this.fillWeatherBlock(); + this.addListeners(); + + this.btnMenu = this.parentNode.querySelector('.dot-menu'); + this.rssMenu = new WeatherMenu(this.btnMenu, 'Weather'); + } +} + +export default Weather; diff --git a/src/js/components/weather/weather.scss b/src/js/components/weather/weather.scss new file mode 100644 index 0000000..41d104c --- /dev/null +++ b/src/js/components/weather/weather.scss @@ -0,0 +1,16 @@ +.weather-content{ + text-align: center; + background-image: url(../../../assets/img/weather.jpg); + background-size: cover; + display: grid; + grid-gap: 5px; + padding: 10px; + text-shadow: 0px 0px 10px rgb(228, 177, 140); +} + +.city:focus, +.city:hover{ + border-radius: 25px; + outline: none; + box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.75); +} \ No newline at end of file diff --git a/src/js/data/constants.js b/src/js/data/constants.js index ee5ec59..edd1fab 100644 --- a/src/js/data/constants.js +++ b/src/js/data/constants.js @@ -1,184 +1,225 @@ -export const APP_NAME = "Start Page"; +export const APP_NAME = 'Start Page'; + +export const backend = 'https://startpage-be.herokuapp.com'; +export const userItemLocalStorage = 'startpage-user'; export const DEVELOPERS = [ { - user: "natein", - link: "https://github.com/natein", + user: 'natein', + link: 'https://github.com/natein', }, { - user: "vadim-bykov", - link: "https://github.com/vadim-bykov", + user: 'vadim-bykov', + link: 'https://github.com/vadim-bykov', }, ]; -export const DEFAULT_COLOR = "#F5D678"; +export const DEFAULT_COLOR = '#F5D678'; export const INITIAL_PAGE = [ { - name: "Start Page", + name: 'Start Page', color: DEFAULT_COLOR, }, ]; -export const COURSE_LINK = "https://rs.school/js/"; -export const COURSE_NAME = "JavaScript/Front-end 2020Q3"; +export const COURSE_LINK = 'https://rs.school/js/'; +export const COURSE_NAME = 'JavaScript/Front-end 2020Q3'; -export const faviconUrl = - "https://www.google.com/s2/favicons?sz=48&domain_url="; +export const faviconUrl = 'https://www.google.com/s2/favicons?sz=48&domain_url='; export const fullPopularLinks = [ { - title: "Wikipedia", - url: "https://ru.wikipedia.org/", + title: 'Wikipedia', + url: 'https://ru.wikipedia.org/', favicon: `${faviconUrl}https://ru.wikipedia.org/`, }, { - title: "YouTube", - url: "https://www.youtube.com/", + title: 'YouTube', + url: 'https://www.youtube.com/', favicon: `${faviconUrl}https://www.youtube.com/`, }, { - title: "facebook", - url: "https://www.facebook.com/", + title: 'facebook', + url: 'https://www.facebook.com/', favicon: `${faviconUrl}https://www.facebook.com/`, }, { - title: "ВКонтакте", - url: "https://vk.com/", + title: 'ВКонтакте', + url: 'https://vk.com/', favicon: `${faviconUrl}https://vk.com/`, }, { - title: "Одноклассники", - url: "https://ok.ru/", + title: 'Одноклассники', + url: 'https://ok.ru/', favicon: `${faviconUrl}https://ok.ru/`, }, { - title: "Yandex", - url: "https://yandex.ru/", + title: 'Yandex', + url: 'https://yandex.ru/', favicon: `${faviconUrl}https://yandex.ru/`, }, { - title: "Linkedin", - url: "https://www.linkedin.com/", + title: 'Linkedin', + url: 'https://www.linkedin.com/', favicon: `${faviconUrl}https://www.linkedin.com/`, }, ]; export const fullShopsLinks = [ { - title: "Joom", - url: "https://www.joom.com/", + title: 'Joom', + url: 'https://www.joom.com/', favicon: `${faviconUrl}https://www.joom.com/`, }, { - title: "Из рук в руки", - url: "https://irr.ru/", + title: 'Из рук в руки', + url: 'https://irr.ru/', favicon: `${faviconUrl}https://irr.ru/`, }, { - title: "Lamoda", - url: "https://www.lamoda.ru/", + title: 'Lamoda', + url: 'https://www.lamoda.ru/', favicon: `${faviconUrl}https://www.lamoda.ru/`, }, { - title: "Wildberries", - url: "https://www.wildberries.ru/", + title: 'Wildberries', + url: 'https://www.wildberries.ru/', favicon: `${faviconUrl}https://www.wildberries.ru/`, }, { - title: "Drom", - url: "https://www.drom.ru/", + title: 'Drom', + url: 'https://www.drom.ru/', favicon: `${faviconUrl}https://www.drom.ru/`, }, { - title: "Avito", - url: "https://www.avito.ru/rossiya", + title: 'Avito', + url: 'https://www.avito.ru/rossiya', favicon: `${faviconUrl}https://www.avito.ru/rossiya`, }, { - title: "Onliner", - url: "https://www.onliner.by/", + title: 'Onliner', + url: 'https://www.onliner.by/', favicon: `${faviconUrl}https://www.onliner.by/`, }, ]; export const fullTravelLinks = [ { - title: "Booking", - url: "https://www.booking.com/", + title: 'Booking', + url: 'https://www.booking.com/', favicon: `${faviconUrl}https://www.booking.com/`, }, { - title: "Tripadvisor", - url: "https://www.tripadvisor.ru/", + title: 'Tripadvisor', + url: 'https://www.tripadvisor.ru/', favicon: `${faviconUrl}https://www.tripadvisor.ru/`, }, { - title: "Tophotels", - url: "https://tophotels.ru/", + title: 'Tophotels', + url: 'https://tophotels.ru/', favicon: `${faviconUrl}https://tophotels.ru/`, }, { - title: "Skyscanner", - url: "https://www.skyscanner.net/", + title: 'Skyscanner', + url: 'https://www.skyscanner.net/', favicon: `${faviconUrl}https://www.skyscanner.net/`, }, { - title: "Expedia", - url: "https://www.expedia.com/", + title: 'Expedia', + url: 'https://www.expedia.com/', favicon: `${faviconUrl}https://www.expedia.com/`, }, { - title: "Joinpro", - url: "http://www.joinpro.ru/", + title: 'Joinpro', + url: 'http://www.joinpro.ru/', favicon: `${faviconUrl}http://www.joinpro.ru/`, }, { - title: "Turpravda", - url: "https://www.turpravda.com/", + title: 'Turpravda', + url: 'https://www.turpravda.com/', favicon: `${faviconUrl}https://www.turpravda.com/`, }, ]; export const fullGoogleLinks = [ { - title: "Sign in", - url: "https://plus.google.com/", + title: 'Sign in', + url: 'https://plus.google.com/', favicon: `${faviconUrl}https://plus.google.com/`, }, { - title: "News", - url: "https://news.google.com/", + title: 'News', + url: 'https://news.google.com/', favicon: `${faviconUrl}https://news.google.com/`, }, { - title: "Maps", - url: "https://www.google.com/maps/", + title: 'Maps', + url: 'https://www.google.com/maps/', favicon: `${faviconUrl}https://www.google.com/maps/`, }, { - title: "Calendar", - url: "https://calendar.google.com/calendar/u/0/r?pli=1", + title: 'Calendar', + url: 'https://calendar.google.com/calendar/u/0/r?pli=1', favicon: `${faviconUrl}https://calendar.google.com/calendar/u/0/r?pli=1`, }, { - title: "Play", - url: "https://play.google.com/store", + title: 'Play', + url: 'https://play.google.com/store', favicon: `${faviconUrl}https://play.google.com/store`, }, { - title: "Blogger", - url: "https://www.blogger.com/", + title: 'Blogger', + url: 'https://www.blogger.com/', favicon: `${faviconUrl}https://www.blogger.com/`, }, { - title: "Translate", - url: "https://translate.google.com/", + title: 'Translate', + url: 'https://translate.google.com/', favicon: `${faviconUrl}https://translate.google.com/`, }, { - title: "Drive", - url: "https://drive.google.com/", + title: 'Drive', + url: 'https://drive.google.com/', favicon: `${faviconUrl}https://drive.google.com/`, }, ]; + +export const classListBlocks = [ + { + class: 'finance', + active: true, + }, + { + class: 'rss', + active: true, + }, + { + class: 'popular', + active: true, + }, + { + class: 'shops', + active: true, + }, + { + class: 'travel', + active: true, + }, + { + class: 'google', + active: true, + }, + { + class: 'weather', + active: true, + }, + { + class: 'todo', + active: true, + }, + { + class: 'calc', + active: false, + }, +]; diff --git a/src/js/services/auth.js b/src/js/services/auth.js new file mode 100644 index 0000000..c7bfc1d --- /dev/null +++ b/src/js/services/auth.js @@ -0,0 +1,54 @@ +import * as Constants from '../data/constants'; + +const { backend, userItemLocalStorage } = Constants; + +// Регистрация пользователя +// Если задать уже имеющийся email или username, возвращает +// statusCode: 400, reason: "User username is already registered" + +export function isLogged() { + const token = localStorage.getItem(userItemLocalStorage); + return !!(token); +} + +export async function registerUser(username, email = '', password) { + const rawResponse = await fetch(`${backend}/auth/register`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, email, password }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {statusCode: code, token: "uuid"} + } + + const errorText = await rawResponse.text(); + throw new Error(errorText); +} + +// Логин +// Если задать неправильный password или username, возвращает +// statusCode: 403, reason: "Invalid username or password" + +export async function loginUser(username, password) { + const rawResponse = await fetch(`${backend}/auth/login`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, password }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {statusCode: code, token: "uuid"} + } + + const errorText = await rawResponse.text(); + throw new Error(errorText); +} diff --git a/src/js/services/pages.js b/src/js/services/pages.js index c6fd3df..9ec8f4d 100644 --- a/src/js/services/pages.js +++ b/src/js/services/pages.js @@ -1,22 +1,154 @@ +import ServiceError from './service-error'; +import * as Constants from '../data/constants'; + +const { backend } = Constants; + +// Модуль получает данные страниц +// При невалидном token ответ +// 403 (Forbidden) Error: You are not authorized + export function getPages() { - const locStorage = localStorage.getItem("startpage_pages"); + const locStorage = localStorage.getItem('startpage_pages'); return (locStorage) ? JSON.parse(locStorage) : null; } export function getCurrentPage() { - const locStorage = localStorage.getItem("startpage_curPage"); + const locStorage = localStorage.getItem('startpage_curPage'); return (locStorage) ? JSON.parse(locStorage) : null; } export function setPages(obj) { - localStorage.setItem("startpage_pages", JSON.stringify(obj)); + localStorage.setItem('startpage_pages', JSON.stringify(obj)); } export function setCurrentPage(idx) { - localStorage.setItem("startpage_curPage", JSON.stringify(idx)) + localStorage.setItem('startpage_curPage', JSON.stringify(idx)); } export function clearPages() { - localStorage.removeItem("startpage_pages"); - localStorage.removeItem("startpage_curPage"); + localStorage.removeItem('startpage_pages'); + localStorage.removeItem('startpage_curPage'); +} + +// Получить список всех страниц + +export async function getPagesList(token) { + const rawResponse = await fetch(`${backend}/pages`, { + method: 'GET', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // [{id: "uuid", userId: "uuid", name: "Page title", data: "{json}"}] + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Получить страницу по идентификатору + +export async function getPageById(id, token) { + const rawResponse = await fetch(`${backend}/pages/${id}`, { + method: 'GET', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", userId: "uuid", name: "Page title", after: null, data: "{json}"} + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Page not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Создать новую страницу +// name - наименование страницы +// data - данные блоков страницы + +export async function createNewPage(name, after, data, token) { + const rawResponse = await fetch(`${backend}/pages`, { + method: 'POST', + headers: { + authorization: `${token}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ name, after, data }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", userId: "uuid", name: "Page title", after: null, data: "{json}"} + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Удалить страницу по идентификатору + +export async function deletePageById(id, token) { + const rawResponse = await fetch(`${backend}/pages/${id}`, { + method: 'DELETE', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + return true; + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Page not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw new ServiceError(errorText, rawResponse.status); +} + +// Обновить страницу по идентификатору +// name - наименование страницы +// data - данные блоков страницы + +export async function updatePagesById(id, name, after, data, token) { + const rawResponse = await fetch(`${backend}/pages/${id}`, { + method: 'PUT', + headers: { + authorization: `${token}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ name, after, data }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", userId: "uuid", name: "Page title", after: null, data: "{json}"} + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Page not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); } diff --git a/src/js/services/service-error.js b/src/js/services/service-error.js new file mode 100644 index 0000000..8e7ee40 --- /dev/null +++ b/src/js/services/service-error.js @@ -0,0 +1,8 @@ +class ServiceError extends Error { + constructor(message, status) { + super(message); + this.status = status; + } +} + +export default ServiceError; diff --git a/src/js/services/todos.js b/src/js/services/todos.js new file mode 100644 index 0000000..e1a1c85 --- /dev/null +++ b/src/js/services/todos.js @@ -0,0 +1,134 @@ +import ServiceError from './service-error'; +import * as Constants from '../data/constants'; + +const { backend } = Constants; + +// Модуль получает данные для списка ToDo +// При невалидном token ответ +// 403 (Forbidden) Error: You are not authorized + +// Получить все задачи + +export async function taskList(token) { + const rawResponse = await fetch(`${backend}/todos`, { + method: 'GET', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // [{id: "uuid", title: "Task title", complete: true, userId: "uuid"}] + } + + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Получить задачу по идентификатору + +export async function taskById(id, token) { + const rawResponse = await fetch(`${backend}/todos/${id}`, { + method: 'GET', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", title: "Task title", complete: true, userId: "uuid"} + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Task not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Создать новую задачу +// title - наименование +// complete - состояние задачи. +// true - выполнено, false - не выполнено, null - не установлено + +export async function createNewTask(title, complete, token) { + const rawResponse = await fetch(`${backend}/todos`, { + method: 'POST', + headers: { + authorization: `${token}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ title, complete }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", title: "Task title", complete: true, userId: "uuid"} + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +} + +// Удалить задачу по идентификатору + +export async function deleteTaskById(id, token) { + const rawResponse = await fetch(`${backend}/todos/${id}`, { + method: 'DELETE', + headers: { + authorization: `${token}`, + }, + }); + + if (rawResponse.ok) { + return true; + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Task not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw new ServiceError(errorText, rawResponse.status); +} + +// Обновить задачу по идентификатору +// title - наименование +// complete - состояние задачи. +// true - выполнено, false - не выполнено, null - не установлено + +export async function updateTaskById(id, title, complete, token) { + const rawResponse = await fetch(`${backend}/todos/${id}`, { + method: 'PUT', + headers: { + authorization: `${token}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ title, complete }), + }); + + if (rawResponse.ok) { + const content = await rawResponse.json(); + return content; // {id: "uuid", title: "Task title", complete: true, userId: "uuid"}] + } + if (rawResponse.status === 403) { + throw new ServiceError('You are not authorized', rawResponse.status); + } + if (rawResponse.status === 404) { + throw new ServiceError('Task not found', rawResponse.status); + } + const errorText = await rawResponse.text(); + throw Error(errorText); +}