From 2b8ff162bd9d9166de9e5d0fa6fa45ccdb5be576 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Thu, 6 Aug 2020 02:04:32 +0530 Subject: [PATCH] adding standard layouts --- Layouts/boxes layout/css/main.css | 347 ++ Layouts/boxes layout/css/main.css.map | 9 + Layouts/boxes layout/css/main.scss | 272 ++ Layouts/boxes layout/index.html | 136 + Layouts/boxes layout/js/components.js | 2918 +++++++++++++++++ Layouts/boxes layout/js/helper.js | 133 + Layouts/many sections layout/css/main.css | 240 ++ Layouts/many sections layout/css/main.css.map | 9 + Layouts/many sections layout/css/main.scss | 178 + Layouts/many sections layout/index.html | 186 ++ Layouts/many sections layout/js/components.js | 2918 +++++++++++++++++ Layouts/many sections layout/js/helper.js | 133 + Layouts/sidebar layout/css/main.css | 349 ++ Layouts/sidebar layout/css/main.css.map | 9 + Layouts/sidebar layout/css/main.scss | 267 ++ Layouts/sidebar layout/index.html | 154 + Layouts/sidebar layout/js/components.js | 2918 +++++++++++++++++ Layouts/sidebar layout/js/helper.js | 133 + Layouts/tabs layout/css/main.css | 233 ++ Layouts/tabs layout/css/main.css.map | 9 + Layouts/tabs layout/css/main.scss | 178 + Layouts/tabs layout/index.html | 204 ++ Layouts/tabs layout/js/components.js | 2918 +++++++++++++++++ Layouts/tabs layout/js/helper.js | 133 + 24 files changed, 14984 insertions(+) create mode 100644 Layouts/boxes layout/css/main.css create mode 100644 Layouts/boxes layout/css/main.css.map create mode 100644 Layouts/boxes layout/css/main.scss create mode 100644 Layouts/boxes layout/index.html create mode 100644 Layouts/boxes layout/js/components.js create mode 100644 Layouts/boxes layout/js/helper.js create mode 100644 Layouts/many sections layout/css/main.css create mode 100644 Layouts/many sections layout/css/main.css.map create mode 100644 Layouts/many sections layout/css/main.scss create mode 100644 Layouts/many sections layout/index.html create mode 100644 Layouts/many sections layout/js/components.js create mode 100644 Layouts/many sections layout/js/helper.js create mode 100644 Layouts/sidebar layout/css/main.css create mode 100644 Layouts/sidebar layout/css/main.css.map create mode 100644 Layouts/sidebar layout/css/main.scss create mode 100644 Layouts/sidebar layout/index.html create mode 100644 Layouts/sidebar layout/js/components.js create mode 100644 Layouts/sidebar layout/js/helper.js create mode 100644 Layouts/tabs layout/css/main.css create mode 100644 Layouts/tabs layout/css/main.css.map create mode 100644 Layouts/tabs layout/css/main.scss create mode 100644 Layouts/tabs layout/index.html create mode 100644 Layouts/tabs layout/js/components.js create mode 100644 Layouts/tabs layout/js/helper.js diff --git a/Layouts/boxes layout/css/main.css b/Layouts/boxes layout/css/main.css new file mode 100644 index 0000000..d5b8015 --- /dev/null +++ b/Layouts/boxes layout/css/main.css @@ -0,0 +1,347 @@ +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap"); +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} + +:root { + scroll-behavior: smooth; +} + +html { + margin-left: calc(100vw - 100%); +} + +body { + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} + +body[data-theme="dark"] { + --accent-color: #4a5cd3; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} + +h1, h2, h3, h4, h5 { + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} + +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2rem; +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +p { + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} + +.hide { + opacity: 0; + pointer-events: none; +} + +.hide-completely { + display: none !important; +} + +.icon { + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} + +#navbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: space-evenly; + -ms-flex-pack: space-evenly; + justify-content: space-evenly; + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: auto; + border-top: solid 1px rgba(var(--text-color), 0.2); + border-right: none; + z-index: 3; + background: rgba(var(--foreground-color), 1); +} + +#navbar .navbar-item { + position: relative; + text-align: center; + cursor: pointer; + padding: 0.3em; + margin: 0.3em; + border-radius: 0.4em; + color: rgba(var(--text-color), 0.8); + font-size: 0.9em; + text-transform: uppercase; + width: 100%; + letter-spacing: 0.08em; + -webkit-tap-highlight-color: transparent; +} + +#navbar .navbar-item h5 { + font-size: 0.6em; + margin-top: 0.4em; + font-weight: 700; +} + +#navbar .active { + color: var(--accent-color); +} + +#navbar .active .icon { + stroke: var(--accent-color); +} + +#main_header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 1rem 0; +} + +#logo { + display: -ms-grid; + display: grid; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; +} + +#logo h4 { + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; +} + +#logo h5 { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +#logo #main_logo { + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; +} + +.toggle { + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; +} + +.toggle input[type='checkbox'] { + display: none; +} + +.toggle .switch { + overflow: hidden; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; +} + +.toggle .circle { + border-radius: 0.5rem; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; +} + +.toggle .circle:first-of-type { + margin-bottom: 0.4rem; +} + +.toggle .circle line { + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; +} + +.toggle input:checked ~ .switch .circle { + -webkit-transform: translateY(-1.7rem); + transform: translateY(-1.7rem); +} + +.page { + padding: 2rem 0; + padding-bottom: 4rem; +} + +.options-tab { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + margin-top: 1rem; + margin-bottom: 1rem; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} + +.options-tab .option { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + border-radius: 0.4rem; + padding: 1.5rem; + margin-right: 1rem; + margin-bottom: 1rem; + width: 9rem; + border: solid 1px rgba(var(--text-color), 0.2); + text-transform: capitalize; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.options-tab .option .icon { + background: rgba(var(--text-color), 0.1); + height: 2.8rem; + width: 2.8rem; + padding: 0.8rem; + border-radius: 2rem; + margin-bottom: 1rem; + stroke: rgba(var(--text-color), 0.4); +} + +.options-tab .option h4 { + font-weight: 400; + font-size: 0.9rem; +} + +#home_page h1 { + margin-top: 3vw; + font-weight: 600; +} + +#home_page p { + margin-bottom: 3rem; +} + +#home_page h2 { + margin-bottom: 1rem; +} + +@media only screen and (min-width: 640px) { + body { + padding: 1rem 6vw; + margin-left: 6rem; + } + p { + max-width: 40rem; + } + #navbar { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + left: 0; + bottom: 0; + top: 0; + right: auto; + width: 6rem; + border-top: none; + border-right: solid 1px rgba(var(--text-color), 0.2); + } + #navbar .navbar-item { + width: auto; + padding: 1.4em 0; + margin: 0.6em; + } + #navbar .navbar-item .icon { + height: 1.4rem; + width: 1.4rem; + } + #navbar .navbar-item h5 { + font-size: 0.8em; + } + #navbar .navbar-item:hover .icon { + stroke: rgba(var(--text-color), 1); + } + #navbar .navbar-item:hover h5 { + color: rgba(var(--text-color), 1); + } + #navbar .navbar-item.active:hover .icon { + stroke: var(--accent-color); + } + #navbar .navbar-item.active:hover h5 { + color: var(--accent-color); + } +} +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/Layouts/boxes layout/css/main.css.map b/Layouts/boxes layout/css/main.css.map new file mode 100644 index 0000000..d5bf903 --- /dev/null +++ b/Layouts/boxes layout/css/main.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "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", + "sources": [ + "main.scss" + ], + "names": [], + "file": "main.css" +} \ No newline at end of file diff --git a/Layouts/boxes layout/css/main.scss b/Layouts/boxes layout/css/main.scss new file mode 100644 index 0000000..cd9d2cd --- /dev/null +++ b/Layouts/boxes layout/css/main.scss @@ -0,0 +1,272 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap'); +*{ + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} +:root{ + scroll-behavior: smooth; +} +html{ + margin-left: calc(100vw - 100%); +} +body{ + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} +body[data-theme="dark"]{ + --accent-color: #4a5cd3; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} +h1, h2, h3, h4, h5{ + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} +h1{ + font-size: 3rem; +} +h2{ + font-size: 2rem; +} +h3{ + font-size: 1.5rem; +} +h4{ + font-size: 1rem; +} +h5{ + font-size: 0.8rem; +} +p{ + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} +.hide{ + opacity: 0; + pointer-events: none; +} +.hide-completely{ + display: none !important; +} +.icon{ + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} +#navbar{ + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-evenly; + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: auto; + border-top: solid 1px rgba(var(--text-color), 0.2); + border-right: none; + z-index: 3; + background: rgba(var(--foreground-color), 1); + .navbar-item{ + position: relative; + text-align: center; + cursor: pointer; + padding: 0.3em; + margin: 0.3em; + border-radius: 0.4em; + color: rgba(var(--text-color), 0.8); + font-size: 0.9em; + text-transform: uppercase; + width: 100%; + letter-spacing: 0.08em; + -webkit-tap-highlight-color: transparent; + h5{ + font-size: 0.6em; + margin-top: 0.4em; + font-weight: 700; + } + } + .active{ + color: var(--accent-color); + .icon{ + stroke: var(--accent-color); + } + } +} +#main_header{ + display: flex; + padding: 1rem 0; +} +#logo{ + display: grid; + align-items: center; + width: 100%; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; + h4{ + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; + } + h5{ + font-family: 'Roboto', sans-serif; + font-weight: 400; + } + #main_logo{ + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; + } +} +.toggle{ + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; + input[type='checkbox']{ + display: none; + } + .switch{ + overflow: hidden; + display: inline-flex; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; + } + .circle{ + border-radius: 0.5rem; + transition: transform 0.3s; + &:first-of-type{ + margin-bottom: 0.4rem; + } + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; + line{ + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + } + } + input:checked ~ .switch .circle{ + transform: translateY(-1.7rem); + } +} +.page{ + padding: 2rem 0; + padding-bottom: 4rem; +} +.options-tab{ + display: flex; + margin-top: 1rem; + margin-bottom: 1rem; + flex-wrap: wrap; + .option{ + display: inline-flex; + flex-direction: column; + border-radius: 0.4rem; + padding: 1.5rem; + margin-right: 1rem; + margin-bottom: 1rem; + width: 9rem; + border: solid 1px rgba(var(--text-color), 0.2); + text-transform: capitalize; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + .icon{ + background: rgba(var(--text-color), 0.1); + height: 2.8rem; + width: 2.8rem; + padding: 0.8rem; + border-radius: 2rem; + margin-bottom: 1rem; + stroke: rgba(var(--text-color), 0.4); + } + h4{ + font-weight: 400; + font-size: 0.9rem; + } + } +} +#home_page{ + h1{ + margin-top: 3vw; + font-weight: 600; + } + p{ + margin-bottom: 3rem; + } + h2{ + margin-bottom: 1rem; + } +} +@media only screen and (min-width: 640px){ + body{ + padding: 1rem 6vw; + margin-left: 6rem; + } + p{ + max-width: 40rem; + } + #navbar{ + justify-content: center; + flex-direction: column; + align-items: stretch; + left: 0; + bottom: 0; + top: 0; + right: auto; + width: 6rem; + border-top: none; + border-right: solid 1px rgba(var(--text-color), 0.2); + .navbar-item{ + width: auto; + padding: 1.4em 0; + margin: 0.6em; + .icon{ + height: 1.4rem; + width: 1.4rem; + } + h5{ + font-size: 0.8em; + } + &:hover{ + .icon{ + stroke: rgba(var(--text-color), 1); + } + h5{ + color: rgba(var(--text-color), 1); + } + } + &.active:hover{ + .icon{ + stroke: var(--accent-color); + } + h5{ + color: var(--accent-color); + } + } + } + } +} \ No newline at end of file diff --git a/Layouts/boxes layout/index.html b/Layouts/boxes layout/index.html new file mode 100644 index 0000000..682a063 --- /dev/null +++ b/Layouts/boxes layout/index.html @@ -0,0 +1,136 @@ + + + + + + + Document + + + + +
+ + +
+ +
+
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisi excepturi eius voluptate quod quasi + dolore, quisquam, voluptatem repellendus sapiente consectetur similique ducimus dicta magni harum, + perspiciatis unde debitis? Eveniet, temporibus! +

+ +

Options

+ +
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+
+
+

User Settings

+
+
+ + + + + + \ No newline at end of file diff --git a/Layouts/boxes layout/js/components.js b/Layouts/boxes layout/js/components.js new file mode 100644 index 0000000..30d8c2f --- /dev/null +++ b/Layouts/boxes layout/js/components.js @@ -0,0 +1,2918 @@ +//Button + +const smButton = document.createElement('template') +smButton.innerHTML = ` + +
+ +
`; +customElements.define('sm-button', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smButton.content.cloneNode(true)) + } + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + dispatch = () => { + if (this.getAttribute('disabled') === 'true') { + this.dispatchEvent(new CustomEvent('disabled', { + bubbles: true, + composed: true + })) + } + else { + this.dispatchEvent(new CustomEvent('clicked', { + bubbles: true, + composed: true + })) + } + } + + connectedCallback() { + this.addEventListener('click', (e) => { + this.dispatch() + }) + this.addEventListener('keyup', (e) => { + if (e.code === "Enter" || e.code === "Space") + this.dispatch() + }) + } + + 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 = () => { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (this.input.value !== '') { + if (this.animate) + this.inputParent.classList.add('animate-label') + else + this.label.classList.add('hide') + this.clearBtn.classList.remove('hide') + } + else { + if (this.animate) + this.inputParent.classList.remove('animate-label') + else + this.label.classList.remove('hide') + this.clearBtn.classList.add('hide') + } + if (this.valueChanged) { + if (this.input.checkValidity()) { + this.helperText.classList.add('hide') + this.inputParent.style.boxShadow = `` + } + else { + this.helperText.classList.remove('hide') + this.inputParent.style.boxShadow = `0 0 0 0.1rem ${this.computedStyle.getPropertyValue('--error-color')}` + } + } + } + + connectedCallback() { + this.inputParent = this.shadowRoot.querySelector('.input') + this.computedStyle = window.getComputedStyle(this.inputParent) + this.clearBtn = this.shadowRoot.querySelector('.clear') + this.label = this.shadowRoot.querySelector('.label') + this.helperText = this.shadowRoot.querySelector('.helper-text') + this.valueChanged = false; + this.animate = this.hasAttribute('animate') + this.input = this.shadowRoot.querySelector('input') + this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') + if (this.hasAttribute('value')) { + this.input.value = this.getAttribute('value') + this.checkInput() + } + if (this.hasAttribute('helper-text')) { + this.helperText.textContent = this.getAttribute('helper-text') + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + this.input.setAttribute('inputmode', 'numeric') + } + else + this.input.setAttribute('type', this.getAttribute('type')) + } + else + this.input.setAttribute('type', 'text') + this.input.addEventListener('keydown', e => { + if (this.getAttribute('type') === 'number') + this.preventNonNumericalInput(e); + }) + this.input.addEventListener('input', e => { + this.checkInput() + }) + this.input.addEventListener('change', e => { + this.valueChanged = true; + if (this.input.checkValidity()) + this.helperText.classList.add('hide') + else + this.helperText.classList.remove('hide') + }) + this.clearBtn.addEventListener('click', e => { + this.input.value = '' + this.checkInput() + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') + this.shadowRoot.querySelector('.label').textContent = newValue; + } + } + }) + +// tab-header + +const smTabs = document.createElement('template') +smTabs.innerHTML = ` + +
+
+ Nothing to see here +
+
+
+ Nothing to see here +
+
+`; + +customElements.define('sm-tabs', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smTabs.content.cloneNode(true)) + this.indicator = this.shadowRoot.querySelector('.indicator'); + this.tabSlot = this.shadowRoot.querySelector('slot[name="tab"]'); + this.panelSlot = this.shadowRoot.querySelector('slot[name="panel"]'); + this.tabHeader = this.shadowRoot.querySelector('.tab-header'); + } + connectedCallback() { + + //animations + let flyInLeft = [ + { + opacity: 0, + transform: 'translateX(-1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyInRight = [ + { + opacity: 0, + transform: 'translateX(1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyOutLeft = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(-1rem)' + } + ], + flyOutRight = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(1rem)' + } + ], + animationOptions = { + duration: 300, + fill: 'forwards', + easing: 'ease' + } + this.prevTab + this.allTabs + + this.shadowRoot.querySelector('slot[name="panel"]').addEventListener('slotchange', () => { + this.shadowRoot.querySelector('slot[name="panel"]').assignedElements().forEach((panel, index) => { + panel.classList.add('hide-completely') + }) + }) + this.shadowRoot.querySelector('slot[name="tab"]').addEventListener('slotchange', () => { + this.allTabs = this.shadowRoot.querySelector('slot[name="tab"]').assignedElements(); + this.shadowRoot.querySelector('slot[name="tab"]').assignedElements().forEach((panel, index) => { + panel.setAttribute('rank', index + 1) + }) + }) + this._targetBodyFlyRight = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInRight, animationOptions) + } + this._targetBodyFlyLeft = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInLeft, animationOptions) + } + this.tabSlot.addEventListener('click', e => { + if (e.target === this.prevTab || !e.target.closest('sm-tab')) + return + if (this.prevTab) + this.prevTab.classList.remove('active') + e.target.classList.add('active') + + e.target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }) + this.indicator.setAttribute('style', `width: ${e.target.getBoundingClientRect().width}px; transform: translateX(${e.target.getBoundingClientRect().left - e.target.parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + + if (this.prevTab) { + let targetBody = e.target.nextElementSibling, + currentBody = this.prevTab.nextElementSibling; + + if (this.prevTab.getAttribute('rank') < e.target.getAttribute('rank')) { + if (currentBody && !targetBody) + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyRight(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyRight(targetBody) + } + } + } else { + if (currentBody && !targetBody) + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyLeft(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyLeft(targetBody) + } + } + } + } else { + e.target.nextElementSibling.classList.remove('hide-completely') + } + this.prevTab = e.target; + }) + let resizeObserver = new ResizeObserver(entries => { + entries.forEach((entry) => { + if (this.prevTab) { + let tabDimensions = this.prevTab.getBoundingClientRect(); + this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + }) + }) + resizeObserver.observe(this) + let observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + let activeElement = this.tabSlot.assignedElements().filter(element => { + if (element.classList.contains('active')) + return true + }) + if (activeElement.length) { + let tabDimensions = activeElement[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - activeElement[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + else { + this.tabSlot.assignedElements()[0].classList.add('active') + this.panelSlot.assignedElements()[0].classList.remove('hide-completely') + let tabDimensions = this.tabSlot.assignedElements()[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + this.prevTab = this.tabSlot.assignedElements()[0]; + } + } + }) + }, + { threshold: 1.0 }) + observer.observe(this) + if (this.hasAttribute('enable-flick') && this.getAttribute('enable-flick') == 'true') { + let touchStartTime = 0, + touchEndTime = 0, + swipeTimeThreshold = 200, + swipeDistanceThreshold = 20, + startingPointX = 0, + endingPointX = 0, + currentIndex = 0; + this.addEventListener('touchstart', e => { + touchStartTime = e.timeStamp + startingPointX = e.changedTouches[0].clientX + }) + this.panelSlot.addEventListener('touchend', e => { + touchEndTime = e.timeStamp + endingPointX = e.changedTouches[0].clientX + if (touchEndTime - touchStartTime < swipeTimeThreshold) { + currentIndex = this.allTabs.findIndex(element => element.classList.contains('active')) + if (startingPointX > endingPointX && startingPointX - endingPointX > swipeDistanceThreshold && currentIndex < this.allTabs.length) { + this.allTabs[currentIndex + 1].click() + } + else if (startingPointX < endingPointX && endingPointX - startingPointX > swipeDistanceThreshold && currentIndex > 0) { + this.allTabs[currentIndex - 1].click() + } + } + }) + } + } +}) + +// 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)) + } +}) + +//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)) + + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + this.input = this.shadowRoot.querySelector('input') + + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.getAttribute('checked') + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.checkbox.classList.add('disabled') + this.isDisabled = true + } + else { + this.checkbox.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } + +}) + +//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') + } + } + } +}) + +//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)) + this.switch = this.shadowRoot.querySelector('.switch'); + this.input = this.shadowRoot.querySelector('input') + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.isChecked + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.switch.classList.add('disabled') + this.isDisabled = true + } + else { + this.switch.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } +}) + +// select +const smSelect = document.createElement('template') +smSelect.innerHTML = ` + +
+
+
+ + + +
+
+ +
+
`; +customElements.define('sm-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + + collapse = () => { + this.optionList.animate(this.slideUp, this.animationOptions) + this.optionList.classList.add('hide') + this.chevron.classList.remove('rotate') + this.open = false + } + connectedCallback() { + this.availableOptions + this.optionList = this.shadowRoot.querySelector('.options') + this.chevron = this.shadowRoot.querySelector('.toggle') + let slot = this.shadowRoot.querySelector('.options slot'), + selection = this.shadowRoot.querySelector('.selection'), + previousOption + this.open = false; + this.slideDown = [ + { transform: `translateY(-0.5rem)` }, + { transform: `translateY(0)` } + ], + this.slideUp = [ + { transform: `translateY(0)` }, + { transform: `translateY(-0.5rem)` } + ], + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: 'ease' + } + selection.addEventListener('click', e => { + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + selection.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.addEventListener('optionSelected', e => { + if (previousOption !== e.target) { + this.setAttribute('value', e.detail.value) + this.shadowRoot.querySelector('.option-text').textContent = e.detail.text; + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + if (previousOption) { + previousOption.classList.remove('check-selected') + } + previousOption = e.target; + } + if(!e.detail.switching) + this.collapse() + + e.target.classList.add('check-selected') + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + if (this.availableOptions[0]) { + let firstElement = this.availableOptions[0]; + previousOption = firstElement; + firstElement.classList.add('check-selected') + this.setAttribute('value', firstElement.getAttribute('value')) + this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent + this.availableOptions.forEach((element, index) => { + element.setAttribute('data-rank', index + 1); + element.setAttribute('tabindex', "0"); + }) + } + }); + document.addEventListener('mousedown', e => { + if (!this.contains(e.target) && this.open) { + this.collapse() + } + }) + } +}) + +// option +const smOption = document.createElement('template') +smOption.innerHTML = ` + +
+ + + + +
`; +customElements.define('sm-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smOption.content.cloneNode(true)) + } + + sendDetails = (switching) => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value'), + switching: switching + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + let validKey = [ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight' + ] + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + if (validKey.includes(e.code)) { + e.preventDefault() + this.sendDetails(true) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +// select +const smStripSelect = document.createElement('template') +smStripSelect.innerHTML = ` + +
+
+ + Previous + + +
+ +
+ + Next + + +
+
`; +customElements.define('sm-strip-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + scrollLeft = () => { + this.select.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.select.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + connectedCallback() { + let previousOption, + slot = this.shadowRoot.querySelector('slot'); + this.selectContainer = this.shadowRoot.querySelector('.select-container') + this.select = this.shadowRoot.querySelector('.select') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.selectOptions + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.previousArrow.classList.add('hide') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.remove('hide') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.nextArrow.classList.add('hide') + this.nextGradient.classList.add('hide') + } + else { + this.nextArrow.classList.remove('hide') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + + const selectObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + } + }) + + selectObserver.observe(this.selectContainer) + this.addEventListener('optionSelected', e => { + if (previousOption === e.target) return; + if (previousOption) + previousOption.classList.remove('active') + e.target.classList.add('active') + e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) + this.setAttribute('value', e.detail.value) + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + previousOption = e.target; + }) + slot.addEventListener('slotchange', e => { + this.selectOptions = slot.assignedElements() + firstElementObserver.observe(this.selectOptions[0]) + lastElementObserver.observe(this.selectOptions[this.selectOptions.length - 1]) + if (this.selectOptions[0]) { + let firstElement = this.selectOptions[0]; + this.setAttribute('value', firstElement.getAttribute('value')) + firstElement.classList.add('active') + previousOption = firstElement; + } + }); + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +// option +const smStripOption = document.createElement('template') +smStripOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-strip-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripOption.content.cloneNode(true)) + } + sendDetails = () => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +//popup +const smPopup = document.createElement('template') +smPopup.innerHTML = ` + + +`; +customElements.define('sm-popup', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) + } + + resumeScrolling = () => { + const scrollY = document.body.style.top; + window.scrollTo(0, parseInt(scrollY || '0') * -1); + setTimeout(() => { + document.body.setAttribute('style', `overflow: auto; top: initial`) + }, 300); + } + + show(pinned, popupStack) { + this.pinned = pinned + this.popupStack = popupStack + this.popupContainer.classList.remove('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(0)'; + else + this.popup.style.transform = 'scale(1)'; + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + } + hide() { + this.popupContainer.classList.add('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(100%)'; + else + this.popup.style.transform = 'scale(0.9)'; + if (typeof this.popupStack !== 'undefined') { + this.popupStack.pop() + if (this.popupStack.items.length === 0) { + this.resumeScrolling() + } + } + else { + this.resumeScrolling() + } + } + + handleTouchStart = (e) => { + this.touchStartY = e.changedTouches[0].clientY + this.popup.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartY < e.changedTouches[0].clientY) { + this.offset = e.changedTouches[0].clientY - this.touchStartY; + this.touchEndAnimataion = window.requestAnimationFrame(this.movePopup) + } + /*else { + offset = touchStartY - e.changedTouches[0].clientY; + this.popup.style.transform = `translateY(-${offset}px)` + }*/ + } + + handleTouchEnd = (e) => { + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.3s' + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndY - this.touchStartY > this.threshold) { + this.hide() + } + else { + this.show() + } + } + else { + if (this.touchEndY > this.touchStartY) + this.hide() + } + } + + movePopup = () => { + this.popup.style.transform = `translateY(${this.offset}px)` + } + + connectedCallback() { + this.pinned = false + this.popupStack + this.popupContainer = this.shadowRoot.querySelector('.popup-container') + this.popup = this.shadowRoot.querySelector('.popup') + this.offset + this.popupHeader = this.shadowRoot.querySelector('.popup-top') + this.touchStartY = 0 + this.touchEndY = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + this.touchEndAnimataion; + + if (this.hasAttribute('heading')) + this.shadowRoot.querySelector('.heading').textContent = this.getAttribute('heading') + + this.popupContainer.addEventListener('mousedown', e => { + if (e.target === this.popupContainer && !this.pinned) { + this.hide() + } + }) + + this.shadowRoot.querySelector('.close').addEventListener('click', e => { + this.hide() + }) + + this.popupHeader.addEventListener('touchstart', this.handleTouchStart) + this.popupHeader.addEventListener('touchmove', this.handleTouchMove) + this.popupHeader.addEventListener('touchend', this.handleTouchEnd) + } + disconnectedCallback() { + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd) + } +}) + +//carousel + +const smCarousel = document.createElement('template') +smCarousel.innerHTML = ` + + +`; + +customElements.define('sm-carousel', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smCarousel.content.cloneNode(true)) + } + + scrollLeft = () => { + this.carousel.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.carousel.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + + connectedCallback() { + this.carousel = this.shadowRoot.querySelector('.carousel') + this.carouselContainer = this.shadowRoot.querySelector('.carousel-container') + this.carouselSlot = this.shadowRoot.querySelector('slot') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.carouselItems + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width/3 + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.previousArrow.classList.remove('expand') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.add('expand') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.nextArrow.classList.remove('expand') + this.nextGradient.classList.add('hide') + } + else{ + this.nextArrow.classList.add('expand') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + + const carouselObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 + } + }) + + carouselObserver.observe(this.carouselContainer) + + this.carouselSlot.addEventListener('slotchange', e => { + this.carouselItems = this.carouselSlot.assignedElements() + firstElementObserver.observe(this.carouselItems[0]) + lastElementObserver.observe(this.carouselItems[this.carouselItems.length - 1]) + }) + + this.addEventListener('keyup', e => { + if (e.code === 'ArrowLeft') + this.scrollRight() + else + this.scrollRight() + }) + + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +//notifications + +const smNotifications = document.createElement('template') +smNotifications.innerHTML = ` + +
+
+` + +customElements.define('sm-notifications', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) + } + + handleTouchStart = (e) => { + this.notification = e.target.closest('.notification') + this.touchStartX = e.changedTouches[0].clientX + this.notification.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartX < e.changedTouches[0].clientX) { + this.offset = e.changedTouches[0].clientX - this.touchStartX; + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + else { + this.offset = -(this.touchStartX - e.changedTouches[0].clientX); + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + } + + handleTouchEnd = (e) => { + this.notification.style.transition = 'height 0.3s, transform 0.3s, opacity 0.3s' + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndX = e.changedTouches[0].clientX + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndX - this.touchStartX > this.threshold) { + this.removeNotification(this.notification) + } + else if (this.touchStartX - this.touchEndX > this.threshold) { + this.removeNotification(this.notification, true) + } + else { + this.resetPosition() + } + } + else { + if (this.touchEndX > this.touchStartX) { + this.removeNotification(this.notification) + } + else { + this.removeNotification(this.notification, true) + } + } + } + + movePopup = () => { + this.notification.style.transform = `translateX(${this.offset}px)` + } + + resetPosition = () => { + this.notification.style.transform = `translateX(0)` + } + + push = (messageHeader, messageBody, options) => { + let notification = document.createElement('div'), + composition = ``, + { pinned, type } = options; + notification.classList.add('notification') + if (pinned) + notification.classList.add('pinned') + composition += ` +
+
+ ` + if (type === 'error') { + composition += ` + + + + + ` + } + else if (type === 'success') { + composition += ` + + + + ` + } + composition += ` +

${messageHeader}

+ + Close + + + +
+

${messageBody}

+
` + notification.innerHTML = composition + this.notificationPanel.prepend(notification) + if (window.innerWidth > 640) { + notification.animate([ + { + transform: `translateX(1rem)`, + opacity: '0' + }, + { + transform: 'translateX(0)', + opacity: '1' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `transform: none;`); + } + } + else { + notification.setAttribute('style', `transform: translateY(0); opacity: 1`) + } + notification.addEventListener('touchstart', this.handleTouchStart) + notification.addEventListener('touchmove', this.handleTouchMove) + notification.addEventListener('touchend', this.handleTouchEnd) + } + + removeNotification = (notification, toLeft) => { + notification.style.height = notification.scrollHeight + 'px'; + if (!this.offset) + this.offset = 0; + + if (toLeft) + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(-100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + else { + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + } + setTimeout( () => { + notification.remove() + }, this.animationOptions.duration*2) + } + + connectedCallback() { + this.notificationPanel = this.shadowRoot.querySelector('.notification-panel') + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: "ease" + } + this.fontSize = Number(window.getComputedStyle(document.body).getPropertyValue('font-size').match(/\d+/)[0]) + this.notification + this.offset + this.touchStartX = 0 + this.touchEndX = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.notificationPanel.getBoundingClientRect().width * 0.3 + this.touchEndAnimataion; + + this.notificationPanel.addEventListener('click', e => { + if (e.target.closest('.close'))( + this.removeNotification(e.target.closest('.notification')) + ) + }) + + const observer = new MutationObserver(mutationList => { + mutationList.forEach(mutation => { + if (mutation.type === 'childList') { + if (mutation.addedNodes.length) { + if (!mutation.addedNodes[0].classList.contains('pinned')) + setTimeout(() => { + this.removeNotification(mutation.addedNodes[0]) + }, 4000); + if (window.innerWidth > 640) + this.notificationPanel.style.padding = '1.5rem 0 3rem 1.5rem'; + else + this.notificationPanel.style.padding = '1rem 1rem 2rem 1rem'; + } + else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { + this.notificationPanel.style.padding = 0; + } + } + }) + }) + observer.observe(this.notificationPanel, { + attributes: true, + childList: true, + subtree: true + }) + } +}) +// sm-menu +const smMenu = document.createElement('template') +smMenu.innerHTML = ` + +
+ +
+ +
+
`; +customElements.define('sm-menu', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenu.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + expand = () => { + if (!this.open) { + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + this.optionList.classList.remove('hide') + this.optionList.classList.add('no-transformations') + this.open = true + this.icon.classList.add('focused') + } + } + collapse = () => { + if (this.open) { + this.open = false + this.icon.classList.remove('focused') + this.optionList.classList.add('hide') + this.optionList.classList.remove('no-transformations') + } + } + connectedCallback() { + this.availableOptions + this.containerDimensions + this.optionList = this.shadowRoot.querySelector('.options') + let slot = this.shadowRoot.querySelector('.options slot'), + menu = this.shadowRoot.querySelector('.menu') + this.icon = this.shadowRoot.querySelector('.icon') + this.open = false; + menu.addEventListener('click', e => { + if (!this.open) { + this.expand() + } else { + this.collapse() + } + }) + menu.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + if (!this.open) { + this.expand() + } else { + this.collapse() + } + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.optionList.addEventListener('click', e => { + this.collapse() + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + this.containerDimensions = this.optionList.getBoundingClientRect() + this.menuDimensions = menu.getBoundingClientRect() + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + }); + window.addEventListener('mousedown', e => { + if (!this.contains(e.target) && e.button !== 2) { + this.collapse() + } + }) + if (this.hasAttribute('set-context') && this.getAttribute('set-context') === 'true') { + this.parentNode.setAttribute('oncontextmenu', 'return false') + this.parentNode.addEventListener('mouseup', e => { + if (e.button === 2) { + this.expand() + } + }) + } + const intersectionObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting && this.open) { + if(window.innerHeight - entry.intersectionRect.top < this.containerDimensions.height) + this.optionList.classList.add('moveUp') + else + this.optionList.classList.remove('moveUp') + if (entry.intersectionRect.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + } + }) + }, { + threshold: 1 + }) + intersectionObserver.observe(this.optionList) + } +}) + +// option +const smMenuOption = document.createElement('template') +smMenuOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-menu-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenuOption.content.cloneNode(true)) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.click() + } + }) + this.setAttribute('tabindex', '0') + } +}) \ No newline at end of file diff --git a/Layouts/boxes layout/js/helper.js b/Layouts/boxes layout/js/helper.js new file mode 100644 index 0000000..a7c51ee --- /dev/null +++ b/Layouts/boxes layout/js/helper.js @@ -0,0 +1,133 @@ +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) +}) +let themeToggler = document.getElementById("theme_toggle") +if (localStorage.theme === "dark") { + darkTheme() + themeToggler.checked = true; +} else { + lightTheme() + themeToggler.checked = false; +} + +function lightTheme() { + document.body.setAttribute("data-theme", "light"); +} + +function darkTheme() { + document.body.setAttribute("data-theme", "dark"); +} +themeToggler.addEventListener("change", () => { + if (themeToggler.checked) { + darkTheme() + localStorage.setItem("theme", "dark"); + } else { + lightTheme() + 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(index) { + let newIndex = index ? index : 1 + return this.items[this.items.length - index]; + } +} +let popupStack = new Stack(), + zIndex = 10; +function showPopup(popup, permission) { + let thisPopup = document.getElementById(popup); + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + popupStack.push({ thisPopup, permission }) + thisPopup.show(permission, popupStack) + zIndex++; + thisPopup.setAttribute('style', `z-index: ${zIndex}`) + return thisPopup; +} +function setAttributes(el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } +} +// displays a popup for asking permission. Use this instead of JS confirm +let confirmation = 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); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(false); + } + }) +} + +// 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'); + if (defaultVal) + input.value = defaultVal; + showPopup('prompt') + input.focus() + input.addEventListener('keyup', e => { + if (e.key === 'Enter') { + resolve(input.value); + hidePopup() + } + }) + popup.querySelector('#prompt_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(input.value); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(null); + } + }) +} + +function formatedTime(time) { + let timeFrag = new Date(parseInt(time)).toString().split(' '), + day = timeFrag[0], + month = timeFrag[1], + date = timeFrag[2], + year = timeFrag[3], + hours = timeFrag[4].slice(0, timeFrag[4].lastIndexOf(':')), + finalTime = ''; + parseInt(hours.split(':')[0]) > 12 ? finalTime = 'PM' : finalTime = 'AM' + return `${hours} ${finalTime} ${day} ${date} ${month} ${year}` +} + +function copyToClipboard(parent) { + let toast = document.getElementById('textCopied'), + textToCopy = parent.querySelector('.copy').textContent; + navigator.clipboard.writeText(textToCopy) + toast.classList.remove('hide'); + setTimeout(() => { + toast.classList.add('hide'); + }, 2000) +} \ No newline at end of file diff --git a/Layouts/many sections layout/css/main.css b/Layouts/many sections layout/css/main.css new file mode 100644 index 0000000..ed1d7e7 --- /dev/null +++ b/Layouts/many sections layout/css/main.css @@ -0,0 +1,240 @@ +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap"); +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} + +:root { + scroll-behavior: smooth; +} + +body { + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} + +body[data-theme="dark"] { + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} + +main { + margin: 1.5rem; +} + +h1, h2, h3, h4, h5 { + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} + +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2rem; +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +p { + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} + +.hide { + opacity: 0; + pointer-events: none; +} + +.hide-completely { + display: none !important; +} + +.flex { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.icon { + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} + +.toggle { + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; +} + +.toggle input[type='checkbox'] { + display: none; +} + +.toggle .switch { + overflow: hidden; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; +} + +.toggle .circle { + border-radius: 0.5rem; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; +} + +.toggle .circle:first-of-type { + margin-bottom: 0.4rem; +} + +.toggle .circle line { + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; +} + +.toggle input:checked ~ .switch .circle { + -webkit-transform: translateY(-1.7rem); + transform: translateY(-1.7rem); +} + +#navbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: -webkit-sticky; + position: sticky; + padding: 1.5rem 2rem; + left: 0; + right: 0; + top: 0; + background: rgba(var(--foreground-color), 1); + border-bottom: solid 1px rgba(var(--text-color), 0.16); + -webkit-box-shadow: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.06); + box-shadow: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.06); + z-index: 3; +} + +#logo { + display: -ms-grid; + display: grid; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; +} + +#logo h4 { + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; +} + +#logo h5 { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +#logo #main_logo { + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; +} + +.section { + margin-top: 3rem; +} + +.section h3 + p { + margin-top: 1rem; +} + +.section:first-of-type { + margin-top: 0; +} + +.card { + padding: 1.5rem; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-width: 20rem; + border-radius: 0.5rem; + margin-right: 1.5rem; + border: solid 1px rgba(var(--text-color), 0.2); +} + +.card h3 { + font-weight: 500; +} + +sm-menu { + margin-left: auto; +} + +@media only screen and (min-width: 640px) { + main { + margin: 1.5rem 4vw; + } + p { + max-width: 40rem; + } +} +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/Layouts/many sections layout/css/main.css.map b/Layouts/many sections layout/css/main.css.map new file mode 100644 index 0000000..d98e069 --- /dev/null +++ b/Layouts/many sections layout/css/main.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "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", + "sources": [ + "main.scss" + ], + "names": [], + "file": "main.css" +} \ No newline at end of file diff --git a/Layouts/many sections layout/css/main.scss b/Layouts/many sections layout/css/main.scss new file mode 100644 index 0000000..5127a65 --- /dev/null +++ b/Layouts/many sections layout/css/main.scss @@ -0,0 +1,178 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap'); +*{ + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} +:root{ + scroll-behavior: smooth; +} +body{ + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} +body[data-theme="dark"]{ + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} +main{ + margin: 1.5rem; +} +h1, h2, h3, h4, h5{ + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} +h1{ + font-size: 3rem; +} +h2{ + font-size: 2rem; +} +h3{ + font-size: 1.5rem; +} +h4{ + font-size: 1rem; +} +h5{ + font-size: 0.8rem; +} +p{ + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} +.hide{ + opacity: 0; + pointer-events: none; +} +.hide-completely{ + display: none !important; +} +.flex{ + display: flex; +} +.icon{ + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} +.toggle{ + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; + input[type='checkbox']{ + display: none; + } + .switch{ + overflow: hidden; + display: inline-flex; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; + } + .circle{ + border-radius: 0.5rem; + transition: transform 0.3s; + &:first-of-type{ + margin-bottom: 0.4rem; + } + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; + line{ + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + } + } + input:checked ~ .switch .circle{ + transform: translateY(-1.7rem); + } +} +#navbar{ + display: flex; + align-items: center; + position: sticky; + padding: 1.5rem 2rem; + left: 0; + right: 0; + top: 0; + background: rgba(var(--foreground-color), 1); + border-bottom: solid 1px rgba(var(--text-color), 0.16); + box-shadow: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.06); + z-index: 3; +} +#logo{ + display: grid; + align-items: center; + width: 100%; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; + h4{ + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; + } + h5{ + font-family: 'Roboto', sans-serif; + font-weight: 400; + } + #main_logo{ + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; + } +} +.section{ + margin-top: 3rem; + h3 + p{ + margin-top: 1rem; + } + &:first-of-type{ + margin-top: 0; + } +} +.card{ + padding: 1.5rem; + display: flex; + flex-direction: column; + min-width: 20rem; + border-radius: 0.5rem; + margin-right: 1.5rem; + border: solid 1px rgba(var(--text-color), 0.2); + h3{ + font-weight: 500; + } +} +sm-menu{ + margin-left: auto; +} +@media only screen and (min-width: 640px){ + main{ + margin: 1.5rem 4vw; + } + p{ + max-width: 40rem; + } +} \ No newline at end of file diff --git a/Layouts/many sections layout/index.html b/Layouts/many sections layout/index.html new file mode 100644 index 0000000..42fb39a --- /dev/null +++ b/Layouts/many sections layout/index.html @@ -0,0 +1,186 @@ + + + + + + + Document + + + + + +
+
+
+

Section Heading

+ + first option + second option + third option + +
+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+
+

Section Heading

+ + first option + second option + third option + +
+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+
+

Section Heading

+ + first option + second option + third option + +
+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/Layouts/many sections layout/js/components.js b/Layouts/many sections layout/js/components.js new file mode 100644 index 0000000..30d8c2f --- /dev/null +++ b/Layouts/many sections layout/js/components.js @@ -0,0 +1,2918 @@ +//Button + +const smButton = document.createElement('template') +smButton.innerHTML = ` + +
+ +
`; +customElements.define('sm-button', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smButton.content.cloneNode(true)) + } + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + dispatch = () => { + if (this.getAttribute('disabled') === 'true') { + this.dispatchEvent(new CustomEvent('disabled', { + bubbles: true, + composed: true + })) + } + else { + this.dispatchEvent(new CustomEvent('clicked', { + bubbles: true, + composed: true + })) + } + } + + connectedCallback() { + this.addEventListener('click', (e) => { + this.dispatch() + }) + this.addEventListener('keyup', (e) => { + if (e.code === "Enter" || e.code === "Space") + this.dispatch() + }) + } + + 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 = () => { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (this.input.value !== '') { + if (this.animate) + this.inputParent.classList.add('animate-label') + else + this.label.classList.add('hide') + this.clearBtn.classList.remove('hide') + } + else { + if (this.animate) + this.inputParent.classList.remove('animate-label') + else + this.label.classList.remove('hide') + this.clearBtn.classList.add('hide') + } + if (this.valueChanged) { + if (this.input.checkValidity()) { + this.helperText.classList.add('hide') + this.inputParent.style.boxShadow = `` + } + else { + this.helperText.classList.remove('hide') + this.inputParent.style.boxShadow = `0 0 0 0.1rem ${this.computedStyle.getPropertyValue('--error-color')}` + } + } + } + + connectedCallback() { + this.inputParent = this.shadowRoot.querySelector('.input') + this.computedStyle = window.getComputedStyle(this.inputParent) + this.clearBtn = this.shadowRoot.querySelector('.clear') + this.label = this.shadowRoot.querySelector('.label') + this.helperText = this.shadowRoot.querySelector('.helper-text') + this.valueChanged = false; + this.animate = this.hasAttribute('animate') + this.input = this.shadowRoot.querySelector('input') + this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') + if (this.hasAttribute('value')) { + this.input.value = this.getAttribute('value') + this.checkInput() + } + if (this.hasAttribute('helper-text')) { + this.helperText.textContent = this.getAttribute('helper-text') + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + this.input.setAttribute('inputmode', 'numeric') + } + else + this.input.setAttribute('type', this.getAttribute('type')) + } + else + this.input.setAttribute('type', 'text') + this.input.addEventListener('keydown', e => { + if (this.getAttribute('type') === 'number') + this.preventNonNumericalInput(e); + }) + this.input.addEventListener('input', e => { + this.checkInput() + }) + this.input.addEventListener('change', e => { + this.valueChanged = true; + if (this.input.checkValidity()) + this.helperText.classList.add('hide') + else + this.helperText.classList.remove('hide') + }) + this.clearBtn.addEventListener('click', e => { + this.input.value = '' + this.checkInput() + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') + this.shadowRoot.querySelector('.label').textContent = newValue; + } + } + }) + +// tab-header + +const smTabs = document.createElement('template') +smTabs.innerHTML = ` + +
+
+ Nothing to see here +
+
+
+ Nothing to see here +
+
+`; + +customElements.define('sm-tabs', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smTabs.content.cloneNode(true)) + this.indicator = this.shadowRoot.querySelector('.indicator'); + this.tabSlot = this.shadowRoot.querySelector('slot[name="tab"]'); + this.panelSlot = this.shadowRoot.querySelector('slot[name="panel"]'); + this.tabHeader = this.shadowRoot.querySelector('.tab-header'); + } + connectedCallback() { + + //animations + let flyInLeft = [ + { + opacity: 0, + transform: 'translateX(-1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyInRight = [ + { + opacity: 0, + transform: 'translateX(1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyOutLeft = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(-1rem)' + } + ], + flyOutRight = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(1rem)' + } + ], + animationOptions = { + duration: 300, + fill: 'forwards', + easing: 'ease' + } + this.prevTab + this.allTabs + + this.shadowRoot.querySelector('slot[name="panel"]').addEventListener('slotchange', () => { + this.shadowRoot.querySelector('slot[name="panel"]').assignedElements().forEach((panel, index) => { + panel.classList.add('hide-completely') + }) + }) + this.shadowRoot.querySelector('slot[name="tab"]').addEventListener('slotchange', () => { + this.allTabs = this.shadowRoot.querySelector('slot[name="tab"]').assignedElements(); + this.shadowRoot.querySelector('slot[name="tab"]').assignedElements().forEach((panel, index) => { + panel.setAttribute('rank', index + 1) + }) + }) + this._targetBodyFlyRight = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInRight, animationOptions) + } + this._targetBodyFlyLeft = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInLeft, animationOptions) + } + this.tabSlot.addEventListener('click', e => { + if (e.target === this.prevTab || !e.target.closest('sm-tab')) + return + if (this.prevTab) + this.prevTab.classList.remove('active') + e.target.classList.add('active') + + e.target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }) + this.indicator.setAttribute('style', `width: ${e.target.getBoundingClientRect().width}px; transform: translateX(${e.target.getBoundingClientRect().left - e.target.parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + + if (this.prevTab) { + let targetBody = e.target.nextElementSibling, + currentBody = this.prevTab.nextElementSibling; + + if (this.prevTab.getAttribute('rank') < e.target.getAttribute('rank')) { + if (currentBody && !targetBody) + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyRight(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyRight(targetBody) + } + } + } else { + if (currentBody && !targetBody) + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyLeft(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyLeft(targetBody) + } + } + } + } else { + e.target.nextElementSibling.classList.remove('hide-completely') + } + this.prevTab = e.target; + }) + let resizeObserver = new ResizeObserver(entries => { + entries.forEach((entry) => { + if (this.prevTab) { + let tabDimensions = this.prevTab.getBoundingClientRect(); + this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + }) + }) + resizeObserver.observe(this) + let observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + let activeElement = this.tabSlot.assignedElements().filter(element => { + if (element.classList.contains('active')) + return true + }) + if (activeElement.length) { + let tabDimensions = activeElement[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - activeElement[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + else { + this.tabSlot.assignedElements()[0].classList.add('active') + this.panelSlot.assignedElements()[0].classList.remove('hide-completely') + let tabDimensions = this.tabSlot.assignedElements()[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + this.prevTab = this.tabSlot.assignedElements()[0]; + } + } + }) + }, + { threshold: 1.0 }) + observer.observe(this) + if (this.hasAttribute('enable-flick') && this.getAttribute('enable-flick') == 'true') { + let touchStartTime = 0, + touchEndTime = 0, + swipeTimeThreshold = 200, + swipeDistanceThreshold = 20, + startingPointX = 0, + endingPointX = 0, + currentIndex = 0; + this.addEventListener('touchstart', e => { + touchStartTime = e.timeStamp + startingPointX = e.changedTouches[0].clientX + }) + this.panelSlot.addEventListener('touchend', e => { + touchEndTime = e.timeStamp + endingPointX = e.changedTouches[0].clientX + if (touchEndTime - touchStartTime < swipeTimeThreshold) { + currentIndex = this.allTabs.findIndex(element => element.classList.contains('active')) + if (startingPointX > endingPointX && startingPointX - endingPointX > swipeDistanceThreshold && currentIndex < this.allTabs.length) { + this.allTabs[currentIndex + 1].click() + } + else if (startingPointX < endingPointX && endingPointX - startingPointX > swipeDistanceThreshold && currentIndex > 0) { + this.allTabs[currentIndex - 1].click() + } + } + }) + } + } +}) + +// 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)) + } +}) + +//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)) + + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + this.input = this.shadowRoot.querySelector('input') + + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.getAttribute('checked') + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.checkbox.classList.add('disabled') + this.isDisabled = true + } + else { + this.checkbox.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } + +}) + +//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') + } + } + } +}) + +//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)) + this.switch = this.shadowRoot.querySelector('.switch'); + this.input = this.shadowRoot.querySelector('input') + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.isChecked + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.switch.classList.add('disabled') + this.isDisabled = true + } + else { + this.switch.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } +}) + +// select +const smSelect = document.createElement('template') +smSelect.innerHTML = ` + +
+
+
+ + + +
+
+ +
+
`; +customElements.define('sm-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + + collapse = () => { + this.optionList.animate(this.slideUp, this.animationOptions) + this.optionList.classList.add('hide') + this.chevron.classList.remove('rotate') + this.open = false + } + connectedCallback() { + this.availableOptions + this.optionList = this.shadowRoot.querySelector('.options') + this.chevron = this.shadowRoot.querySelector('.toggle') + let slot = this.shadowRoot.querySelector('.options slot'), + selection = this.shadowRoot.querySelector('.selection'), + previousOption + this.open = false; + this.slideDown = [ + { transform: `translateY(-0.5rem)` }, + { transform: `translateY(0)` } + ], + this.slideUp = [ + { transform: `translateY(0)` }, + { transform: `translateY(-0.5rem)` } + ], + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: 'ease' + } + selection.addEventListener('click', e => { + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + selection.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.addEventListener('optionSelected', e => { + if (previousOption !== e.target) { + this.setAttribute('value', e.detail.value) + this.shadowRoot.querySelector('.option-text').textContent = e.detail.text; + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + if (previousOption) { + previousOption.classList.remove('check-selected') + } + previousOption = e.target; + } + if(!e.detail.switching) + this.collapse() + + e.target.classList.add('check-selected') + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + if (this.availableOptions[0]) { + let firstElement = this.availableOptions[0]; + previousOption = firstElement; + firstElement.classList.add('check-selected') + this.setAttribute('value', firstElement.getAttribute('value')) + this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent + this.availableOptions.forEach((element, index) => { + element.setAttribute('data-rank', index + 1); + element.setAttribute('tabindex', "0"); + }) + } + }); + document.addEventListener('mousedown', e => { + if (!this.contains(e.target) && this.open) { + this.collapse() + } + }) + } +}) + +// option +const smOption = document.createElement('template') +smOption.innerHTML = ` + +
+ + + + +
`; +customElements.define('sm-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smOption.content.cloneNode(true)) + } + + sendDetails = (switching) => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value'), + switching: switching + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + let validKey = [ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight' + ] + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + if (validKey.includes(e.code)) { + e.preventDefault() + this.sendDetails(true) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +// select +const smStripSelect = document.createElement('template') +smStripSelect.innerHTML = ` + +
+
+ + Previous + + +
+ +
+ + Next + + +
+
`; +customElements.define('sm-strip-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + scrollLeft = () => { + this.select.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.select.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + connectedCallback() { + let previousOption, + slot = this.shadowRoot.querySelector('slot'); + this.selectContainer = this.shadowRoot.querySelector('.select-container') + this.select = this.shadowRoot.querySelector('.select') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.selectOptions + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.previousArrow.classList.add('hide') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.remove('hide') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.nextArrow.classList.add('hide') + this.nextGradient.classList.add('hide') + } + else { + this.nextArrow.classList.remove('hide') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + + const selectObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + } + }) + + selectObserver.observe(this.selectContainer) + this.addEventListener('optionSelected', e => { + if (previousOption === e.target) return; + if (previousOption) + previousOption.classList.remove('active') + e.target.classList.add('active') + e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) + this.setAttribute('value', e.detail.value) + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + previousOption = e.target; + }) + slot.addEventListener('slotchange', e => { + this.selectOptions = slot.assignedElements() + firstElementObserver.observe(this.selectOptions[0]) + lastElementObserver.observe(this.selectOptions[this.selectOptions.length - 1]) + if (this.selectOptions[0]) { + let firstElement = this.selectOptions[0]; + this.setAttribute('value', firstElement.getAttribute('value')) + firstElement.classList.add('active') + previousOption = firstElement; + } + }); + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +// option +const smStripOption = document.createElement('template') +smStripOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-strip-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripOption.content.cloneNode(true)) + } + sendDetails = () => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +//popup +const smPopup = document.createElement('template') +smPopup.innerHTML = ` + + +`; +customElements.define('sm-popup', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) + } + + resumeScrolling = () => { + const scrollY = document.body.style.top; + window.scrollTo(0, parseInt(scrollY || '0') * -1); + setTimeout(() => { + document.body.setAttribute('style', `overflow: auto; top: initial`) + }, 300); + } + + show(pinned, popupStack) { + this.pinned = pinned + this.popupStack = popupStack + this.popupContainer.classList.remove('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(0)'; + else + this.popup.style.transform = 'scale(1)'; + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + } + hide() { + this.popupContainer.classList.add('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(100%)'; + else + this.popup.style.transform = 'scale(0.9)'; + if (typeof this.popupStack !== 'undefined') { + this.popupStack.pop() + if (this.popupStack.items.length === 0) { + this.resumeScrolling() + } + } + else { + this.resumeScrolling() + } + } + + handleTouchStart = (e) => { + this.touchStartY = e.changedTouches[0].clientY + this.popup.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartY < e.changedTouches[0].clientY) { + this.offset = e.changedTouches[0].clientY - this.touchStartY; + this.touchEndAnimataion = window.requestAnimationFrame(this.movePopup) + } + /*else { + offset = touchStartY - e.changedTouches[0].clientY; + this.popup.style.transform = `translateY(-${offset}px)` + }*/ + } + + handleTouchEnd = (e) => { + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.3s' + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndY - this.touchStartY > this.threshold) { + this.hide() + } + else { + this.show() + } + } + else { + if (this.touchEndY > this.touchStartY) + this.hide() + } + } + + movePopup = () => { + this.popup.style.transform = `translateY(${this.offset}px)` + } + + connectedCallback() { + this.pinned = false + this.popupStack + this.popupContainer = this.shadowRoot.querySelector('.popup-container') + this.popup = this.shadowRoot.querySelector('.popup') + this.offset + this.popupHeader = this.shadowRoot.querySelector('.popup-top') + this.touchStartY = 0 + this.touchEndY = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + this.touchEndAnimataion; + + if (this.hasAttribute('heading')) + this.shadowRoot.querySelector('.heading').textContent = this.getAttribute('heading') + + this.popupContainer.addEventListener('mousedown', e => { + if (e.target === this.popupContainer && !this.pinned) { + this.hide() + } + }) + + this.shadowRoot.querySelector('.close').addEventListener('click', e => { + this.hide() + }) + + this.popupHeader.addEventListener('touchstart', this.handleTouchStart) + this.popupHeader.addEventListener('touchmove', this.handleTouchMove) + this.popupHeader.addEventListener('touchend', this.handleTouchEnd) + } + disconnectedCallback() { + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd) + } +}) + +//carousel + +const smCarousel = document.createElement('template') +smCarousel.innerHTML = ` + + +`; + +customElements.define('sm-carousel', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smCarousel.content.cloneNode(true)) + } + + scrollLeft = () => { + this.carousel.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.carousel.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + + connectedCallback() { + this.carousel = this.shadowRoot.querySelector('.carousel') + this.carouselContainer = this.shadowRoot.querySelector('.carousel-container') + this.carouselSlot = this.shadowRoot.querySelector('slot') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.carouselItems + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width/3 + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.previousArrow.classList.remove('expand') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.add('expand') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.nextArrow.classList.remove('expand') + this.nextGradient.classList.add('hide') + } + else{ + this.nextArrow.classList.add('expand') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + + const carouselObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 + } + }) + + carouselObserver.observe(this.carouselContainer) + + this.carouselSlot.addEventListener('slotchange', e => { + this.carouselItems = this.carouselSlot.assignedElements() + firstElementObserver.observe(this.carouselItems[0]) + lastElementObserver.observe(this.carouselItems[this.carouselItems.length - 1]) + }) + + this.addEventListener('keyup', e => { + if (e.code === 'ArrowLeft') + this.scrollRight() + else + this.scrollRight() + }) + + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +//notifications + +const smNotifications = document.createElement('template') +smNotifications.innerHTML = ` + +
+
+` + +customElements.define('sm-notifications', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) + } + + handleTouchStart = (e) => { + this.notification = e.target.closest('.notification') + this.touchStartX = e.changedTouches[0].clientX + this.notification.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartX < e.changedTouches[0].clientX) { + this.offset = e.changedTouches[0].clientX - this.touchStartX; + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + else { + this.offset = -(this.touchStartX - e.changedTouches[0].clientX); + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + } + + handleTouchEnd = (e) => { + this.notification.style.transition = 'height 0.3s, transform 0.3s, opacity 0.3s' + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndX = e.changedTouches[0].clientX + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndX - this.touchStartX > this.threshold) { + this.removeNotification(this.notification) + } + else if (this.touchStartX - this.touchEndX > this.threshold) { + this.removeNotification(this.notification, true) + } + else { + this.resetPosition() + } + } + else { + if (this.touchEndX > this.touchStartX) { + this.removeNotification(this.notification) + } + else { + this.removeNotification(this.notification, true) + } + } + } + + movePopup = () => { + this.notification.style.transform = `translateX(${this.offset}px)` + } + + resetPosition = () => { + this.notification.style.transform = `translateX(0)` + } + + push = (messageHeader, messageBody, options) => { + let notification = document.createElement('div'), + composition = ``, + { pinned, type } = options; + notification.classList.add('notification') + if (pinned) + notification.classList.add('pinned') + composition += ` +
+
+ ` + if (type === 'error') { + composition += ` + + + + + ` + } + else if (type === 'success') { + composition += ` + + + + ` + } + composition += ` +

${messageHeader}

+ + Close + + + +
+

${messageBody}

+
` + notification.innerHTML = composition + this.notificationPanel.prepend(notification) + if (window.innerWidth > 640) { + notification.animate([ + { + transform: `translateX(1rem)`, + opacity: '0' + }, + { + transform: 'translateX(0)', + opacity: '1' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `transform: none;`); + } + } + else { + notification.setAttribute('style', `transform: translateY(0); opacity: 1`) + } + notification.addEventListener('touchstart', this.handleTouchStart) + notification.addEventListener('touchmove', this.handleTouchMove) + notification.addEventListener('touchend', this.handleTouchEnd) + } + + removeNotification = (notification, toLeft) => { + notification.style.height = notification.scrollHeight + 'px'; + if (!this.offset) + this.offset = 0; + + if (toLeft) + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(-100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + else { + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + } + setTimeout( () => { + notification.remove() + }, this.animationOptions.duration*2) + } + + connectedCallback() { + this.notificationPanel = this.shadowRoot.querySelector('.notification-panel') + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: "ease" + } + this.fontSize = Number(window.getComputedStyle(document.body).getPropertyValue('font-size').match(/\d+/)[0]) + this.notification + this.offset + this.touchStartX = 0 + this.touchEndX = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.notificationPanel.getBoundingClientRect().width * 0.3 + this.touchEndAnimataion; + + this.notificationPanel.addEventListener('click', e => { + if (e.target.closest('.close'))( + this.removeNotification(e.target.closest('.notification')) + ) + }) + + const observer = new MutationObserver(mutationList => { + mutationList.forEach(mutation => { + if (mutation.type === 'childList') { + if (mutation.addedNodes.length) { + if (!mutation.addedNodes[0].classList.contains('pinned')) + setTimeout(() => { + this.removeNotification(mutation.addedNodes[0]) + }, 4000); + if (window.innerWidth > 640) + this.notificationPanel.style.padding = '1.5rem 0 3rem 1.5rem'; + else + this.notificationPanel.style.padding = '1rem 1rem 2rem 1rem'; + } + else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { + this.notificationPanel.style.padding = 0; + } + } + }) + }) + observer.observe(this.notificationPanel, { + attributes: true, + childList: true, + subtree: true + }) + } +}) +// sm-menu +const smMenu = document.createElement('template') +smMenu.innerHTML = ` + +
+ +
+ +
+
`; +customElements.define('sm-menu', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenu.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + expand = () => { + if (!this.open) { + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + this.optionList.classList.remove('hide') + this.optionList.classList.add('no-transformations') + this.open = true + this.icon.classList.add('focused') + } + } + collapse = () => { + if (this.open) { + this.open = false + this.icon.classList.remove('focused') + this.optionList.classList.add('hide') + this.optionList.classList.remove('no-transformations') + } + } + connectedCallback() { + this.availableOptions + this.containerDimensions + this.optionList = this.shadowRoot.querySelector('.options') + let slot = this.shadowRoot.querySelector('.options slot'), + menu = this.shadowRoot.querySelector('.menu') + this.icon = this.shadowRoot.querySelector('.icon') + this.open = false; + menu.addEventListener('click', e => { + if (!this.open) { + this.expand() + } else { + this.collapse() + } + }) + menu.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + if (!this.open) { + this.expand() + } else { + this.collapse() + } + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.optionList.addEventListener('click', e => { + this.collapse() + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + this.containerDimensions = this.optionList.getBoundingClientRect() + this.menuDimensions = menu.getBoundingClientRect() + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + }); + window.addEventListener('mousedown', e => { + if (!this.contains(e.target) && e.button !== 2) { + this.collapse() + } + }) + if (this.hasAttribute('set-context') && this.getAttribute('set-context') === 'true') { + this.parentNode.setAttribute('oncontextmenu', 'return false') + this.parentNode.addEventListener('mouseup', e => { + if (e.button === 2) { + this.expand() + } + }) + } + const intersectionObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting && this.open) { + if(window.innerHeight - entry.intersectionRect.top < this.containerDimensions.height) + this.optionList.classList.add('moveUp') + else + this.optionList.classList.remove('moveUp') + if (entry.intersectionRect.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + } + }) + }, { + threshold: 1 + }) + intersectionObserver.observe(this.optionList) + } +}) + +// option +const smMenuOption = document.createElement('template') +smMenuOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-menu-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenuOption.content.cloneNode(true)) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.click() + } + }) + this.setAttribute('tabindex', '0') + } +}) \ No newline at end of file diff --git a/Layouts/many sections layout/js/helper.js b/Layouts/many sections layout/js/helper.js new file mode 100644 index 0000000..a7c51ee --- /dev/null +++ b/Layouts/many sections layout/js/helper.js @@ -0,0 +1,133 @@ +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) +}) +let themeToggler = document.getElementById("theme_toggle") +if (localStorage.theme === "dark") { + darkTheme() + themeToggler.checked = true; +} else { + lightTheme() + themeToggler.checked = false; +} + +function lightTheme() { + document.body.setAttribute("data-theme", "light"); +} + +function darkTheme() { + document.body.setAttribute("data-theme", "dark"); +} +themeToggler.addEventListener("change", () => { + if (themeToggler.checked) { + darkTheme() + localStorage.setItem("theme", "dark"); + } else { + lightTheme() + 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(index) { + let newIndex = index ? index : 1 + return this.items[this.items.length - index]; + } +} +let popupStack = new Stack(), + zIndex = 10; +function showPopup(popup, permission) { + let thisPopup = document.getElementById(popup); + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + popupStack.push({ thisPopup, permission }) + thisPopup.show(permission, popupStack) + zIndex++; + thisPopup.setAttribute('style', `z-index: ${zIndex}`) + return thisPopup; +} +function setAttributes(el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } +} +// displays a popup for asking permission. Use this instead of JS confirm +let confirmation = 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); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(false); + } + }) +} + +// 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'); + if (defaultVal) + input.value = defaultVal; + showPopup('prompt') + input.focus() + input.addEventListener('keyup', e => { + if (e.key === 'Enter') { + resolve(input.value); + hidePopup() + } + }) + popup.querySelector('#prompt_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(input.value); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(null); + } + }) +} + +function formatedTime(time) { + let timeFrag = new Date(parseInt(time)).toString().split(' '), + day = timeFrag[0], + month = timeFrag[1], + date = timeFrag[2], + year = timeFrag[3], + hours = timeFrag[4].slice(0, timeFrag[4].lastIndexOf(':')), + finalTime = ''; + parseInt(hours.split(':')[0]) > 12 ? finalTime = 'PM' : finalTime = 'AM' + return `${hours} ${finalTime} ${day} ${date} ${month} ${year}` +} + +function copyToClipboard(parent) { + let toast = document.getElementById('textCopied'), + textToCopy = parent.querySelector('.copy').textContent; + navigator.clipboard.writeText(textToCopy) + toast.classList.remove('hide'); + setTimeout(() => { + toast.classList.add('hide'); + }, 2000) +} \ No newline at end of file diff --git a/Layouts/sidebar layout/css/main.css b/Layouts/sidebar layout/css/main.css new file mode 100644 index 0000000..97dd133 --- /dev/null +++ b/Layouts/sidebar layout/css/main.css @@ -0,0 +1,349 @@ +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap"); +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} + +:root { + scroll-behavior: smooth; +} + +body { + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} + +body[data-theme="dark"] { + --accent-color: #4a5cd3; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} + +h1, h2, h3, h4, h5 { + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} + +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2rem; +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +p { + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} + +.hide { + opacity: 0; + pointer-events: none; +} + +.hide-completely { + display: none !important; +} + +.no-transformations { + -webkit-transform: none !important; + transform: none !important; +} + +.icon { + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} + +#navbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: fixed; + left: 0; + bottom: 0; + top: 0; + -webkit-box-shadow: 0.2rem 0 0.5rem rgba(0, 0, 0, 0.1); + box-shadow: 0.2rem 0 0.5rem rgba(0, 0, 0, 0.1); + z-index: 3; + padding: 1rem 0; + background: rgba(var(--foreground-color), 1); + border-right: solid 1px rgba(var(--text-color), 0.16); + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; +} + +#navbar .navbar-item { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + position: relative; + text-align: center; + cursor: pointer; + padding: 0.6rem 1.5rem; + padding-right: 3rem; + border-radius: 0.4em; + color: rgba(var(--text-color), 0.8); + font-size: 0.9em; + text-transform: uppercase; + width: 100%; + -webkit-tap-highlight-color: transparent; +} + +#navbar .navbar-item .icon { + margin-right: 1rem; +} + +#navbar .navbar-item h4 { + text-transform: capitalize; + font-weight: 600; +} + +#navbar .active { + color: var(--accent-color); +} + +#navbar .active .icon { + stroke: var(--accent-color); +} + +#logo { + display: -ms-grid; + display: grid; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; + padding: 0.6rem 2rem; + margin-bottom: 1rem; +} + +#logo h4 { + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; +} + +#logo h5 { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +#logo #main_logo { + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; +} + +.toggle { + margin-top: auto; + position: relative; + cursor: pointer; + padding: 0; +} + +.toggle input[type='checkbox'] { + display: none; +} + +.toggle .switch { + overflow: hidden; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; + margin-right: 1rem; +} + +.toggle .circle { + border-radius: 0.5rem; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; +} + +.toggle .circle:first-of-type { + margin-bottom: 0.4rem; +} + +.toggle .circle line { + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; +} + +.toggle input:checked ~ .switch .circle { + -webkit-transform: translateY(-1.7rem); + transform: translateY(-1.7rem); +} + +.page { + padding: 2rem 1.5rem; + max-height: 100%; + overflow-y: auto; +} + +.options-tab { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + margin-top: 1rem; + margin-bottom: 1rem; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} + +.options-tab .option { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + border-radius: 0.4rem; + padding: 1.5rem; + margin-right: 1rem; + margin-bottom: 1rem; + width: 9rem; + border: solid 1px rgba(var(--text-color), 0.2); + text-transform: capitalize; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.options-tab .option .icon { + background: rgba(var(--text-color), 0.1); + height: 2.8rem; + width: 2.8rem; + padding: 0.8rem; + border-radius: 2rem; + margin-bottom: 1rem; + stroke: rgba(var(--text-color), 0.4); +} + +.options-tab .option h4 { + font-weight: 400; + font-size: 0.9rem; +} + +#home_page h1 { + font-weight: 600; +} + +#home_page p { + margin-bottom: 3rem; +} + +#home_page h2 { + margin-bottom: 1rem; +} + +@media screen and (max-width: 640px) { + #main_header { + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 1.5rem; + } + #main_header .icon { + stroke-width: 12; + height: 3rem; + width: 3rem; + background: rgba(var(--foreground-color), 1); + border-radius: 2rem; + padding: 1rem; + -webkit-box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.16), 0 0.4rem 0.8rem rgba(0, 0, 0, 0.16); + box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.16), 0 0.4rem 0.8rem rgba(0, 0, 0, 0.16); + } +} + +@media only screen and (min-width: 640px) { + main { + display: -ms-grid; + display: grid; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 2rem; + height: 100vh; + overflow-y: auto; + } + p { + max-width: 40rem; + } + #navbar { + position: relative; + -webkit-transform: none; + transform: none; + height: 100%; + } + #main_header { + display: none; + } +} + +@media (hover: hover) { + .navbar-item:hover { + background: rgba(var(--text-color), 0.1); + } +} +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/Layouts/sidebar layout/css/main.css.map b/Layouts/sidebar layout/css/main.css.map new file mode 100644 index 0000000..dc6fa32 --- /dev/null +++ b/Layouts/sidebar layout/css/main.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "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", + "sources": [ + "main.scss" + ], + "names": [], + "file": "main.css" +} \ No newline at end of file diff --git a/Layouts/sidebar layout/css/main.scss b/Layouts/sidebar layout/css/main.scss new file mode 100644 index 0000000..83dc1d2 --- /dev/null +++ b/Layouts/sidebar layout/css/main.scss @@ -0,0 +1,267 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap'); +*{ + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} +:root{ + scroll-behavior: smooth; +} +body{ + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} +body[data-theme="dark"]{ + --accent-color: #4a5cd3; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} +h1, h2, h3, h4, h5{ + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} +h1{ + font-size: 3rem; +} +h2{ + font-size: 2rem; +} +h3{ + font-size: 1.5rem; +} +h4{ + font-size: 1rem; +} +h5{ + font-size: 0.8rem; +} +p{ + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} +.hide{ + opacity: 0; + pointer-events: none; +} +.hide-completely{ + display: none !important; +} +.no-transformations{ + transform: none !important; +} +.icon{ + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} +#navbar{ + display: flex; + flex-direction: column; + align-items: center; + position: fixed; + left: 0; + bottom: 0; + top: 0; + box-shadow: 0.2rem 0 0.5rem rgba(0, 0, 0, 0.1); + z-index: 3; + padding: 1rem 0; + background: rgba(var(--foreground-color), 1); + border-right: solid 1px rgba(var(--text-color), 0.16); + transform: translateX(-100%); + transition: transform 0.3s; + .navbar-item{ + display: flex; + position: relative; + text-align: center; + cursor: pointer; + padding: 0.6rem 1.5rem; + padding-right: 3rem; + border-radius: 0.4em; + color: rgba(var(--text-color), 0.8); + font-size: 0.9em; + text-transform: uppercase; + width: 100%; + -webkit-tap-highlight-color: transparent; + .icon{ + margin-right: 1rem; + } + h4{ + text-transform: capitalize; + font-weight: 600; + } + } + .active{ + color: var(--accent-color); + .icon{ + stroke: var(--accent-color); + } + } +} +#logo{ + display: grid; + align-items: center; + width: 100%; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; + padding: 0.6rem 2rem; + margin-bottom: 1rem; + h4{ + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; + } + h5{ + font-family: 'Roboto', sans-serif; + font-weight: 400; + } + #main_logo{ + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; + } +} +.toggle{ + margin-top: auto; + position: relative; + cursor: pointer; + padding: 0; + input[type='checkbox']{ + display: none; + } + .switch{ + overflow: hidden; + display: inline-flex; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; + margin-right: 1rem; + } + .circle{ + border-radius: 0.5rem; + transition: transform 0.3s; + &:first-of-type{ + margin-bottom: 0.4rem; + } + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; + line{ + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + } + } + input:checked ~ .switch .circle{ + transform: translateY(-1.7rem); + } +} +.page{ + padding: 2rem 1.5rem; + max-height: 100%; + overflow-y: auto; +} +.options-tab{ + display: flex; + margin-top: 1rem; + margin-bottom: 1rem; + flex-wrap: wrap; + .option{ + display: inline-flex; + flex-direction: column; + border-radius: 0.4rem; + padding: 1.5rem; + margin-right: 1rem; + margin-bottom: 1rem; + width: 9rem; + border: solid 1px rgba(var(--text-color), 0.2); + text-transform: capitalize; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + .icon{ + background: rgba(var(--text-color), 0.1); + height: 2.8rem; + width: 2.8rem; + padding: 0.8rem; + border-radius: 2rem; + margin-bottom: 1rem; + stroke: rgba(var(--text-color), 0.4); + } + h4{ + font-weight: 400; + font-size: 0.9rem; + } + } +} +#home_page{ + h1{ + font-weight: 600; + } + p{ + margin-bottom: 3rem; + } + h2{ + margin-bottom: 1rem; + } +} +@media screen and (max-width: 640px){ + #main_header{ + position: sticky; + top: 0; + padding: 1.5rem; + .icon{ + stroke-width: 12; + height: 3rem; + width: 3rem; + background: rgba(var(--foreground-color), 1); + border-radius: 2rem; + padding: 1rem; + box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.16), + 0 0.4rem 0.8rem rgba(0, 0, 0, 0.16); + } + } +} +@media only screen and (min-width: 640px){ + main{ + display: grid; + grid-template-columns: auto 1fr; + gap: 2rem; + height: 100vh; + overflow-y: auto; + } + p{ + max-width: 40rem; + } + #navbar{ + position: relative; + transform: none; + height: 100%; + } + #main_header{ + display: none; + } +} +@media (hover: hover){ + .navbar-item:hover{ + background: rgba(var(--text-color), 0.1); + } +} \ No newline at end of file diff --git a/Layouts/sidebar layout/index.html b/Layouts/sidebar layout/index.html new file mode 100644 index 0000000..3e0df48 --- /dev/null +++ b/Layouts/sidebar layout/index.html @@ -0,0 +1,154 @@ + + + + + + + Document + + + + +
+ +
+ + menu + + + +
+
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisi excepturi eius voluptate quod quasi + dolore, quisquam, voluptatem repellendus sapiente consectetur similique ducimus dicta magni harum, +

+

Options

+ +
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+ + transfer + + +

Option

+
+
+
+
+

User Settings

+
+
+ + + + + + \ No newline at end of file diff --git a/Layouts/sidebar layout/js/components.js b/Layouts/sidebar layout/js/components.js new file mode 100644 index 0000000..30d8c2f --- /dev/null +++ b/Layouts/sidebar layout/js/components.js @@ -0,0 +1,2918 @@ +//Button + +const smButton = document.createElement('template') +smButton.innerHTML = ` + +
+ +
`; +customElements.define('sm-button', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smButton.content.cloneNode(true)) + } + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + dispatch = () => { + if (this.getAttribute('disabled') === 'true') { + this.dispatchEvent(new CustomEvent('disabled', { + bubbles: true, + composed: true + })) + } + else { + this.dispatchEvent(new CustomEvent('clicked', { + bubbles: true, + composed: true + })) + } + } + + connectedCallback() { + this.addEventListener('click', (e) => { + this.dispatch() + }) + this.addEventListener('keyup', (e) => { + if (e.code === "Enter" || e.code === "Space") + this.dispatch() + }) + } + + 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 = () => { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (this.input.value !== '') { + if (this.animate) + this.inputParent.classList.add('animate-label') + else + this.label.classList.add('hide') + this.clearBtn.classList.remove('hide') + } + else { + if (this.animate) + this.inputParent.classList.remove('animate-label') + else + this.label.classList.remove('hide') + this.clearBtn.classList.add('hide') + } + if (this.valueChanged) { + if (this.input.checkValidity()) { + this.helperText.classList.add('hide') + this.inputParent.style.boxShadow = `` + } + else { + this.helperText.classList.remove('hide') + this.inputParent.style.boxShadow = `0 0 0 0.1rem ${this.computedStyle.getPropertyValue('--error-color')}` + } + } + } + + connectedCallback() { + this.inputParent = this.shadowRoot.querySelector('.input') + this.computedStyle = window.getComputedStyle(this.inputParent) + this.clearBtn = this.shadowRoot.querySelector('.clear') + this.label = this.shadowRoot.querySelector('.label') + this.helperText = this.shadowRoot.querySelector('.helper-text') + this.valueChanged = false; + this.animate = this.hasAttribute('animate') + this.input = this.shadowRoot.querySelector('input') + this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') + if (this.hasAttribute('value')) { + this.input.value = this.getAttribute('value') + this.checkInput() + } + if (this.hasAttribute('helper-text')) { + this.helperText.textContent = this.getAttribute('helper-text') + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + this.input.setAttribute('inputmode', 'numeric') + } + else + this.input.setAttribute('type', this.getAttribute('type')) + } + else + this.input.setAttribute('type', 'text') + this.input.addEventListener('keydown', e => { + if (this.getAttribute('type') === 'number') + this.preventNonNumericalInput(e); + }) + this.input.addEventListener('input', e => { + this.checkInput() + }) + this.input.addEventListener('change', e => { + this.valueChanged = true; + if (this.input.checkValidity()) + this.helperText.classList.add('hide') + else + this.helperText.classList.remove('hide') + }) + this.clearBtn.addEventListener('click', e => { + this.input.value = '' + this.checkInput() + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') + this.shadowRoot.querySelector('.label').textContent = newValue; + } + } + }) + +// tab-header + +const smTabs = document.createElement('template') +smTabs.innerHTML = ` + +
+
+ Nothing to see here +
+
+
+ Nothing to see here +
+
+`; + +customElements.define('sm-tabs', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smTabs.content.cloneNode(true)) + this.indicator = this.shadowRoot.querySelector('.indicator'); + this.tabSlot = this.shadowRoot.querySelector('slot[name="tab"]'); + this.panelSlot = this.shadowRoot.querySelector('slot[name="panel"]'); + this.tabHeader = this.shadowRoot.querySelector('.tab-header'); + } + connectedCallback() { + + //animations + let flyInLeft = [ + { + opacity: 0, + transform: 'translateX(-1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyInRight = [ + { + opacity: 0, + transform: 'translateX(1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyOutLeft = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(-1rem)' + } + ], + flyOutRight = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(1rem)' + } + ], + animationOptions = { + duration: 300, + fill: 'forwards', + easing: 'ease' + } + this.prevTab + this.allTabs + + this.shadowRoot.querySelector('slot[name="panel"]').addEventListener('slotchange', () => { + this.shadowRoot.querySelector('slot[name="panel"]').assignedElements().forEach((panel, index) => { + panel.classList.add('hide-completely') + }) + }) + this.shadowRoot.querySelector('slot[name="tab"]').addEventListener('slotchange', () => { + this.allTabs = this.shadowRoot.querySelector('slot[name="tab"]').assignedElements(); + this.shadowRoot.querySelector('slot[name="tab"]').assignedElements().forEach((panel, index) => { + panel.setAttribute('rank', index + 1) + }) + }) + this._targetBodyFlyRight = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInRight, animationOptions) + } + this._targetBodyFlyLeft = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInLeft, animationOptions) + } + this.tabSlot.addEventListener('click', e => { + if (e.target === this.prevTab || !e.target.closest('sm-tab')) + return + if (this.prevTab) + this.prevTab.classList.remove('active') + e.target.classList.add('active') + + e.target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }) + this.indicator.setAttribute('style', `width: ${e.target.getBoundingClientRect().width}px; transform: translateX(${e.target.getBoundingClientRect().left - e.target.parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + + if (this.prevTab) { + let targetBody = e.target.nextElementSibling, + currentBody = this.prevTab.nextElementSibling; + + if (this.prevTab.getAttribute('rank') < e.target.getAttribute('rank')) { + if (currentBody && !targetBody) + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyRight(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyRight(targetBody) + } + } + } else { + if (currentBody && !targetBody) + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyLeft(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyLeft(targetBody) + } + } + } + } else { + e.target.nextElementSibling.classList.remove('hide-completely') + } + this.prevTab = e.target; + }) + let resizeObserver = new ResizeObserver(entries => { + entries.forEach((entry) => { + if (this.prevTab) { + let tabDimensions = this.prevTab.getBoundingClientRect(); + this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + }) + }) + resizeObserver.observe(this) + let observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + let activeElement = this.tabSlot.assignedElements().filter(element => { + if (element.classList.contains('active')) + return true + }) + if (activeElement.length) { + let tabDimensions = activeElement[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - activeElement[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + else { + this.tabSlot.assignedElements()[0].classList.add('active') + this.panelSlot.assignedElements()[0].classList.remove('hide-completely') + let tabDimensions = this.tabSlot.assignedElements()[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + this.prevTab = this.tabSlot.assignedElements()[0]; + } + } + }) + }, + { threshold: 1.0 }) + observer.observe(this) + if (this.hasAttribute('enable-flick') && this.getAttribute('enable-flick') == 'true') { + let touchStartTime = 0, + touchEndTime = 0, + swipeTimeThreshold = 200, + swipeDistanceThreshold = 20, + startingPointX = 0, + endingPointX = 0, + currentIndex = 0; + this.addEventListener('touchstart', e => { + touchStartTime = e.timeStamp + startingPointX = e.changedTouches[0].clientX + }) + this.panelSlot.addEventListener('touchend', e => { + touchEndTime = e.timeStamp + endingPointX = e.changedTouches[0].clientX + if (touchEndTime - touchStartTime < swipeTimeThreshold) { + currentIndex = this.allTabs.findIndex(element => element.classList.contains('active')) + if (startingPointX > endingPointX && startingPointX - endingPointX > swipeDistanceThreshold && currentIndex < this.allTabs.length) { + this.allTabs[currentIndex + 1].click() + } + else if (startingPointX < endingPointX && endingPointX - startingPointX > swipeDistanceThreshold && currentIndex > 0) { + this.allTabs[currentIndex - 1].click() + } + } + }) + } + } +}) + +// 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)) + } +}) + +//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)) + + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + this.input = this.shadowRoot.querySelector('input') + + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.getAttribute('checked') + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.checkbox.classList.add('disabled') + this.isDisabled = true + } + else { + this.checkbox.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } + +}) + +//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') + } + } + } +}) + +//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)) + this.switch = this.shadowRoot.querySelector('.switch'); + this.input = this.shadowRoot.querySelector('input') + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.isChecked + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.switch.classList.add('disabled') + this.isDisabled = true + } + else { + this.switch.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } +}) + +// select +const smSelect = document.createElement('template') +smSelect.innerHTML = ` + +
+
+
+ + + +
+
+ +
+
`; +customElements.define('sm-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + + collapse = () => { + this.optionList.animate(this.slideUp, this.animationOptions) + this.optionList.classList.add('hide') + this.chevron.classList.remove('rotate') + this.open = false + } + connectedCallback() { + this.availableOptions + this.optionList = this.shadowRoot.querySelector('.options') + this.chevron = this.shadowRoot.querySelector('.toggle') + let slot = this.shadowRoot.querySelector('.options slot'), + selection = this.shadowRoot.querySelector('.selection'), + previousOption + this.open = false; + this.slideDown = [ + { transform: `translateY(-0.5rem)` }, + { transform: `translateY(0)` } + ], + this.slideUp = [ + { transform: `translateY(0)` }, + { transform: `translateY(-0.5rem)` } + ], + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: 'ease' + } + selection.addEventListener('click', e => { + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + selection.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.addEventListener('optionSelected', e => { + if (previousOption !== e.target) { + this.setAttribute('value', e.detail.value) + this.shadowRoot.querySelector('.option-text').textContent = e.detail.text; + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + if (previousOption) { + previousOption.classList.remove('check-selected') + } + previousOption = e.target; + } + if(!e.detail.switching) + this.collapse() + + e.target.classList.add('check-selected') + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + if (this.availableOptions[0]) { + let firstElement = this.availableOptions[0]; + previousOption = firstElement; + firstElement.classList.add('check-selected') + this.setAttribute('value', firstElement.getAttribute('value')) + this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent + this.availableOptions.forEach((element, index) => { + element.setAttribute('data-rank', index + 1); + element.setAttribute('tabindex', "0"); + }) + } + }); + document.addEventListener('mousedown', e => { + if (!this.contains(e.target) && this.open) { + this.collapse() + } + }) + } +}) + +// option +const smOption = document.createElement('template') +smOption.innerHTML = ` + +
+ + + + +
`; +customElements.define('sm-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smOption.content.cloneNode(true)) + } + + sendDetails = (switching) => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value'), + switching: switching + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + let validKey = [ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight' + ] + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + if (validKey.includes(e.code)) { + e.preventDefault() + this.sendDetails(true) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +// select +const smStripSelect = document.createElement('template') +smStripSelect.innerHTML = ` + +
+
+ + Previous + + +
+ +
+ + Next + + +
+
`; +customElements.define('sm-strip-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + scrollLeft = () => { + this.select.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.select.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + connectedCallback() { + let previousOption, + slot = this.shadowRoot.querySelector('slot'); + this.selectContainer = this.shadowRoot.querySelector('.select-container') + this.select = this.shadowRoot.querySelector('.select') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.selectOptions + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.previousArrow.classList.add('hide') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.remove('hide') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.nextArrow.classList.add('hide') + this.nextGradient.classList.add('hide') + } + else { + this.nextArrow.classList.remove('hide') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + + const selectObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + } + }) + + selectObserver.observe(this.selectContainer) + this.addEventListener('optionSelected', e => { + if (previousOption === e.target) return; + if (previousOption) + previousOption.classList.remove('active') + e.target.classList.add('active') + e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) + this.setAttribute('value', e.detail.value) + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + previousOption = e.target; + }) + slot.addEventListener('slotchange', e => { + this.selectOptions = slot.assignedElements() + firstElementObserver.observe(this.selectOptions[0]) + lastElementObserver.observe(this.selectOptions[this.selectOptions.length - 1]) + if (this.selectOptions[0]) { + let firstElement = this.selectOptions[0]; + this.setAttribute('value', firstElement.getAttribute('value')) + firstElement.classList.add('active') + previousOption = firstElement; + } + }); + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +// option +const smStripOption = document.createElement('template') +smStripOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-strip-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripOption.content.cloneNode(true)) + } + sendDetails = () => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +//popup +const smPopup = document.createElement('template') +smPopup.innerHTML = ` + + +`; +customElements.define('sm-popup', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) + } + + resumeScrolling = () => { + const scrollY = document.body.style.top; + window.scrollTo(0, parseInt(scrollY || '0') * -1); + setTimeout(() => { + document.body.setAttribute('style', `overflow: auto; top: initial`) + }, 300); + } + + show(pinned, popupStack) { + this.pinned = pinned + this.popupStack = popupStack + this.popupContainer.classList.remove('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(0)'; + else + this.popup.style.transform = 'scale(1)'; + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + } + hide() { + this.popupContainer.classList.add('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(100%)'; + else + this.popup.style.transform = 'scale(0.9)'; + if (typeof this.popupStack !== 'undefined') { + this.popupStack.pop() + if (this.popupStack.items.length === 0) { + this.resumeScrolling() + } + } + else { + this.resumeScrolling() + } + } + + handleTouchStart = (e) => { + this.touchStartY = e.changedTouches[0].clientY + this.popup.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartY < e.changedTouches[0].clientY) { + this.offset = e.changedTouches[0].clientY - this.touchStartY; + this.touchEndAnimataion = window.requestAnimationFrame(this.movePopup) + } + /*else { + offset = touchStartY - e.changedTouches[0].clientY; + this.popup.style.transform = `translateY(-${offset}px)` + }*/ + } + + handleTouchEnd = (e) => { + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.3s' + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndY - this.touchStartY > this.threshold) { + this.hide() + } + else { + this.show() + } + } + else { + if (this.touchEndY > this.touchStartY) + this.hide() + } + } + + movePopup = () => { + this.popup.style.transform = `translateY(${this.offset}px)` + } + + connectedCallback() { + this.pinned = false + this.popupStack + this.popupContainer = this.shadowRoot.querySelector('.popup-container') + this.popup = this.shadowRoot.querySelector('.popup') + this.offset + this.popupHeader = this.shadowRoot.querySelector('.popup-top') + this.touchStartY = 0 + this.touchEndY = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + this.touchEndAnimataion; + + if (this.hasAttribute('heading')) + this.shadowRoot.querySelector('.heading').textContent = this.getAttribute('heading') + + this.popupContainer.addEventListener('mousedown', e => { + if (e.target === this.popupContainer && !this.pinned) { + this.hide() + } + }) + + this.shadowRoot.querySelector('.close').addEventListener('click', e => { + this.hide() + }) + + this.popupHeader.addEventListener('touchstart', this.handleTouchStart) + this.popupHeader.addEventListener('touchmove', this.handleTouchMove) + this.popupHeader.addEventListener('touchend', this.handleTouchEnd) + } + disconnectedCallback() { + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd) + } +}) + +//carousel + +const smCarousel = document.createElement('template') +smCarousel.innerHTML = ` + + +`; + +customElements.define('sm-carousel', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smCarousel.content.cloneNode(true)) + } + + scrollLeft = () => { + this.carousel.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.carousel.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + + connectedCallback() { + this.carousel = this.shadowRoot.querySelector('.carousel') + this.carouselContainer = this.shadowRoot.querySelector('.carousel-container') + this.carouselSlot = this.shadowRoot.querySelector('slot') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.carouselItems + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width/3 + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.previousArrow.classList.remove('expand') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.add('expand') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.nextArrow.classList.remove('expand') + this.nextGradient.classList.add('hide') + } + else{ + this.nextArrow.classList.add('expand') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + + const carouselObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 + } + }) + + carouselObserver.observe(this.carouselContainer) + + this.carouselSlot.addEventListener('slotchange', e => { + this.carouselItems = this.carouselSlot.assignedElements() + firstElementObserver.observe(this.carouselItems[0]) + lastElementObserver.observe(this.carouselItems[this.carouselItems.length - 1]) + }) + + this.addEventListener('keyup', e => { + if (e.code === 'ArrowLeft') + this.scrollRight() + else + this.scrollRight() + }) + + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +//notifications + +const smNotifications = document.createElement('template') +smNotifications.innerHTML = ` + +
+
+` + +customElements.define('sm-notifications', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) + } + + handleTouchStart = (e) => { + this.notification = e.target.closest('.notification') + this.touchStartX = e.changedTouches[0].clientX + this.notification.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartX < e.changedTouches[0].clientX) { + this.offset = e.changedTouches[0].clientX - this.touchStartX; + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + else { + this.offset = -(this.touchStartX - e.changedTouches[0].clientX); + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + } + + handleTouchEnd = (e) => { + this.notification.style.transition = 'height 0.3s, transform 0.3s, opacity 0.3s' + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndX = e.changedTouches[0].clientX + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndX - this.touchStartX > this.threshold) { + this.removeNotification(this.notification) + } + else if (this.touchStartX - this.touchEndX > this.threshold) { + this.removeNotification(this.notification, true) + } + else { + this.resetPosition() + } + } + else { + if (this.touchEndX > this.touchStartX) { + this.removeNotification(this.notification) + } + else { + this.removeNotification(this.notification, true) + } + } + } + + movePopup = () => { + this.notification.style.transform = `translateX(${this.offset}px)` + } + + resetPosition = () => { + this.notification.style.transform = `translateX(0)` + } + + push = (messageHeader, messageBody, options) => { + let notification = document.createElement('div'), + composition = ``, + { pinned, type } = options; + notification.classList.add('notification') + if (pinned) + notification.classList.add('pinned') + composition += ` +
+
+ ` + if (type === 'error') { + composition += ` + + + + + ` + } + else if (type === 'success') { + composition += ` + + + + ` + } + composition += ` +

${messageHeader}

+ + Close + + + +
+

${messageBody}

+
` + notification.innerHTML = composition + this.notificationPanel.prepend(notification) + if (window.innerWidth > 640) { + notification.animate([ + { + transform: `translateX(1rem)`, + opacity: '0' + }, + { + transform: 'translateX(0)', + opacity: '1' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `transform: none;`); + } + } + else { + notification.setAttribute('style', `transform: translateY(0); opacity: 1`) + } + notification.addEventListener('touchstart', this.handleTouchStart) + notification.addEventListener('touchmove', this.handleTouchMove) + notification.addEventListener('touchend', this.handleTouchEnd) + } + + removeNotification = (notification, toLeft) => { + notification.style.height = notification.scrollHeight + 'px'; + if (!this.offset) + this.offset = 0; + + if (toLeft) + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(-100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + else { + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + } + setTimeout( () => { + notification.remove() + }, this.animationOptions.duration*2) + } + + connectedCallback() { + this.notificationPanel = this.shadowRoot.querySelector('.notification-panel') + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: "ease" + } + this.fontSize = Number(window.getComputedStyle(document.body).getPropertyValue('font-size').match(/\d+/)[0]) + this.notification + this.offset + this.touchStartX = 0 + this.touchEndX = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.notificationPanel.getBoundingClientRect().width * 0.3 + this.touchEndAnimataion; + + this.notificationPanel.addEventListener('click', e => { + if (e.target.closest('.close'))( + this.removeNotification(e.target.closest('.notification')) + ) + }) + + const observer = new MutationObserver(mutationList => { + mutationList.forEach(mutation => { + if (mutation.type === 'childList') { + if (mutation.addedNodes.length) { + if (!mutation.addedNodes[0].classList.contains('pinned')) + setTimeout(() => { + this.removeNotification(mutation.addedNodes[0]) + }, 4000); + if (window.innerWidth > 640) + this.notificationPanel.style.padding = '1.5rem 0 3rem 1.5rem'; + else + this.notificationPanel.style.padding = '1rem 1rem 2rem 1rem'; + } + else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { + this.notificationPanel.style.padding = 0; + } + } + }) + }) + observer.observe(this.notificationPanel, { + attributes: true, + childList: true, + subtree: true + }) + } +}) +// sm-menu +const smMenu = document.createElement('template') +smMenu.innerHTML = ` + +
+ +
+ +
+
`; +customElements.define('sm-menu', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenu.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + expand = () => { + if (!this.open) { + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + this.optionList.classList.remove('hide') + this.optionList.classList.add('no-transformations') + this.open = true + this.icon.classList.add('focused') + } + } + collapse = () => { + if (this.open) { + this.open = false + this.icon.classList.remove('focused') + this.optionList.classList.add('hide') + this.optionList.classList.remove('no-transformations') + } + } + connectedCallback() { + this.availableOptions + this.containerDimensions + this.optionList = this.shadowRoot.querySelector('.options') + let slot = this.shadowRoot.querySelector('.options slot'), + menu = this.shadowRoot.querySelector('.menu') + this.icon = this.shadowRoot.querySelector('.icon') + this.open = false; + menu.addEventListener('click', e => { + if (!this.open) { + this.expand() + } else { + this.collapse() + } + }) + menu.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + if (!this.open) { + this.expand() + } else { + this.collapse() + } + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.optionList.addEventListener('click', e => { + this.collapse() + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + this.containerDimensions = this.optionList.getBoundingClientRect() + this.menuDimensions = menu.getBoundingClientRect() + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + }); + window.addEventListener('mousedown', e => { + if (!this.contains(e.target) && e.button !== 2) { + this.collapse() + } + }) + if (this.hasAttribute('set-context') && this.getAttribute('set-context') === 'true') { + this.parentNode.setAttribute('oncontextmenu', 'return false') + this.parentNode.addEventListener('mouseup', e => { + if (e.button === 2) { + this.expand() + } + }) + } + const intersectionObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting && this.open) { + if(window.innerHeight - entry.intersectionRect.top < this.containerDimensions.height) + this.optionList.classList.add('moveUp') + else + this.optionList.classList.remove('moveUp') + if (entry.intersectionRect.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + } + }) + }, { + threshold: 1 + }) + intersectionObserver.observe(this.optionList) + } +}) + +// option +const smMenuOption = document.createElement('template') +smMenuOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-menu-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenuOption.content.cloneNode(true)) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.click() + } + }) + this.setAttribute('tabindex', '0') + } +}) \ No newline at end of file diff --git a/Layouts/sidebar layout/js/helper.js b/Layouts/sidebar layout/js/helper.js new file mode 100644 index 0000000..a7c51ee --- /dev/null +++ b/Layouts/sidebar layout/js/helper.js @@ -0,0 +1,133 @@ +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) +}) +let themeToggler = document.getElementById("theme_toggle") +if (localStorage.theme === "dark") { + darkTheme() + themeToggler.checked = true; +} else { + lightTheme() + themeToggler.checked = false; +} + +function lightTheme() { + document.body.setAttribute("data-theme", "light"); +} + +function darkTheme() { + document.body.setAttribute("data-theme", "dark"); +} +themeToggler.addEventListener("change", () => { + if (themeToggler.checked) { + darkTheme() + localStorage.setItem("theme", "dark"); + } else { + lightTheme() + 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(index) { + let newIndex = index ? index : 1 + return this.items[this.items.length - index]; + } +} +let popupStack = new Stack(), + zIndex = 10; +function showPopup(popup, permission) { + let thisPopup = document.getElementById(popup); + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + popupStack.push({ thisPopup, permission }) + thisPopup.show(permission, popupStack) + zIndex++; + thisPopup.setAttribute('style', `z-index: ${zIndex}`) + return thisPopup; +} +function setAttributes(el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } +} +// displays a popup for asking permission. Use this instead of JS confirm +let confirmation = 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); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(false); + } + }) +} + +// 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'); + if (defaultVal) + input.value = defaultVal; + showPopup('prompt') + input.focus() + input.addEventListener('keyup', e => { + if (e.key === 'Enter') { + resolve(input.value); + hidePopup() + } + }) + popup.querySelector('#prompt_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(input.value); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(null); + } + }) +} + +function formatedTime(time) { + let timeFrag = new Date(parseInt(time)).toString().split(' '), + day = timeFrag[0], + month = timeFrag[1], + date = timeFrag[2], + year = timeFrag[3], + hours = timeFrag[4].slice(0, timeFrag[4].lastIndexOf(':')), + finalTime = ''; + parseInt(hours.split(':')[0]) > 12 ? finalTime = 'PM' : finalTime = 'AM' + return `${hours} ${finalTime} ${day} ${date} ${month} ${year}` +} + +function copyToClipboard(parent) { + let toast = document.getElementById('textCopied'), + textToCopy = parent.querySelector('.copy').textContent; + navigator.clipboard.writeText(textToCopy) + toast.classList.remove('hide'); + setTimeout(() => { + toast.classList.add('hide'); + }, 2000) +} \ No newline at end of file diff --git a/Layouts/tabs layout/css/main.css b/Layouts/tabs layout/css/main.css new file mode 100644 index 0000000..cfd785f --- /dev/null +++ b/Layouts/tabs layout/css/main.css @@ -0,0 +1,233 @@ +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap"); +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} + +:root { + scroll-behavior: smooth; +} + +body { + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} + +body[data-theme="dark"] { + --accent-color: #6a7dff; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} + +h1, h2, h3, h4, h5 { + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} + +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2rem; +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +p { + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} + +.hide { + opacity: 0; + pointer-events: none; +} + +.hide-completely { + display: none !important; +} + +.icon { + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} + +.toggle { + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; +} + +.toggle input[type='checkbox'] { + display: none; +} + +.toggle .switch { + overflow: hidden; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; +} + +.toggle .circle { + border-radius: 0.5rem; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; +} + +.toggle .circle:first-of-type { + margin-bottom: 0.4rem; +} + +.toggle .circle line { + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; +} + +.toggle input:checked ~ .switch .circle { + -webkit-transform: translateY(-1.7rem); + transform: translateY(-1.7rem); +} + +sm-tabs { + margin-bottom: 2rem; +} + +sm-tabs::part(tab-header) { + border-bottom: solid 1px rgba(var(--text-color), 0.2); + padding: 0 1.5rem; + margin-bottom: 2rem; +} + +sm-tabs::part(panel-container) { + padding: 0 1.5rem; +} + +#navbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 1.5rem 2rem; + background: rgba(var(--foreground-color), 1); + z-index: 3; +} + +#logo { + display: -ms-grid; + display: grid; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -ms-grid-columns: auto 1fr; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; +} + +#logo h4 { + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; +} + +#logo h5 { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +#logo #main_logo { + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; +} + +.section { + margin-top: 3rem; +} + +.section h3 + p { + margin-top: 1rem; +} + +.section:first-of-type { + margin-top: 0; +} + +.card { + padding: 1.5rem; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + width: 20rem; + border-radius: 0.5rem; + margin-right: 1.5rem; + border: solid 1px rgba(var(--text-color), 0.2); +} + +.card h3 { + font-weight: 500; +} + +@media only screen and (min-width: 640px) { + sm-tabs::part(tab-header), sm-tabs::part(panel-container) { + padding: 0 4vw; + } + p { + max-width: 40rem; + } +} +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/Layouts/tabs layout/css/main.css.map b/Layouts/tabs layout/css/main.css.map new file mode 100644 index 0000000..df4b6bc --- /dev/null +++ b/Layouts/tabs layout/css/main.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "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", + "sources": [ + "main.scss" + ], + "names": [], + "file": "main.css" +} \ No newline at end of file diff --git a/Layouts/tabs layout/css/main.scss b/Layouts/tabs layout/css/main.scss new file mode 100644 index 0000000..04a64ea --- /dev/null +++ b/Layouts/tabs layout/css/main.scss @@ -0,0 +1,178 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Roboto:wght@400;500;700&display=swap'); +*{ + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} +:root{ + scroll-behavior: smooth; +} +body{ + --accent-color: #303F9F; + --text-color: 17, 17, 17; + --foreground-color: 255, 255, 255; + background: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 1); + font-size: 16px; +} +body[data-theme="dark"]{ + --accent-color: #6a7dff; + --foreground-color: 20, 20, 20; + --text-color: 238, 238, 238; +} +h1, h2, h3, h4, h5{ + font-family: 'Poppins', sans-serif; + text-transform: capitalize; +} +h1{ + font-size: 3rem; +} +h2{ + font-size: 2rem; +} +h3{ + font-size: 1.5rem; +} +h4{ + font-size: 1rem; +} +h5{ + font-size: 0.8rem; +} +p{ + margin: 1.5rem 0; + line-height: 1.7; + color: rgba(var(--text-color), 0.8); +} +.hide{ + opacity: 0; + pointer-events: none; +} +.hide-completely{ + display: none !important; +} +.icon{ + height: 1.2rem; + width: 1.2rem; + fill: none; + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; +} +.toggle{ + position: relative; + cursor: pointer; + z-index: 1; + padding: 0; + input[type='checkbox']{ + display: none; + } + .switch{ + overflow: hidden; + display: inline-flex; + flex-direction: column; + justify-items: center; + padding: 0.2rem; + min-height: 1.6rem; + max-height: 1.6rem; + border-radius: 0.5rem; + position: relative; + margin: 0; + } + .circle{ + border-radius: 0.5rem; + transition: transform 0.3s; + &:first-of-type{ + margin-bottom: 0.4rem; + } + fill: rgba(var(--text-color), 0.8); + overflow: visible; + stroke-linecap: round; + stroke-linejoin: round; + height: 1.2rem; + width: 1.2rem; + line{ + stroke: rgba(var(--text-color), 0.8); + stroke-width: 6; + } + } + input:checked ~ .switch .circle{ + transform: translateY(-1.7rem); + } +} +sm-tabs{ + margin-bottom: 2rem; + &::part(tab-header){ + border-bottom: solid 1px rgba(var(--text-color), 0.2); + padding: 0 1.5rem; + margin-bottom: 2rem; + } + &::part(panel-container){ + padding: 0 1.5rem; + } +} +#navbar{ + display: flex; + align-items: center; + padding: 1.5rem 2rem; + background: rgba(var(--foreground-color), 1); + z-index: 3; +} +#logo{ + display: grid; + align-items: center; + width: 100%; + grid-template-columns: auto 1fr; + gap: 0.6rem 0.6rem; + margin-right: 1rem; + h4{ + text-transform: capitalize; + font-size: 1.2rem; + font-weight: 600; + } + h5{ + font-family: 'Roboto', sans-serif; + font-weight: 400; + } + #main_logo{ + height: 1.4rem; + width: 1.4rem; + fill: rgba(var(--text-color), 1); + stroke: none; + } +} +.section{ + margin-top: 3rem; + h3 + p{ + margin-top: 1rem; + } + &:first-of-type{ + margin-top: 0; + } +} +.card{ + padding: 1.5rem; + display: flex; + flex-direction: column; + width: 20rem; + border-radius: 0.5rem; + margin-right: 1.5rem; + border: solid 1px rgba(var(--text-color), 0.2); + h3{ + font-weight: 500; + } +} +@media only screen and (min-width: 640px){ + sm-tabs{ + &::part(tab-header), + &::part(panel-container){ + padding: 0 4vw; + } + } + p{ + max-width: 40rem; + } +} \ No newline at end of file diff --git a/Layouts/tabs layout/index.html b/Layouts/tabs layout/index.html new file mode 100644 index 0000000..9e937fb --- /dev/null +++ b/Layouts/tabs layout/index.html @@ -0,0 +1,204 @@ + + + + + + + Document + + + + + +
+ + Page 1 + +
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+ Page 2 + +
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+

Heading

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +

+ +
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+

Card

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam, officia. +

+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/Layouts/tabs layout/js/components.js b/Layouts/tabs layout/js/components.js new file mode 100644 index 0000000..30d8c2f --- /dev/null +++ b/Layouts/tabs layout/js/components.js @@ -0,0 +1,2918 @@ +//Button + +const smButton = document.createElement('template') +smButton.innerHTML = ` + +
+ +
`; +customElements.define('sm-button', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smButton.content.cloneNode(true)) + } + static get observedAttributes() { + return ['disabled'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + dispatch = () => { + if (this.getAttribute('disabled') === 'true') { + this.dispatchEvent(new CustomEvent('disabled', { + bubbles: true, + composed: true + })) + } + else { + this.dispatchEvent(new CustomEvent('clicked', { + bubbles: true, + composed: true + })) + } + } + + connectedCallback() { + this.addEventListener('click', (e) => { + this.dispatch() + }) + this.addEventListener('keyup', (e) => { + if (e.code === "Enter" || e.code === "Space") + this.dispatch() + }) + } + + 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 = () => { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (this.input.value !== '') { + if (this.animate) + this.inputParent.classList.add('animate-label') + else + this.label.classList.add('hide') + this.clearBtn.classList.remove('hide') + } + else { + if (this.animate) + this.inputParent.classList.remove('animate-label') + else + this.label.classList.remove('hide') + this.clearBtn.classList.add('hide') + } + if (this.valueChanged) { + if (this.input.checkValidity()) { + this.helperText.classList.add('hide') + this.inputParent.style.boxShadow = `` + } + else { + this.helperText.classList.remove('hide') + this.inputParent.style.boxShadow = `0 0 0 0.1rem ${this.computedStyle.getPropertyValue('--error-color')}` + } + } + } + + connectedCallback() { + this.inputParent = this.shadowRoot.querySelector('.input') + this.computedStyle = window.getComputedStyle(this.inputParent) + this.clearBtn = this.shadowRoot.querySelector('.clear') + this.label = this.shadowRoot.querySelector('.label') + this.helperText = this.shadowRoot.querySelector('.helper-text') + this.valueChanged = false; + this.animate = this.hasAttribute('animate') + this.input = this.shadowRoot.querySelector('input') + this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') + if (this.hasAttribute('value')) { + this.input.value = this.getAttribute('value') + this.checkInput() + } + if (this.hasAttribute('helper-text')) { + this.helperText.textContent = this.getAttribute('helper-text') + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + this.input.setAttribute('inputmode', 'numeric') + } + else + this.input.setAttribute('type', this.getAttribute('type')) + } + else + this.input.setAttribute('type', 'text') + this.input.addEventListener('keydown', e => { + if (this.getAttribute('type') === 'number') + this.preventNonNumericalInput(e); + }) + this.input.addEventListener('input', e => { + this.checkInput() + }) + this.input.addEventListener('change', e => { + this.valueChanged = true; + if (this.input.checkValidity()) + this.helperText.classList.add('hide') + else + this.helperText.classList.remove('hide') + }) + this.clearBtn.addEventListener('click', e => { + this.input.value = '' + this.checkInput() + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') + this.shadowRoot.querySelector('.label').textContent = newValue; + } + } + }) + +// tab-header + +const smTabs = document.createElement('template') +smTabs.innerHTML = ` + +
+
+ Nothing to see here +
+
+
+ Nothing to see here +
+
+`; + +customElements.define('sm-tabs', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smTabs.content.cloneNode(true)) + this.indicator = this.shadowRoot.querySelector('.indicator'); + this.tabSlot = this.shadowRoot.querySelector('slot[name="tab"]'); + this.panelSlot = this.shadowRoot.querySelector('slot[name="panel"]'); + this.tabHeader = this.shadowRoot.querySelector('.tab-header'); + } + connectedCallback() { + + //animations + let flyInLeft = [ + { + opacity: 0, + transform: 'translateX(-1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyInRight = [ + { + opacity: 0, + transform: 'translateX(1rem)' + }, + { + opacity: 1, + transform: 'none' + } + ], + flyOutLeft = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(-1rem)' + } + ], + flyOutRight = [ + { + opacity: 1, + transform: 'none' + }, + { + opacity: 0, + transform: 'translateX(1rem)' + } + ], + animationOptions = { + duration: 300, + fill: 'forwards', + easing: 'ease' + } + this.prevTab + this.allTabs + + this.shadowRoot.querySelector('slot[name="panel"]').addEventListener('slotchange', () => { + this.shadowRoot.querySelector('slot[name="panel"]').assignedElements().forEach((panel, index) => { + panel.classList.add('hide-completely') + }) + }) + this.shadowRoot.querySelector('slot[name="tab"]').addEventListener('slotchange', () => { + this.allTabs = this.shadowRoot.querySelector('slot[name="tab"]').assignedElements(); + this.shadowRoot.querySelector('slot[name="tab"]').assignedElements().forEach((panel, index) => { + panel.setAttribute('rank', index + 1) + }) + }) + this._targetBodyFlyRight = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInRight, animationOptions) + } + this._targetBodyFlyLeft = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInLeft, animationOptions) + } + this.tabSlot.addEventListener('click', e => { + if (e.target === this.prevTab || !e.target.closest('sm-tab')) + return + if (this.prevTab) + this.prevTab.classList.remove('active') + e.target.classList.add('active') + + e.target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }) + this.indicator.setAttribute('style', `width: ${e.target.getBoundingClientRect().width}px; transform: translateX(${e.target.getBoundingClientRect().left - e.target.parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + + if (this.prevTab) { + let targetBody = e.target.nextElementSibling, + currentBody = this.prevTab.nextElementSibling; + + if (this.prevTab.getAttribute('rank') < e.target.getAttribute('rank')) { + if (currentBody && !targetBody) + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyRight(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutLeft, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyRight(targetBody) + } + } + } else { + if (currentBody && !targetBody) + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + } + else if (targetBody && !currentBody) { + this._targetBodyFlyLeft(targetBody) + } + else if (currentBody && targetBody) { + currentBody.animate(flyOutRight, animationOptions).onfinish = () => { + currentBody.classList.add('hide-completely') + this._targetBodyFlyLeft(targetBody) + } + } + } + } else { + e.target.nextElementSibling.classList.remove('hide-completely') + } + this.prevTab = e.target; + }) + let resizeObserver = new ResizeObserver(entries => { + entries.forEach((entry) => { + if (this.prevTab) { + let tabDimensions = this.prevTab.getBoundingClientRect(); + this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + }) + }) + resizeObserver.observe(this) + let observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + let activeElement = this.tabSlot.assignedElements().filter(element => { + if (element.classList.contains('active')) + return true + }) + if (activeElement.length) { + let tabDimensions = activeElement[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - activeElement[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + } + else { + this.tabSlot.assignedElements()[0].classList.add('active') + this.panelSlot.assignedElements()[0].classList.remove('hide-completely') + let tabDimensions = this.tabSlot.assignedElements()[0].getBoundingClientRect(); + this.indicator.setAttribute('style', `transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + this.prevTab = this.tabSlot.assignedElements()[0]; + } + } + }) + }, + { threshold: 1.0 }) + observer.observe(this) + if (this.hasAttribute('enable-flick') && this.getAttribute('enable-flick') == 'true') { + let touchStartTime = 0, + touchEndTime = 0, + swipeTimeThreshold = 200, + swipeDistanceThreshold = 20, + startingPointX = 0, + endingPointX = 0, + currentIndex = 0; + this.addEventListener('touchstart', e => { + touchStartTime = e.timeStamp + startingPointX = e.changedTouches[0].clientX + }) + this.panelSlot.addEventListener('touchend', e => { + touchEndTime = e.timeStamp + endingPointX = e.changedTouches[0].clientX + if (touchEndTime - touchStartTime < swipeTimeThreshold) { + currentIndex = this.allTabs.findIndex(element => element.classList.contains('active')) + if (startingPointX > endingPointX && startingPointX - endingPointX > swipeDistanceThreshold && currentIndex < this.allTabs.length) { + this.allTabs[currentIndex + 1].click() + } + else if (startingPointX < endingPointX && endingPointX - startingPointX > swipeDistanceThreshold && currentIndex > 0) { + this.allTabs[currentIndex - 1].click() + } + } + }) + } + } +}) + +// 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)) + } +}) + +//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)) + + this.checkbox = this.shadowRoot.querySelector('.checkbox'); + this.input = this.shadowRoot.querySelector('input') + + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.getAttribute('checked') + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.checkbox.classList.add('disabled') + this.isDisabled = true + } + else { + this.checkbox.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } + +}) + +//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') + } + } + } +}) + +//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)) + this.switch = this.shadowRoot.querySelector('.switch'); + this.input = this.shadowRoot.querySelector('input') + this.isChecked = false + this.isDisabled = false + } + + static get observedAttributes() { + return ['disabled', 'checked'] + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + this.setAttribute('disabled', val) + } + + get checked() { + return this.isChecked + } + + set checked(value) { + this.setAttribute('checked', value) + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + this.isChecked = !this.isChecked + this.setAttribute('checked', this.isChecked) + } + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'disabled') { + if (newValue === 'true') { + this.switch.classList.add('disabled') + this.isDisabled = true + } + else { + this.switch.classList.remove('disabled') + this.isDisabled = false + } + } + if (name === 'checked') { + if (newValue == 'true') { + this.isChecked = true + this.input.checked = true + this.dispatch() + } + else { + this.isChecked = false + this.input.checked = false + this.dispatch() + } + } + } + } +}) + +// select +const smSelect = document.createElement('template') +smSelect.innerHTML = ` + +
+
+
+ + + +
+
+ +
+
`; +customElements.define('sm-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + + collapse = () => { + this.optionList.animate(this.slideUp, this.animationOptions) + this.optionList.classList.add('hide') + this.chevron.classList.remove('rotate') + this.open = false + } + connectedCallback() { + this.availableOptions + this.optionList = this.shadowRoot.querySelector('.options') + this.chevron = this.shadowRoot.querySelector('.toggle') + let slot = this.shadowRoot.querySelector('.options slot'), + selection = this.shadowRoot.querySelector('.selection'), + previousOption + this.open = false; + this.slideDown = [ + { transform: `translateY(-0.5rem)` }, + { transform: `translateY(0)` } + ], + this.slideUp = [ + { transform: `translateY(0)` }, + { transform: `translateY(-0.5rem)` } + ], + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: 'ease' + } + selection.addEventListener('click', e => { + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + selection.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') + if (!this.open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + this.open = true + } else { + this.collapse() + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.addEventListener('optionSelected', e => { + if (previousOption !== e.target) { + this.setAttribute('value', e.detail.value) + this.shadowRoot.querySelector('.option-text').textContent = e.detail.text; + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + if (previousOption) { + previousOption.classList.remove('check-selected') + } + previousOption = e.target; + } + if(!e.detail.switching) + this.collapse() + + e.target.classList.add('check-selected') + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + if (this.availableOptions[0]) { + let firstElement = this.availableOptions[0]; + previousOption = firstElement; + firstElement.classList.add('check-selected') + this.setAttribute('value', firstElement.getAttribute('value')) + this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent + this.availableOptions.forEach((element, index) => { + element.setAttribute('data-rank', index + 1); + element.setAttribute('tabindex', "0"); + }) + } + }); + document.addEventListener('mousedown', e => { + if (!this.contains(e.target) && this.open) { + this.collapse() + } + }) + } +}) + +// option +const smOption = document.createElement('template') +smOption.innerHTML = ` + +
+ + + + +
`; +customElements.define('sm-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smOption.content.cloneNode(true)) + } + + sendDetails = (switching) => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value'), + switching: switching + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + let validKey = [ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight' + ] + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + if (validKey.includes(e.code)) { + e.preventDefault() + this.sendDetails(true) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +// select +const smStripSelect = document.createElement('template') +smStripSelect.innerHTML = ` + +
+
+ + Previous + + +
+ +
+ + Next + + +
+
`; +customElements.define('sm-strip-select', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripSelect.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + scrollLeft = () => { + this.select.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.select.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + connectedCallback() { + let previousOption, + slot = this.shadowRoot.querySelector('slot'); + this.selectContainer = this.shadowRoot.querySelector('.select-container') + this.select = this.shadowRoot.querySelector('.select') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.selectOptions + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.previousArrow.classList.add('hide') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.remove('hide') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.nextArrow.classList.add('hide') + this.nextGradient.classList.add('hide') + } + else { + this.nextArrow.classList.remove('hide') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.selectContainer, + threshold: 0.95 + }) + + const selectObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.selectContainer.getBoundingClientRect().width + } + }) + + selectObserver.observe(this.selectContainer) + this.addEventListener('optionSelected', e => { + if (previousOption === e.target) return; + if (previousOption) + previousOption.classList.remove('active') + e.target.classList.add('active') + e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) + this.setAttribute('value', e.detail.value) + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + previousOption = e.target; + }) + slot.addEventListener('slotchange', e => { + this.selectOptions = slot.assignedElements() + firstElementObserver.observe(this.selectOptions[0]) + lastElementObserver.observe(this.selectOptions[this.selectOptions.length - 1]) + if (this.selectOptions[0]) { + let firstElement = this.selectOptions[0]; + this.setAttribute('value', firstElement.getAttribute('value')) + firstElement.classList.add('active') + previousOption = firstElement; + } + }); + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +// option +const smStripOption = document.createElement('template') +smStripOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-strip-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smStripOption.content.cloneNode(true)) + } + sendDetails = () => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + } + + connectedCallback() { + this.addEventListener('click', e => { + this.sendDetails() + }) + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.sendDetails(false) + } + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + this.sendDetails() + }, 0); + } + } +}) + +//popup +const smPopup = document.createElement('template') +smPopup.innerHTML = ` + + +`; +customElements.define('sm-popup', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) + } + + resumeScrolling = () => { + const scrollY = document.body.style.top; + window.scrollTo(0, parseInt(scrollY || '0') * -1); + setTimeout(() => { + document.body.setAttribute('style', `overflow: auto; top: initial`) + }, 300); + } + + show(pinned, popupStack) { + this.pinned = pinned + this.popupStack = popupStack + this.popupContainer.classList.remove('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(0)'; + else + this.popup.style.transform = 'scale(1)'; + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + } + hide() { + this.popupContainer.classList.add('hide') + if (window.innerWidth < 648) + this.popup.style.transform = 'translateY(100%)'; + else + this.popup.style.transform = 'scale(0.9)'; + if (typeof this.popupStack !== 'undefined') { + this.popupStack.pop() + if (this.popupStack.items.length === 0) { + this.resumeScrolling() + } + } + else { + this.resumeScrolling() + } + } + + handleTouchStart = (e) => { + this.touchStartY = e.changedTouches[0].clientY + this.popup.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartY < e.changedTouches[0].clientY) { + this.offset = e.changedTouches[0].clientY - this.touchStartY; + this.touchEndAnimataion = window.requestAnimationFrame(this.movePopup) + } + /*else { + offset = touchStartY - e.changedTouches[0].clientY; + this.popup.style.transform = `translateY(-${offset}px)` + }*/ + } + + handleTouchEnd = (e) => { + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.3s' + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndY - this.touchStartY > this.threshold) { + this.hide() + } + else { + this.show() + } + } + else { + if (this.touchEndY > this.touchStartY) + this.hide() + } + } + + movePopup = () => { + this.popup.style.transform = `translateY(${this.offset}px)` + } + + connectedCallback() { + this.pinned = false + this.popupStack + this.popupContainer = this.shadowRoot.querySelector('.popup-container') + this.popup = this.shadowRoot.querySelector('.popup') + this.offset + this.popupHeader = this.shadowRoot.querySelector('.popup-top') + this.touchStartY = 0 + this.touchEndY = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + this.touchEndAnimataion; + + if (this.hasAttribute('heading')) + this.shadowRoot.querySelector('.heading').textContent = this.getAttribute('heading') + + this.popupContainer.addEventListener('mousedown', e => { + if (e.target === this.popupContainer && !this.pinned) { + this.hide() + } + }) + + this.shadowRoot.querySelector('.close').addEventListener('click', e => { + this.hide() + }) + + this.popupHeader.addEventListener('touchstart', this.handleTouchStart) + this.popupHeader.addEventListener('touchmove', this.handleTouchMove) + this.popupHeader.addEventListener('touchend', this.handleTouchEnd) + } + disconnectedCallback() { + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd) + } +}) + +//carousel + +const smCarousel = document.createElement('template') +smCarousel.innerHTML = ` + + +`; + +customElements.define('sm-carousel', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smCarousel.content.cloneNode(true)) + } + + scrollLeft = () => { + this.carousel.scrollBy({ + top: 0, + left: -this.scrollDistance, + behavior: 'smooth' + }) + } + + scrollRight = () => { + this.carousel.scrollBy({ + top: 0, + left: this.scrollDistance, + behavior: 'smooth' + }) + } + + connectedCallback() { + this.carousel = this.shadowRoot.querySelector('.carousel') + this.carouselContainer = this.shadowRoot.querySelector('.carousel-container') + this.carouselSlot = this.shadowRoot.querySelector('slot') + this.nextArrow = this.shadowRoot.querySelector('.next-item') + this.previousArrow = this.shadowRoot.querySelector('.previous-item') + this.nextGradient = this.shadowRoot.querySelector('.right') + this.previousGradient = this.shadowRoot.querySelector('.left') + this.carouselItems + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width/3 + const firstElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.previousArrow.classList.remove('expand') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.add('expand') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.nextArrow.classList.remove('expand') + this.nextGradient.classList.add('hide') + } + else{ + this.nextArrow.classList.add('expand') + this.nextGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + + const carouselObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting) { + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 + } + }) + + carouselObserver.observe(this.carouselContainer) + + this.carouselSlot.addEventListener('slotchange', e => { + this.carouselItems = this.carouselSlot.assignedElements() + firstElementObserver.observe(this.carouselItems[0]) + lastElementObserver.observe(this.carouselItems[this.carouselItems.length - 1]) + }) + + this.addEventListener('keyup', e => { + if (e.code === 'ArrowLeft') + this.scrollRight() + else + this.scrollRight() + }) + + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + disconnectedCallback() { + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) + } +}) + +//notifications + +const smNotifications = document.createElement('template') +smNotifications.innerHTML = ` + +
+
+` + +customElements.define('sm-notifications', class extends HTMLElement{ + constructor() { + super() + this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) + } + + handleTouchStart = (e) => { + this.notification = e.target.closest('.notification') + this.touchStartX = e.changedTouches[0].clientX + this.notification.style.transition = 'initial' + this.touchStartTime = e.timeStamp + } + + handleTouchMove = (e) => { + if (this.touchStartX < e.changedTouches[0].clientX) { + this.offset = e.changedTouches[0].clientX - this.touchStartX; + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + else { + this.offset = -(this.touchStartX - e.changedTouches[0].clientX); + this.touchEndAnimataion = requestAnimationFrame(this.movePopup) + } + } + + handleTouchEnd = (e) => { + this.notification.style.transition = 'height 0.3s, transform 0.3s, opacity 0.3s' + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndX = e.changedTouches[0].clientX + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndX - this.touchStartX > this.threshold) { + this.removeNotification(this.notification) + } + else if (this.touchStartX - this.touchEndX > this.threshold) { + this.removeNotification(this.notification, true) + } + else { + this.resetPosition() + } + } + else { + if (this.touchEndX > this.touchStartX) { + this.removeNotification(this.notification) + } + else { + this.removeNotification(this.notification, true) + } + } + } + + movePopup = () => { + this.notification.style.transform = `translateX(${this.offset}px)` + } + + resetPosition = () => { + this.notification.style.transform = `translateX(0)` + } + + push = (messageHeader, messageBody, options) => { + let notification = document.createElement('div'), + composition = ``, + { pinned, type } = options; + notification.classList.add('notification') + if (pinned) + notification.classList.add('pinned') + composition += ` +
+
+ ` + if (type === 'error') { + composition += ` + + + + + ` + } + else if (type === 'success') { + composition += ` + + + + ` + } + composition += ` +

${messageHeader}

+ + Close + + + +
+

${messageBody}

+
` + notification.innerHTML = composition + this.notificationPanel.prepend(notification) + if (window.innerWidth > 640) { + notification.animate([ + { + transform: `translateX(1rem)`, + opacity: '0' + }, + { + transform: 'translateX(0)', + opacity: '1' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `transform: none;`); + } + } + else { + notification.setAttribute('style', `transform: translateY(0); opacity: 1`) + } + notification.addEventListener('touchstart', this.handleTouchStart) + notification.addEventListener('touchmove', this.handleTouchMove) + notification.addEventListener('touchend', this.handleTouchEnd) + } + + removeNotification = (notification, toLeft) => { + notification.style.height = notification.scrollHeight + 'px'; + if (!this.offset) + this.offset = 0; + + if (toLeft) + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(-100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + else { + notification.animate([ + { + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.setAttribute('style', `height: 0; margin-bottom: 0`); + } + } + setTimeout( () => { + notification.remove() + }, this.animationOptions.duration*2) + } + + connectedCallback() { + this.notificationPanel = this.shadowRoot.querySelector('.notification-panel') + this.animationOptions = { + duration: 300, + fill: "forwards", + easing: "ease" + } + this.fontSize = Number(window.getComputedStyle(document.body).getPropertyValue('font-size').match(/\d+/)[0]) + this.notification + this.offset + this.touchStartX = 0 + this.touchEndX = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.threshold = this.notificationPanel.getBoundingClientRect().width * 0.3 + this.touchEndAnimataion; + + this.notificationPanel.addEventListener('click', e => { + if (e.target.closest('.close'))( + this.removeNotification(e.target.closest('.notification')) + ) + }) + + const observer = new MutationObserver(mutationList => { + mutationList.forEach(mutation => { + if (mutation.type === 'childList') { + if (mutation.addedNodes.length) { + if (!mutation.addedNodes[0].classList.contains('pinned')) + setTimeout(() => { + this.removeNotification(mutation.addedNodes[0]) + }, 4000); + if (window.innerWidth > 640) + this.notificationPanel.style.padding = '1.5rem 0 3rem 1.5rem'; + else + this.notificationPanel.style.padding = '1rem 1rem 2rem 1rem'; + } + else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { + this.notificationPanel.style.padding = 0; + } + } + }) + }) + observer.observe(this.notificationPanel, { + attributes: true, + childList: true, + subtree: true + }) + } +}) +// sm-menu +const smMenu = document.createElement('template') +smMenu.innerHTML = ` + +
+ +
+ +
+
`; +customElements.define('sm-menu', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenu.content.cloneNode(true)) + } + static get observedAttributes() { + return ['value'] + } + get value() { + return this.getAttribute('value') + } + set value(val) { + this.setAttribute('value', val) + } + expand = () => { + if (!this.open) { + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + this.optionList.classList.remove('hide') + this.optionList.classList.add('no-transformations') + this.open = true + this.icon.classList.add('focused') + } + } + collapse = () => { + if (this.open) { + this.open = false + this.icon.classList.remove('focused') + this.optionList.classList.add('hide') + this.optionList.classList.remove('no-transformations') + } + } + connectedCallback() { + this.availableOptions + this.containerDimensions + this.optionList = this.shadowRoot.querySelector('.options') + let slot = this.shadowRoot.querySelector('.options slot'), + menu = this.shadowRoot.querySelector('.menu') + this.icon = this.shadowRoot.querySelector('.icon') + this.open = false; + menu.addEventListener('click', e => { + if (!this.open) { + this.expand() + } else { + this.collapse() + } + }) + menu.addEventListener('keydown', e => { + if (e.code === 'ArrowDown' || e.code === 'ArrowRight') { + e.preventDefault() + this.availableOptions[0].focus() + } + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + if (!this.open) { + this.expand() + } else { + this.collapse() + } + } + }) + this.optionList.addEventListener('keydown', e => { + if (e.code === 'ArrowUp' || e.code === 'ArrowRight') { + e.preventDefault() + if (document.activeElement.previousElementSibling) { + document.activeElement.previousElementSibling.focus() + } + } + if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') { + e.preventDefault() + if (document.activeElement.nextElementSibling) + document.activeElement.nextElementSibling.focus() + } + }) + this.optionList.addEventListener('click', e => { + this.collapse() + }) + slot.addEventListener('slotchange', e => { + this.availableOptions = slot.assignedElements() + this.containerDimensions = this.optionList.getBoundingClientRect() + this.menuDimensions = menu.getBoundingClientRect() + if (this.containerDimensions.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + }); + window.addEventListener('mousedown', e => { + if (!this.contains(e.target) && e.button !== 2) { + this.collapse() + } + }) + if (this.hasAttribute('set-context') && this.getAttribute('set-context') === 'true') { + this.parentNode.setAttribute('oncontextmenu', 'return false') + this.parentNode.addEventListener('mouseup', e => { + if (e.button === 2) { + this.expand() + } + }) + } + const intersectionObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting && this.open) { + if(window.innerHeight - entry.intersectionRect.top < this.containerDimensions.height) + this.optionList.classList.add('moveUp') + else + this.optionList.classList.remove('moveUp') + if (entry.intersectionRect.left > this.containerDimensions.width) { + this.optionList.style.right = 0 + } + else { + this.optionList.style.right = 'auto' + } + } + }) + }, { + threshold: 1 + }) + intersectionObserver.observe(this.optionList) + } +}) + +// option +const smMenuOption = document.createElement('template') +smMenuOption.innerHTML = ` + +
+ +
`; +customElements.define('sm-menu-option', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).append(smMenuOption.content.cloneNode(true)) + } + + connectedCallback() { + this.addEventListener('keyup', e => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + this.click() + } + }) + this.setAttribute('tabindex', '0') + } +}) \ No newline at end of file diff --git a/Layouts/tabs layout/js/helper.js b/Layouts/tabs layout/js/helper.js new file mode 100644 index 0000000..a7c51ee --- /dev/null +++ b/Layouts/tabs layout/js/helper.js @@ -0,0 +1,133 @@ +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) +}) +let themeToggler = document.getElementById("theme_toggle") +if (localStorage.theme === "dark") { + darkTheme() + themeToggler.checked = true; +} else { + lightTheme() + themeToggler.checked = false; +} + +function lightTheme() { + document.body.setAttribute("data-theme", "light"); +} + +function darkTheme() { + document.body.setAttribute("data-theme", "dark"); +} +themeToggler.addEventListener("change", () => { + if (themeToggler.checked) { + darkTheme() + localStorage.setItem("theme", "dark"); + } else { + lightTheme() + 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(index) { + let newIndex = index ? index : 1 + return this.items[this.items.length - index]; + } +} +let popupStack = new Stack(), + zIndex = 10; +function showPopup(popup, permission) { + let thisPopup = document.getElementById(popup); + document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + popupStack.push({ thisPopup, permission }) + thisPopup.show(permission, popupStack) + zIndex++; + thisPopup.setAttribute('style', `z-index: ${zIndex}`) + return thisPopup; +} +function setAttributes(el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } +} +// displays a popup for asking permission. Use this instead of JS confirm +let confirmation = 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); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(false); + } + }) +} + +// 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'); + if (defaultVal) + input.value = defaultVal; + showPopup('prompt') + input.focus() + input.addEventListener('keyup', e => { + if (e.key === 'Enter') { + resolve(input.value); + hidePopup() + } + }) + popup.querySelector('#prompt_message').textContent = message; + popup.querySelector('.submit-btn').onclick = () => { + hidePopup() + resolve(input.value); + } + popup.querySelector('.cancel-btn').onclick = () => { + hidePopup() + resolve(null); + } + }) +} + +function formatedTime(time) { + let timeFrag = new Date(parseInt(time)).toString().split(' '), + day = timeFrag[0], + month = timeFrag[1], + date = timeFrag[2], + year = timeFrag[3], + hours = timeFrag[4].slice(0, timeFrag[4].lastIndexOf(':')), + finalTime = ''; + parseInt(hours.split(':')[0]) > 12 ? finalTime = 'PM' : finalTime = 'AM' + return `${hours} ${finalTime} ${day} ${date} ${month} ${year}` +} + +function copyToClipboard(parent) { + let toast = document.getElementById('textCopied'), + textToCopy = parent.querySelector('.copy').textContent; + navigator.clipboard.writeText(textToCopy) + toast.classList.remove('hide'); + setTimeout(() => { + toast.classList.add('hide'); + }, 2000) +} \ No newline at end of file