From d4a7d43ed1147f4180b7c4404cfdcc628bff6676 Mon Sep 17 00:00:00 2001 From: MisterGetman <99386291+MisterGetman@users.noreply.github.com> Date: Sat, 29 Jun 2024 03:00:43 +0300 Subject: [PATCH] Done. --- package-lock.json | 22 +++++++++ package.json | 6 ++- src/1-timer.html | 39 +++++++++++++++ src/2-snackbar.html | 37 ++++++++++++++ src/css/1-timer.css | 73 ++++++++++++++++++++++++++++ src/css/2-snackbar.css | 80 ++++++++++++++++++++++++++++++ src/index.html | 9 ++++ src/js/1-timer.js | 107 +++++++++++++++++++++++++++++++++++++++++ src/js/2-snackbar.js | 43 +++++++++++++++++ src/main.js | 0 src/page-2.html | 15 ------ src/page-3.html | 15 ------ src/public/favicon.svg | 1 - 13 files changed, 414 insertions(+), 33 deletions(-) create mode 100644 src/1-timer.html create mode 100644 src/2-snackbar.html create mode 100644 src/css/1-timer.css create mode 100644 src/css/2-snackbar.css create mode 100644 src/js/1-timer.js create mode 100644 src/js/2-snackbar.js delete mode 100644 src/main.js delete mode 100644 src/page-2.html delete mode 100644 src/page-3.html delete mode 100644 src/public/favicon.svg diff --git a/package-lock.json b/package-lock.json index ce19a72..b1a621d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.0", "license": "ISC", "dependencies": { + "flatpickr": "^4.6.13", + "izitoast": "^1.4.0", "vite-plugin-full-reload": "^1.0.5", "vite-plugin-html-inject": "^1.0.1" }, @@ -398,6 +400,11 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -452,6 +459,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/izitoast": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/izitoast/-/izitoast-1.4.0.tgz", + "integrity": "sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w==" + }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -811,6 +823,11 @@ "@esbuild/win32-x64": "0.18.20" } }, + "flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -852,6 +869,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "izitoast": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/izitoast/-/izitoast-1.4.0.tgz", + "integrity": "sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w==" + }, "minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", diff --git a/package.json b/package.json index 59f278d..9b53a3c 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,14 @@ "preview": "vite preview" }, "devDependencies": { - "vite": "^4.5.2", - "glob": "^8.1.0" + "glob": "^8.1.0", + "vite": "^4.5.2" }, "author": "me", "license": "ISC", "dependencies": { + "flatpickr": "^4.6.13", + "izitoast": "^1.4.0", "vite-plugin-full-reload": "^1.0.5", "vite-plugin-html-inject": "^1.0.1" } diff --git a/src/1-timer.html b/src/1-timer.html new file mode 100644 index 0000000..ad09570 --- /dev/null +++ b/src/1-timer.html @@ -0,0 +1,39 @@ + + + + + + + 1-timer + + + + + 2-snackbar +
+ + + +
+
+ 00 + Days +
+
+ 00 + Hours +
+
+ 00 + Minutes +
+
+ 00 + Seconds +
+
+
+ + + + diff --git a/src/2-snackbar.html b/src/2-snackbar.html new file mode 100644 index 0000000..8915da7 --- /dev/null +++ b/src/2-snackbar.html @@ -0,0 +1,37 @@ + + + + + + + 2-snackbar + + + + +
+ 1-timer +
+ + +
+ State + + +
+ + +
+
+ + + diff --git a/src/css/1-timer.css b/src/css/1-timer.css new file mode 100644 index 0000000..2c83869 --- /dev/null +++ b/src/css/1-timer.css @@ -0,0 +1,73 @@ +.wrapper { + margin: 20px 8px 0px; +} + +#datetime-picker { + padding: 8px 16px; + margin-bottom: 32px; + border: 1px solid #808080; + border-radius: 4px; + width: 272px; + font-weight: 400; + font-size: 16px; + line-height: 1.5; + letter-spacing: 0.04em; + color: #2e2f42; + cursor: not-allowed; +} + +#datetime-picker.inp-enabled { + cursor: pointer; +} + +button { + border: none; + border-radius: 8px; + padding: 9px 16px; + min-width: 75px; + background: #cfcfcf; + font-weight: 500; + font-size: 16px; + line-height: 1.5; + letter-spacing: 0.04em; + color: #989898; + cursor: not-allowed; +} + +.btn-enabled { + background-color: #4e75ff; + color: #fff; + cursor: pointer; +} + +.enabled:hover, +.enabled:focus { + background: #6c8cff; +} + +.timer { + display: flex; + gap: 24px; +} + +.field { + display: flex; + flex-direction: column; + align-items: center; +} + +.value { + font-weight: 400; + font-size: 40px; + line-height: 1.2; + letter-spacing: 0.04em; + color: #2e2f42; +} + +.label { + font-weight: 400; + font-size: 16px; + line-height: 1.5; + color: #2e2f42; + text-transform: uppercase; +} diff --git a/src/css/2-snackbar.css b/src/css/2-snackbar.css new file mode 100644 index 0000000..99adbdf --- /dev/null +++ b/src/css/2-snackbar.css @@ -0,0 +1,80 @@ +.form { + width: 360px; +} + +.form > label { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 16px; +} + +input { + border: 1px solid #808080; + border-radius: 4px; + height: 40px; + padding-left: 16px; + font-size: 16px; + line-height: 1.5; + letter-spacing: 0.04em; + color: #2e2f42; + outline: transparent; + background-color: #fff; + transition: background-color 250ms linear; +} + +input:hover, +fieldset:hover { + border: 1px solid #000; +} + +label { + font-weight: 400; + font-size: 16px; + line-height: 1.5; + color: #2e2f42; +} + +fieldset { + margin-bottom: 24px; + border-radius: 4px; + border: 1px solid #808080; + font-weight: 400; + font-size: 16px; + line-height: 1.5; + letter-spacing: 0.04em; + color: #2e2f42; + transition: border 250ms linear; +} + +legend { + margin-left: 28px; +} + +input[name='state'] { + width: 20px; + height: 20px; + display: inline-block; + margin-left: 48px; + vertical-align: top; +} + +button[type='submit'] { + border: none; + border-radius: 8px; + padding: 8px 16px; + min-width: 360px; + height: 40px; + background: #4e75ff; + font-weight: 500; + font-size: 16px; + line-height: 1.5; + letter-spacing: 0.04em; + color: #fff; + transition: background-color 250ms linear; +} + +button:hover, +button:focus { + background: #6c8cff; +} diff --git a/src/index.html b/src/index.html index 491246b..3912922 100644 --- a/src/index.html +++ b/src/index.html @@ -8,6 +8,15 @@ + diff --git a/src/js/1-timer.js b/src/js/1-timer.js new file mode 100644 index 0000000..f030a09 --- /dev/null +++ b/src/js/1-timer.js @@ -0,0 +1,107 @@ +import flatpickr from 'flatpickr'; +import 'flatpickr/dist/flatpickr.min.css'; +import iziToast from 'izitoast'; +import 'izitoast/dist/css/iziToast.min.css'; + +let userSelectedDate; +const refs = { + days: document.querySelector('[data-days]'), + hours: document.querySelector('[data-hours]'), + minutes: document.querySelector('[data-minutes]'), + seconds: document.querySelector('[data-seconds]'), +}; + +const inputElem = document.querySelector('#datetime-picker'); +const buttonElem = document.querySelector('[data-start]'); + +disableButtonElem(); +enableInputElem(); + +buttonElem.addEventListener('click', startCountdown); + +const options = { + enableTime: true, + time_24hr: true, + defaultDate: new Date(), + minuteIncrement: 1, + onClose(selectedDates) { + handleDateSelection(selectedDates); + }, +}; + +flatpickr('#datetime-picker', options); + +function handleDateSelection(selectedDates) { + if (isFutureDate(selectedDates[0])) { + userSelectedDate = selectedDates[0]; + enableButtonElem(); + } else { + disableButtonElem(); + iziToast.show({ + title: 'Error:', + message: 'Please choose a date in the future', + backgroundColor: 'tomato', + position: 'topRight', + }); + } +} + +function isFutureDate(selectedDate) { + return selectedDate.getTime() > Date.now(); +} + +function startCountdown() { + disableButtonElem(); + disableInputElem(); + + let msLeft = userSelectedDate - Date.now(); + + const countdownId = setInterval(() => tick((msLeft -= 1000)), 1000); + setTimeout(() => finishCountdown(countdownId), msLeft); +} + +function finishCountdown(id) { + clearInterval(id); + enableInputElem(); +} + +function tick(msLeft) { + const timeObj = convertMs(msLeft); + for (const key in refs) { + refs[key].textContent = timeObj[key].toString().padStart(2, '0'); + } +} + +function convertMs(ms) { + const second = 1000; + const minute = second * 60; + const hour = minute * 60; + const day = hour * 24; + + const days = Math.floor(ms / day); + const hours = Math.floor((ms % day) / hour); + const minutes = Math.floor(((ms % day) % hour) / minute); + const seconds = Math.floor((((ms % day) % hour) % minute) / second); + + return { days, hours, minutes, seconds }; +} + +function disableButtonElem() { + buttonElem.disabled = true; + buttonElem.classList.remove('btn-enabled'); +} + +function enableButtonElem() { + buttonElem.disabled = false; + buttonElem.classList.add('btn-enabled'); +} + +function disableInputElem() { + inputElem.disabled = true; + inputElem.classList.remove('inp-enabled'); +} + +function enableInputElem() { + inputElem.disabled = false; + inputElem.classList.add('inp-enabled'); +} diff --git a/src/js/2-snackbar.js b/src/js/2-snackbar.js new file mode 100644 index 0000000..e0ade69 --- /dev/null +++ b/src/js/2-snackbar.js @@ -0,0 +1,43 @@ +import iziToast from 'izitoast'; +import 'izitoast/dist/css/iziToast.min.css'; + +const formElem = document.querySelector('form'); + +const generatePromise = ({ delay, state }) => { + return new Promise((res, rej) => { + setTimeout(() => { + if (state === 'fulfilled') { + res(delay); + } else { + rej(delay); + } + }, delay); + }); +}; + +const handleSubmit = e => { + e.preventDefault(); + + generatePromise({ + delay: formElem.delay.value, + state: formElem.state.value, + }) + .then(delay => { + iziToast.show({ + title: '✅', + message: `Fulfilled promise in ${delay}ms`, + backgroundColor: 'limegreen', + position: 'topRight', + }); + }) + .catch(delay => { + iziToast.show({ + title: '❌', + message: `Rejected promise in ${delay}ms`, + backgroundColor: 'tomato', + position: 'topRight', + }); + }); +}; + +formElem.addEventListener('submit', handleSubmit); diff --git a/src/main.js b/src/main.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/page-2.html b/src/page-2.html deleted file mode 100644 index 5aad26c..0000000 --- a/src/page-2.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Page 2 - - -
- Go to home -

Welcome to page 2

-
- - diff --git a/src/page-3.html b/src/page-3.html deleted file mode 100644 index 9e52b5b..0000000 --- a/src/page-3.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Page 3 - - -
- Go to home -

Welcome to page 3

-
- - diff --git a/src/public/favicon.svg b/src/public/favicon.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/src/public/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file