diff --git a/aggregate.css b/aggregate.css new file mode 100644 index 0000000..738c4ec --- /dev/null +++ b/aggregate.css @@ -0,0 +1,712 @@ +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; +} + +:root { + scroll-behavior: smooth; +} + +body { + --primary-color: #303F9F; + --text: 17, 17, 17; + --text-light: 85, 85, 85; + --foreground: 255, 255, 255; + --background: #e8e8e8; + --dark-shade: #dadada; + background: var(--foreground); + color: rgba(var(--text), 1); + font-size: 16px; + margin: 1.5rem; +} + +a { + font-weight: 600; + text-decoration: none; + color: var(--primary-color); +} + +.dark-text { + color: #111; +} + +h1, h2, h3, h4, h5 { + font-weight: 600; +} + +button { + position: relative; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + text-transform: uppercase; + letter-spacing: 0.1em; + padding: 0.6rem 1rem; + font-weight: 600; + cursor: pointer; + border-radius: 0.2em; + color: var(--primary-color); + -webkit-transition: -webkit-transform 0.3s, -webkit-clip-path 0.3s; + transition: -webkit-transform 0.3s, -webkit-clip-path 0.3s; + transition: transform 0.3s, clip-path 0.3s; + transition: transform 0.3s, clip-path 0.3s, -webkit-transform 0.3s, -webkit-clip-path 0.3s; + border: none; + -webkit-clip-path: circle(100%); + clip-path: circle(100%); + background: rgba(var(--text), 0.1); + -webkit-tap-highlight-color: transparent; +} + +button:focus { + outline: thin solid rgba(var(--text-light), 0.4); +} + +button:disabled { + cursor: default; + background: rgba(var(--text-light), 1); +} + +button:disabled ~ .loader { + opacity: 0; +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type=text]::-ms-clear { + display: none; + width: 0; + height: 0; +} + +input[type=text]::-ms-reveal { + display: none; + width: 0; + height: 0; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-results-button, +input[type="search"]::-webkit-search-results-decoration { + display: none; +} + +input[type=number] { + -moz-appearance: textfield; +} + +input:invalid { + outline: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +::-moz-focus-inner { + border: none; +} + +.bottom-padding { + padding-bottom: 1em; +} + +.top-padding { + padding-top: 1em; +} + +.bottom-margin { + margin-bottom: 1em; +} + +.top-margin { + margin-top: 1em; +} + +.flex { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.grid { + display: -ms-grid; + display: grid; +} + +.grid-2 { + -ms-grid-columns: auto auto; + grid-template-columns: auto auto; + gap: 1em; +} + +.light-text { + color: rgba(var(--text-light), 1); +} + +.hide { + opacity: 0; + pointer-events: none; +} + +.hide-completely { + display: none !important; +} + +.breakable { + word-break: break-all; +} + +.separator { + padding: .1em; +} + +.no-transformations { + -webkit-transform: none !important; + transform: none !important; +} + +.loader { + fill: none; + stroke-width: 10; + stroke: var(--primary-color); + height: 2rem; + width: 2rem; + overflow: visible; + stroke-dashoffset: 230; + stroke-dasharray: 230; + padding: 2px; + -ms-grid-column-align: center; + justify-self: center; +} + +@-webkit-keyframes rotate { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes rotate { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@-webkit-keyframes load { + 50% { + stroke-dashoffset: 0; + } + 100% { + stroke-dashoffset: -210; + } +} + +@keyframes load { + 50% { + stroke-dashoffset: 0; + } + 100% { + stroke-dashoffset: -210; + } +} + +.animate-loader { + -webkit-animation: load 2.6s infinite, rotate 1s infinite linear; + animation: load 2.6s infinite, rotate 1s infinite linear; +} + +.expand { + width: 100%; +} + +.fade-left { + -webkit-animation: fadeleft 0.3s; + animation: fadeleft 0.3s; +} + +.fade-right { + -webkit-animation: faderight 0.3s; + animation: faderight 0.3s; +} + +@-webkit-keyframes faderight { + from { + opacity: 0; + -webkit-transform: translateX(-1em); + transform: translateX(-1em); + } + to { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes faderight { + from { + opacity: 0; + -webkit-transform: translateX(-1em); + transform: translateX(-1em); + } + to { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@-webkit-keyframes fadeleft { + from { + opacity: 0; + -webkit-transform: translateX(1em); + transform: translateX(1em); + } + to { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeleft { + from { + opacity: 0; + -webkit-transform: translateX(1em); + transform: translateX(1em); + } + to { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +#logo { + display: -ms-inline-grid; + display: inline-grid; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.2rem; + margin-right: 1rem; +} + +#logo h4 { + letter-spacing: 0.06rem; + word-spacing: 0.12rem; + margin-top: 0.2rem; +} + +#logo h5 { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +#logo #main_logo { + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text), 1); + stroke: none; +} + +.input { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + width: 100%; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: relative; + padding: 0.8em; + margin-bottom: 1.5em; + border-radius: 0.2em; + background: rgba(var(--text), 0.1); + border: 0.1em solid transparent; +} + +.input:last-of-type { + margin-bottom: 0; +} + +.input:focus-within { + border: 0.1em solid var(--primary-color); +} + +.input label { + opacity: .7; + font-weight: 500; + font-size: 1em; + position: absolute; + -webkit-transition: -webkit-transform 0.3s ease; + transition: -webkit-transform 0.3s ease; + transition: transform 0.3s ease; + transition: transform 0.3s ease, -webkit-transform 0.3s ease; + -webkit-transform-origin: left; + transform-origin: left; + pointer-events: none; + will-change: contents; + text-transform: capitalize; +} + +.input input { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + font-size: 1rem; + border: none; + background: transparent; + outline: none; + color: rgba(var(--text), 1); +} + +.animate-label input { + -webkit-transform: translateY(0.5em); + transform: translateY(0.5em); +} + +.animate-label label { + -webkit-transform: translateY(-60%) scale(0.7); + transform: translateY(-60%) scale(0.7); + opacity: 1; + color: var(--primary-color); +} +.solid-background { + background: var(--background) !important; +} + +form { + width: 100%; +} + +.popup-container { + display: -ms-grid; + display: grid; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + place-items: center; + background: rgba(0, 0, 0, 0.24); + z-index: 10; + -webkit-transition: opacity 0.3s ease; + transition: opacity 0.3s ease; +} + +.popup-container .popup { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-item-align: end; + align-self: flex-end; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + width: calc(100% - 2rem); + margin-bottom: 1rem; + border-radius: 0.5rem; + padding: 1.5rem; + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + background: rgba(var(--foreground), 1); + -webkit-transform: translateY(100%); + transform: translateY(100%); + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; + -webkit-box-shadow: 0 2rem 2rem rgba(0, 0, 0, 0.24); + box-shadow: 0 2rem 2rem rgba(0, 0, 0, 0.24); + overflow-y: auto; +} + +.popup-container .popup h5 { + margin: 0.5rem 0; +} + +.popup-container .popup button:first-of-type { + margin-left: auto; +} + +.popup-container .popup .container-header { + display: -ms-grid; + display: grid; + -ms-grid-columns: auto 1fr auto; + grid-template-columns: auto 1fr auto; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0; + margin-bottom: 1.5rem; +} + +.popup-container .popup .container-header .icon { + cursor: pointer; + padding-right: 0.4rem; + stroke-width: 8; +} + +.popup-container .popup .container-header .btn { + padding: 0.6em 1.2em; +} + +.popup-container .popup p { + margin-bottom: 1.5rem !important; +} + +#show_message { + -webkit-transform: translate(0, -100%); + transform: translate(0, -100%); + -webkit-transition: opacity 0.3s, -webkit-transform 0.3s; + transition: opacity 0.3s, -webkit-transform 0.3s; + transition: transform 0.3s, opacity 0.3s; + transition: transform 0.3s, opacity 0.3s, -webkit-transform 0.3s; + -webkit-box-shadow: 0 0.4rem 0.8rem rgba(0, 0, 0, 0.16); + box-shadow: 0 0.4rem 0.8rem rgba(0, 0, 0, 0.16); + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + color: rgba(var(--text), 1); + position: fixed; + right: 0; + top: 0; + width: calc(100% - 2rem); + margin: 1rem; + border-radius: 0.5rem; + border: solid 1px rgba(var(--text), 0.2); + max-width: 100%; + z-index: 40; + background: rgba(var(--foreground), 1); +} + +#show_message #error_icon { + fill: #E53935; +} + +#show_message #done_icon { + fill: #00C853; +} + +#show_message .notification-icon { + height: 2em; + width: 2em; + margin: 1em 0 1em 1em; + fill: rgba(var(--text), 1); +} + +#show_message div { + padding: 1em; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +#show_message div h5 { + opacity: 0.8; +} + +#show_message span { + font-weight: 500; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +#show_message button { + padding: 1rem; + margin: 0 1em 0 0; + border: none; + background: none; +} + +#show_message button svg { + height: 1em; + width: 1em; + stroke: rgba(var(--text), 1); + stroke-width: 6; +} + +#confirmation, #prompt { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + padding: 1.5rem; +} + +#confirmation p, #prompt p { + margin: 1rem; + font-size: 1rem; + font-weight: 500; + color: rgba(var(--rgb-bw), 1) !important; +} + +#confirmation h4, #prompt h4 { + font-weight: 500; + margin-bottom: 1.5rem; +} + +#confirmation .input, #prompt .input { + margin-bottom: 1rem; +} + +#confirmation .btns, #prompt .btns { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: right; + -ms-flex-pack: right; + justify-content: right; + width: 100%; +} + +#confirmation .btns button, #prompt .btns button { + background: none; +} + +#confirmation .btns button:first-of-type, #prompt .btns button:first-of-type { + margin-right: 0.6em; +} +.primary-btn { + background: var(--primary-color); + padding: 0.8em 1.6em; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + color: rgba(var(--foreground), 1); +} +.icon { + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} +.notification-dot::after { + content: ''; + position: absolute; + z-index: 1; + top: 0; + right: 0; + height: 0.6em; + width: 0.6em; + background-color: #E53935; + border-radius: 0.4em; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; +} + +.shrink.notification-dot::after { + -webkit-transform: scale(0); + transform: scale(0); +} +#textCopied { + padding: 1rem; + border-radius: 2rem; + background: rgba(var(--foreground), 1); + position: fixed; + bottom: 0; + pointer-events: none; + margin: 2rem 0; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + -webkit-transition: 0.3s opacity ease; + transition: 0.3s opacity ease; + z-index: 20; +} +.tabs { + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 1rem 0 1rem 0; + background: rgba(var(--foreground), 1); + z-index: 2; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + overflow-x: auto; +} + +.tabs .tab { + cursor: pointer; + opacity: 0.6; + margin-right: 1.5rem; + -webkit-tap-highlight-color: transparent; + white-space: nowrap; + font-size: 1.1rem; +} + +.tabs .tab:last-of-type { + margin-right: 0; +} + +.tabs .tab.active { + opacity: 1; +} + +.tabs .line { + position: absolute; + height: 0.12rem; + background: rgba(var(--text), 1); + width: 1px; + bottom: 0; + -webkit-transition: width 0.4s, -webkit-transform 0.4s; + transition: width 0.4s, -webkit-transform 0.4s; + transition: transform 0.4s, width 0.4s; + transition: transform 0.4s, width 0.4s, -webkit-transform 0.4s; +} +@media only screen and (min-width: 640px) { + .popup-container .popup { + width: 24rem; + -ms-flex-item-align: center; + -ms-grid-row-align: center; + align-self: center; + border-radius: 0.2rem; + height: auto; + -webkit-transform: translateY(1rem); + transform: translateY(1rem); + } + #show_message { + -webkit-transform: translate(100%, 0); + transform: translate(100%, 0); + margin: 1rem; + max-width: 60vw; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + border-radius: 0.2rem; + } +} \ No newline at end of file diff --git a/aggregate.js b/aggregate.js new file mode 100644 index 0000000..4816f4d --- /dev/null +++ b/aggregate.js @@ -0,0 +1,349 @@ +//Checks for internet connection status +if (!navigator.onLine) + notify('There seems to be a problem connecting to the internet.', 'error', 'fixed', true) +window.addEventListener('offline', () => { + notify('There seems to be a problem connecting to the internet.', 'error', 'fixed', true) +}) +window.addEventListener('online', () => { + notify('We are back online.', '', '', true) +}) + +// **** requires HTML**** +let themeToggler = document.getElementById("theme_toggle"), + body = document.querySelector("body"); +if (localStorage.theme === "dark") { + nightlight(); + themeToggler.checked = true; +} else { + daylight(); + themeToggler.checked = false; +} + +function daylight() { + body.setAttribute("data-theme", "light"); +} + +function nightlight() { + body.setAttribute("data-theme", "dark"); +} +themeToggler.addEventListener("change", () => { + if (themeToggler.checked) { + nightlight(); + localStorage.setItem("theme", "dark"); + } else { + daylight(); + localStorage.setItem("theme", "light"); + } +}); +// function required for popups or modals to appear +class Stack { + constructor() { + this.items = []; + } + push(element) { + this.items.push(element); + } + pop() { + if (this.items.length == 0) + return "Underflow"; + return this.items.pop(); + } + peek() { + return this.items[this.items.length - 1]; + } +} +let popupStack = new Stack(), + zIndex = 10; +function showPopup(popup, permission) { + let thisPopup = document.getElementById(popup); + thisPopup.parentNode.classList.remove('hide'); + thisPopup.classList.add('no-transformations'); + popupStack.push({ popup, permission }) + zIndex++; + thisPopup.parentNode.setAttribute('style', `z-index: ${zIndex}`) + document.getElementById('main_page') + if (popup === 'main_loader') { + loader.classList.add('animate-loader') + document.querySelector('main').classList.add('hide-completely') + } +} + +// hides the popup or modal +function hidePopup() { + if (popupStack.peek() === undefined) + return + let { popup, permission } = popupStack.pop(); + thisPopup = document.getElementById(popup); + thisPopup.closest('.popup-container').classList.add('hide'); + thisPopup.closest('.popup').classList.remove('no-transformations'); + setTimeout(() => { + clearAllInputs(thisPopup) + zIndex--; + thisPopup.parentNode.setAttribute('style', `z-index: ${zIndex}`) + }, 400) + if (popup === 'main_loader' || popup === 'sign_in_popup') { + //loader.classList.remove('animate-loader') + document.querySelector('main').classList.remove('hide-completely') + } +} + +addEventListener('mousedown', e => { + if (e.target.classList.contains('popup-container') && popupStack.peek() !== undefined && popupStack.peek().permission !== 'no') { + hidePopup() + } +}) + +function setAttributes(el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } +} + +function clearAllInputs(parent) { + parent.querySelectorAll("input").forEach((field) => { + if (field.getAttribute('type') !== 'radio') { + field.value = ''; + if (field.closest('.input')) { + field.closest('.input').classList.remove('animate-label') + } + } + else + field.checked = false + }) + if (parent.querySelector("button[type='submit']")) + parent.querySelector("button[type='submit']").disabled = true; +} + +//Function for displaying toast notifications. +// **** requires HTML**** +/*options +message - notifiation body text. +mode - error or normal notification. only error has to be specified. +fixed - if set true notification will not fade after 4s; +sound - set true to enable notification sound. ! should only be used for important tasks. +setAside - set true to add notification inside notification panel +*/ +function notify(message, mode, behavior, sound) { + let banner = document.getElementById('show_message'), + back; + if (mode === 'error') { + banner.querySelector('#error_icon').classList.remove('hide-completely') + banner.querySelector('#done_icon').classList.add('hide-completely') + } + else { + banner.querySelector('#error_icon').classList.add('hide-completely') + banner.querySelector('#done_icon').classList.remove('hide-completely') + } + + banner.style.background = back; + banner.classList.add('no-transformations') + banner.classList.remove('hide') + banner.querySelector('#notification_text').textContent = message.charAt(0).toUpperCase() + message.slice(1); + if (navigator.onLine && sound) { + notificationSound.currentTime = 0; + notificationSound.play(); + } + banner.querySelector('#hide_banner_btn').onclick = function () { + banner.classList.add('hide') + banner.classList.remove('no-transformations') + } + clearTimeout(currentTimeout) + if (behavior === 'fixed') return; + currentTimeout = setTimeout(() => { + banner.classList.add('hide') + banner.classList.remove('no-transformations') + }, 4000) +} +// **** requires HTML**** +// displays a popup for asking permission. Use this instead of JS confirm +let askConfirmation = function (message) { + return new Promise(resolve => { + let popup = document.getElementById('confirmation'); + showPopup('confirmation') + popup.querySelector('#confirm_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(true); + } + }) +} +// **** requires HTML**** +// displays a popup for asking user input. Use this instead of JS prompt +let askPrompt = function (message, defaultVal) { + return new Promise(resolve => { + let popup = document.getElementById('prompt'), + input = popup.querySelector('input'); + input.value = defaultVal; + showPopup('prompt') + popup.querySelector('#prompt_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(input.value); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(null); + } + }) +} + +// prevents non numerical input on firefox +function preventNonNumericalInput(e) { + e = e || window.event; + let charCode = (typeof e.which == "undefined") ? e.keyCode : e.which, + charStr = String.fromCharCode(charCode); + + if (!charStr.match(/[+-]?([0-9]*[.])?[0-9]+/)) + e.preventDefault(); +} + +function areInputsEmpty(parent) { + let allInputs = parent.querySelectorAll(".input input"); + return [...allInputs].every(input => input.checkValidity()) +} + +function formValidation(formElement, e) { + if (formElement.getAttribute('type') === 'number') + preventNonNumericalInput(e); + let parent = formElement.closest('.popup'), + submitBtn = parent.querySelector("button[type='submit']"); + if (areInputsEmpty(parent)) + submitBtn.disabled = false; + else + submitBtn.disabled = true; +} +// **** requires HTML**** +let allForms = document.querySelectorAll('form'); +allForms.forEach((form) => { + form.addEventListener('input', (e) => { + if (e.target.closest('.input')) { + let parent = e.target.closest('.input'); + if (parent.firstElementChild.value !== '') + parent.classList.add('animate-label') + else + parent.classList.remove('animate-label') + formValidation(parent.firstElementChild, e) + if (e.key === 'Enter') { + parent.closest('form').querySelector("button[type='submit']").click(); + } + } + }) +}) +allForms.forEach((form) => { + form.addEventListener('blur', (e) => { + if (e.target.closest('.input')) { + let parent = e.target.closest('.input'); + if (parent.firstElementChild.value === '') + parent.classList.remove('animate-label') + } + }, true) +}) + +// **** requires HTML**** +let tabMounted = false; +function showTab(tab) { + let targetTab = tab.getAttribute('data-target'), + activeTabRank = parseInt(tab.parentNode.querySelector('.active').dataset.rank), + currentTabRank = parseInt(tab.dataset.rank), + currentBody = document.getElementById(tab.parentNode.querySelector('.active').dataset.target), + targetBody = document.getElementById(targetTab), + targetGroup = targetBody.dataset.tabGroup, + tabGroupMembers = document.querySelectorAll(`[data-tab-group = '${targetGroup}']`), + allTabs = tab.parentNode.querySelectorAll('.tab'), + line = tab.parentNode.querySelector('.line') + if (tabMounted && currentBody.isEqualNode(targetBody)) + return + if (activeTabRank < currentTabRank) { + targetBody.classList.add('fly-in-from-right') + currentBody.classList.add('fly-out-to-left') + } else { + targetBody.classList.add('fly-in-from-left') + currentBody.classList.add('fly-out-to-right') + } + setTimeout(() => { + tabGroupMembers.forEach(member => { + member.classList.add('hide-completely') + }) + targetBody.classList.remove('hide-completely') + currentBody.classList.remove('fly-out-to-right', 'fly-out-to-left') + }, 200) + setTimeout(() => { + targetBody.classList.remove('fly-in-from-right', 'fly-in-from-left') + }, 400) + allTabs.forEach(thisTab => { + thisTab.classList.remove('active') + }) + tab.classList.add('active') + line.setAttribute('style', `transform: translateX(${tab.offsetLeft}px); width: ${tab.getBoundingClientRect().width}px`) + tabMounted = true; +} + +let loader = document.getElementById("loader_page"); + +function loading(status) { + if (status) loader.classList.remove("hide-completely"); + else loader.classList.add("hide-completely"); +} +// **** requires HTML**** +function copyToClipboard(parent) { + let input = document.createElement('textarea'), + toast = document.getElementById('textCopied'), + textToCopy = parent.querySelector('.copy'); + input.setAttribute('readonly', ''); + input.setAttribute('style', 'position: absolute; left: -9999px'); + document.body.appendChild(input); + input.value = textToCopy.textContent; + input.select(); + document.execCommand('copy'); + document.body.removeChild(input); + toast.classList.remove('hide'); + setTimeout(() => { + toast.classList.add('hide'); + }, 2000) +} +// **** requires HTML**** +let loader = document.getElementById("loader_page"); + +function loading(status) { + if (status) loader.classList.remove("hide-completely"); + else loader.classList.add("hide-completely"); +} + +//show or hide element group from a group + +function showElement(elem, classGroup) { + let allGroups = document.querySelectorAll(`.${classGroup}`), + thisElement = elem; + if (typeof elem === 'string') + thisElement = document.getElementById(elem); + allGroups.forEach(group => { + group.classList.add('hide-completely') + }) + thisElement.classList.remove('hide-completely') +} +// **** requires HTML**** +let tips = [ + 'Loading Local Bitcoin++', + 'wash your hands often', + `Don't shake hands with others`, + 'Stay inside' +], currentIndex = 0, tipsLength = tips.length, + tipContainer = document.getElementById('tip_container'); + +function changeTips() { + if (tipsLength > currentIndex) + currentIndex++ + if (tipsLength === currentIndex) + currentIndex = 0 + tipContainer.textContent = tips[currentIndex] +} +window.addEventListener('load', () => { + document.querySelectorAll('textarea').forEach((textArea) => { + textArea.setAttribute('style', 'height:' + (textArea.scrollHeight) + 'px;overflow-y:hidden;'); + textArea.addEventListener("input", OnInput, false); + }) +}) +function OnInput(e) { + this.style.height = 'auto'; + this.style.height = (this.scrollHeight) + 'px'; +} \ No newline at end of file diff --git a/aggregate.svg b/aggregate.svg new file mode 100644 index 0000000..86b83fb --- /dev/null +++ b/aggregate.svg @@ -0,0 +1,323 @@ + + + +withdraw + + + +user + + + + + + + + + + + + + transfer + + + + + + token + + + + + + + + +tick + + + + + +subtract + + + + + +sign out + + + + + + + +share + + + + + + + +settings + + + + + + + send + + + + +sell + + + +Search + + + + + + +refresh + + + + + + +play + + + + + +pay + + + + + + +pause + + + + + + +password + + + + + + + +options + + + + + + +open project + + + +notification bell + + + + + + +multiple projects + + + + + + +manage + + + + + + + + + + +leaderboard + + + + + + +home + + + + + +guest + + + + + + + + +file + + + + + +edit + + + + +deposit + + + +dashboard + + + + + + + +copy + + + + contract creation + + + + + + + + + + + +contract + + + + + + + + + +close + + + + + + +clear + + + + + + + +chevron_right + + + + + +chevron_left + + + + + + + + + + +checkbox + + + + + + +buy + + + + + + + + + + +background + + + +back_arrow + + + + + + +audio loader + + + + + + + + +add_sections + + + + + + + +add + + + + + + + diff --git a/components.js b/components.js new file mode 100644 index 0000000..f688c50 --- /dev/null +++ b/components.js @@ -0,0 +1,886 @@ +//Button + +const smBtn = document.createElement('template') +smBtn.innerHTML = ` + +
+ + +
`; +customElements.define('sm-btn', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smBtn.content.cloneNode(true)) + } + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + connectedCallback() { + let disabledEvent = new CustomEvent('disabled', { + bubbles: true, + composed: true + }) + let clicked = new CustomEvent('clicked', { + bubbles: true, + composed: true + }) + this.shadowRoot.querySelector('button').textContent = this.getAttribute('value') + this.addEventListener('click', (e) => { + if (this.getAttribute('disabled') === 'true') { + this.dispatchEvent(disabledEvent) + } + else + this.dispatchEvent(clicked) + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + } + }) + +//Input +const smInput = document.createElement('template') +smInput.innerHTML = ` + + +`; +customElements.define('sm-input', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smInput.content.cloneNode(true)) + } + static get observedAttributes() { + return ['placeholder'] + } + + get value() { + return this.shadowRoot.querySelector('input').value + } + + set value(val) { + this.shadowRoot.querySelector('input').value = val; + } + + get placeholder() { + return this.getAttribute('placeholder') + } + + set placeholder(val) { + this.setAttribute('placeholder', val) + } + + get type() { + return this.getAttribute('type') + } + + get isValid() { + return this.shadowRoot.querySelector('input').checkValidity() + } + + preventNonNumericalInput(e) { + let keyCode = e.keyCode; + if (!((keyCode > 47 && keyCode < 56) || (keyCode > 36 && keyCode < 39) || (keyCode > 95 && keyCode < 104) || keyCode === 110 || (keyCode > 7 && keyCode < 19))) { + e.preventDefault(); + } + } + + checkInput(input, label, inputParent, clear) { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (input.value !== '') { + if (this.animate) + inputParent.classList.add('animate-label') + else + label.classList.add('hide') + clear.classList.remove('hide') + } + else { + if (this.animate) + inputParent.classList.remove('animate-label') + else + label.classList.remove('hide') + clear.classList.add('hide') + } + } + + connectedCallback() { + let input = this.shadowRoot.querySelector('input'), + inputParent = this.shadowRoot.querySelector('.input'), + clearBtn = this.shadowRoot.querySelector('.clear'), + label = this.shadowRoot.querySelector('.label') + this.animate = this.hasAttribute('animate') + this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') + if (this.hasAttribute('value')) { + input.value = this.getAttribute('value') + this.checkInput(input, inputParent, clearBtn) + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + input.setAttribute('inputmode', 'numeric') + } + else + input.setAttribute('type', this.getAttribute('type')) + } + else + input.setAttribute('type', 'text') + input.addEventListener('keydown', e => { + if (this.getAttribute('type') === 'number') + this.preventNonNumericalInput(e); + }) + input.addEventListener('input', e => { + this.checkInput(input, label, inputParent, clearBtn) + }) + clearBtn.addEventListener('click', e => { + input.value = '' + this.checkInput(input, label, inputParent, clearBtn) + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') + this.shadowRoot.querySelector('.label').textContent = newValue; + } + } + }) + +// tab-header + +const smTabHeader = document.createElement('template') +smTabHeader.innerHTML = ` + +
+ Nothing to see here +
+
+`; + +customElements.define('sm-tab-header', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smTabHeader.content.cloneNode(true)) + + this.indicator = this.shadowRoot.querySelector('.indicator'); + } + static get observedAttributes() { + return ['type'] + } + connectedCallback() { + this.prevTab = '' + this.type = this.getAttribute('type') + this.addEventListener('switchTab', e => { + if (e.target === this.prevTab) + return + if (this.type === 'tab') { + if (this.prevTab) + this.prevTab.classList.remove('tab-active') + setTimeout(() => { + e.target.classList.add('tab-active') + }, 200); + } + else { + if (this.prevTab) + this.prevTab.classList.remove('line-active') + setTimeout(() => { + e.target.classList.add('line-active') + }, 200); + } + setTimeout(() => { + this.indicator.classList.add('transition') + }, 100); + this.indicator.setAttribute('style', `width: ${e.detail.width}px; transform: translateX(${e.detail.left - 1}px)`) + e.target.scrollIntoView({behavior: 'smooth', inline: 'center'}) + this.prevTab = e.target; + }) + } +}) +// tab + +const smTab = document.createElement('template') +smTab.innerHTML = ` + +
+ +
+`; + +customElements.define('sm-tab', class extends HTMLElement { + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smTab.content.cloneNode(true)) + } + connectedCallback() { + let width = 0, left = 0; + if ('ResizeObserver' in window) { + let resizeObserver = new ResizeObserver(entries => { + entries.forEach(entry => { + width = entry.contentRect.width; + left = this.getBoundingClientRect().left - this.parentNode.offsetLeft + }) + }) + resizeObserver.observe(this) + } + else { + let observer = new IntersectionObserver((entries, observer) => { + if (entries[0].isIntersecting) { + width = entry.contentRect.width; + left = this.getBoundingClientRect().left - this.parentNode.offsetLeft + } + }, { + threshold: 1 + }) + observer.observe(this) + } + let switchTab = new CustomEvent('switchTab', { + bubbles: true, + composed: true, + detail: { + panel: this.getAttribute('panel'), + } + }) + this.addEventListener('click', () => { + switchTab.detail.width = width; + switchTab.detail.left = left; + this.dispatchEvent(switchTab) + }) + if (this.hasAttribute('active')) { + setTimeout(() => { + switchTab.detail.width = width; + switchTab.detail.left = left; + this.dispatchEvent(switchTab) + }, 0); + + } + } +}) + +//chcekbox + +const smCheckbox = document.createElement('template') +smCheckbox.innerHTML = ` + +` +customElements.define('sm-checkbox', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smCheckbox.content.cloneNode(true)) + } + + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + connectedCallback() { + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + if (this.hasAttribute('disabled')) { + this.checkbox.classList.add('disabled') + } + else { + this.checkbox.classList.remove('disabled') + } + } + attributeChangedCallback(name, oldValue, newValue) { + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.checkbox.classList.add('disabled') + } + else { + this.checkbox.classList.remove('disabled') + } + } + } + } + +}) + +//sm-audio + +const smAudio = document.createElement('template') +smAudio.innerHTML = ` + +
+ + play + + + + pause + + + +
/
+ +
+ +`; + +customElements.define('sm-audio', class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }).append(smAudio.content.cloneNode(true)) + + this.playing = false; + } + static get observedAttributes() { + return ['src'] + } + play() { + this.audio.play() + this.playing = false; + this.pauseBtn.classList.remove('hide') + this.playBtn.classList.add('hide') + } + pause() { + this.audio.pause() + this.playing = true; + this.pauseBtn.classList.add('hide') + this.playBtn.classList.remove('hide') + } + + get isPlaying() { + return this.playing; + } + + connectedCallback() { + this.playBtn = this.shadowRoot.querySelector('.play'); + this.pauseBtn = this.shadowRoot.querySelector('.pause'); + this.audio = this.shadowRoot.querySelector('audio') + this.playBtn.addEventListener('click', e => { + this.play() + }) + this.pauseBtn.addEventListener('click', e => { + this.pause() + }) + this.audio.addEventListener('ended', e => { + this.pause() + }) + let width; + if ('ResizeObserver' in window) { + let resizeObserver = new ResizeObserver(entries => { + entries.forEach(entry => { + width = entry.contentRect.width; + }) + }) + resizeObserver.observe(this) + } + else { + let observer = new IntersectionObserver((entries, observer) => { + if (entries[0].isIntersecting) + width = this.shadowRoot.querySelector('.audio').offsetWidth; + }, { + threshold: 1 + }) + observer.observe(this) + } + this.audio.addEventListener('timeupdate', e => { + let time = this.audio.currentTime, + minutes = Math.floor(time / 60), + seconds = Math.floor(time - minutes * 60), + y = seconds < 10 ? "0" + seconds : seconds; + this.shadowRoot.querySelector('.current-time').textContent = `${minutes}:${y}` + this.shadowRoot.querySelector('.track').style.width = (width / this.audio.duration) * this.audio.currentTime + 'px' + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'src') { + if (this.hasAttribute('src') && newValue.trim() !== '') { + this.shadowRoot.querySelector('audio').src = newValue; + this.shadowRoot.querySelector('audio').onloadedmetadata = () => { + let duration = this.audio.duration, + minutes = Math.floor(duration / 60), + seconds = Math.floor(duration - minutes * 60), + y = seconds < 10 ? "0" + seconds : seconds; + this.shadowRoot.querySelector('.duration').textContent = `${minutes}:${y}`; + } + } + else + this.classList.add('disabled') + } + } + } +}) + +//sm-switch + +const smSwitch = document.createElement('template') +smSwitch.innerHTML = ` + +` + +customElements.define('sm-switch', class extends HTMLElement{ + constructor() { + super() + this.attachShadow({mode: 'open'}).append(smSwitch.content.cloneNode(true)) + } + + connectedCallback() { + + } +})