From 25c4f7ab647a08d62bd54cb27f9609fdaf32fe20 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Sun, 31 May 2020 18:29:30 +0530 Subject: [PATCH 01/41] bug fix fixed issue of input box retaining data after refresh in Firefox --- FLO_webWallet_mainnet.html | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/FLO_webWallet_mainnet.html b/FLO_webWallet_mainnet.html index e2fe273..0a2872d 100644 --- a/FLO_webWallet_mainnet.html +++ b/FLO_webWallet_mainnet.html @@ -8,11 +8,7 @@ - - - - - - - -

FLOTestnet Web Wallet

- - -
- -
- -
-
- -
-
-
- - - - - - - -
FLO AddressPrivate Key
-
-
- -
-
-
-
- -
-
-
- - -
-
-
-
-
- - - - -
-
-
- -
-
- - -
-
-
- - - - - - - - - - - - - - - - - + + + +FLO webWallet Testnet + + + + + + + + + + +

FLOTestnet Web Wallet

+ + +
+ +
+ +
+
+ +
+
+
+ + + + + + + +
FLO AddressPrivate Key
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ + + + +
+
+
+ +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + From ac5d4805e5f208b35ba1c02da762b4d4a1e65a1f Mon Sep 17 00:00:00 2001 From: Vivek Teega Date: Thu, 10 Sep 2020 13:13:56 +0530 Subject: [PATCH 04/41] Settting flosight as the reliable server for webwallet, for now. todo : undo this commit later on --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 97a1234..3c471b6 100644 --- a/index.html +++ b/index.html @@ -1723,7 +1723,7 @@ const floGlobals = { blockchain: "FLO", apiURL: { - FLO: ['https://explorer.mediciland.com/', 'https://livenet.flocha.in/', 'https://flosight.duckdns.org/','http://livenet-explorer.floexperiments.com/'], + FLO: ['https://flosight.duckdns.org/'], FLO_TEST: ['https://testnet-flosight.duckdns.org/', 'https://testnet.flocha.in/'] }, fee: 0.0005 From 3439ea5f4a6c656120ea75eefdd8693aacf12ac0 Mon Sep 17 00:00:00 2001 From: Sai Raj <39055732+sairajzero@users.noreply.github.com> Date: Fri, 11 Sep 2020 17:21:27 +0530 Subject: [PATCH 05/41] bug fix: floData length issue Fixed: floData with 16 char didnot sent Adjusted max floData size to 1040 --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 3c471b6..4a4e6ae 100644 --- a/index.html +++ b/index.html @@ -6363,12 +6363,12 @@ floDataCount = this.floData.length; //flochange -- creating unique data character count logic for floData. This string is prefixed before actual floData string in Raw Transaction - if (floDataCount <= 16) { + if (floDataCount < 16) { floDataCountString = floDataCount.toString(16); floDataCountString = "0"+ floDataCountString; } else if (floDataCount < 253) { floDataCountString = floDataCount.toString(16); - } else if (floDataCount <= 1023) { + } else if (floDataCount <= 1040) { floDataCountAdjusted = (floDataCount - 253) + parseInt("0xfd00fd"); floDataCountStringAdjusted = floDataCountAdjusted.toString(16); floDataCountString = floDataCountStringAdjusted.substr(0,2)+ floDataCountStringAdjusted.substr(4,2)+ floDataCountStringAdjusted.substr(2,2); @@ -8950,4 +8950,4 @@ setElements(); } - \ No newline at end of file + From 7e470d520fedb398ea4eafc4857731b67ae06f9b Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Fri, 11 Sep 2020 17:48:08 +0530 Subject: [PATCH 06/41] Added FLO data character counter --- index.html | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 4a4e6ae..4226bcb 100644 --- a/index.html +++ b/index.html @@ -193,6 +193,20 @@ transition: border 0.2s ease; } + textarea{ + margin-bottom: 2rem; + } + + #show_character_count{ + position: absolute; + bottom: 0; + right: 0; + font-size: 0.8rem; + margin: 0.4rem 0.8rem; + } + + + #overlay { opacity: 0; pointer-events: none; @@ -1969,10 +1983,11 @@
- +
1040/1040
@@ -2780,6 +2795,15 @@ function backToTop() { document.getElementById('transactions-container').scrollTop = 0; } + + let showCharacterCount = document.getElementById('show_character_count') + document.getElementById('flotextdata').addEventListener('input', function(){ + if(1040 - this.value.length){ + showCharacterCount.textContent = `${1040 - this.value.length}/1040` + } + else + showCharacterCount.textContent = `You can only add FLO data upto 1040 characters.` + }) diff --git a/index.html b/index.html index 4226bcb..6119427 100644 --- a/index.html +++ b/index.html @@ -165,7 +165,7 @@ color: var(--accent-color); } - .text-area textarea{ + .text-area textarea { resize: none; } @@ -193,11 +193,11 @@ transition: border 0.2s ease; } - textarea{ + textarea { margin-bottom: 2rem; } - #show_character_count{ + #show_character_count { position: absolute; bottom: 0; right: 0; @@ -1799,7 +1799,8 @@ -
+
@@ -1816,14 +1817,14 @@ - +
Send
Send FLO data
-
+
+ id="floAddr" />
- +
@@ -1894,7 +1896,7 @@

- @@ -1963,22 +1965,22 @@
To send FLO data, make sure you have enough balance.
- +
-
-
@@ -2006,7 +2008,7 @@
-
@@ -2042,7 +2044,7 @@ Dark mode active : 6pm - 6am @@ -2052,7 +2054,7 @@ Dark mode @@ -2109,9 +2111,8 @@

Recover FLO address

Please enter your private key.
- +
@@ -2163,13 +2164,13 @@ function daylight() { html.setAttribute("style", `--bw: #000;--back-color: #EBEBEB;--body-color: #fff;--sidenav-color:#f0f0f0;--sec-color: #ccc;--opac-accent-color: rgb(210, 13, 29, 0.1);` - ); + ); } function nightlight() { html.setAttribute("style", `--bw: #fff;--back-color: #222;--body-color: #2a2a2a;--sidenav-color:#313131;--sec-color: #555;--opac-accent-color: rgb(210, 13, 29, 0.2);` - ); + ); } function autoMode() { @@ -2446,7 +2447,7 @@ floatPart = parts[1], intPart = parts[0]; intPartText.textContent = intPart; - if(floatPart) + if (floatPart) floatPartText.textContent = `.${floatPart}`; document.getElementById('sdright').setAttribute('style', 'pointer-events: all; opacity: 1') loading.classList.remove('show', 'spin'); @@ -2561,50 +2562,50 @@ floWebWallet.readTransactions(address).then((receivedData) => { if (receivedData === undefined) tCont.innerHTML = - `

Data is not synced yet, Try again after sometime.

`; + `

Data is not synced yet, Try again after sometime.

`; else - if (receivedData.length === 0) { - tCont.innerHTML = - `

There are no transactions to show.

`; - } else { - receivedData.reverse(); - document.querySelector('loading').children[0].classList.remove('spin'); - tCont.innerHTML = ''; - if (receivedData.length < 10) { - limit = receivedData.length; - tCont.appendChild(createTransactionCard(address, receivedData)) + if (receivedData.length === 0) { + tCont.innerHTML = + `

There are no transactions to show.

`; } else { - let timeout; - scrollingEvent = tCont.addEventListener('scroll', (event) => { - clearTimeout(timeout) - timeout = setTimeout(() => { - if (tCont.scrollTop > window.innerHeight) - document.getElementById('scrollToTop').classList.add( - 'show'); - else - document.getElementById('scrollToTop').classList.remove( - 'show'); - }, 200); - }) - tCont.appendChild(createTransactionCard(address, receivedData)) - const load = target => { - const sm = new IntersectionObserver((entries, observer) => { - entries.forEach((entry) => { - if (entry.isIntersecting && entry.target == tCont - .lastElementChild && receivedData.length >= limit) { - tCont.appendChild(createTransactionCard(address, - receivedData)) - document.querySelectorAll('transaction-card') - .forEach(load); - observer.disconnect(); - } - }) + receivedData.reverse(); + document.querySelector('loading').children[0].classList.remove('spin'); + tCont.innerHTML = ''; + if (receivedData.length < 10) { + limit = receivedData.length; + tCont.appendChild(createTransactionCard(address, receivedData)) + } else { + let timeout; + scrollingEvent = tCont.addEventListener('scroll', (event) => { + clearTimeout(timeout) + timeout = setTimeout(() => { + if (tCont.scrollTop > window.innerHeight) + document.getElementById('scrollToTop').classList.add( + 'show'); + else + document.getElementById('scrollToTop').classList.remove( + 'show'); + }, 200); }) - sm.observe(target) + tCont.appendChild(createTransactionCard(address, receivedData)) + const load = target => { + const sm = new IntersectionObserver((entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting && entry.target == tCont + .lastElementChild && receivedData.length >= limit) { + tCont.appendChild(createTransactionCard(address, + receivedData)) + document.querySelectorAll('transaction-card') + .forEach(load); + observer.disconnect(); + } + }) + }) + sm.observe(target) + } + document.querySelectorAll('transaction-card').forEach(load); } - document.querySelectorAll('transaction-card').forEach(load); } - } }).catch((error) => { console.log(error); alert(error); @@ -2719,7 +2720,7 @@ floWebWallet.getLabels().then((labelAddr) => { if (Object.entries(labelAddr).length === 0) document.getElementById('monitor-list').innerHTML = - `

Add a FLO address to start monitoring

`; + `

Add a FLO address to start monitoring

`; else Object.keys(labelAddr).forEach((address) => { createAddressCards(address, labelAddr[address]); @@ -2796,4682 +2797,4682 @@ document.getElementById('transactions-container').scrollTop = 0; } - let showCharacterCount = document.getElementById('show_character_count') - document.getElementById('flotextdata').addEventListener('input', function(){ - if(1040 - this.value.length){ - showCharacterCount.textContent = `${1040 - this.value.length}/1040` + let showCharacterCount = document.getElementById('show_character_count') + document.getElementById('flotextdata').addEventListener('input', function () { + if (1040 - this.value.length) { + showCharacterCount.textContent = `${1040 - this.value.length}/1040` } else - showCharacterCount.textContent = `You can only add FLO data upto 1040 characters.` + showCharacterCount.textContent = `You can only add FLO data upto 1040 characters.` }) - + - - + - - + + - + + \ No newline at end of file diff --git a/testnet.html b/testnet.html index dea84b8..778056f 100644 --- a/testnet.html +++ b/testnet.html @@ -1,817 +1,900 @@ + -FLO webWallet Testnet - + FLO webWallet Testnet + - - - + + - + /* for custom scrollbar for webkit browser*/ + ::-webkit-scrollbar { + width: 6px; + } + + ::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + } + + ::-webkit-scrollbar-thumb { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + } + + + - + window["loadwait"] = false; + window["refreshwait"] = false; + const fee = 0.0005; + -

FLOTestnet Web Wallet

- +

FLOTestnet Web Wallet

-
- -
-
-
- -
-
-
- - - - - - - -
FLO AddressPrivate Key
-
-
- -
-
-
-
+
+ +
-
-
-
- +
+
-
-
+
+
+
+ + + + + + + +
FLO AddressPrivate Key
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ + + +
-
- - -
+ +
+
+
-
-
+
+ -
-
- - -
-
-
+ - - - - - - - - var change = utxoAmt-sendAmt-fee; - if(change>0) - trx.addoutput(sender, change); - console.log(sender+":"+ change); + + function validateAddr(inputtxt) { + try { + var addr = new Bitcoin.Address(inputtxt); + return true; + + } catch { + return false; + } + } + + function verifyWIF(wif, addr) { + try { + var key = new Bitcoin.ECKey(wif); + if (key.priv == null) { + return false; + } + key.setCompressed(true); + var bitcoinAddress = key.getBitcoinAddress(); + if (addr == bitcoinAddress) + return true; + else + return false; + } + catch (e) { + // browser does not have sufficient JavaScript support to generate a bitcoin address + alert(e); + console.log("error"); + } + } + + + + + + - - - - + + }); + break; + } + table.deleteRow(0); + }; + db.close(); + }; + }); + } + + function removedata(param) { + param.parentNode.removeChild(param); + } + + function editLabel(param) { + param.getElementsByTagName('input')[0].disabled = false; + param.getElementsByTagName('button')[0].style.display = "none"; + param.getElementsByTagName('button')[1].style.display = "block"; + } + function saveLabel(param) { + label = param.getElementsByTagName('input')[0]; + label.disabled = true; + label = label.value; + param.getElementsByTagName('button')[0].style.display = "block"; + param.getElementsByTagName('button')[1].style.display = "none"; + var idb = indexedDB.open("FLO_webWallet_testnet"); + idb.onerror = function (event) { + console.log("Error in opening IndexedDB!"); + }; + idb.onsuccess = function (event) { + var db = event.target.result; + var obslabel = db.transaction('Label', "readwrite").objectStore('Label'); + obslabel.put(label, param.id); + db.close(); + } + } + + + - + + \ No newline at end of file From 0c342eaf2d1947d1d14f27e54f9378c04a707245 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Fri, 11 Sep 2020 18:20:42 +0530 Subject: [PATCH 08/41] fixed max character issue --- index.html | 12 ++++++------ testnet.html | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index 6119427..d0f298d 100644 --- a/index.html +++ b/index.html @@ -205,8 +205,6 @@ margin: 0.4rem 0.8rem; } - - #overlay { opacity: 0; pointer-events: none; @@ -2797,10 +2795,12 @@ document.getElementById('transactions-container').scrollTop = 0; } - let showCharacterCount = document.getElementById('show_character_count') - document.getElementById('flotextdata').addEventListener('input', function () { - if (1040 - this.value.length) { - showCharacterCount.textContent = `${1040 - this.value.length}/1040` + let showCharacterCount = document.getElementById('show_character_count') + document.getElementById('flotextdata').addEventListener('input', function(e){ + if(this.value.length > 1040) + e.preventDefault() + if(1040 - this.value.length){ + showCharacterCount.textContent = `${1040 - this.value.length}/1040` } else showCharacterCount.textContent = `You can only add FLO data upto 1040 characters.` diff --git a/testnet.html b/testnet.html index 778056f..d60a046 100644 --- a/testnet.html +++ b/testnet.html @@ -125,6 +125,18 @@ clear: both; } + textarea { + margin-bottom: 2rem; + } + + #show_character_count { + position: absolute; + bottom: 0; + right: 0; + font-size: 0.8rem; + margin: 0.4rem 0.8rem; + } + @media screen and (max-width: 800px) { .col-50 { width: 100%; @@ -601,6 +613,7 @@ FLO Data +
1040/1040
+ +
+
+

Edit display card

+ + Copy FLO address + + +
+ + + + + + + +

+
+
+ + +
+ + + +
+
+
+
+ + Add new address to monitor + + + +
+
+ + Go back to monitoring page + + + +

+
+
+ + Refresh transactions + + +
+ + + + + + +
+ +
+
+
+ + + + + + +
Available balance
+ 0 + FLO(s) +
To send FLO data, make sure you have enough balance.
+
+
+ + + +
+
+
+
+ + +
+
+ + +
+
+ + +
1040/1040
+
+ +
+
+
+
+ + Go back to monitoring page + + + +

Send

+
+
+
+
+ + +
+ +
+
+
+ + + + + + +

Transaction successful

+

+ +
+
+ +

+
+
+
+
+
+

Dark mode

+
+ + Automatic
+ Dark mode active : 6pm - 6am +
+ +
+
+ Manual
+ Dark mode +
+ +
+
+
+

Clear all local data

+
This will delete all local Web Wallet data like added addresses and locally stored + transactions.After clearing local data you may experience slow loading of newly added address, + please proceed cautiously!
+ +
+
+

About

+
Version 2.7.4
+ +
Powered by
+ + + + + + + + + + + + + +
+
+
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/script/components.js b/script/components.js new file mode 100644 index 0000000..e366405 --- /dev/null +++ b/script/components.js @@ -0,0 +1,4168 @@ +//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)) + } + + get disabled() { + return this.isDisabled + } + + set disabled(value) { + if (value && !this.isDisabled) { + this.isDisabled = true + this.setAttribute('disable', '') + this.button.removeAttribute('tabindex') + } else if (!value && this.isDisabled) { + this.isDisabled = false + this.removeAttribute('disable') + } + } + + dispatch() { + if (this.isDisabled) { + this.dispatchEvent(new CustomEvent('disable', { + bubbles: true, + composed: true + })) + } else { + this.dispatchEvent(new CustomEvent('clicked', { + bubbles: true, + composed: true + })) + } + } + + connectedCallback() { + this.isDisabled = false + this.button = this.shadowRoot.querySelector('.button') + if (this.hasAttribute('disable') && !this.isDisabled) + this.isDisabled = true + this.addEventListener('click', (e) => { + this.dispatch() + }) + } + }) + +//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; + this.checkInput() + this.fireEvent() + } + + 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() + } + + get validity() { + return this.shadowRoot.querySelector('input').validity + } + + set disabled(value) { + if (value) + this.shadowRoot.querySelector('.input').classList.add('disabled') + else + this.shadowRoot.querySelector('.input').classList.remove('disabled') + } + set readOnly(value) { + if (value) { + this.shadowRoot.querySelector('input').setAttribute('readonly', '') + this.shadowRoot.querySelector('.input').classList.add('readonly') + } else { + this.shadowRoot.querySelector('input').removeAttribute('readonly') + this.shadowRoot.querySelector('.input').classList.remove('readonly') + } + } + + setValidity = (message) => { + this.feedbackText.textContent = message + } + + showValidity = () => { + this.feedbackText.classList.remove('hide-completely') + } + + hideValidity = () => { + this.feedbackText.classList.add('hide-completely') + } + + focusIn = () => { + this.input.focus() + } + + focusOut = () => { + this.input.blur() + } + + fireEvent = () => { + let event = new Event('input', { + bubbles: true, + cancelable: true, + composed: true + }); + this.dispatchEvent(event); + } + + checkInput = (e) => { + 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') + if (!this.readonly) + this.clearBtn.classList.remove('hide') + } else { + if (this.animate) + this.inputParent.classList.remove('animate-label') + else + this.label.classList.remove('hide') + if (!this.readonly) + this.clearBtn.classList.add('hide') + } + } + + + connectedCallback() { + this.inputParent = this.shadowRoot.querySelector('.input') + this.clearBtn = this.shadowRoot.querySelector('.clear') + this.label = this.shadowRoot.querySelector('.label') + this.feedbackText = this.shadowRoot.querySelector('.feedback-text') + this.valueChanged = false; + this.readonly = false + this.isNumeric = false + this.min + this.max + 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('required')) { + this.input.setAttribute('required', '') + } + if (this.hasAttribute('min')) { + let minValue = this.getAttribute('min') + this.input.setAttribute('min', minValue) + this.min = parseInt(minValue) + } + if (this.hasAttribute('max')) { + let maxValue = this.getAttribute('max') + this.input.setAttribute('max', maxValue) + this.max = parseInt(maxValue) + } + if (this.hasAttribute('minlength')) { + let minValue = this.getAttribute('minlength') + this.input.setAttribute('minlength', minValue) + } + if (this.hasAttribute('maxlength')) { + let maxValue = this.getAttribute('maxlength') + this.input.setAttribute('maxlength', maxValue) + } + if (this.hasAttribute('pattern')) { + this.input.setAttribute('pattern', this.getAttribute('pattern')) + } + if (this.hasAttribute('readonly')) { + this.input.setAttribute('readonly', '') + this.readonly = true + } + if (this.hasAttribute('disabled')) { + this.inputParent.classList.add('disabled') + } + if (this.hasAttribute('error-text')) { + this.feedbackText.textContent = this.getAttribute('error-text') + } + if (this.hasAttribute('type')) { + if (this.getAttribute('type') === 'number') { + this.input.setAttribute('inputmode', 'numeric') + this.input.setAttribute('type', 'number') + this.isNumeric = true + } else + this.input.setAttribute('type', this.getAttribute('type')) + } else + this.input.setAttribute('type', 'text') + this.input.addEventListener('input', e => { + this.checkInput(e) + }) + this.clearBtn.addEventListener('click', e => { + this.value = '' + }) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + if (name === 'placeholder') { + this.shadowRoot.querySelector('.label').textContent = newValue; + this.setAttribute('aria-label', newValue); + } + } + } + }) + +//textarea +const smTextarea = document.createElement('template') +smTextarea.innerHTML = ` + + +`; +customElements.define('sm-textarea', + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(smTextarea.content.cloneNode(true)) + this.textarea = this.shadowRoot.querySelector('textarea') + } + get value() { + return this.textarea.value + } + set value(val) { + this.textarea.value = val; + this.textareaBox.dataset.value = val + this.checkInput() + this.fireEvent() + } + focusIn = () => { + this.textarea.focus() + } + fireEvent() { + let event = new Event('input', { + bubbles: true, + cancelable: true, + composed: true + }); + this.dispatchEvent(event); + } + checkInput = () => { + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') + return; + if (this.textarea.value !== '') { + this.placeholder.classList.add('hide') + } else { + this.placeholder.classList.remove('hide') + } + } + connectedCallback() { + this.textareaBox = this.shadowRoot.querySelector('.textarea') + this.placeholder = this.shadowRoot.querySelector('.placeholder') + + if(this.hasAttribute('placeholder')) + this.placeholder.textContent = this.getAttribute('placeholder') + + if (this.hasAttribute('value')) { + this.textarea.value = this.getAttribute('value') + this.checkInput() + } + if (this.hasAttribute('required')) { + this.textarea.setAttribute('required', '') + } + if (this.hasAttribute('readonly')) { + this.textarea.setAttribute('readonly', '') + } + if (this.hasAttribute('rows')) { + this.textarea.setAttribute('rows', this.getAttribute('rows')) + } + this.textarea.addEventListener('input', e => { + this.textareaBox.dataset.value = this.textarea.value + this.checkInput() + }) + } + }) + +// 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.isChecked + } + + set checked(value) { + if (value) { + this.setAttribute('checked', '') + } + else { + this.removeAttribute('checked') + } + } + + set value(val) { + this.val = val + this.setAttribute('value', value) + } + + get value() { + return getAttribute('value') + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + handleKeyup = e => { + if ((e.code === "Enter" || e.code === "Space") && this.isDisabled == false) { + if (this.hasAttribute('checked')) { + this.input.checked = false + this.removeAttribute('checked') + } + else { + this.input.checked = true + this.setAttribute('checked', '') + } + } + } + handleChange = e => { + if (this.input.checked) { + this.setAttribute('checked', '') + } + else { + this.removeAttribute('checked') + } + } + + connectedCallback() { + this.val = '' + this.addEventListener('keyup', this.handleKeyup) + this.input.addEventListener('change', this.handleChange) + } + 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 + } + } + else if (name === 'checked') { + if (this.hasAttribute('checked')) { + this.isChecked = true + this.input.checked = true + } + else { + this.input.checked = false + this.isChecked = false + } + this.dispatch() + } + } + } + disconnectedCallback() { + this.removeEventListener('keyup', this.handleKeyup) + this.removeEventListener('change', this.handleChange) + } +}) + +//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 + } + + get disabled() { + return this.getAttribute('disabled') + } + + set disabled(val) { + if (val) { + this.disabled = true + this.setAttribute('disabled', '') + this.switch.classList.add('disabled') + } else { + this.disabled = false + this.removeAttribute('disabled') + this.switch.classList.remove('disabled') + + } + } + + get checked() { + return this.isChecked + } + + set checked(value) { + if (value) { + this.setAttribute('checked', '') + this.isChecked = true + this.input.checked = true + } else { + this.removeAttribute('checked') + this.isChecked = false + this.input.checked = false + } + } + + dispatch = () => { + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true + })) + } + + connectedCallback() { + if (this.hasAttribute('disabled')) + this.switch.classList.add('disabled') + if (this.hasAttribute('checked')) + this.input.checked = true + this.addEventListener('keyup', e => { + if ((e.code === "Enter" || e.code === "Space") && !this.isDisabled) { + this.input.click() + } + }) + this.input.addEventListener('click', e => { + if (this.input.checked) + this.checked = true + else + this.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, + detail: { + value: e.detail.value + } + })) + 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)) + + this.allowClosing = false + } + + resumeScrolling = () => { + const scrollY = document.body.style.top; + window.scrollTo(0, parseInt(scrollY || '0') * -1); + setTimeout(() => { + document.body.style.overflow = 'auto'; + document.body.style.top= 'initial' + }, 300); + } + + show = (pinned, popupStack) => { + if (popupStack) + this.popupStack = popupStack + if (this.popupStack && !this.hasAttribute('open')) { + this.popupStack.push({ + popup: this, + permission: pinned + }) + if (this.popupStack.items.length > 1) { + this.popupStack.items[this.popupStack.items.length - 2].popup.classList.add('stacked') + } + this.dispatchEvent( + new CustomEvent("popupopened", { + bubbles: true, + detail: { + popup: this, + popupStack: this.popupStack + } + }) + ) + this.setAttribute('open', '') + this.pinned = pinned + } + this.popupContainer.classList.remove('hide') + this.popup.style.transform = 'none'; + document.body.style.overflow = 'hidden'; + document.body.style.top= `-${window.scrollY}px` + return this.popupStack + } + hide = () => { + if (window.innerWidth < 640) + this.popup.style.transform = 'translateY(100%)'; + else + this.popup.style.transform = 'translateY(3rem)'; + this.popupContainer.classList.add('hide') + this.removeAttribute('open') + if (typeof this.popupStack !== 'undefined') { + this.popupStack.pop() + if (this.popupStack.items.length) { + this.popupStack.items[this.popupStack.items.length - 1].popup.classList.remove('stacked') + } else { + this.resumeScrolling() + } + } else { + this.resumeScrolling() + } + + if (this.inputFields.length) { + setTimeout(() => { + this.inputFields.forEach(field => { + if (field.type === 'radio' || field.tagName === 'SM-CHECKBOX') + field.checked = false + if (field.tagName === 'SM-INPUT' || field.tagName === 'TEXTAREA'|| field.tagName === 'SM-TEXTAREA') + field.value = '' + }) + }, 300); + } + setTimeout(() => { + this.dispatchEvent( + new CustomEvent("popupclosed", { + bubbles: true, + detail: { + popup: this, + popupStack: this.popupStack + } + }) + ) + }, 300); + } + + handleTouchStart = (e) => { + this.touchStartY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.1s' + 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 { + this.offset = this.touchStartY - e.changedTouches[0].clientY; + this.popup.style.transform = `translateY(-${this.offset}px)` + }*/ + } + + handleTouchEnd = (e) => { + this.touchEndTime = e.timeStamp + cancelAnimationFrame(this.touchEndAnimataion) + this.touchEndY = e.changedTouches[0].clientY + this.popup.style.transition = 'transform 0.3s' + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + if (this.touchEndTime - this.touchStartTime > 200) { + if (this.touchEndY - this.touchStartY > this.threshold) { + if (this.pinned) { + this.show() + return + } else + this.hide() + } else { + this.show() + } + } else { + if (this.touchEndY > this.touchStartY) + if (this.pinned) { + this.show() + return + } + else + 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.popupBodySlot = this.shadowRoot.querySelector('.popup-body slot') + this.offset + this.popupHeader = this.shadowRoot.querySelector('.popup-top') + this.touchStartY = 0 + this.touchEndY = 0 + this.touchStartTime = 0 + this.touchEndTime = 0 + this.touchEndAnimataion; + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + + if (this.hasAttribute('open')) + this.show() + this.popupContainer.addEventListener('mousedown', e => { + if (e.target === this.popupContainer && !this.pinned) { + if (this.pinned) { + this.show() + return + } else + this.hide() + } + }) + + this.popupBodySlot.addEventListener('slotchange', () => { + setTimeout(() => { + this.threshold = this.popup.getBoundingClientRect().height * 0.3 + }, 200); + this.inputFields = this.querySelectorAll('sm-input', 'sm-checkbox', 'textarea', 'sm-textarea', 'radio') + }) + + this.popupHeader.addEventListener('touchstart', (e) => { this.handleTouchStart(e) }, {passive: true}) + this.popupHeader.addEventListener('touchmove', (e) => {this.handleTouchMove(e)}, {passive: true}) + this.popupHeader.addEventListener('touchend', (e) => {this.handleTouchEnd(e)}, {passive: true}) + } + disconnectedCallback() { + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart, {passive: true}) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove, {passive: true}) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd, {passive: true}) + } +}) + +//carousel + +const smCarousel = document.createElement('template') +smCarousel.innerHTML = ` + + +`; + +customElements.define('sm-carousel', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(smCarousel.content.cloneNode(true)) + } + + static get observedAttributes() { + return ['indicator'] + } + + 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.indicatorsContainer = this.shadowRoot.querySelector('.indicators') + this.carouselItems + this.indicators + this.showIndicator = false + this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 + let frag = document.createDocumentFragment(); + if (this.hasAttribute('indicator')) + this.showIndicator = true + + + let firstVisible = false, + lastVisible = false + const allElementsObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (this.showIndicator) + if (entry.isIntersecting) { + this.indicators[parseInt(entry.target.attributes.rank.textContent)].classList.add('active') + } + else + this.indicators[parseInt(entry.target.attributes.rank.textContent)].classList.remove('active') + if (!entry.target.previousElementSibling) + if (entry.isIntersecting) { + this.previousArrow.classList.remove('expand') + firstVisible = true + } + else { + this.previousArrow.classList.add('expand') + firstVisible = false + } + if (!entry.target.nextElementSibling) + if (entry.isIntersecting) { + this.nextArrow.classList.remove('expand') + lastVisible = true + } + else { + this.nextArrow.classList.add('expand') + + lastVisible = false + } + if (firstVisible && lastVisible) + this.indicatorsContainer.classList.add('hide') + else + this.indicatorsContainer.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() + this.carouselItems.forEach(item => allElementsObserver.observe(item)) + if (this.showIndicator) { + this.indicatorsContainer.innerHTML = `` + this.carouselItems.forEach((item, index) => { + let dot = document.createElement('div') + dot.classList.add('dot') + frag.append(dot) + item.setAttribute('rank', index) + }) + this.indicatorsContainer.append(frag) + this.indicators = this.indicatorsContainer.children + } + }) + + 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) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'indicator') { + if (this.hasAttribute('indicator')) + this.showIndicator = true + else + this.showIndicator = false + } + } + + 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) => { + e.preventDefault() + 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 = '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 = (messageBody, type, pinned) => { + let notification = document.createElement('div'), + composition = `` + notification.classList.add('notification') + if (pinned) + notification.classList.add('pinned') + if (type === 'error') { + composition += ` + + + + + ` + } else if (type === 'success') { + composition += ` + + + ` + } + composition += ` +

${messageBody}

+ + Close + + + ` + 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) => { + 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.remove() + } + else { + notification.animate([{ + transform: `translateX(${this.offset}px)`, + opacity: '1' + }, + { + transform: `translateX(100%)`, + opacity: '0' + } + ], this.animationOptions).onfinish = () => { + notification.remove() + } + } + } + + clearAll = () => { + Array.from(this.notificationPanel.children).forEach(child => { + this.removeNotification(child) + }) + } + + 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]) + }, 5000); + 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) { + this.optionList.classList.remove('hide') + this.optionList.classList.add('no-transformations') + this.open = true + this.icon.classList.add('focused') + this.availableOptions.forEach(option => { + option.setAttribute('tabindex', '0') + }) + } + } + collapse() { + if (this.open) { + this.open = false + this.icon.classList.remove('focused') + this.optionList.classList.add('hide') + this.optionList.classList.remove('no-transformations') + this.availableOptions.forEach(option => { + option.removeAttribute('tabindex') + }) + } + } + 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() + }); + window.addEventListener('mousedown', e => { + if (!this.contains(e.target) && e.button !== 2) { + this.collapse() + } + }) + } +}) + +// 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() + } + }) + } +}) + +// tab-header + +const smTabHeader = document.createElement('template') +smTabHeader.innerHTML = ` + +
+
+ +
+
+
+`; + +customElements.define('sm-tab-header', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(smTabHeader.content.cloneNode(true)) + + this.indicator = this.shadowRoot.querySelector('.indicator'); + this.tabSlot = this.shadowRoot.querySelector('slot'); + this.tabHeader = this.shadowRoot.querySelector('.tab-header'); + } + + sendDetails(element) { + this.dispatchEvent( + new CustomEvent("switchtab", { + bubbles: true, + detail: { + target: this.target, + rank: parseInt(element.getAttribute('rank')) + } + }) + ) + } + + moveIndiactor(tabDimensions) { + //if(this.isTab) + this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabHeader.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`) + //else + //this.indicator.setAttribute('style', `width: calc(${tabDimensions.width}px - 1.6rem); transform: translateX(calc(${ tabDimensions.left - this.tabHeader.getBoundingClientRect().left + this.tabHeader.scrollLeft}px + 0.8rem)`) + } + + connectedCallback() { + if (!this.hasAttribute('target') || this.getAttribute('target').value === '') return; + this.prevTab + this.allTabs + this.activeTab + this.isTab = false + this.target = this.getAttribute('target') + + if (this.hasAttribute('variant') && this.getAttribute('variant') === 'tab') { + this.isTab = true + } + + this.tabSlot.addEventListener('slotchange', () => { + this.tabSlot.assignedElements().forEach((tab, index) => { + tab.setAttribute('rank', index) + }) + }) + this.allTabs = this.tabSlot.assignedElements(); + + 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.moveIndiactor(e.target.getBoundingClientRect()) + this.sendDetails(e.target) + this.prevTab = e.target; + this.activeTab = e.target; + }) + let resizeObserver = new ResizeObserver(entries => { + entries.forEach((entry) => { + if (this.prevTab) { + let tabDimensions = this.activeTab.getBoundingClientRect(); + this.moveIndiactor(tabDimensions) + } + }) + }) + resizeObserver.observe(this) + let observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + this.indicator.style.transition = 'none' + if (this.activeTab) { + let tabDimensions = this.activeTab.getBoundingClientRect(); + this.moveIndiactor(tabDimensions) + } else { + this.allTabs[0].classList.add('active') + let tabDimensions = this.allTabs[0].getBoundingClientRect(); + this.moveIndiactor(tabDimensions) + this.sendDetails(this.allTabs[0]) + this.prevTab = this.tabSlot.assignedElements()[0]; + this.activeTab = this.prevTab; + } + } + }) + }, { + threshold: 1.0 + }) + observer.observe(this) + } +}) + +// tab-panels + +const smTabPanels = document.createElement('template') +smTabPanels.innerHTML = ` + +
+ Nothing to see here. +
+`; + +customElements.define('sm-tab-panels', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(smTabPanels.content.cloneNode(true)) + this.panelSlot = this.shadowRoot.querySelector('slot'); + } + 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.prevPanel + this.allPanels + this.previousRank + + this.panelSlot.addEventListener('slotchange', () => { + this.panelSlot.assignedElements().forEach((panel) => { + panel.classList.add('hide-completely') + }) + }) + this.allPanels = this.panelSlot.assignedElements() + this._targetBodyFlyRight = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInRight, animationOptions) + } + this._targetBodyFlyLeft = (targetBody) => { + targetBody.classList.remove('hide-completely') + targetBody.animate(flyInLeft, animationOptions) + } + document.addEventListener('switchtab', e => { + if (e.detail.target !== this.id) + return + + if (this.prevPanel) { + let targetBody = this.allPanels[e.detail.rank], + currentBody = this.prevPanel; + if (this.previousRank < e.detail.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 { + this.allPanels[e.detail.rank].classList.remove('hide-completely') + } + this.previousRank = e.detail.rank + this.prevPanel = this.allPanels[e.detail.rank]; + }) + } +}) + + +const slidingSection = document.createElement('template') +slidingSection.innerHTML = ` + +
+ +
+` + +customElements.define('sm-sliding-section', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(slidingSection.content.cloneNode(true)) + } + connectedCallback() { + + } +}) + +const section = document.createElement('template') +section.innerHTML = ` + +
+ +
+` + +customElements.define('sm-section', class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(section.content.cloneNode(true)) + } +}) + +const textField = document.createElement('template') +textField.innerHTML = ` + +
+
+
+ + Edit + + + + Save + + +
+
+` + +customElements.define('text-field', class extends HTMLElement{ + constructor(){ + super() + this.attachShadow({ + mode: 'open' + }).append(textField.content.cloneNode(true)) + + this.textField = this.shadowRoot.querySelector('.text-field') + this.textContainer = this.textField.children[0] + this.iconsContainer = this.textField.children[1] + this.editButton = this.textField.querySelector('.edit-button') + this.saveButton = this.textField.querySelector('.save-button') + this.isTextEditable = false + this.isDisabled = false + } + + static get observedAttributes(){ + return ['disable'] + } + + get value(){ + return this.text + } + set value(val) { + this.text = val + this.textContainer.textContent = val + this.setAttribute('value', val) + } + set disabled(val) { + this.isDisabled = val + if(this.isDisabled) + this.setAttribute('disable', '') + else + this.removeAttribute('disable') + } + fireEvent = (value) => { + let event = new CustomEvent('contentchanged', { + bubbles: true, + cancelable: true, + composed: true, + detail: { + value + } + }); + this.dispatchEvent(event); + } + + setEditable = () => { + if(this.isTextEditable) return + this.textContainer.contentEditable = true + this.textContainer.classList.add('editable') + this.textContainer.focus() + document.execCommand('selectAll', false, null); + this.editButton.animate(this.rotateOut, this.animOptions).onfinish = () => { + this.editButton.classList.add('hide') + } + setTimeout(() => { + this.saveButton.classList.remove('hide') + this.saveButton.animate(this.rotateIn, this.animOptions) + }, 100); + this.isTextEditable = true + } + setNonEditable = () => { + if (!this.isTextEditable) return + this.textContainer.contentEditable = false + this.textContainer.classList.remove('editable') + + if (this.text !== this.textContainer.textContent.trim()) { + this.setAttribute('value', this.textContainer.textContent) + this.text = this.textContainer.textContent.trim() + this.fireEvent(this.text) + } + this.saveButton.animate(this.rotateOut, this.animOptions).onfinish = () => { + this.saveButton.classList.add('hide') + } + setTimeout(() => { + this.editButton.classList.remove('hide') + this.editButton.animate(this.rotateIn, this.animOptions) + }, 100); + this.isTextEditable = false + } + + revert = () => { + if (this.textContainer.isContentEditable) { + this.value = this.text + this.setNonEditable() + } + } + + connectedCallback(){ + this.text + if (this.hasAttribute('value')) { + this.text = this.getAttribute('value') + this.textContainer.textContent = this.text + } + if(this.hasAttribute('disable')) + this.isDisabled = true + else + this.isDisabled = false + + this.rotateOut = [ + { + transform: 'rotate(0)', + opacity: 1 + }, + { + transform: 'rotate(90deg)', + opacity: 0 + }, + ] + this.rotateIn = [ + { + transform: 'rotate(-90deg)', + opacity: 0 + }, + { + transform: 'rotate(0)', + opacity: 1 + }, + ] + this.animOptions = { + duration: 300, + easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)', + fill: 'forwards' + } + if (!this.isDisabled) { + this.iconsContainer.classList.remove('hide') + this.textContainer.addEventListener('dblclick', this.setEditable) + this.editButton.addEventListener('click', this.setEditable) + this.saveButton.addEventListener('click', this.setNonEditable) + } + } + attributeChangedCallback(name) { + if (name === 'disable') { + if (this.hasAttribute('disable')) { + this.iconsContainer.classList.add('hide') + this.textContainer.removeEventListener('dblclick', this.setEditable) + this.editButton.removeEventListener('click', this.setEditable) + this.saveButton.removeEventListener('click', this.setNonEditable) + this.revert() + } + else { + this.iconsContainer.classList.remove('hide') + this.textContainer.addEventListener('dblclick', this.setEditable) + this.editButton.addEventListener('click', this.setEditable) + this.saveButton.addEventListener('click', this.setNonEditable) + } + } + } + disconnectedCallback() { + this.textContainer.removeEventListener('dblclick', this.setEditable) + this.editButton.removeEventListener('click', this.setEditable) + this.saveButton.removeEventListener('click', this.setNonEditable) + } +}) + +//Color Grid +const colorGrid = document.createElement('template'); +colorGrid.innerHTML =` + +
+
`; + +customElements.define('color-grid', +class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(colorGrid.content.cloneNode(true)) + + this.colorArray = [] + this.container = this.shadowRoot.querySelector('.color-tile-container') + } + + set colors(arr) { + this.colorArray = arr + this.renderTiles() + } + + set selectedColor(color) { + if (this.colorArray.includes(color) && this.container.querySelector(`[data-color="${color}"]`)) { + this.container.querySelector(`[data-color="${color}"] input`).checked = true + } + } + + randString(length) { + let result = ''; + let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + for (let i = 0; i < length; i++) + result += characters.charAt(Math.floor(Math.random() * characters.length)); + return result; + } + + renderTiles() { + this.container.innerHTML = '' + const frag = document.createDocumentFragment() + const groupName = this.randString(6) + this.colorArray.forEach(color => { + const label = document.createElement('label') + label.classList.add('color-tile') + label.setAttribute('data-color', color) + if(color.includes('--')) + label.setAttribute('style', `background-color: var(${color})`) + else + label.setAttribute('style', `background-color: ${color}`) + label.innerHTML = ` + +
+ ` + frag.append(label) + }) + this.container.append(frag) + } + + handleChange(e) { + const clickedTile = e.target.closest('.color-tile') + const clickedTileColor = clickedTile.dataset.color + const tileSelected = new CustomEvent('colorselected', { + bubbles: true, + composed: true, + detail: { + value: clickedTileColor, + } + }) + this.dispatchEvent(tileSelected) + } + + connectedCallback() { + this.container.addEventListener('change', this.handleChange) + } + + disconnectedCallback() { + this.container.removeEventListener('change', this.handleChange) + } +}) + +const pinInput = document.createElement('template'); +pinInput.innerHTML = ` + + +
+
+ +
+`; + +customElements.define('pin-input', + + class extends HTMLElement { + constructor() { + super() + this.attachShadow({ + mode: 'open' + }).append(pinInput.content.cloneNode(true)) + + this.pinDigits = 4 + + this.arrayOfInput = []; + this.container = this.shadowRoot.querySelector('.pin-container'); + this.toggleButton = this.shadowRoot.querySelector('button') + } + + set value(val) { + this.arrayOfInput.forEach((input, index) => input.value = val[index] ? val[index] : '') + } + + get value() { + return this.getValue() + } + + set pinLength(val) { + this.pinDigits = val + this.setAttribute('pin-length', val) + this.style.setProperty('--pin-length', val) + this.render() + } + + get isValid(){ + return this.arrayOfInput.every(input => input.value.trim().length) + } + + clear = () => { + this.value = '' + } + + focusIn = () => { + this.arrayOfInput[0].focus(); + } + + getValue = () => { + return this.arrayOfInput.reduce((acc, val) => { + return acc += val.value + }, '') + } + + render = () => { + this.container.innerHTML = '' + const frag = document.createDocumentFragment(); + + for (let i = 0; i < this.pinDigits; i++) { + const inputBox = document.createElement('input') + inputBox.setAttribute('type', 'password') + inputBox.setAttribute('inputmode', 'numeric') + inputBox.setAttribute('maxlength', '1') + inputBox.setAttribute('required', '') + this.arrayOfInput.push(inputBox); + frag.append(inputBox); + } + this.container.append(frag); + } + + handleKeydown = (e) => { + const activeInput = e.target.closest('input') + if (/[0-9]/.test(e.key)) { + if (activeInput.value.trim().length > 2) { + e.preventDefault(); + } + else { + if (activeInput.nextElementSibling) { + setTimeout(() => { + activeInput.nextElementSibling.focus(); + }, 0) + } + } + } + else if (e.key === "Backspace") { + if(activeInput.previousElementSibling) + setTimeout(() => { + activeInput.previousElementSibling.focus(); + }, 0) + } + else if (e.key.length === 1 && !/[0-9]/.test(e.key)) { + e.preventDefault(); + } + } + + handleInput = () => { + if (this.isValid) { + this.fireEvent(this.getValue()) + } + } + + fireEvent = (value) => { + let event = new CustomEvent('pincomplete', { + bubbles: true, + cancelable: true, + composed: true, + detail: { + value + } + }); + this.dispatchEvent(event); + } + + toggleVisiblity = () => { + if (this.arrayOfInput[0].getAttribute('type') === 'password') { + this.toggleButton.innerHTML = ` + + Hide + ` + this.arrayOfInput.forEach(input => input.setAttribute('type', 'text')) + } + else { + this.toggleButton.innerHTML = ` + + Show + ` + this.arrayOfInput.forEach(input => input.setAttribute('type', 'password')) + + } + } + + connectedCallback() { + if (this.hasAttribute('pin-length')) { + const pinLength = parseInt(this.getAttribute('pin-length')) + this.pinDigits = pinLength + this.style.setProperty('--pin-length', pinLength) + } + + this.render() + + this.toggleButton.addEventListener('click', this.toggleVisiblity) + + this.container.addEventListener('input', this.handleInput); + this.container.addEventListener('keydown', this.handleKeydown); + } + disconnectedCallback() { + this.toggleButton.removeEventListener('click', this.toggleVisiblity) + + this.container.removeEventListener('input', this.handleInput); + this.container.removeEventListener('keydown', this.handleKeydown); + } + }) From ecd003ce562da8dd5a8d8a817e5c6ec3ee420c29 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Wed, 24 Feb 2021 18:35:19 +0530 Subject: [PATCH 10/41] Feat: check and fix FLO data when sending FLO --- css/main.css | 9 ++++-- css/main.min.css | 2 +- css/main.scss | 8 +++-- index.html | 77 ++++++++++++++++++++++++++++++++++++++++++-- new.html | 2 +- script/components.js | 11 ++++--- 6 files changed, 97 insertions(+), 12 deletions(-) diff --git a/css/main.css b/css/main.css index dd7863e..d03d41b 100644 --- a/css/main.css +++ b/css/main.css @@ -22,7 +22,7 @@ body { --background-color: #efefef; --error-color: red; color: rgba(var(--text-color), 1); - background: url(lighthouse.svg) no-repeat; + background: url(lighthouse.svg) no-repeat center; background-size: cover; } @@ -378,7 +378,12 @@ sm-textarea { } .contact-card__more { - padding: 0.2rem; + padding: 0.4rem; +} + +.more-icon { + height: 1.2rem; + width: 1.2rem; } #add_contact_button { diff --git a/css/main.min.css b/css/main.min.css index 2174cba..eb24e35 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1 +1 @@ -.interact,button{-webkit-tap-highlight-color:transparent;cursor:pointer;overflow:hidden}*,::after,::before{padding:0;margin:0;box-sizing:border-box;font-family:Roboto,sans-serif}:root{font-size:clamp(1rem,1.2vmax,3rem);--ease-in-overshhot:cubic-bezier(0.6, -0.28, 0.735, 0.045);--ease-out-overshhot:cubic-bezier(0.175, 0.885, 0.32, 1.275)}body{--accent-color:#3D5AFE;--secondary-color:#ffac2e;--text-color:17,17,17;--text-color-light:100,100,100;--foreground-color:255,255,255;--background-color:#efefef;--error-color:red;color:rgba(var(--text-color),1);background:url(lighthouse.svg) no-repeat;background-size:cover}body[data-theme=dark]{--accent-color:#657cff;--secondary-color:#d60739;--text-color:240,240,240;--text-color-light:170,170,170;--foreground-color:20,20,20;--error-color:rgb(255, 106, 106)}body[data-theme=dark] .contact-card{margin:.2rem 0;box-shadow:0 .1rem .5rem rgba(var(--text-color),.1)}button{position:relative;display:inline-flex;border:none;background:0 0;outline:0}.fab,.ripple{position:absolute}button:focus-visible{outline:solid rgba(var(--text-color),1)}sm-input,sm-textarea{--border-radius:0.5rem}.flex{display:flex}.grid{display:grid}.align-center{align-items:center}.justify-right{margin-left:auto}.direction-column{flex-direction:column}.space-between{justify-content:space-between}.hide{opacity:0;pointer-events:none}.hide-completely{display:none!important}.no-transformations{transform:none!important}.ripple{border-radius:50%;transform:scale(0);background:rgba(var(--text-color),.2);pointer-events:none}.interact{position:relative}.icon{width:1.5rem;height:1.5rem;fill:rgba(var(--text-color),.7)}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{font-weight:500;margin-bottom:.5rem}#confirmation_popup sm-button,#prompt_popup sm-button{margin:0}#confirmation_popup .flex,#prompt_popup .flex{padding:0;margin-top:1rem}#confirmation_popup .flex sm-button:first-of-type,#prompt_popup .flex sm-button:first-of-type{margin-right:.6rem;margin-left:auto}.popup__header{padding:.5rem 1.5rem 0 1rem;display:grid;grid-template-columns:auto 1fr;gap:.5rem;align-items:center;width:100%}.popup__header__close{padding:.5rem;cursor:pointer}.popup__header__title{font-size:1.1rem;font-weight:500;color:rgba(var(--text-color),.8)}.header__company-name,.header__title,.nav-button__name{color:rgba(var(--text-color),.7)}.close-icon{height:2rem;width:2rem}.home_page__section{backdrop-filter:blur(1rem)}.home_page__section--left{background-color:rgba(var(--foreground-color),.8)}.home_page__section--right{overflow:hidden;background-color:rgba(var(--foreground-color),.9)}.contact-card,.nav-button__icon{background:rgba(var(--foreground-color),1)}.home_page__header{gap:.2rem 1rem}.home_page__header--left{padding:1.5rem .7rem 1.5rem 1rem;grid-template-columns:1fr auto;grid-template-areas:"company setting" "app setting"}.home_page__header--right{padding:1.5rem 1rem}.header__company-name{grid-area:company;font-weight:500;align-self:flex-end}.header__app-name{align-self:flex-start;line-height:1;grid-area:app;font-weight:700;color:rgba(var(--text-color),.9)}.header__settings-button{grid-area:setting;padding:.8rem}.header__settings-button__icon{transform:rotate(90deg)}.home_page__nav{padding:1.5rem 1rem;gap:1rem;grid-template-columns:repeat(3,1fr);justify-items:center}.nav-button{display:flex;flex-direction:column;align-items:center;text-align:center;width:4rem}.nav-button__icon{height:3rem;width:3rem;padding:.8rem;border-radius:.8rem;margin-bottom:.8rem;fill:var(--accent-color)}.nav-button__name{font-size:.9rem;font-weight:500}.fab{display:inline-flex;border-radius:5rem;aspect-ratio:1/1;padding:.7rem;background:#FF1F1F;box-shadow:0 .5rem .5rem rgba(0,0,0,.16)}.fab__icon{height:1.8rem;width:1.8rem;fill:#fff}#base_header{width:100%}.header__title{font-weight:500}.contact-card__name,.contact-option__name{font-weight:500;color:rgba(var(--text-color),.8)}#search_contacts{width:14rem;--padding:0.5rem 0.6rem;--background:rgba(var(--text-color), 0.1)}.search__icon{height:1.2rem}#selected_contact_options{margin-top:.5rem;list-style:none}.contact-option:not(:last-of-type){margin-right:.5rem}.contact-option:first-of-type{padding-right:.6rem;margin-right:auto;margin-left:-.6rem}.contact-option__button{display:inline-flex;align-items:center;border-radius:.3rem;padding:.5rem .6rem}.contact-option__icon{height:1.2rem;width:1.2rem;margin-right:.5rem}.contact-option__name{font-size:.9rem}#contacts_container{display:grid;gap:1rem;height:100%;align-content:flex-start;overflow-y:auto;list-style:none;padding:0 1rem 6rem;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}.contact-card{position:relative;overflow:hidden;display:flex;width:100%;flex-direction:column;padding:1rem;min-height:8rem;cursor:pointer;align-self:flex-start;justify-content:center;border-radius:.5rem;box-shadow:0 .2rem .4rem rgba(0,0,0,.06)}.contact-card__checkbox{--border-radius:0.5rem;--border-color:rgba(var(--text-color), 0.6);position:absolute;top:0;right:0;margin:1rem}.contact-card__initial{width:3rem;height:3rem;display:flex;padding:1.5rem;font-size:1.2rem;text-align:center;user-select:none;border-radius:1rem;margin-bottom:1.5rem;align-items:center;justify-content:center;background-color:rgba(var(--text-color),.1)}.contact-card__name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:1rem}.contact__grid{gap:1rem;grid-template-columns:1fr auto}.contact-card__more{padding:.2rem}#add_contact_button{right:0;bottom:0;margin:1.5rem}@media all and (max-width:640px){#main_card{min-height:100vh}.fab{position:fixed}#home_page{height:100%;grid-template-rows:auto 1fr}.home_page__section--right{align-content:flex-start}.home_page__nav{padding:.5rem 1rem 2rem;grid-template-columns:repeat(4,1fr)}}@media all and (min-width:640px){body{place-content:center;min-height:100vh}sm-popup{--width:24rem}.popup__header{padding:1rem 1.5rem 0 1rem}#main_card{width:80vw;height:80vh;border-radius:.5rem;box-shadow:0 1.5rem 1.5rem rgba(0,0,0,.16)}#home_page{height:100%;overflow-y:auto;grid-template-columns:20rem 1fr}.home_page__section--left{border-radius:.5rem 0 0 .5rem}.home_page__section--right{grid-template-rows:auto 1fr;border-radius:0 .5rem .5rem 0;box-shadow:-.5rem 0 1rem rgba(0,0,0,.06)}.home_page__header--left{padding:1.8rem 1.7rem 2rem 2.5rem}.home_page__header--right{padding:2rem 2.5rem}.home_page__nav{padding:0 2.5rem}#contacts_container{padding:0 2.5rem 8rem}#add_contact_button{right:0;bottom:0;margin:2rem 2.5rem}}@media all and (min-width:1280px){#main_card{width:65vw;height:85vh}}@media all and (min-width:1920px){#main_card{width:65vw;height:70vh}}@media (any-hover:hover){.contact-card__checkbox:not([checked]):not(:focus-within),.contact-card__more:not(:focus-within){opacity:0;transition:opacity .3s}.contact-card:hover .contact-card__checkbox,.contact-card:hover .contact-card__more{opacity:1}.contact-option__button{transition:background .3s}.contact-option__button:hover{background-color:rgba(var(--text-color),.1)}} \ No newline at end of file +.interact,button{-webkit-tap-highlight-color:transparent}*,::after,::before{padding:0;margin:0;box-sizing:border-box;font-family:Roboto,sans-serif}:root{font-size:clamp(1rem,1.2vmax,3rem);--ease-in-overshhot:cubic-bezier(0.6, -0.28, 0.735, 0.045);--ease-out-overshhot:cubic-bezier(0.175, 0.885, 0.32, 1.275)}body{--accent-color:#3D5AFE;--secondary-color:#ffac2e;--text-color:17,17,17;--text-color-light:100,100,100;--foreground-color:255,255,255;--background-color:#efefef;--error-color:red;color:rgba(var(--text-color),1);background:url(lighthouse.svg) center no-repeat;background-size:cover}body[data-theme=dark]{--accent-color:#657cff;--secondary-color:#d60739;--text-color:240,240,240;--text-color-light:170,170,170;--foreground-color:20,20,20;--error-color:rgb(255, 106, 106)}body[data-theme=dark] .contact-card{margin:.2rem 0;box-shadow:0 .1rem .5rem rgba(var(--text-color),.1)}button{position:relative;overflow:hidden;display:inline-flex;border:none;background:0 0;cursor:pointer;outline:0}.fab,.ripple{position:absolute}button:focus-visible{outline:solid rgba(var(--text-color),1)}sm-input,sm-textarea{--border-radius:0.5rem}.flex{display:flex}.grid{display:grid}.align-center{align-items:center}.justify-right{margin-left:auto}.direction-column{flex-direction:column}.space-between{justify-content:space-between}.hide{opacity:0;pointer-events:none}.hide-completely{display:none!important}.no-transformations{transform:none!important}.ripple{border-radius:50%;transform:scale(0);background:rgba(var(--text-color),.2);pointer-events:none}.contact-card,.interact{position:relative;cursor:pointer;overflow:hidden}.icon{width:1.5rem;height:1.5rem;fill:rgba(var(--text-color),.7)}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{font-weight:500;margin-bottom:.5rem}#confirmation_popup sm-button,#prompt_popup sm-button{margin:0}#confirmation_popup .flex,#prompt_popup .flex{padding:0;margin-top:1rem}#confirmation_popup .flex sm-button:first-of-type,#prompt_popup .flex sm-button:first-of-type{margin-right:.6rem;margin-left:auto}.popup__header{padding:.5rem 1.5rem 0 1rem;display:grid;grid-template-columns:auto 1fr;gap:.5rem;align-items:center;width:100%}.popup__header__close{padding:.5rem;cursor:pointer}.popup__header__title{font-size:1.1rem;font-weight:500;color:rgba(var(--text-color),.8)}.header__company-name,.header__title,.nav-button__name{color:rgba(var(--text-color),.7);font-weight:500}.close-icon{height:2rem;width:2rem}.home_page__section{backdrop-filter:blur(1rem)}.home_page__section--left{background-color:rgba(var(--foreground-color),.8)}.home_page__section--right{overflow:hidden;background-color:rgba(var(--foreground-color),.9)}.contact-card,.nav-button__icon{background:rgba(var(--foreground-color),1)}.home_page__header{gap:.2rem 1rem}.home_page__header--left{padding:1.5rem .7rem 1.5rem 1rem;grid-template-columns:1fr auto;grid-template-areas:"company setting" "app setting"}.home_page__header--right{padding:1.5rem 1rem}.header__company-name{grid-area:company;align-self:flex-end}.header__app-name{align-self:flex-start;line-height:1;grid-area:app;font-weight:700;color:rgba(var(--text-color),.9)}.header__settings-button{grid-area:setting;padding:.8rem}.header__settings-button__icon{transform:rotate(90deg)}.home_page__nav{padding:1.5rem 1rem;gap:1rem;grid-template-columns:repeat(3,1fr);justify-items:center}.nav-button{display:flex;flex-direction:column;align-items:center;text-align:center;width:4rem}.nav-button__icon{height:3rem;width:3rem;padding:.8rem;border-radius:.8rem;margin-bottom:.8rem;fill:var(--accent-color)}.nav-button__name{font-size:.9rem}.fab{display:inline-flex;border-radius:5rem;aspect-ratio:1/1;padding:.7rem;background:#FF1F1F;box-shadow:0 .5rem .5rem rgba(0,0,0,.16)}.fab__icon{height:1.8rem;width:1.8rem;fill:#fff}#base_header{width:100%}#search_contacts{width:14rem;--padding:0.5rem 0.6rem;--background:rgba(var(--text-color), 0.1)}.search__icon{height:1.2rem}#selected_contact_options{margin-top:.5rem;list-style:none}.contact-option:not(:last-of-type){margin-right:.5rem}.contact-option:first-of-type{padding-right:.6rem;margin-right:auto;margin-left:-.6rem}.contact-option__button{display:inline-flex;align-items:center;border-radius:.3rem;padding:.5rem .6rem}.contact-option__icon{height:1.2rem;width:1.2rem;margin-right:.5rem}.contact-option__name{font-weight:500;font-size:.9rem;color:rgba(var(--text-color),.8)}#contacts_container{display:grid;gap:1rem;height:100%;align-content:flex-start;overflow-y:auto;list-style:none;padding:0 1rem 6rem;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}.contact-card{display:flex;width:100%;flex-direction:column;padding:1rem;min-height:8rem;align-self:flex-start;justify-content:center;border-radius:.5rem;box-shadow:0 .2rem .4rem rgba(0,0,0,.06)}.contact-card__checkbox{--border-radius:0.5rem;--border-color:rgba(var(--text-color), 0.6);position:absolute;top:0;right:0;margin:1rem}.contact-card__initial{width:3rem;height:3rem;display:flex;padding:1.5rem;font-size:1.2rem;text-align:center;user-select:none;border-radius:1rem;margin-bottom:1.5rem;align-items:center;justify-content:center;background-color:rgba(var(--text-color),.1)}.contact-card__name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:500;font-size:1rem;color:rgba(var(--text-color),.8)}.contact__grid{gap:1rem;grid-template-columns:1fr auto}.contact-card__more{padding:.4rem}.more-icon{height:1.2rem;width:1.2rem}#add_contact_button{right:0;bottom:0;margin:1.5rem}@media all and (max-width:640px){#main_card{min-height:100vh}.fab{position:fixed}#home_page{height:100%;grid-template-rows:auto 1fr}.home_page__section--right{align-content:flex-start}.home_page__nav{padding:.5rem 1rem 2rem;grid-template-columns:repeat(4,1fr)}}@media all and (min-width:640px){body{place-content:center;min-height:100vh}sm-popup{--width:24rem}.popup__header{padding:1rem 1.5rem 0 1rem}#main_card{width:80vw;height:80vh;border-radius:.5rem;box-shadow:0 1.5rem 1.5rem rgba(0,0,0,.16)}#home_page{height:100%;overflow-y:auto;grid-template-columns:20rem 1fr}.home_page__section--left{border-radius:.5rem 0 0 .5rem}.home_page__section--right{grid-template-rows:auto 1fr;border-radius:0 .5rem .5rem 0;box-shadow:-.5rem 0 1rem rgba(0,0,0,.06)}.home_page__header--left{padding:1.8rem 1.7rem 2rem 2.5rem}.home_page__header--right{padding:2rem 2.5rem}.home_page__nav{padding:0 2.5rem}#contacts_container{padding:0 2.5rem 8rem}#add_contact_button{right:0;bottom:0;margin:2rem 2.5rem}}@media all and (min-width:1280px){#main_card{width:65vw;height:85vh}}@media all and (min-width:1920px){#main_card{width:65vw;height:70vh}}@media (any-hover:hover){.contact-card__checkbox:not([checked]):not(:focus-within),.contact-card__more:not(:focus-within){opacity:0;transition:opacity .3s}.contact-card:hover .contact-card__checkbox,.contact-card:hover .contact-card__more{opacity:1}.contact-option__button{transition:background .3s}.contact-option__button:hover{background-color:rgba(var(--text-color),.1)}} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index e8ae55d..50e1f92 100644 --- a/css/main.scss +++ b/css/main.scss @@ -20,7 +20,7 @@ body{ --background-color: #efefef; --error-color: red; color: rgba(var(--text-color), 1); - background: url(lighthouse.svg) no-repeat; + background: url(lighthouse.svg) no-repeat center; background-size: cover; } body[data-theme='dark']{ @@ -333,7 +333,11 @@ sm-textarea{ grid-template-columns: 1fr auto; } .contact-card__more{ - padding: 0.2rem; + padding: 0.4rem; +} +.more-icon{ + height: 1.2rem; + width: 1.2rem; } #add_contact_button{ diff --git a/index.html b/index.html index d0f298d..4a09bd5 100644 --- a/index.html +++ b/index.html @@ -81,10 +81,27 @@ border: 1px transparent solid; } + .flex{ + display: flex; + } + .grid{ + display: grid; + } + .column-grid{ + grid-auto-flow: column; + } + .align-center{ + align-items: center; + } + .gap-1{ + gap: 1rem; + } + .primaryButton { background: var(--accent-color) !important; color: white !important; border: 1px solid var(--accent-color); + width: 100%; } .primaryButton:hover { @@ -205,6 +222,11 @@ margin: 0.4rem 0.8rem; } + #flo_data_status:not(:empty){ + color: red; + padding: 0.5rem 0; + } + #overlay { opacity: 0; pointer-events: none; @@ -542,6 +564,9 @@ opacity: 0; pointer-events: none; } + .hide-completely{ + display: none; + } #menu { display: none; @@ -1989,6 +2014,11 @@
1040/1040
+

+
+ + +
@@ -2146,7 +2176,6 @@ mode = setInterval(() => { let d = new Date(), t = d.getHours(); - console.log(t); if (t > 6 && t < 18) { daylight(); } else { @@ -2761,6 +2790,7 @@ floWebWallet.sendTransaction(sender, receiver, amount, floData, privKey).then((transactionid) => { document.getElementById('transactionId').textContent = `transaction ID : ${transactionid}`; showInnerPage('transaction-complete'); + document.getElementById('sdright').querySelectorAll('input, textarea').forEach(field => field.value = '') }).catch((error) => { alert(error) }) @@ -2796,15 +2826,58 @@ } let showCharacterCount = document.getElementById('show_character_count') - document.getElementById('flotextdata').addEventListener('input', function(e){ + let isFloDataChanged = false + const floDataOptions = document.getElementById('flo_data_options') + const checkFloDataButton = document.getElementById('check_flo_data_button') + const sendFloDataButton = document.getElementById('sendBtn') + const sentFloData = document.getElementById('flotextdata') + const floDataStaus = document.getElementById('flo_data_status') + sentFloData.addEventListener('keydown', function(e){ if(this.value.length > 1040) e.preventDefault() + }) + + sentFloData.addEventListener('input', function(e){ + isFloDataChanged = true + if(this.value.trim() !== ''){ + sendFloDataButton.classList.add('hide-completely') + floDataOptions.classList.remove('hide-completely') + } + else{ + sendFloDataButton.classList.remove('hide-completely') + floDataOptions.classList.add('hide-completely') + } if(1040 - this.value.length){ showCharacterCount.textContent = `${1040 - this.value.length}/1040` } else showCharacterCount.textContent = `You can only add FLO data upto 1040 characters.` }) + function checkFloData(){ + isFloDataChanged = false + const floDataText = sentFloData.value.trim() + let isValid = true + for(char of floDataText){ + // if(/^[a-z0-9\s!"#$%&'()*+,.\/:;<=>?@\[\]^_`{|}~-]*$/i.test(char)){ + if(!/^[\x20-\x7E\s]+/.test(char)){ + isValid = false + break; + } + } + if(isValid){ + sendFloDataButton.classList.remove('hide-completely') + floDataOptions.classList.add('hide-completely') + floDataStaus.textContent = '' + } + else{ + floDataStaus.textContent = 'FLO data contains invalid characters. Use "Fix" to remove invalid characters.' + } + } + function removeInvalid(){ + const floDataText = sentFloData.value.trim() + sentFloData.value = floDataText.replace(/[^\x20-\x7E]*/gm, '') + checkFloData() + } diff --git a/script/components.js b/script/components.js index 086ad03..fe1ecb9 100644 --- a/script/components.js +++ b/script/components.js @@ -195,6 +195,7 @@ border: none; display: -webkit-box; display: -ms-flexbox; display: flex; + --font-size: 1rem; --border-radius: 0.3rem; --padding: 0.7rem 1rem; --background: rgba(var(--text-color), 0.06); @@ -262,7 +263,7 @@ border: none; .label { opacity: .7; font-weight: 400; - font-size: 1rem; + font-size: var(--font-size); position: absolute; top: 0; -webkit-transition: -webkit-transform 0.3s; @@ -300,7 +301,7 @@ border: none; flex: 1; } input{ - font-size: 1rem; + font-size: var(--font-size); border: none; background: transparent; outline: none; @@ -510,13 +511,17 @@ customElements.define('sm-input', this.max = parseInt(maxValue) } if (this.hasAttribute('minlength')) { - let minValue = this.getAttribute('minlength') + const minValue = this.getAttribute('minlength') this.input.setAttribute('minlength', minValue) } if (this.hasAttribute('maxlength')) { - let maxValue = this.getAttribute('maxlength') + const maxValue = this.getAttribute('maxlength') this.input.setAttribute('maxlength', maxValue) } + if (this.hasAttribute('step')) { + const steps = this.getAttribute('step') + this.input.setAttribute('step', steps) + } if (this.hasAttribute('pattern')) { this.input.setAttribute('pattern', this.getAttribute('pattern')) } From 64f2690c23542d49ca514a370ff7106e92eeb843 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Fri, 26 Feb 2021 03:07:19 +0530 Subject: [PATCH 13/41] fix: page on mobile not taking full height of view-port --- css/main.css | 41 +++++++++++++++++----------- css/main.min.css | 2 +- css/main.scss | 39 +++++++++++++++------------ new.html | 64 +++++++++++++++++++++++++++++++++++--------- script/components.js | 6 +++-- 5 files changed, 103 insertions(+), 49 deletions(-) diff --git a/css/main.css b/css/main.css index d7e9760..1b8de75 100644 --- a/css/main.css +++ b/css/main.css @@ -8,6 +8,7 @@ } :root { + height: 100%; font-size: clamp(1rem, 1.2vmax, 3rem); --ease-in-overshhot: cubic-bezier(0.6, -0.28, 0.735, 0.045); --ease-out-overshhot: cubic-bezier(0.175, 0.885, 0.32, 1.275); @@ -21,8 +22,7 @@ body { --background-color: #efefef; --error-color: red; color: rgba(var(--text-color), 1); - background: url(lighthouse.svg) no-repeat center; - background-size: cover; + height: calc(100%); } body[data-theme=dark] { @@ -81,6 +81,10 @@ sm-textarea { justify-content: space-between; } +.full-width { + width: 100%; +} + .hide { opacity: 0; pointer-events: none; @@ -161,13 +165,14 @@ sm-textarea { #main_card { height: 100%; grid-row: 1/3; + background: rgba(var(--foreground-color), 0.9); } #main_header { grid-area: header; height: max-content; gap: 0.2rem 0.5rem; - padding: 1.5rem 0.7rem 1.5rem 1rem; + padding: 1rem 0.7rem 1rem 1rem; grid-template-columns: 1fr auto; grid-template-areas: "company setting" "app setting"; } @@ -227,7 +232,7 @@ sm-textarea { } .navbar__item__title { - font-size: 0.9rem; + font-size: 0.85rem; font-weight: 500; color: rgba(var(--text-color), 0.7); } @@ -248,6 +253,10 @@ sm-textarea { fill: white; } +#dashboard_page__header { + height: 7.5rem; +} + #base_header { width: 100%; } @@ -259,7 +268,7 @@ sm-textarea { } #send_flo_form { - padding: 0 1.5rem; + padding: 0 1rem; gap: 1rem; } @@ -274,7 +283,6 @@ sm-textarea { } #selected_contact_options { - margin-top: 0.5rem; list-style: none; } @@ -334,6 +342,7 @@ sm-textarea { border-radius: 0.5rem; background: rgba(var(--foreground-color), 1); box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.06); + -webkit-tap-highlight-color: transparent; } .contact-hidden-options { @@ -397,15 +406,10 @@ sm-textarea { } @media all and (max-width: 640px) { - body { - min-height: 100vh; - } - #main_card { min-height: 100%; grid-template-rows: auto 1fr auto; grid-template-areas: "header" "page" "nav"; - background: rgba(var(--foreground-color), 0.9); } #main_navbar { @@ -427,8 +431,10 @@ sm-textarea { } @media all and (min-width: 640px) { body { - place-content: center; - min-height: 100vh; + align-items: center; + justify-content: center; + background: url(lighthouse.svg) no-repeat center; + background-size: cover; } sm-popup { @@ -450,7 +456,6 @@ sm-textarea { grid-template-rows: min-content 1fr; grid-template-areas: "header page" "nav page"; box-shadow: 0 1.5rem 1.5rem rgba(0, 0, 0, 0.16); - background: rgba(var(--foreground-color), 0.9); } #main_header { @@ -464,6 +469,10 @@ sm-textarea { align-content: flex-start; } + .navbar__item__title { + font-size: 0.9rem; + } + #page_container { position: relative; grid-row: 1/3; @@ -480,7 +489,7 @@ sm-textarea { } .page__header { - padding: 2rem 1.5rem; + padding: 1.5rem; } #contacts_container { @@ -490,7 +499,7 @@ sm-textarea { #add_contact_button { right: 0; bottom: 0; - margin: 2rem 2.5rem; + margin: 2rem; } } @media all and (min-width: 1280px) { diff --git a/css/main.min.css b/css/main.min.css index 6b9deba..605820d 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1 +1 @@ -.interact,button{-webkit-tap-highlight-color:transparent;cursor:pointer;overflow:hidden}*,::after,::before{padding:0;margin:0;box-sizing:border-box;font-family:Roboto,sans-serif}:root{font-size:clamp(1rem,1.2vmax,3rem);--ease-in-overshhot:cubic-bezier(0.6, -0.28, 0.735, 0.045);--ease-out-overshhot:cubic-bezier(0.175, 0.885, 0.32, 1.275)}body{--accent-color:#6300d4;--text-color:17,17,17;--text-color-light:100,100,100;--foreground-color:255,255,255;--background-color:#efefef;--error-color:red;color:rgba(var(--text-color),1);background:url(lighthouse.svg) center no-repeat;background-size:cover}body[data-theme=dark]{--accent-color:#d7b5ff;--text-color:240,240,240;--text-color-light:170,170,170;--foreground-color:20,20,20;--error-color:rgb(255, 106, 106)}body[data-theme=dark] .contact-card{margin:.2rem 0;box-shadow:0 .1rem .5rem rgba(var(--text-color),.1)}button{position:relative;display:inline-flex;border:none;background:0 0;outline:0}button:focus-visible{outline:solid rgba(var(--text-color),1)}sm-input,sm-textarea{--border-radius:0.5rem}.flex{display:flex}.grid{display:grid}.align-center{align-items:center}#confirmation_popup,#prompt_popup,.direction-column{flex-direction:column}.justify-right{margin-left:auto}.space-between{justify-content:space-between}.hide{opacity:0;pointer-events:none}.hide-completely{display:none!important}.no-transformations{transform:none!important}.ripple{position:absolute;border-radius:50%;transform:scale(0);background:rgba(var(--text-color),.2);pointer-events:none}.interact,.page{position:relative}.icon{width:1.5rem;height:1.5rem;fill:rgba(var(--text-color),.7)}#confirmation_popup h4,#prompt_popup h4{font-weight:500;margin-bottom:.5rem}#confirmation_popup sm-button,#prompt_popup sm-button{margin:0}#confirmation_popup .flex,#prompt_popup .flex{padding:0;margin-top:1rem}#confirmation_popup .flex sm-button:first-of-type,#prompt_popup .flex sm-button:first-of-type{margin-right:.6rem;margin-left:auto}.popup__header{padding:.5rem 1.5rem 0 1rem;display:grid;grid-template-columns:auto 1fr;gap:.5rem;align-items:center;width:100%}.popup__header__close{padding:.5rem;cursor:pointer}.close-icon{height:2rem;width:2rem}#main_card{height:100%;grid-row:1/3}#main_header{grid-area:header;height:max-content;gap:.2rem .5rem;padding:1.5rem .7rem 1.5rem 1rem;grid-template-columns:1fr auto;grid-template-areas:"company setting" "app setting"}#page_container{grid-area:page}.page{display:flex;flex-direction:column;height:100%;width:100%}.header__company-name{grid-area:company;font-weight:500;color:rgba(var(--text-color),.7);align-self:flex-end}.header__app-name{align-self:flex-start;line-height:1;grid-area:app;font-weight:700;color:rgba(var(--text-color),.9)}.header__settings-button{grid-area:setting;padding:.8rem}#main_navbar{grid-area:nav}.navbar__item{display:flex;align-items:center;padding:.8rem;border-radius:.5rem}.navbar__item--active .navbar__item__icon{fill:var(--accent-color)}.navbar__item--active .navbar__item__title{color:var(--accent-color)}.navbar__item__icon{margin-right:1rem;fill:rgba(var(--text-color),.7)}.navbar__item__title{font-size:.9rem;font-weight:500;color:rgba(var(--text-color),.7)}.fab{position:absolute;display:inline-flex;border-radius:5rem;aspect-ratio:1/1;padding:.7rem;background:#FF1F1F;box-shadow:0 .5rem .5rem rgba(0,0,0,.16)}.fab__icon{height:1.8rem;width:1.8rem;fill:#fff}#base_header{width:100%}.header__title{font-size:1.1rem;font-weight:700;color:rgba(var(--text-color),.8)}.contact-card__name,.contact-option__name{font-weight:500;color:rgba(var(--text-color),.8)}#send_flo_form{padding:0 1.5rem;gap:1rem}#search_contacts{width:14rem;--padding:0.5rem 0.6rem;--background:rgba(var(--text-color), 0.1)}.search__icon{height:1.2rem}#selected_contact_options{margin-top:.5rem;list-style:none}.contact-option:not(:last-of-type){margin-right:.5rem}.contact-option:first-of-type{padding-right:.6rem;margin-right:auto;margin-left:-.6rem}.contact-option__button{display:inline-flex;align-items:center;border-radius:.3rem;padding:.5rem .6rem}.contact-option__icon{height:1.2rem;width:1.2rem;margin-right:.5rem}.contact-option__name{font-size:.9rem}#contacts_container{display:flex;flex-wrap:wrap;height:100%;min-height:0;overflow-y:auto;list-style:none;align-content:flex-start;padding:0 0 6rem 1rem;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}.contact-card{position:relative;overflow:hidden;display:flex;width:calc(50% - 1rem);flex-direction:column;align-items:center;margin:0 1rem 1rem 0;padding:1.5rem 1rem;flex-shrink:0;cursor:pointer;align-self:flex-start;justify-content:center;border-radius:.5rem;background:rgba(var(--foreground-color),1);box-shadow:0 .2rem .4rem rgba(0,0,0,.06)}.contact-hidden-options{position:absolute;width:100%;top:0;padding:.5rem .8rem}.contact-card__checkbox{--border-radius:0.5rem;--border-color:rgba(var(--text-color), 0.6)}.contact-card__initial{width:3rem;height:3rem;display:flex;padding:1.5rem;font-size:1.2rem;text-align:center;user-select:none;border-radius:1rem;margin:1.5rem 0;align-items:center;justify-content:center;background-color:rgba(var(--text-color),.1)}.contact-card__name{text-align:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:1rem}.contact-card__more{padding:.4rem}.more-icon{height:1.2rem;width:1.2rem}#add_contact_button{right:0;bottom:0;margin:1.5rem}.page__header{padding:1rem 1rem 2rem}.page__title{font-size:1.1rem}@media all and (max-width:640px){body{min-height:100vh}#main_card{min-height:100%;grid-template-rows:auto 1fr auto;grid-template-areas:"header" "page" "nav";background:rgba(var(--foreground-color),.9)}#main_navbar{flex-direction:row}.navbar__item{flex:1;flex-direction:column}.navbar__item__icon{margin:0 0 .5rem}}@media all and (min-width:640px){body{place-content:center;min-height:100vh}sm-popup{--width:24rem}.popup__header{padding:1rem 1.5rem 0 1rem}#main_card{position:relative;width:80vw;height:80vh;border-radius:.5rem;overflow:hidden;backdrop-filter:blur(1rem);grid-template-columns:15rem 1fr;grid-template-rows:min-content 1fr;grid-template-areas:"header page" "nav page";box-shadow:0 1.5rem 1.5rem rgba(0,0,0,.16);background:rgba(var(--foreground-color),.9)}#main_header{padding:1.5rem 1.2rem 1.5rem 2rem}#main_navbar{flex-direction:column;height:100%;padding:0 1rem;align-content:flex-start}#page_container{position:relative;grid-row:1/3}#page_container::after{content:"";position:absolute;left:0;height:65%;width:1px;background:rgba(var(--text-color),.2);top:50%;transform:translateY(-50%)}.page__header{padding:2rem 1.5rem}#contacts_container{padding:0 1rem 8rem 2rem}#add_contact_button{right:0;bottom:0;margin:2rem 2.5rem}}@media all and (min-width:1280px){#main_card{width:65vw;height:85vh}.contact-card{width:calc(33.33% - 1rem)}}@media all and (min-width:1920px){#main_card{width:60vw;height:65vh}}@media (any-hover:hover){.contact-card__checkbox:not([checked]):not(:focus-within),.contact-card__more:not(:focus-within){opacity:0;transition:opacity .3s}.contact-card:hover .contact-card__checkbox,.contact-card:hover .contact-card__more{opacity:1}.contact-option__button{transition:background .3s}.contact-option__button:hover{background-color:rgba(var(--text-color),.1)}} \ No newline at end of file +.contact-card,.interact,button{cursor:pointer;-webkit-tap-highlight-color:transparent;overflow:hidden}*,::after,::before{padding:0;margin:0;box-sizing:border-box;font-family:Roboto,sans-serif}:root{height:100%;font-size:clamp(1rem,1.2vmax,3rem);--ease-in-overshhot:cubic-bezier(0.6, -0.28, 0.735, 0.045);--ease-out-overshhot:cubic-bezier(0.175, 0.885, 0.32, 1.275)}body{--accent-color:#6300d4;--text-color:17,17,17;--text-color-light:100,100,100;--foreground-color:255,255,255;--background-color:#efefef;--error-color:red;color:rgba(var(--text-color),1);height:calc(100%)}body[data-theme=dark]{--accent-color:#d7b5ff;--text-color:240,240,240;--text-color-light:170,170,170;--foreground-color:20,20,20;--error-color:rgb(255, 106, 106)}body[data-theme=dark] .contact-card{margin:.2rem 0;box-shadow:0 .1rem .5rem rgba(var(--text-color),.1)}button{position:relative;display:inline-flex;border:none;background:0 0;outline:0}button:focus-visible{outline:solid rgba(var(--text-color),1)}sm-input,sm-textarea{--border-radius:0.5rem}.flex{display:flex}.grid{display:grid}.align-center{align-items:center}#confirmation_popup,#prompt_popup,.direction-column{flex-direction:column}.justify-right{margin-left:auto}.space-between{justify-content:space-between}.full-width{width:100%}.hide{opacity:0;pointer-events:none}.hide-completely{display:none!important}.no-transformations{transform:none!important}.ripple{position:absolute;border-radius:50%;transform:scale(0);background:rgba(var(--text-color),.2);pointer-events:none}.interact,.page{position:relative}.icon{width:1.5rem;height:1.5rem;fill:rgba(var(--text-color),.7)}#confirmation_popup h4,#prompt_popup h4{font-weight:500;margin-bottom:.5rem}#confirmation_popup sm-button,#prompt_popup sm-button{margin:0}#confirmation_popup .flex,#prompt_popup .flex{padding:0;margin-top:1rem}#confirmation_popup .flex sm-button:first-of-type,#prompt_popup .flex sm-button:first-of-type{margin-right:.6rem;margin-left:auto}.popup__header{padding:.5rem 1.5rem 0 1rem;display:grid;grid-template-columns:auto 1fr;gap:.5rem;align-items:center;width:100%}.popup__header__close{padding:.5rem;cursor:pointer}.close-icon{height:2rem;width:2rem}#main_card{height:100%;grid-row:1/3;background:rgba(var(--foreground-color),.9)}#main_header{grid-area:header;height:max-content;gap:.2rem .5rem;padding:1rem .7rem 1rem 1rem;grid-template-columns:1fr auto;grid-template-areas:"company setting" "app setting"}#page_container{grid-area:page}.page{display:flex;flex-direction:column;height:100%;width:100%}.header__company-name{grid-area:company;font-weight:500;color:rgba(var(--text-color),.7);align-self:flex-end}.header__app-name{align-self:flex-start;line-height:1;grid-area:app;font-weight:700;color:rgba(var(--text-color),.9)}.header__settings-button{grid-area:setting;padding:.8rem}#main_navbar{grid-area:nav}.navbar__item{display:flex;align-items:center;padding:.8rem;border-radius:.5rem}.navbar__item--active .navbar__item__icon{fill:var(--accent-color)}.navbar__item--active .navbar__item__title{color:var(--accent-color)}.navbar__item__icon{margin-right:1rem;fill:rgba(var(--text-color),.7)}.navbar__item__title{font-size:.85rem;font-weight:500;color:rgba(var(--text-color),.7)}.fab{position:absolute;display:inline-flex;border-radius:5rem;aspect-ratio:1/1;padding:.7rem;background:#FF1F1F;box-shadow:0 .5rem .5rem rgba(0,0,0,.16)}.fab__icon{height:1.8rem;width:1.8rem;fill:#fff}#dashboard_page__header{height:7.5rem}#base_header{width:100%}.header__title{font-size:1.1rem;font-weight:700;color:rgba(var(--text-color),.8)}.contact-card__name,.contact-option__name{font-weight:500;color:rgba(var(--text-color),.8)}#send_flo_form{padding:0 1rem;gap:1rem}#search_contacts{width:14rem;--padding:0.5rem 0.6rem;--background:rgba(var(--text-color), 0.1)}.search__icon{height:1.2rem}#selected_contact_options{list-style:none}.contact-option:not(:last-of-type){margin-right:.5rem}.contact-option:first-of-type{padding-right:.6rem;margin-right:auto;margin-left:-.6rem}.contact-option__button{display:inline-flex;align-items:center;border-radius:.3rem;padding:.5rem .6rem}.contact-option__icon{height:1.2rem;width:1.2rem;margin-right:.5rem}.contact-option__name{font-size:.9rem}#contacts_container{display:flex;flex-wrap:wrap;height:100%;min-height:0;overflow-y:auto;list-style:none;align-content:flex-start;padding:0 0 6rem 1rem;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}.contact-card{position:relative;display:flex;width:calc(50% - 1rem);flex-direction:column;align-items:center;margin:0 1rem 1rem 0;padding:1.5rem 1rem;flex-shrink:0;align-self:flex-start;justify-content:center;border-radius:.5rem;background:rgba(var(--foreground-color),1);box-shadow:0 .2rem .4rem rgba(0,0,0,.06)}.contact-hidden-options{position:absolute;width:100%;top:0;padding:.5rem .8rem}.contact-card__checkbox{--border-radius:0.5rem;--border-color:rgba(var(--text-color), 0.6)}.contact-card__initial{width:3rem;height:3rem;display:flex;padding:1.5rem;font-size:1.2rem;text-align:center;user-select:none;border-radius:1rem;margin:1.5rem 0;align-items:center;justify-content:center;background-color:rgba(var(--text-color),.1)}.contact-card__name{text-align:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:1rem}.contact-card__more{padding:.4rem}.more-icon{height:1.2rem;width:1.2rem}#add_contact_button{right:0;bottom:0;margin:1.5rem}.page__header{padding:1rem 1rem 2rem}.page__title{font-size:1.1rem}@media all and (max-width:640px){#main_card{min-height:100%;grid-template-rows:auto 1fr auto;grid-template-areas:"header" "page" "nav"}#main_navbar{flex-direction:row}.navbar__item{flex:1;flex-direction:column}.navbar__item__icon{margin:0 0 .5rem}}@media all and (min-width:640px){body{align-items:center;justify-content:center;background:url(lighthouse.svg) center no-repeat;background-size:cover}sm-popup{--width:24rem}.popup__header{padding:1rem 1.5rem 0 1rem}#main_card{position:relative;width:80vw;height:80vh;border-radius:.5rem;overflow:hidden;backdrop-filter:blur(1rem);grid-template-columns:15rem 1fr;grid-template-rows:min-content 1fr;grid-template-areas:"header page" "nav page";box-shadow:0 1.5rem 1.5rem rgba(0,0,0,.16)}#main_header{padding:1.5rem 1.2rem 1.5rem 2rem}#main_navbar{flex-direction:column;height:100%;padding:0 1rem;align-content:flex-start}.navbar__item__title{font-size:.9rem}#page_container{position:relative;grid-row:1/3}#page_container::after{content:"";position:absolute;left:0;height:65%;width:1px;background:rgba(var(--text-color),.2);top:50%;transform:translateY(-50%)}.page__header{padding:1.5rem}#contacts_container{padding:0 1rem 8rem 2rem}#add_contact_button{right:0;bottom:0;margin:2rem}}@media all and (min-width:1280px){#main_card{width:65vw;height:85vh}.contact-card{width:calc(33.33% - 1rem)}}@media all and (min-width:1920px){#main_card{width:60vw;height:65vh}}@media (any-hover:hover){.contact-card__checkbox:not([checked]):not(:focus-within),.contact-card__more:not(:focus-within){opacity:0;transition:opacity .3s}.contact-card:hover .contact-card__checkbox,.contact-card:hover .contact-card__more{opacity:1}.contact-option__button{transition:background .3s}.contact-option__button:hover{background-color:rgba(var(--text-color),.1)}} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index 39178dc..afd6042 100644 --- a/css/main.scss +++ b/css/main.scss @@ -7,6 +7,7 @@ font-family: 'Roboto', sans-serif; } :root{ + height: 100%; font-size: clamp(1rem, 1.2vmax, 3rem); --ease-in-overshhot: cubic-bezier(0.6, -0.28, 0.735, 0.045); --ease-out-overshhot: cubic-bezier(0.175, 0.885, 0.32, 1.275); @@ -19,8 +20,7 @@ body{ --background-color: #efefef; --error-color: red; color: rgba(var(--text-color), 1); - background: url(lighthouse.svg) no-repeat center; - background-size: cover; + height: calc(100%); } body[data-theme='dark']{ --accent-color:#d7b5ff; @@ -68,6 +68,9 @@ sm-textarea{ .space-between{ justify-content: space-between; } +.full-width{ + width: 100%; +} .hide{ opacity: 0; pointer-events: none; @@ -134,12 +137,13 @@ sm-textarea{ #main_card{ height: 100%; grid-row: 1/3; + background: rgba(var(--foreground-color), 0.9); } #main_header{ grid-area: header; height: max-content; gap: 0.2rem 0.5rem; - padding: 1.5rem 0.7rem 1.5rem 1rem; + padding: 1rem 0.7rem 1rem 1rem; grid-template-columns: 1fr auto; grid-template-areas: 'company setting' 'app setting'; } @@ -179,7 +183,6 @@ sm-textarea{ padding: 0.8rem; border-radius: 0.5rem; &--active{ - .navbar__item__icon{ fill: var(--accent-color); } @@ -194,7 +197,7 @@ sm-textarea{ fill: rgba(var(--text-color), 0.7); } .navbar__item__title{ - font-size: 0.9rem; + font-size: 0.85rem; font-weight: 500; color: rgba(var(--text-color), 0.7); } @@ -214,7 +217,9 @@ sm-textarea{ fill: white; } - +#dashboard_page__header{ + height: 7.5rem; +} #base_header{ width: 100%; } @@ -224,7 +229,7 @@ sm-textarea{ color: rgba(var(--text-color), 0.8); } #send_flo_form{ - padding: 0 1.5rem; + padding: 0 1rem; gap: 1rem; } @@ -238,7 +243,6 @@ sm-textarea{ height: 1.2rem; } #selected_contact_options{ - margin-top: 0.5rem; list-style: none; } .contact-option{ @@ -295,6 +299,7 @@ sm-textarea{ border-radius: 0.5rem; background: rgba(var(--foreground-color), 1); box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.06); + -webkit-tap-highlight-color: transparent; } .contact-hidden-options{ position: absolute; @@ -352,14 +357,10 @@ sm-textarea{ // mobile only styles @media all and (max-width: 640px){ - body{ - min-height: 100vh; - } #main_card{ min-height: 100%; grid-template-rows: auto 1fr auto; grid-template-areas: 'header' 'page' 'nav'; - background: rgba(var(--foreground-color), 0.9); } #main_navbar{ flex-direction: row; @@ -379,8 +380,10 @@ sm-textarea{ // desktop only styles @media all and (min-width: 640px){ body{ - place-content: center; - min-height: 100vh; + align-items: center; + justify-content: center; + background: url(lighthouse.svg) no-repeat center; + background-size: cover; } sm-popup{ --width: 24rem; @@ -399,7 +402,6 @@ sm-textarea{ grid-template-rows: min-content 1fr; grid-template-areas: 'header page' 'nav page'; box-shadow: 0 1.5rem 1.5rem rgba(0, 0, 0, 0.16); - background: rgba(var(--foreground-color), 0.9); } #main_header{ padding: 1.5rem 1.2rem 1.5rem 2rem; @@ -410,6 +412,9 @@ sm-textarea{ padding: 0 1rem; align-content: flex-start; } + .navbar__item__title{ + font-size: 0.9rem; + } #page_container{ position: relative; grid-row: 1/3; @@ -425,7 +430,7 @@ sm-textarea{ } } .page__header{ - padding: 2rem 1.5rem; + padding: 1.5rem; } #contacts_container{ padding: 0 1rem 8rem 2rem; @@ -433,7 +438,7 @@ sm-textarea{ #add_contact_button{ right: 0; bottom: 0; - margin: 2rem 2.5rem; + margin: 2rem; } } @media all and (min-width: 1280px){ diff --git a/new.html b/new.html index 31519b6..e72bdde 100644 --- a/new.html +++ b/new.html @@ -11,7 +11,7 @@ - +