diff --git a/Standard UI Components/README.md b/Standard UI Components/README.md new file mode 100644 index 0000000..c51a6e6 --- /dev/null +++ b/Standard UI Components/README.md @@ -0,0 +1,2 @@ +# compoents + This is a Native Web Component library diff --git a/Standard UI Components/components.js b/Standard UI Components/components.js new file mode 100644 index 0000000..07565e8 --- /dev/null +++ b/Standard UI Components/components.js @@ -0,0 +1,2585 @@ +//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 = (label, inputParent, clear, helperText) => { + 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 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', `width: ${tabDimensions.width}px; 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', `width: ${tabDimensions.width}px; 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.tabHeader) + if (this.hasAttribute('swipable') && this.getAttribute('swipable') == '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) + } + + 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 + } + else { + this.isChecked = false + this.input.checked = false + } + } + } + } + +}) + +//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) + } + + 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 + } + else { + this.isChecked = false + this.input.checked = false + } + } + } + } +}) + +// select +const smSelect = document.createElement('template') +smSelect.innerHTML = ` + +
+
+
select title
+
+ + + +
+
+ +
+
`; +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') + 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.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' + }, + open = false; + selection.addEventListener('click', e => { + if (!open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + 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 (!open) { + this.optionList.classList.remove('hide') + this.optionList.animate(this.slideDown, this.animationOptions) + this.chevron.classList.add('rotate') + 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) + setTimeout(() => { + this.collapse() + }, 200); + + 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.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.preventDefault() + this.sendDetails(false) + } + if (validKey.includes(e.key)) { + 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)) + } + connectedCallback() { + this.addEventListener('click', e => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + }) + if (this.hasAttribute('default')) { + setTimeout(() => { + let optionSelected = new CustomEvent('optionSelected', { + bubbles: true, + composed: true, + detail: { + text: this.textContent, + value: this.getAttribute('value') + } + }) + this.dispatchEvent(optionSelected) + }, 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)) + } + + show() { + 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)'; + const scrollY = document.body.style.top; + document.body.setAttribute('style', `overflow: auto; top: initial`) + window.scrollTo(0, parseInt(scrollY || '0') * -1); + } + + 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.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.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.add('hide', 'shrink') + this.previousGradient.classList.add('hide') + } + else { + this.previousArrow.classList.remove('hide', 'shrink') + this.previousGradient.classList.remove('hide') + } + }, { + root: this.carouselContainer, + threshold: 0.9 + }) + const lastElementObserver = new IntersectionObserver(entries => { + if (entries[0].isIntersecting){ + this.nextArrow.classList.add('hide', 'shrink') + this.nextGradient.classList.add('hide') + } + else{ + this.nextArrow.classList.remove('hide', 'shrink') + 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, type ,pinned) => { + let notification = document.createElement('div'), + composition = ``; + 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 + }) + } +}) \ No newline at end of file diff --git a/Standard UI Components/css/main.css b/Standard UI Components/css/main.css new file mode 100644 index 0000000..5b8a4f5 --- /dev/null +++ b/Standard UI Components/css/main.css @@ -0,0 +1,272 @@ +@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"); +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} + +html { + scroll-behavior: smooth; +} + +body { + --accent-color: rgb(9, 155, 82); + --light-accent-shade: rgb(223, 255, 239); + --text: 17, 17, 17; + --foreground: 255, 255, 255; + --font-family: 'Roboto', sans-serif; + --error-color: red; +} + +.hide-completely { + display: none; +} + +h1, h2, h3, h4.h5 { + font-family: 'Poppins', sans-serif; + font-weight: 500; +} + +a:-webkit-any-link { + text-decoration: none; + color: var(--accent-color); +} + +a:-moz-any-link { + text-decoration: none; + color: var(--accent-color); +} + +a:any-link { + text-decoration: none; + color: var(--accent-color); +} + +h2 { + margin: 3rem 0 1rem 0; + text-transform: capitalize; +} + +h2:first-of-type { + margin-top: 1rem; +} + +h1.headline { + color: rgba(var(--text), 1); +} + +section { + height: 100vh; + 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-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background-size: cover; + text-align: center; + color: rgba(var(--text), 1); + padding: 2rem; + border-bottom: 1px solid rgba(var(--text), 0.2); +} + +section h1 { + font-weight: 600; + font-size: 2.5rem; + line-height: 1.2em; + z-index: 1; + text-transform: uppercase; + letter-spacing: 0.2rem; +} + +section h4 { + z-index: 1; + font-weight: 400; + margin-top: 1.2rem; +} + +section .background-box { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-item-align: center; + align-self: center; + position: absolute; + border: solid 1px rgba(var(--text), 0.3); + width: 60vw; + height: 40%; + -webkit-transform: skewX(-10deg); + transform: skewX(-10deg); + background: rgba(var(--foreground), 1); + z-index: -1; +} + +section a { + margin-top: 2rem; + font-weight: 500; +} + +section sm-button { + margin-top: 4rem; +} + +section sm-button:first-of-type { + margin-right: 1rem; +} + +pre { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + max-width: 100%; +} + +code { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + color: #311B92; + background: rgba(var(--text), 0.1); + padding: 0.2rem 0.4rem; + border-radius: 0.2rem; + margin: 0.2rem 0; +} + +code.extend { + padding: 0 1.5rem; + line-height: 1.6; + margin: 1rem 0; + overflow-x: auto; + max-width: 100%; +} + +main { + height: 100vh; +} + +p { + margin: 1rem 0 1.5rem 0; + line-height: 1.6em; + color: rgba(var(--text), 0.8); + font-size: 1rem; +} + +p::first-letter { + text-transform: capitalize; +} + +ol { + padding: 0.6rem 1rem; +} + +ol li { + margin-bottom: 1rem; +} + +ol li:last-of-type { + margin-bottom: 0; +} + +ol li::first-letter { + text-transform: capitalize; +} + +.left { + max-height: 100%; + overflow-y: auto; +} + +.left h3 { + padding: 1.5rem; +} + +.right { + padding: 1.5rem; +} + +.right h1 { + margin-bottom: 1.5rem; +} + +.list { + list-style: none; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + margin-bottom: 0.5rem; +} + +.list .item { + padding: 0.6rem 1.5rem; + text-transform: capitalize; +} + +.list .active { + color: var(--accent-color); + background: rgba(var(--text), 0.06); +} + +.card { + height: 24rem; + margin-right: 1rem; + border-radius: 0.4rem; + width: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +sm-carousel { + margin-bottom: 1rem; +} + +@media screen and (min-width: 640px) { + main { + display: -ms-grid; + display: grid; + -ms-grid-columns: 14rem minmax(0, 1fr); + grid-template-columns: 14rem minmax(0, 1fr); + gap: 1.5rem; + } + p { + max-width: 46vw; + } + .background-box { + width: 24rem !important; + height: 50% !important; + } +} + +@media screen and (max-width: 640px) { + .left { + position: fixed; + z-index: 2; + background: rgba(var(--foreground), 1); + -webkit-box-shadow: 0.5rem 0 2rem rgba(0, 0, 0, 0.1); + box-shadow: 0.5rem 0 2rem rgba(0, 0, 0, 0.1); + height: 100vh; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +@media (any-hover: hover) { + .item:hover { + background: rgba(var(--text), 0.1); + cursor: pointer; + } +} +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/Standard UI Components/css/main.css.map b/Standard UI Components/css/main.css.map new file mode 100644 index 0000000..bc096ce --- /dev/null +++ b/Standard UI Components/css/main.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "AAAA,OAAO,CAAC,wFAAI;AACZ,OAAO,CAAC,yFAAI;AACZ,AAAA,CAAC,CAAA;EACG,UAAU,EAAE,UAAU;EACtB,OAAO,EAAE,CAAC;EACV,MAAM,EAAE,CAAC;EACT,WAAW,EAAE,oBAAoB;CACpC;;AACD,AAAA,IAAI,CAAA;EACA,eAAe,EAAE,MAAM;CAC1B;;AACD,AAAA,IAAI,CAAA;EACA,cAAc,CAAA,gBAAC;EACf,oBAAoB,CAAA,mBAAC;EACrB,MAAM,CAAA,WAAC;EACP,YAAY,CAAA,cAAC;EACb,aAAa,CAAA,qBAAC;EACd,aAAa,CAAA,IAAC;CACjB;;AACD,AAAA,gBAAgB,CAAA;EACZ,OAAO,EAAE,IAAI;CAChB;;AACD,AAAA,EAAE,EAAC,EAAE,EAAC,EAAE,EAAC,EAAE,AAAA,GAAG,CAAA;EACV,WAAW,EAAE,qBAAqB;EAClC,WAAW,EAAE,GAAG;CACnB;;AACD,AAAA,CAAC,AAAA,SAAS,CAAA;EACN,eAAe,EAAE,IAAI;EACrB,KAAK,EAAE,mBAAmB;CAC7B;;AACD,AAAA,EAAE,CAAA;EACA,MAAM,EAAE,aAAa;EACrB,cAAc,EAAE,UAAU;CAI3B;;AAND,AAGE,EAHA,AAGC,cAAc,CAAA;EACX,UAAU,EAAE,IAAI;CACnB;;AAEH,AAAA,EAAE,AAAA,SAAS,CAAA;EACP,KAAK,EAAE,oBAAoB;CAC9B;;AACD,AAAA,OAAO,CAAA;EACH,MAAM,EAAE,KAAK;EACb,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,MAAM;EACtB,eAAe,EAAE,MAAM;EACvB,WAAW,EAAE,MAAM;EACnB,eAAe,EAAE,KAAK;EACtB,UAAU,EAAE,MAAM;EAClB,KAAK,EAAE,oBAAoB;EAC3B,OAAO,EAAE,IAAI;EACb,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,sBAAqB;CAmCjD;;AA7CD,AAWI,OAXG,CAWH,EAAE,CAAA;EACE,WAAW,EAAE,GAAG;EAChB,SAAS,EAAE,MAAM;EACjB,WAAW,EAAE,KAAK;EAClB,OAAO,EAAE,CAAC;EACV,cAAc,EAAE,SAAS;EACzB,cAAc,EAAE,MAAM;CACzB;;AAlBL,AAmBI,OAnBG,CAmBH,EAAE,CAAA;EACE,OAAO,EAAE,CAAC;EACV,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;CACrB;;AAvBL,AAwBI,OAxBG,CAwBH,eAAe,CAAA;EACX,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,MAAM;EAClB,QAAQ,EAAE,QAAQ;EAClB,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,sBAAsB;EACxC,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,GAAG;EACX,SAAS,EAAE,aAAa;EACxB,UAAU,EAAE,0BAA0B;EACtC,OAAO,EAAE,EAAE;CACd;;AAlCL,AAmCI,OAnCG,CAmCH,CAAC,CAAA;EACG,UAAU,EAAE,IAAI;EAChB,WAAW,EAAE,GAAG;CACnB;;AAtCL,AAuCI,OAvCG,CAuCH,SAAS,CAAA;EACL,UAAU,EAAE,IAAI;CACnB;;AAzCL,AA0CI,OA1CG,CA0CH,SAAS,AAAA,cAAc,CAAA;EACnB,YAAY,EAAE,IAAI;CACrB;;AAEL,AAAA,GAAG,CAAA;EACC,OAAO,EAAE,WAAW;EAChB,SAAS,EAAE,IAAI;CACtB;;AACD,AAAA,IAAI,CAAA;EACA,OAAO,EAAE,WAAW;EACpB,KAAK,EAAE,OAAO;EACd,UAAU,EAAE,sBAAqB;EACjC,OAAO,EAAE,aAAa;EACtB,aAAa,EAAE,MAAM;EACrB,MAAM,EAAE,QAAQ;CACnB;;AACD,AAAA,IAAI,AAAA,OAAO,CAAA;EACP,OAAO,EAAE,QAAQ;EACjB,WAAW,EAAE,GAAG;EAChB,MAAM,EAAE,MAAM;EACd,UAAU,EAAE,IAAI;EAChB,SAAS,EAAE,IAAI;CAClB;;AACD,AAAA,IAAI,CAAA;EACA,MAAM,EAAE,KAAK;CAChB;;AACD,AAAA,CAAC,CAAA;EACG,MAAM,EAAE,eAAe;EACvB,WAAW,EAAE,KAAK;EAClB,KAAK,EAAE,sBAAqB;EAC5B,SAAS,EAAE,IAAI;CAIlB;;AARD,AAKI,CALH,AAKI,cAAc,CAAA;EACX,cAAc,EAAE,UAAU;CAC7B;;AAEL,AAAA,EAAE,CAAA;EACE,OAAO,EAAE,WAAW;CAUvB;;AAXD,AAEI,EAFF,CAEE,EAAE,CAAA;EACE,aAAa,EAAE,IAAI;CAOtB;;AAVL,AAIQ,EAJN,CAEE,EAAE,AAEG,aAAa,CAAA;EACV,aAAa,EAAE,CAAC;CACnB;;AANT,AAOQ,EAPN,CAEE,EAAE,AAKG,cAAc,CAAA;EACX,cAAc,EAAE,UAAU;CAC7B;;AAGT,AAAA,KAAK,CAAA;EACD,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,IAAI;CAInB;;AAND,AAGI,KAHC,CAGD,EAAE,CAAA;EACE,OAAO,EAAE,MAAM;CAClB;;AAEL,AAAA,MAAM,CAAA;EACF,OAAO,EAAE,MAAM;CAIlB;;AALD,AAEI,MAFE,CAEF,EAAE,CAAA;EACE,aAAa,EAAE,MAAM;CACxB;;AAEL,AAAA,KAAK,CAAA;EACD,UAAU,EAAE,IAAI;EAChB,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,MAAM;EACtB,aAAa,EAAE,MAAM;CASxB;;AAbD,AAKI,KALC,CAKD,KAAK,CAAA;EACD,OAAO,EAAE,aAAa;EACtB,cAAc,EAAE,UAAU;CAC7B;;AARL,AASI,KATC,CASD,OAAO,CAAA;EACH,KAAK,EAAE,mBAAmB;EAC1B,UAAU,EAAE,uBAAsB;CACrC;;AAEL,AAAA,KAAK,CAAA;EACD,MAAM,EAAE,KAAK;EACb,YAAY,EAAE,IAAI;EAClB,aAAa,EAAE,MAAM;EACrB,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,KAAK;CACpB;;AACD,AAAA,WAAW,CAAA;EACP,aAAa,EAAE,IAAI;CACtB;;AACD,MAAM,CAAC,MAAM,MAAM,SAAS,EAAE,KAAK;EAC/B,AAAA,IAAI,CAAA;IACA,OAAO,EAAE,IAAI;IACb,qBAAqB,EAAE,KAAK,CAAC,cAAc;IAC3C,GAAG,EAAE,MAAM;GACd;EACD,AAAA,CAAC,CAAA;IACG,SAAS,EAAE,IAAI;GAClB;EACD,AAAA,eAAe,CAAA;IACX,KAAK,EAAE,gBAAgB;IACvB,MAAM,EAAE,cAAc;GACzB;;;AAEL,MAAM,CAAC,MAAM,MAAM,SAAS,EAAE,KAAK;EAC/B,AAAA,KAAK,CAAA;IACD,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,0BAA0B;IACtC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAgB;IAC1C,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,iBAAiB;GAC/B;;;AAEL,MAAM,EAAE,SAAS,EAAE,KAAK;EACpB,AAAA,KAAK,AAAA,MAAM,CAAA;IACP,UAAU,EAAE,sBAAqB;IACjC,MAAM,EAAE,OAAO;GAClB", + "sources": [ + "main.scss" + ], + "names": [], + "file": "main.css" +} \ No newline at end of file diff --git a/Standard UI Components/css/main.scss b/Standard UI Components/css/main.scss new file mode 100644 index 0000000..8074d48 --- /dev/null +++ b/Standard UI Components/css/main.scss @@ -0,0 +1,196 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); +*{ + box-sizing: border-box; + padding: 0; + margin: 0; + font-family: 'Roboto', sans-serif; +} +html{ + scroll-behavior: smooth; +} +body{ + --accent-color: rgb(9, 155, 82); + --light-accent-shade: rgb(223, 255, 239); + --text: 17, 17, 17; + --foreground: 255, 255, 255; + --font-family: 'Roboto', sans-serif; + --error-color: red; +} +.hide-completely{ + display: none; +} +h1,h2,h3,h4.h5{ + font-family: 'Poppins', sans-serif; + font-weight: 500; +} +a:any-link{ + text-decoration: none; + color: var(--accent-color) +} +h2{ + margin: 3rem 0 1rem 0; + text-transform: capitalize; + &:first-of-type{ + margin-top: 1rem; + } +} +h1.headline{ + color: rgba(var(--text), 1); +} +section{ + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-size: cover; + text-align: center; + color: rgba(var(--text), 1); + padding: 2rem; + border-bottom: 1px solid rgba(var(--text), .2); + h1{ + font-weight: 600; + font-size: 2.5rem; + line-height: 1.2em; + z-index: 1; + text-transform: uppercase; + letter-spacing: 0.2rem; + } + h4{ + z-index: 1; + font-weight: 400; + margin-top: 1.2rem; + } + .background-box{ + display: flex; + align-self: center; + position: absolute; + border: solid 1px rgba(var(--text), 0.3); + width: 60vw; + height: 40%; + transform: skewX(-10deg); + background: rgba(var(--foreground), 1); + z-index: -1; + } + a{ + margin-top: 2rem; + font-weight: 500; + } + sm-button{ + margin-top: 4rem; + } + sm-button:first-of-type{ + margin-right: 1rem; + } +} +pre{ + display: inline-flex; + max-width: 100%; +} +code{ + display: inline-flex; + color: #311B92; + background: rgba(var(--text), .1); + padding: 0.2rem 0.4rem; + border-radius: 0.2rem; + margin: 0.2rem 0; +} +code.extend{ + padding: 0 1.5rem; + line-height: 1.6; + margin: 1rem 0; + overflow-x: auto; + max-width: 100%; +} +main{ + height: 100vh; +} +p{ + margin: 1rem 0 1.5rem 0; + line-height: 1.6em; + color: rgba(var(--text), .8); + font-size: 1rem; + &::first-letter{ + text-transform: capitalize; + } +} +ol{ + padding: 0.6rem 1rem; + li{ + margin-bottom: 1rem; + &:last-of-type{ + margin-bottom: 0; + } + &::first-letter{ + text-transform: capitalize; + } + } +} +.left{ + max-height: 100%; + overflow-y: auto; + h3{ + padding: 1.5rem; + } +} +.right{ + padding: 1.5rem; + h1{ + margin-bottom: 1.5rem; + } +} +.list{ + list-style: none; + display: flex; + flex-direction: column; + margin-bottom: 0.5rem; + .item{ + padding: 0.6rem 1.5rem; + text-transform: capitalize; + } + .active{ + color: var(--accent-color); + background: rgba(var(--text), .06); + } +} +.card{ + height: 24rem; + margin-right: 1rem; + border-radius: 0.4rem; + width: 100%; + object-fit: cover; +} +sm-carousel{ + margin-bottom: 1rem; +} +@media screen and (min-width: 640px){ + main{ + display: grid; + grid-template-columns: 14rem minmax(0, 1fr); + gap: 1.5rem; + } + p{ + max-width: 46vw; + } + .background-box{ + width: 24rem !important; + height: 50% !important; + } +} +@media screen and (max-width: 640px){ + .left{ + position: fixed; + z-index: 2; + background: rgba(var(--foreground), 1); + box-shadow: 0.5rem 0 2rem rgba(0,0,0, 0.1); + height: 100vh; + transform: translateX(-100%); + } +} +@media (any-hover: hover){ + .item:hover{ + background: rgba(var(--text), .1); + cursor: pointer; + } +} \ No newline at end of file diff --git a/Standard UI Components/index.html b/Standard UI Components/index.html new file mode 100644 index 0000000..bd20e77 --- /dev/null +++ b/Standard UI Components/index.html @@ -0,0 +1,260 @@ + + + + + + + SM Components + + + + + +
+
+

SM Web Components

+

UI components based on custom elements API.

+
+ Get started +
+
+
+ +
+
+

Overview

+

+ These components are based on HTML5 custom elements API.
It uses 'sm' namespace for all components. + So every component tag starts with 'sm-'. +

+

+ They can replace some older UI elements like <select> <input> <checkbox> <button> + But also more modern additions like popups(modals), tabs and many more. +

+

+ Some of the components have some cool tricks under their sleeves like custom events and variantions. + They allow developers to access more information about component or simply react to whatever state change happen to them. + We will go more in-depth about all the variantions and customs events related to each event as we go further. +

+
+ +
+

Quick Start

+

+ To start using SM Components +

+
    +
  1. Download components.js from Github.
  2. +
  3. Add file to bottom of your HTML file just before closing </body> tag with <script>tag.
  4. +
+

Now you are ready to use them in your HTML markup as any other standard HMTL tags 😄.

+
+ +
+

Buttons

+

+ Buttons are used in various basic UI interactions to perform an action. +

+ primary + default + outlined + no-outline + disabled +

+<sm-button variant="primary">primary</sm-button>
+<sm-button>default</sm-button>
+<sm-button variant="outlined">outlined</sm-button>
+<sm-button variant="no-outline">no-outline</sm-button>
+<sm-button variant="primary" disabled="true">disabled</sm-button>
+                
+
+ + + +
+

Checkbox

+

+ To start using SM Components +

+ +
+ +
+

Input

+

+ To start using SM Components +

+ +
+ +
+

Notifications

+

+ To start using SM Components +

+

Example

+ + + push success notification + + push error notification + + push notification +
+ + + +
+

Switch

+

+ To start using SM Components +

+ + + + +
+ +
+

Select

+

+ <sm-select> is very similar to starndatd HTML5 select and it's markup stucture is also identical.

+ + option1 + option2 something + option3 + +
+ +
+

Strip-select

+

+ To start using SM Components +

+ + option1 + option1 + option1 + option1 + option1 + option1 + option1 + +
+ +
+

Tabs

+

+ To start using SM Components +

+ + inbox + + gjdhnsrfijbgn
bdfjnbj +
+ sent + + jadifjoaijdiajdo + dosfighjoi
+ flkmgklfmzkl
+ hbdsfhb +
+ draft + + Lorem ipsum dolor, sit amet consectetur adipisicing elit. Facere neque incidunt aut laudantium, quam + id, + molestiae vero blanditiis nisi alias in magnam autem quasi cumque eveniet qui cupiditate nam + corrupti? + + spam + + Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis fuga ipsam, explicabo, eius + accusamus + consectetur ex sunt soluta voluptatem iure totam nulla expedita suscipit minus molestiae similique + odio optio + quibusdam. + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/Standard UI Components/resources/artwork1.svg b/Standard UI Components/resources/artwork1.svg new file mode 100644 index 0000000..79db554 --- /dev/null +++ b/Standard UI Components/resources/artwork1.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/Standard UI Components/resources/artwork2.svg b/Standard UI Components/resources/artwork2.svg new file mode 100644 index 0000000..39743b3 --- /dev/null +++ b/Standard UI Components/resources/artwork2.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/Standard UI Components/resources/background.jpg b/Standard UI Components/resources/background.jpg new file mode 100644 index 0000000..b40bd99 Binary files /dev/null and b/Standard UI Components/resources/background.jpg differ diff --git a/Standard UI Components/resources/background2.jpg b/Standard UI Components/resources/background2.jpg new file mode 100644 index 0000000..547690d Binary files /dev/null and b/Standard UI Components/resources/background2.jpg differ diff --git a/Standard UI Components/resources/background3.jpg b/Standard UI Components/resources/background3.jpg new file mode 100644 index 0000000..6117f07 Binary files /dev/null and b/Standard UI Components/resources/background3.jpg differ diff --git a/Standard UI Components/resources/background4.jpg b/Standard UI Components/resources/background4.jpg new file mode 100644 index 0000000..c487619 Binary files /dev/null and b/Standard UI Components/resources/background4.jpg differ diff --git a/Standard UI Components/resources/background5.jpg b/Standard UI Components/resources/background5.jpg new file mode 100644 index 0000000..6c3ab74 Binary files /dev/null and b/Standard UI Components/resources/background5.jpg differ diff --git a/Standard UI Components/resources/background6.jpg b/Standard UI Components/resources/background6.jpg new file mode 100644 index 0000000..11cad7c Binary files /dev/null and b/Standard UI Components/resources/background6.jpg differ