From 6ded0d217eeb636a24f13f0a006fff81d3ce790f Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Sun, 13 Aug 2023 05:03:49 +0530 Subject: [PATCH] Adding KYC searching --- assets/undraw_file_searching_re_3evy.svg | 9 + css/main.css | 133 +++++++++++ css/main.min.css | 2 +- css/main.scss | 124 +++++++++++ index.html | 270 +++++++++++++++++++---- scripts/components.min.js | 2 +- 6 files changed, 500 insertions(+), 40 deletions(-) create mode 100644 assets/undraw_file_searching_re_3evy.svg diff --git a/assets/undraw_file_searching_re_3evy.svg b/assets/undraw_file_searching_re_3evy.svg new file mode 100644 index 0000000..86dc87e --- /dev/null +++ b/assets/undraw_file_searching_re_3evy.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/css/main.css b/css/main.css index 02bea90..a31acfb 100644 --- a/css/main.css +++ b/css/main.css @@ -657,6 +657,78 @@ h3 { color: rgba(var(--text-color), 0.8); } +#kyc_status { + display: flex; + flex-direction: column; + gap: 0.5rem; + min-height: 100%; + padding-bottom: 2rem; +} +#kyc_status header { + margin-bottom: 3rem; +} +#kyc_status sm-input { + font-weight: 500; + font-size: 1rem; +} +#kyc_status .icon-only { + width: 3.2rem; + height: 3.2rem; + border-radius: 0.5rem; +} +#kyc_status > *:not(:first-child) { + margin: 0 auto; + width: min(100% - 2rem, 40rem); +} + +#search_illustration { + height: 12rem; + justify-self: center; + align-self: center; + margin-bottom: 1rem; + flex-shrink: 0; +} + +#verification_result { + display: grid; + gap: 1rem; + background-color: rgba(var(--text-color), 0.05); + border-radius: 0.5rem; + padding: max(1rem, 2vw); + line-height: 1.7; + width: 100%; +} +#verification_result:empty { + display: none; +} +#verification_result .icon { + width: 3rem; + height: 3rem; +} +#verification_result[data-status=valid] .icon { + fill: var(--green); +} +#verification_result[data-status=invalid] .icon { + fill: var(--danger-color); +} + +.info { + font-size: 0.9rem; + color: rgba(var(--text-color), 0.8); + gap: 0.3rem 1.5rem; +} +.info > :first-child { + font-weight: 500; + min-width: 5rem; + text-align: end; +} +.info:last-child { + text-align: start; +} +.info sm-copy { + font-weight: 500; +} + #user_popup_button { background-color: rgba(var(--text-color), 0.06); border-radius: 2rem; @@ -820,6 +892,64 @@ h3 { max-height: 100%; } +#commit_approvals { + position: fixed; + bottom: 0; + right: 0; + border-radius: 0.5rem; + margin: 1rem; + padding: 1rem 1.2rem; +} +#commit_approvals[data-count="0"] { + display: none; +} +#commit_approvals::after { + content: attr(data-count); + display: flex; + margin-left: 0.5rem; + background-color: rgba(var(--background-color), 1); + color: rgba(var(--text-color), 1); + font-size: 0.9rem; + font-weight: 500; + height: 1.2rem; + min-width: 1.2rem; + padding: 0.2rem; + line-height: 1; + border-radius: 1.5rem; + align-items: center; + justify-content: center; + -webkit-animation: pulse 0.5s infinite alternate; + animation: pulse 0.5s infinite alternate; +} + +#commit_approvals_popup { + --width: min(32rem, 100%); +} + +.kyc-to-commit { + padding: 1rem; + border-radius: 0.5rem; + background-color: rgba(var(--foreground-color), 1); + justify-items: flex-start; +} + +@-webkit-keyframes pulse { + 0% { + transform: scale(1); + } + 100% { + transform: scale(1.15); + } +} + +@keyframes pulse { + 0% { + transform: scale(1); + } + 100% { + transform: scale(1.15); + } +} @media screen and (max-width: 40rem) { theme-toggle { order: 2; @@ -833,6 +963,9 @@ h3 { } } @media screen and (min-width: 64rem) { + h1 { + font-size: 3vw; + } sm-popup { --width: 24rem; } diff --git a/css/main.min.css b/css/main.min.css index 364ec3d..47b71a7 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1 +1 @@ -*{padding:0;margin:0;box-sizing:border-box;font-family:"Roboto",sans-serif}:root{font-size:clamp(1rem,1.2vmax,1.2rem)}html,body{height:100%}body{--accent-color: #365eff;--text-color: 30, 30, 30;--background-color: 240, 240, 240;--foreground-color: 250, 250, 250;--danger-color: rgb(255, 75, 75);--green: #1cad59;scrollbar-width:thin;scrollbar-gutter:stable;color:rgba(var(--text-color), 1);background-color:rgba(var(--background-color), 1);transition:background-color .3s}body[data-theme=dark]{--accent-color: #86afff;--text-color: 220, 220, 220;--background-color: 10, 10, 10;--foreground-color: 24, 24, 24;--danger-color: rgb(255, 106, 106);--green: #00e676}body[data-theme=dark] sm-popup::part(popup){background-color:rgba(var(--foreground-color), 1)}body[data-theme=dark] ::-webkit-calendar-picker-indicator{filter:invert(1)}p,strong{font-size:.9rem;max-width:65ch;line-height:1.7;color:rgba(var(--text-color), 0.9)}p:not(:last-of-type),strong:not(:last-of-type){margin-bottom:1.5rem}a{text-decoration:none;color:var(--accent-color)}a:focus-visible{box-shadow:0 0 0 .1rem rgba(var(--text-color), 1) inset}button,.button{-webkit-user-select:none;-moz-user-select:none;user-select:none;position:relative;display:inline-flex;border:none;background-color:rgba(0,0,0,0);overflow:hidden;color:inherit;-webkit-tap-highlight-color:rgba(0,0,0,0);align-items:center;font-size:inherit;font-weight:500;white-space:nowrap;padding:.6rem .9rem;border-radius:.3rem;justify-content:center}button:focus-visible,.button:focus-visible{outline:var(--accent-color) solid medium}button:not(:disabled),.button:not(:disabled){cursor:pointer}.button{background-color:rgba(var(--text-color), 0.02);border:solid thin rgba(var(--text-color), 0.06)}.button--primary{color:rgba(var(--background-color), 1);background-color:var(--accent-color)}.button--primary .icon{fill:rgba(var(--background-color), 1)}.button--colored{color:var(--accent-color)}.button--colored .icon{fill:var(--accent-color)}.button--danger{background-color:rgba(255,115,115,.062745098);color:var(--danger-color)}.button--danger .icon{fill:var(--danger-color)}.button--small{padding:.4rem .6rem}.button--outlined{border:solid rgba(var(--text-color), 0.3) .1rem;background-color:rgba(var(--foreground-color), 1)}.button--transparent{background-color:rgba(0,0,0,0)}button:disabled{opacity:.4;cursor:not-allowed;filter:saturate(0)}.cta{text-transform:uppercase;font-size:.9rem;font-weight:700;letter-spacing:.05em;padding:.8rem 1rem}.icon{width:1.2rem;height:1.2rem;fill:rgba(var(--text-color), 0.8);flex-shrink:0}.icon-only{padding:.5rem;border-radius:.3rem}.icon--big{width:3rem;height:3rem}a:-webkit-any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}a:-moz-any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}a:any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}details{padding:1rem 0}details summary{display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;align-items:center;justify-content:space-between;color:var(--accent-color)}details[open] summary{margin-bottom:1rem}details[open]>summary .down-arrow{transform:rotate(180deg)}sm-input,sm-textarea{font-size:.9rem;--border-radius: 0.5rem;--background-color: rgba(var(--foreground-color), 1)}sm-input button .icon,sm-textarea button .icon{fill:var(--accent-color)}sm-textarea{--max-height: auto}sm-spinner{--size: 1rem;--stroke-width: 0.1rem}sm-form{--gap: 1rem}sm-chips{--gap: 0.3rem}sm-chip{position:relative;font-size:.9rem;--border-radius: 0.5rem;--padding: 0.5rem 0.8rem;--background: rgba(var(--text-color), 0.06);-webkit-user-select:none;-moz-user-select:none;user-select:none}sm-chip[selected]{--background: var(--accent-color);color:rgba(var(--background-color), 1)}sm-select::part(options){max-height:40vh}sm-option{flex-shrink:0}sm-option::part(option){grid-template-columns:none}ul{list-style:none}fieldset{display:grid;gap:.5rem;padding:1rem;border-radius:.5rem;border:solid 1px rgba(var(--text-color), 0.3)}fieldset legend{font-size:.9rem;font-weight:500}input[type=radio]{height:1.1em;width:1.1em;margin-right:.5rem;accent-color:var(--accent-color)}.overflow-ellipsis{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.wrap-around{overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.full-bleed{grid-column:1/-1}.h1{font-size:1.5rem}.h2{font-size:1.2rem}h3{font-size:1.2rem;line-height:1.3}.h4{font-size:.9rem}.h5{font-size:.75rem}.uppercase{text-transform:uppercase}.capitalize::first-letter{text-transform:uppercase}.sticky{position:-webkit-sticky;position:sticky}.top-0{top:0}.flex{display:flex}.flex-1{flex:1}.flex-wrap{flex-wrap:wrap}.grid{display:grid}.flow-column{grid-auto-flow:column}.gap-0-3{gap:.3rem}.gap-0-5{gap:.5rem}.gap-1{gap:1rem}.gap-1-5{gap:1.5rem}.gap-2{gap:2rem}.gap-3{gap:3rem}.text-align-right{text-align:right}.align-start{align-content:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.align-content-center{align-content:center}.text-center{text-align:center}.justify-start{justify-content:start}.justify-content-center{justify-content:center}.justify-items-center{justify-items:center}.justify-right{margin-left:auto}.justify-items-center{justify-items:center}.align-self-center{align-self:center}.justify-self-center{justify-self:center}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.flex-direction-column{flex-direction:column}.space-between{justify-content:space-between}.w-100{width:100%}.h-100{height:100%}.margin-left-0-5{margin-left:.5rem}.margin-left-auto{margin-left:auto}.margin-right-0-5{margin-right:.5rem}.margin-right-auto{margin-right:auto}.ripple{height:8rem;width:8rem;position:absolute;border-radius:50%;transform:scale(0);background:radial-gradient(circle, rgba(var(--text-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%);pointer-events:none}.button--primary .ripple,.button--danger .ripple{background:radial-gradient(circle, rgba(var(--background-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%)}.interact{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.empty-state{display:grid;width:100%;padding:1.5rem 0}.observe-empty-state:empty{display:none !important}.observe-empty-state:not(:empty)+.empty-state{display:none}.bullet-point{display:flex;align-items:center;justify-content:center;margin:0 .8ch}.bullet-point::after{content:"";height:.4ch;width:.4ch;border-radius:.5em;background-color:var(--accent-color)}.multi-state-button{display:grid;text-align:center;align-items:center;justify-items:center}.multi-state-button>*{grid-area:1/1/2/2}.multi-state-button button{z-index:1;width:100%}.password-field label{display:flex;justify-content:center}.password-field label input:checked~.visible{display:none}.password-field label input:not(:checked)~.invisible{display:none}#app_body{display:block;height:100%;width:100%}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{margin-bottom:.5rem}#confirmation_popup .flex,#prompt_popup .flex{margin-top:1rem}.popup__header{position:relative;display:grid;gap:.5rem;width:100%;padding:0 1.5rem 0 .5rem;align-items:center;grid-template-columns:auto 1fr}.popup__header>*{grid-row:1}.popup__header h3,.popup__header h4{grid-column:1/-1;justify-self:center;align-self:center}.popup__header__close{grid-column:1}#loading{position:fixed;display:grid;top:0;left:0;bottom:0;right:0;place-content:center;justify-items:center;background:rgba(var(--foreground-color), 1);z-index:10}#loading h4{margin-top:1.5rem;font-weight:500}#loading sm-spinner{--size: 1.5rem}#sign_in,#sign_up{display:grid;width:100%;height:100%;justify-items:center;align-content:center;padding:1.5rem}#sign_in section,#sign_up section{width:min(26rem,100%)}#sign_in sm-form,#sign_up sm-form{margin:2rem 0}.generated-keys-wrapper{padding:1rem;background-color:rgba(var(--foreground-color), 1);border-radius:.5rem}#flo_id_warning{padding-bottom:1.5rem}#flo_id_warning .icon{height:3rem;width:3rem;padding:.8rem;overflow:visible;background-color:#ffc107;border-radius:3rem;fill:rgba(0,0,0,.8)}#main_header{display:flex;gap:1rem;padding:max(1rem,1.5vw);width:100%;align-items:center}.app-brand{display:flex;gap:.3rem;align-items:center}.app-brand .icon{height:1.7rem;width:1.7rem}.app-name__company{font-size:.8rem;font-weight:500;color:rgba(var(--text-color), 0.8)}#user_popup_button{background-color:rgba(var(--text-color), 0.06);border-radius:2rem;font-size:.9rem;text-overflow:ellipsis;overflow:hidden}#home_page{display:flex;flex-direction:column;gap:1rem}#home_page section{display:grid;width:min(100% - 2rem,42rem);margin:0 auto;margin-bottom:1rem}.card{padding:1.5rem;border-radius:.5rem;box-shadow:0 0 0 1px rgba(var(--text-color), 0.1);background-color:rgba(var(--foreground-color), 1);transition:box-shadow .2s,background-color .2s}.drag-drop-container{text-align:center;cursor:pointer;align-items:center;justify-items:center;padding:3vmax 1rem}.drag-drop-container.drag-over,.drag-drop-container:hover{box-shadow:0 0 0 .1rem var(--accent-color)}.drag-drop-container .drag-drop-text{font-size:1.1rem;color:rgba(var(--text-color), 0.9)}.drag-drop-container p{max-width:100%;color:rgba(var(--text-color), 0.6)}.drag-drop-container strong{font-weight:500;color:rgba(var(--text-color), 0.8);background-color:rgba(var(--text-color), 0.06);border-radius:3rem;padding:.1rem .8rem}#upload_icon{width:8rem;height:8rem;margin-bottom:2rem;border-radius:5rem;padding:2rem;fill:rgba(var(--text-color), 0.5);background-color:rgba(var(--background-color), 1);overflow:visible}#file_upload_section{margin-bottom:3rem}#file_upload_section fieldset{padding:.5rem}#file_upload_section label.interact{display:flex;align-items:center;border-radius:.3rem;padding:.5rem 1rem .5rem 0}#file_upload_section label.interact input{height:1.2em;width:1.2em;accent-color:var(--accent-color)}#verification_list{display:grid;gap:1rem}.verification-item{display:grid;grid-template-columns:6rem 1fr auto;align-items:center;justify-content:space-between;gap:1rem;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.verification-item time{font-size:.9rem;color:rgba(var(--text-color), 0.8)}.kyc-request{display:grid;gap:1rem;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.kyc-request time{font-size:.9rem;color:rgba(var(--text-color), 0.8)}.kyc-request h4{font-size:1.1rem}.kyc-request sm-copy{font-weight:500}.kyc-request button{text-transform:uppercase;padding:.4rem .6rem;font-size:.85rem;letter-spacing:.02em}.revoke-card{display:flex;flex-wrap:wrap;gap:1rem;justify-content:space-between;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.revoke-card label{gap:1rem;font-weight:500;cursor:pointer}.revoke-card input{accent-color:var(--accent-color);height:1.3em;width:1.3em}.revoke-card span:last-of-type{font-size:.9rem;color:rgba(var(--text-color), 0.8);font-weight:400}.revoke-card .button{padding:.5rem 1rem}#view_file_popup .button{padding:.5rem 1rem}#view_file_popup img{max-width:100%;max-height:100%}@media screen and (max-width: 40rem){theme-toggle{order:2}#user_popup_button{flex:1;order:1}.hide-on-small{display:none !important}}@media screen and (min-width: 64rem){sm-popup{--width: 24rem}.popup__header{padding:1.5rem 1.5rem 0 .75rem}#view_file_popup{--width: min(56rem, 100%)}}@media(any-hover: hover){::-webkit-scrollbar{width:.5rem;height:.5rem}::-webkit-scrollbar-thumb{background:rgba(var(--text-color), 0.3);border-radius:1rem}::-webkit-scrollbar-thumb:hover{background:rgba(var(--text-color), 0.5)}.interact:not([disabled]){transition:background-color .3s}.interact:not([disabled]):hover{background-color:rgba(var(--text-color), 0.06)}.button:not([disabled]){transition:background-color .3s,filter .3s}.button:not([disabled]):hover{filter:contrast(2)}}@supports(overflow: overlay){body{overflow:overlay}}.hidden{display:none !important}.progress{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:.3rem;border-radius:2rem;background-color:rgba(var(--text-color), 0.1);overflow:hidden}.progress__bar{position:absolute;top:0;left:0;width:0;height:100%;background-color:var(--accent-color);transition:width .3s ease-in-out} \ No newline at end of file +*{padding:0;margin:0;box-sizing:border-box;font-family:"Roboto",sans-serif}:root{font-size:clamp(1rem,1.2vmax,1.2rem)}html,body{height:100%}body{--accent-color: #365eff;--text-color: 30, 30, 30;--background-color: 240, 240, 240;--foreground-color: 250, 250, 250;--danger-color: rgb(255, 75, 75);--green: #1cad59;scrollbar-width:thin;scrollbar-gutter:stable;color:rgba(var(--text-color), 1);background-color:rgba(var(--background-color), 1);transition:background-color .3s}body[data-theme=dark]{--accent-color: #86afff;--text-color: 220, 220, 220;--background-color: 10, 10, 10;--foreground-color: 24, 24, 24;--danger-color: rgb(255, 106, 106);--green: #00e676}body[data-theme=dark] sm-popup::part(popup){background-color:rgba(var(--foreground-color), 1)}body[data-theme=dark] ::-webkit-calendar-picker-indicator{filter:invert(1)}p,strong{font-size:.9rem;max-width:65ch;line-height:1.7;color:rgba(var(--text-color), 0.9)}p:not(:last-of-type),strong:not(:last-of-type){margin-bottom:1.5rem}a{text-decoration:none;color:var(--accent-color)}a:focus-visible{box-shadow:0 0 0 .1rem rgba(var(--text-color), 1) inset}button,.button{-webkit-user-select:none;-moz-user-select:none;user-select:none;position:relative;display:inline-flex;border:none;background-color:rgba(0,0,0,0);overflow:hidden;color:inherit;-webkit-tap-highlight-color:rgba(0,0,0,0);align-items:center;font-size:inherit;font-weight:500;white-space:nowrap;padding:.6rem .9rem;border-radius:.3rem;justify-content:center}button:focus-visible,.button:focus-visible{outline:var(--accent-color) solid medium}button:not(:disabled),.button:not(:disabled){cursor:pointer}.button{background-color:rgba(var(--text-color), 0.02);border:solid thin rgba(var(--text-color), 0.06)}.button--primary{color:rgba(var(--background-color), 1);background-color:var(--accent-color)}.button--primary .icon{fill:rgba(var(--background-color), 1)}.button--colored{color:var(--accent-color)}.button--colored .icon{fill:var(--accent-color)}.button--danger{background-color:rgba(255,115,115,.062745098);color:var(--danger-color)}.button--danger .icon{fill:var(--danger-color)}.button--small{padding:.4rem .6rem}.button--outlined{border:solid rgba(var(--text-color), 0.3) .1rem;background-color:rgba(var(--foreground-color), 1)}.button--transparent{background-color:rgba(0,0,0,0)}button:disabled{opacity:.4;cursor:not-allowed;filter:saturate(0)}.cta{text-transform:uppercase;font-size:.9rem;font-weight:700;letter-spacing:.05em;padding:.8rem 1rem}.icon{width:1.2rem;height:1.2rem;fill:rgba(var(--text-color), 0.8);flex-shrink:0}.icon-only{padding:.5rem;border-radius:.3rem}.icon--big{width:3rem;height:3rem}a:-webkit-any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}a:-moz-any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}a:any-link:focus-visible{outline:rgba(var(--text-color), 1) .1rem solid}details{padding:1rem 0}details summary{display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;align-items:center;justify-content:space-between;color:var(--accent-color)}details[open] summary{margin-bottom:1rem}details[open]>summary .down-arrow{transform:rotate(180deg)}sm-input,sm-textarea{font-size:.9rem;--border-radius: 0.5rem;--background-color: rgba(var(--foreground-color), 1)}sm-input button .icon,sm-textarea button .icon{fill:var(--accent-color)}sm-textarea{--max-height: auto}sm-spinner{--size: 1rem;--stroke-width: 0.1rem}sm-form{--gap: 1rem}sm-chips{--gap: 0.3rem}sm-chip{position:relative;font-size:.9rem;--border-radius: 0.5rem;--padding: 0.5rem 0.8rem;--background: rgba(var(--text-color), 0.06);-webkit-user-select:none;-moz-user-select:none;user-select:none}sm-chip[selected]{--background: var(--accent-color);color:rgba(var(--background-color), 1)}sm-select::part(options){max-height:40vh}sm-option{flex-shrink:0}sm-option::part(option){grid-template-columns:none}ul{list-style:none}fieldset{display:grid;gap:.5rem;padding:1rem;border-radius:.5rem;border:solid 1px rgba(var(--text-color), 0.3)}fieldset legend{font-size:.9rem;font-weight:500}input[type=radio]{height:1.1em;width:1.1em;margin-right:.5rem;accent-color:var(--accent-color)}.overflow-ellipsis{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.wrap-around{overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.full-bleed{grid-column:1/-1}.h1{font-size:1.5rem}.h2{font-size:1.2rem}h3{font-size:1.2rem;line-height:1.3}.h4{font-size:.9rem}.h5{font-size:.75rem}.uppercase{text-transform:uppercase}.capitalize::first-letter{text-transform:uppercase}.sticky{position:-webkit-sticky;position:sticky}.top-0{top:0}.flex{display:flex}.flex-1{flex:1}.flex-wrap{flex-wrap:wrap}.grid{display:grid}.flow-column{grid-auto-flow:column}.gap-0-3{gap:.3rem}.gap-0-5{gap:.5rem}.gap-1{gap:1rem}.gap-1-5{gap:1.5rem}.gap-2{gap:2rem}.gap-3{gap:3rem}.text-align-right{text-align:right}.align-start{align-content:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.align-content-center{align-content:center}.text-center{text-align:center}.justify-start{justify-content:start}.justify-content-center{justify-content:center}.justify-items-center{justify-items:center}.justify-right{margin-left:auto}.justify-items-center{justify-items:center}.align-self-center{align-self:center}.justify-self-center{justify-self:center}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.flex-direction-column{flex-direction:column}.space-between{justify-content:space-between}.w-100{width:100%}.h-100{height:100%}.margin-left-0-5{margin-left:.5rem}.margin-left-auto{margin-left:auto}.margin-right-0-5{margin-right:.5rem}.margin-right-auto{margin-right:auto}.ripple{height:8rem;width:8rem;position:absolute;border-radius:50%;transform:scale(0);background:radial-gradient(circle, rgba(var(--text-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%);pointer-events:none}.button--primary .ripple,.button--danger .ripple{background:radial-gradient(circle, rgba(var(--background-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%)}.interact{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.empty-state{display:grid;width:100%;padding:1.5rem 0}.observe-empty-state:empty{display:none !important}.observe-empty-state:not(:empty)+.empty-state{display:none}.bullet-point{display:flex;align-items:center;justify-content:center;margin:0 .8ch}.bullet-point::after{content:"";height:.4ch;width:.4ch;border-radius:.5em;background-color:var(--accent-color)}.multi-state-button{display:grid;text-align:center;align-items:center;justify-items:center}.multi-state-button>*{grid-area:1/1/2/2}.multi-state-button button{z-index:1;width:100%}.password-field label{display:flex;justify-content:center}.password-field label input:checked~.visible{display:none}.password-field label input:not(:checked)~.invisible{display:none}#app_body{display:block;height:100%;width:100%}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{margin-bottom:.5rem}#confirmation_popup .flex,#prompt_popup .flex{margin-top:1rem}.popup__header{position:relative;display:grid;gap:.5rem;width:100%;padding:0 1.5rem 0 .5rem;align-items:center;grid-template-columns:auto 1fr}.popup__header>*{grid-row:1}.popup__header h3,.popup__header h4{grid-column:1/-1;justify-self:center;align-self:center}.popup__header__close{grid-column:1}#loading{position:fixed;display:grid;top:0;left:0;bottom:0;right:0;place-content:center;justify-items:center;background:rgba(var(--foreground-color), 1);z-index:10}#loading h4{margin-top:1.5rem;font-weight:500}#loading sm-spinner{--size: 1.5rem}#sign_in,#sign_up{display:grid;width:100%;height:100%;justify-items:center;align-content:center;padding:1.5rem}#sign_in section,#sign_up section{width:min(26rem,100%)}#sign_in sm-form,#sign_up sm-form{margin:2rem 0}.generated-keys-wrapper{padding:1rem;background-color:rgba(var(--foreground-color), 1);border-radius:.5rem}#flo_id_warning{padding-bottom:1.5rem}#flo_id_warning .icon{height:3rem;width:3rem;padding:.8rem;overflow:visible;background-color:#ffc107;border-radius:3rem;fill:rgba(0,0,0,.8)}#main_header{display:flex;gap:1rem;padding:max(1rem,1.5vw);width:100%;align-items:center}.app-brand{display:flex;gap:.3rem;align-items:center}.app-brand .icon{height:1.7rem;width:1.7rem}.app-name__company{font-size:.8rem;font-weight:500;color:rgba(var(--text-color), 0.8)}#kyc_status{display:flex;flex-direction:column;gap:.5rem;min-height:100%;padding-bottom:2rem}#kyc_status header{margin-bottom:3rem}#kyc_status sm-input{font-weight:500;font-size:1rem}#kyc_status .icon-only{width:3.2rem;height:3.2rem;border-radius:.5rem}#kyc_status>*:not(:first-child){margin:0 auto;width:min(100% - 2rem,40rem)}#search_illustration{height:12rem;justify-self:center;align-self:center;margin-bottom:1rem;flex-shrink:0}#verification_result{display:grid;gap:1rem;background-color:rgba(var(--text-color), 0.05);border-radius:.5rem;padding:max(1rem,2vw);line-height:1.7;width:100%}#verification_result:empty{display:none}#verification_result .icon{width:3rem;height:3rem}#verification_result[data-status=valid] .icon{fill:var(--green)}#verification_result[data-status=invalid] .icon{fill:var(--danger-color)}.info{font-size:.9rem;color:rgba(var(--text-color), 0.8);gap:.3rem 1.5rem}.info>:first-child{font-weight:500;min-width:5rem;text-align:end}.info:last-child{text-align:start}.info sm-copy{font-weight:500}#user_popup_button{background-color:rgba(var(--text-color), 0.06);border-radius:2rem;font-size:.9rem;text-overflow:ellipsis;overflow:hidden}#home_page{display:flex;flex-direction:column;gap:1rem}#home_page section{display:grid;width:min(100% - 2rem,42rem);margin:0 auto;margin-bottom:1rem}.card{padding:1.5rem;border-radius:.5rem;box-shadow:0 0 0 1px rgba(var(--text-color), 0.1);background-color:rgba(var(--foreground-color), 1);transition:box-shadow .2s,background-color .2s}.drag-drop-container{text-align:center;cursor:pointer;align-items:center;justify-items:center;padding:3vmax 1rem}.drag-drop-container.drag-over,.drag-drop-container:hover{box-shadow:0 0 0 .1rem var(--accent-color)}.drag-drop-container .drag-drop-text{font-size:1.1rem;color:rgba(var(--text-color), 0.9)}.drag-drop-container p{max-width:100%;color:rgba(var(--text-color), 0.6)}.drag-drop-container strong{font-weight:500;color:rgba(var(--text-color), 0.8);background-color:rgba(var(--text-color), 0.06);border-radius:3rem;padding:.1rem .8rem}#upload_icon{width:8rem;height:8rem;margin-bottom:2rem;border-radius:5rem;padding:2rem;fill:rgba(var(--text-color), 0.5);background-color:rgba(var(--background-color), 1);overflow:visible}#file_upload_section{margin-bottom:3rem}#file_upload_section fieldset{padding:.5rem}#file_upload_section label.interact{display:flex;align-items:center;border-radius:.3rem;padding:.5rem 1rem .5rem 0}#file_upload_section label.interact input{height:1.2em;width:1.2em;accent-color:var(--accent-color)}#verification_list{display:grid;gap:1rem}.verification-item{display:grid;grid-template-columns:6rem 1fr auto;align-items:center;justify-content:space-between;gap:1rem;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.verification-item time{font-size:.9rem;color:rgba(var(--text-color), 0.8)}.kyc-request{display:grid;gap:1rem;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.kyc-request time{font-size:.9rem;color:rgba(var(--text-color), 0.8)}.kyc-request h4{font-size:1.1rem}.kyc-request sm-copy{font-weight:500}.kyc-request button{text-transform:uppercase;padding:.4rem .6rem;font-size:.85rem;letter-spacing:.02em}.revoke-card{display:flex;flex-wrap:wrap;gap:1rem;justify-content:space-between;padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1)}.revoke-card label{gap:1rem;font-weight:500;cursor:pointer}.revoke-card input{accent-color:var(--accent-color);height:1.3em;width:1.3em}.revoke-card span:last-of-type{font-size:.9rem;color:rgba(var(--text-color), 0.8);font-weight:400}.revoke-card .button{padding:.5rem 1rem}#view_file_popup .button{padding:.5rem 1rem}#view_file_popup img{max-width:100%;max-height:100%}#commit_approvals{position:fixed;bottom:0;right:0;border-radius:.5rem;margin:1rem;padding:1rem 1.2rem}#commit_approvals[data-count="0"]{display:none}#commit_approvals::after{content:attr(data-count);display:flex;margin-left:.5rem;background-color:rgba(var(--background-color), 1);color:rgba(var(--text-color), 1);font-size:.9rem;font-weight:500;height:1.2rem;min-width:1.2rem;padding:.2rem;line-height:1;border-radius:1.5rem;align-items:center;justify-content:center;-webkit-animation:pulse .5s infinite alternate;animation:pulse .5s infinite alternate}#commit_approvals_popup{--width: min(32rem, 100%)}.kyc-to-commit{padding:1rem;border-radius:.5rem;background-color:rgba(var(--foreground-color), 1);justify-items:flex-start}@-webkit-keyframes pulse{0%{transform:scale(1)}100%{transform:scale(1.15)}}@keyframes pulse{0%{transform:scale(1)}100%{transform:scale(1.15)}}@media screen and (max-width: 40rem){theme-toggle{order:2}#user_popup_button{flex:1;order:1}.hide-on-small{display:none !important}}@media screen and (min-width: 64rem){h1{font-size:3vw}sm-popup{--width: 24rem}.popup__header{padding:1.5rem 1.5rem 0 .75rem}#view_file_popup{--width: min(56rem, 100%)}}@media(any-hover: hover){::-webkit-scrollbar{width:.5rem;height:.5rem}::-webkit-scrollbar-thumb{background:rgba(var(--text-color), 0.3);border-radius:1rem}::-webkit-scrollbar-thumb:hover{background:rgba(var(--text-color), 0.5)}.interact:not([disabled]){transition:background-color .3s}.interact:not([disabled]):hover{background-color:rgba(var(--text-color), 0.06)}.button:not([disabled]){transition:background-color .3s,filter .3s}.button:not([disabled]):hover{filter:contrast(2)}}@supports(overflow: overlay){body{overflow:overlay}}.hidden{display:none !important}.progress{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:.3rem;border-radius:2rem;background-color:rgba(var(--text-color), 0.1);overflow:hidden}.progress__bar{position:absolute;top:0;left:0;width:0;height:100%;background-color:var(--accent-color);transition:width .3s ease-in-out} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index ea47eae..575bc26 100644 --- a/css/main.scss +++ b/css/main.scss @@ -618,6 +618,82 @@ h3 { color: rgba(var(--text-color), 0.8); } } + +#kyc_status { + display: flex; + flex-direction: column; + gap: 0.5rem; + min-height: 100%; + padding-bottom: 2rem; + header { + margin-bottom: 3rem; + } + sm-input { + font-weight: 500; + font-size: 1rem; + } + .icon-only { + width: 3.2rem; + height: 3.2rem; + border-radius: 0.5rem; + } + & > * { + &:not(:first-child) { + margin: 0 auto; + width: min(calc(100% - 2rem), 40rem); + } + } +} +#search_illustration { + height: 12rem; + justify-self: center; + align-self: center; + margin-bottom: 1rem; + flex-shrink: 0; +} +#verification_result { + display: grid; + gap: 1rem; + background-color: rgba(var(--text-color), 0.05); + border-radius: 0.5rem; + padding: max(1rem, 2vw); + line-height: 1.7; + width: 100%; + &:empty { + display: none; + } + .icon { + width: 3rem; + height: 3rem; + } + &[data-status="valid"] { + .icon { + fill: var(--green); + } + } + &[data-status="invalid"] { + .icon { + fill: var(--danger-color); + } + } +} +.info { + font-size: 0.9rem; + color: rgba(var(--text-color), 0.8); + gap: 0.3rem 1.5rem; + > :first-child { + font-weight: 500; + min-width: 5rem; + text-align: end; + } + &:last-child { + text-align: start; + } + sm-copy { + font-weight: 500; + } +} + #user_popup_button { background-color: rgba(var(--text-color), 0.06); border-radius: 2rem; @@ -776,6 +852,51 @@ h3 { max-height: 100%; } } +#commit_approvals { + &[data-count="0"] { + display: none; + } + position: fixed; + bottom: 0; + right: 0; + border-radius: 0.5rem; + margin: 1rem; + padding: 1rem 1.2rem; + &::after { + content: attr(data-count); + display: flex; + margin-left: 0.5rem; + background-color: rgba(var(--background-color), 1); + color: rgba(var(--text-color), 1); + font-size: 0.9rem; + font-weight: 500; + height: 1.2rem; + min-width: 1.2rem; + padding: 0.2rem; + line-height: 1; + border-radius: 1.5rem; + align-items: center; + justify-content: center; + animation: pulse 0.5s infinite alternate; + } +} +#commit_approvals_popup { + --width: min(32rem, 100%); +} +.kyc-to-commit { + padding: 1rem; + border-radius: 0.5rem; + background-color: rgba(var(--foreground-color), 1); + justify-items: flex-start; +} +@keyframes pulse { + 0% { + transform: scale(1); + } + 100% { + transform: scale(1.15); + } +} @media screen and (max-width: 40rem) { theme-toggle { order: 2; @@ -790,6 +911,9 @@ h3 { } @media screen and (min-width: 64rem) { + h1 { + font-size: 3vw; + } sm-popup { --width: 24rem; } diff --git a/index.html b/index.html index 17dfdbc..8d6b466 100644 --- a/index.html +++ b/index.html @@ -81,6 +81,19 @@
+ + +
+
@@ -260,7 +273,7 @@ }) } catch (err) { floGlobals.isPrivKeySecured = passwordType === 'PIN/Password'; - if (['#/landing', '#/sign_in', '#/sign_up'].includes(window.location.hash)) { + if (['#/landing', '#/sign_in', '#/sign_up', 'kyc_status'].includes(window.location.hash)) { router.routeTo(window.location.hash); } else { location.hash = `#/sign_in`; @@ -401,7 +414,10 @@ renderElem(getRef('app_body'), html`
-

Loading RanchiMall KYC Docs

+
+

Loading RanchiMall KYC Docs

+ +
`); }) @@ -471,6 +487,115 @@ `); }) + router.addRoute('kyc_status', (state) => { + renderElem(getRef('app_body'), html` +
+
+
+ +
+
RanchiMall
+

+ KYC Docs +

+
+
+ +
+

KYC verification

+ +
+ +
+ + +
+
+
+ +
+ `); + getRef('address_verify').customValidation = floCrypto.validateAddr; + if (state.params?.address) + verify(state.params?.address) + }) + function initSearch() { + location.hash = `#/kyc_status?address=${getRef('address_verify').value.trim()}`; + } + async function verify(address) { + if (address) { + if (getRef('address_verify').value.trim() !== address) + getRef('address_verify').value = address; + } else { + address = getRef('address_verify').value.trim(); + } + getRef('verification_result').classList.add('hidden'); + if (address === '') + return renderElem(getRef('verification_result'), html``); + if (!floCrypto.validateAddr(address)) { + getRef('address_verify').isValid + return renderElem(getRef('verification_result'), html``); + } + if (address === '') return renderElem(getRef('verification_result'), html`Please enter an address`); + const floAddress = getFloAddress(address) + const btcAddress = getBtcAddress(floAddress) + await floCloudAPI.requestGeneralData('userKycRequests', { + senderID: [floAddress, btcAddress] + }) + const kycRequests = floDapps.getNextGeneralData('userKycRequests', '0'); + const verifiedRequest = Object.values(kycRequests).find(request => { + const wasSentByAddress = request.senderID === floAddress || request.senderID === btcAddress + const isApproved = request.tag === 'approved' + return wasSentByAddress && isApproved + }); + getRef('verification_result').classList.remove('hidden'); + if (verifiedRequest) { + const { tag_key, tag_time } = verifiedRequest; + const issuedBy = floCrypto.getFloID(tag_key) + console.log(issuedBy) + getRef('verification_result').dataset.status = 'valid'; + renderElem(getRef('verification_result'), html` +
+ +

KYC successfully verified

+
+
+ Issued by +
+

${floGlobals.appObjects.kycDocs.issuers[issuedBy]?.name || 'Unknown'}

+
+

BTC address

+ +
+
+

FLO address

+ +
+
+
+
+ Issued on + ${getFormattedTime(tag_time, 'date-only')} +
+
+ Verified on + ${getFormattedTime(Date.now(), 'date-only')} +
+ `); + } else { + renderElem(getRef('verification_result'), html` +
+ +

KYC data not found

+
+ `); + getRef('verification_result').dataset.status = 'invalid'; + } + + } let isShowingFloID = false; const docTypeNames = { 'aadhaar': 'Aadhaar Card', @@ -624,13 +749,13 @@ if (status) { switch (status) { case 'approved': - if (tag !== 'approved') return; + if (tag !== 'approved' && !floGlobals.approvalsToBeCommitted.has(floAddress)) return; break; case 'rejected': if (note !== 'rejected') return; break; case 'pending': - if (tag || note) return; + if (tag || note || floGlobals.approvalsToBeCommitted.has(floAddress)) return; break; } } @@ -656,7 +781,7 @@ let hasUploaded = {} const renderedRequests = Object.keys(requests).reverse().map((key) => { const { time, tag, message: { docType } } = requests[key]; - if (!isVerified && tag === 'verified') + if (!isVerified && tag === 'approved') isVerified = true; if (!tag) { concurrentRequests++; @@ -743,6 +868,7 @@ ${renderedKycRequests?.length ? renderedKycRequests : html`
  • No requests found
  • `} + `); } else { @@ -1062,8 +1188,10 @@ requestVC, docVC, }) + getRef('commit_approvals').dataset.count = floGlobals.approvalsToBeCommitted.size closePopup() floGlobals.currentRequest = null; + renderHome() } async function rejectKyc(e) { const confirmation = await getConfirmation('Are you sure you want to reject this user?', { @@ -1138,7 +1266,45 @@ getRef('aggregator_balance').classList.add('hidden') } } - async function approveAddresses() { + function initApproval() { + const approvedAddress = [...floGlobals.approvalsToBeCommitted.keys()].map(address => { + function removeKYC(e) { + floGlobals.approvalsToBeCommitted.delete(address) + getRef('commit_approvals').dataset.count = floGlobals.approvalsToBeCommitted.size + renderHome() + e.target.closest('li').remove() + if (floGlobals.approvalsToBeCommitted.size === 0) { + closePopup() + } + } + return html` +
  • +
    +

    BTC address

    + ${getBtcAddress(address)} +
    +
    +

    FLO address

    + ${address} +
    + +
  • + ` + }) + renderElem(getRef('commit_approvals_popup__content'), html` +
    +

    KYCs to commit

    + +
    + + `) + openPopup('commit_approvals_popup').closed.then(() => { + renderElem(getRef('commit_approvals_popup__content'), html``) + }) + } + async function commitApprovals() { const approverPrivateKey = await floDapps.user.private if (!floGlobals.isSubAdmin) { return notify('You are not authorized to approve KYC', 'error') @@ -1146,37 +1312,45 @@ if (floGlobals.approvalsToBeCommitted.size === 0) { return notify('Enter at least one address to approve', 'error') } - const addresses = [...floGlobals.approvalsToBeCommitted.keys()] - let floData = `KYC|APPROVE_KYC|${addresses.join('+')}` - console.log(floData) - if (floData.length > 1040) { - return notify('Too many addresses. Try removing one and resubmitting.', 'error') - } - getConfirmation('Approve entered addresses?', { + const confirmation = await getConfirmation('Approve all listed KYCs?', { confirmText: 'Approve', - }).then((res) => { - if (!res) return - buttonLoader(getRef('submit_kyc'), true) - floBlockchainAPI.writeData(approverAddress, floData, approverPrivateKey, approverAddress).then(txId => { - notify(`Approval request submitted. TXID: ${txId}`, 'success') - getRef('kyc_addresses_container').innerHTML = ''; - }).catch(e => { - notify(e, 'error') - }).finally(() => { - buttonLoader(getRef('submit_kyc'), false) - setTimeout(() => { - checkBalance() - }, 1000) - }) }) - // try { - // await floCloudAPI.tagApplicationData(requestVC, 'verified') - // await floCloudAPI.tagApplicationData(docVC, 'verified') - // notify('User verified successfully', 'success'); - // } catch (err) { - // console.error(err) - // notify('Error verifying user', 'error'); - // } + if (!confirmation) return + const addresses = [...floGlobals.approvalsToBeCommitted.keys()] + // create multiple txs if addresses exceed 1040 characters + const addressesPerTx = 30 + const chunks = Math.ceil(addresses.size / addressesPerTx) + let txPromises = [] + for (let i = 0; i < chunks; i++) { + const chunk = addresses.slice(i * addressesPerTx, (i + 1) * addressesPerTx) + const floData = `KYC|APPROVE_KYC|${chunk.join('+')}` + txPromises.push(floBlockchainAPI.writeData(floGlobals.myFloID, floData, approverPrivateKey, floGlobals.myFloID)) + } + const cloudPromises = [...floGlobals.approvalsToBeCommitted.values()].flatMap(({ requestVC, docVC }) => { + floGlobals.generalData[floCloudAPI.util.filterKey('userKycRequests')][requestVC].tag = 'approved' + return [ + floCloudAPI.tagApplicationData(requestVC, 'approved'), + floCloudAPI.tagApplicationData(docVC, 'approved') + ] + }) + buttonLoader(getRef('submit_kyc'), true) + try { + const [txIds, responses] = await Promise.all([...txPromises, ...cloudPromises]) + console.log(`Approval request submitted. TXIDs: ${txIds.join(', ')}`) + notify('Users approved successfully', 'success'); + floGlobals.approvalsToBeCommitted.clear() + getRef('commit_approvals').dataset.count = floGlobals.approvalsToBeCommitted.size + renderHome() + closePopup() + } catch (err) { + console.error(err) + notify('Error approving users', 'error'); + } finally { + buttonLoader(getRef('submit_kyc'), false) + setTimeout(() => { + checkBalance() + }, 1000) + } } async function revokeKycs() { try { @@ -1222,13 +1396,26 @@ } } + async function clearCredentials() { + await floDapps.clearCredentials(); + location.reload(); + } diff --git a/scripts/components.min.js b/scripts/components.min.js index 8e5623a..f50c5bb 100644 --- a/scripts/components.min.js +++ b/scripts/components.min.js @@ -2,7 +2,7 @@ const smChips = document.createElement("template"); smChips.innerHTML = '
    ', customElements.define("sm-chips", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smChips.content.cloneNode(!0)), this.chipsWrapper = this.shadowRoot.querySelector(".sm-chips"), this.coverLeft = this.shadowRoot.querySelector(".cover--left"), this.coverRight = this.shadowRoot.querySelector(".cover--right"), this.navButtonLeft = this.shadowRoot.querySelector(".nav-button--left"), this.navButtonRight = this.shadowRoot.querySelector(".nav-button--right"), this.slottedOptions = void 0, this._value = void 0, this.scrollDistance = 0, this.assignedElements = [], this.scrollLeft = this.scrollLeft.bind(this), this.scrollRight = this.scrollRight.bind(this), this.fireEvent = this.fireEvent.bind(this), this.setSelectedOption = this.setSelectedOption.bind(this) } get value() { return this._value } set value(t) { this.setSelectedOption(t) } scrollLeft() { this.chipsWrapper.scrollBy({ left: -this.scrollDistance, behavior: "smooth" }) } scrollRight() { this.chipsWrapper.scrollBy({ left: this.scrollDistance, behavior: "smooth" }) } setSelectedOption(t) { this._value !== t && (this._value = t, this.assignedElements.forEach(e => { e.value == t ? (e.setAttribute("selected", ""), e.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" })) : e.removeAttribute("selected") })) } fireEvent() { this.dispatchEvent(new CustomEvent("change", { bubbles: !0, composed: !0, detail: { value: this._value } })) } connectedCallback() { this.setAttribute("role", "listbox"); const t = this.shadowRoot.querySelector("slot"); t.addEventListener("slotchange", e => { n.disconnect(), i.disconnect(), this.observeSelf.disconnect(), clearTimeout(this.slotChangeTimeout), this.slotChangeTimeout = setTimeout(() => { this.assignedElements = t.assignedElements(), this.assignedElements.forEach(t => { t.hasAttribute("selected") && (this._value = t.value) }), this.observeSelf.observe(this) }, 0) }); const e = new ResizeObserver(t => { t.forEach(t => { if (t.contentBoxSize) { const e = Array.isArray(t.contentBoxSize) ? t.contentBoxSize[0] : t.contentBoxSize; this.scrollDistance = .6 * e.inlineSize } else this.scrollDistance = .6 * t.contentRect.width }) }); e.observe(this), this.observeSelf = new IntersectionObserver((t, e) => { t.forEach(t => { t.isIntersecting && !this.hasAttribute("multiline") && this.assignedElements.length > 0 && (n.observe(this.assignedElements[0]), i.observe(this.assignedElements[this.assignedElements.length - 1]), e.unobserve(this)) }) }, { threshold: 1 }), this.chipsWrapper.addEventListener("option-clicked", t => { this._value !== t.target.value && (this.setSelectedOption(t.target.value), this.fireEvent()) }); const n = new IntersectionObserver(t => { t.forEach(t => { t.isIntersecting ? (this.navButtonLeft.classList.add("hide"), this.coverLeft.classList.add("hide")) : (this.navButtonLeft.classList.remove("hide"), this.coverLeft.classList.remove("hide")) }) }, { threshold: 1, root: this }), i = new IntersectionObserver(t => { t.forEach(t => { t.isIntersecting ? (this.navButtonRight.classList.add("hide"), this.coverRight.classList.add("hide")) : (this.navButtonRight.classList.remove("hide"), this.coverRight.classList.remove("hide")) }) }, { threshold: 1, root: this }); this.navButtonLeft.addEventListener("click", this.scrollLeft), this.navButtonRight.addEventListener("click", this.scrollRight) } disconnectedCallback() { this.navButtonLeft.removeEventListener("click", this.scrollLeft), this.navButtonRight.removeEventListener("click", this.scrollRight) } }); const smChip = document.createElement("template"); smChip.innerHTML = ' ', customElements.define("sm-chip", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smChip.content.cloneNode(!0)), this._value = void 0, this.radioButton = this.shadowRoot.querySelector("input"), this.fireEvent = this.fireEvent.bind(this), this.handleKeyDown = this.handleKeyDown.bind(this) } get value() { return this._value } fireEvent() { this.dispatchEvent(new CustomEvent("option-clicked", { bubbles: !0, composed: !0, detail: { value: this._value } })) } handleKeyDown(t) { "Enter" !== t.key && "Space" !== t.key || this.fireEvent() } connectedCallback() { this.setAttribute("role", "option"), this.setAttribute("tabindex", "0"), this._value = this.getAttribute("value"), this.addEventListener("click", this.fireEvent), this.addEventListener("keydown", this.handleKeyDown) } disconnectedCallback() { this.removeEventListener("click", this.fireEvent), this.removeEventListener("keydown", this.handleKeyDown) } }); const smCopy = document.createElement("template"); smCopy.innerHTML = '

    ', customElements.define("sm-copy", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smCopy.content.cloneNode(!0)), this.copyContent = this.shadowRoot.querySelector(".copy-content"), this.copyButton = this.shadowRoot.querySelector(".copy-button"), this.copy = this.copy.bind(this) } static get observedAttributes() { return ["value"] } set value(t) { this.setAttribute("value", t) } get value() { return this.getAttribute("value") } fireEvent() { this.dispatchEvent(new CustomEvent("copy", { composed: !0, bubbles: !0, cancelable: !0 })) } copy() { navigator.clipboard.writeText(this.copyContent.textContent).then(t => this.fireEvent()).catch(t => console.error(t)) } connectedCallback() { this.copyButton.addEventListener("click", this.copy) } attributeChangedCallback(t, n, o) { "value" === t && (this.copyContent.textContent = o) } disconnectedCallback() { this.copyButton.removeEventListener("click", this.copy) } }); const smForm = document.createElement("template"); smForm.innerHTML = `
    `, customElements.define("sm-form", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smForm.content.cloneNode(!0)), this.form = this.shadowRoot.querySelector("form"), this.invalidFieldsCount, this.skipSubmit = !1, this.isFormValid = void 0, this.supportedElements = "input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio", this._requiredElements = [], this.debounce = this.debounce.bind(this), this._checkValidity = this._checkValidity.bind(this), this.handleKeydown = this.handleKeydown.bind(this), this.reset = this.reset.bind(this), this.elementsChanged = this.elementsChanged.bind(this) } static get observedAttributes() { return ["skip-submit"] } get validity() { return this.isFormValid } debounce(t, e) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { t.apply(null, s) }, e) } } _checkValidity() { this.submitButton && 0 !== this._requiredElements.length && (this.invalidFieldsCount = 0, this._requiredElements.forEach(([t, e]) => { (!t.disabled && e && !t.isValid || !e && !t.checkValidity()) && this.invalidFieldsCount++ }), this.isFormValid === (0 === this.invalidFieldsCount) || (this.isFormValid = 0 === this.invalidFieldsCount, this.dispatchEvent(new CustomEvent(this.isFormValid ? "valid" : "invalid", { bubbles: !0, composed: !0 })), this.skipSubmit || (this.submitButton.disabled = !this.isFormValid))) } handleKeydown(t) { if ("Enter" === t.key && t.target.tagName.includes("INPUT")) { if (0 === this.invalidFieldsCount) this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })); else for (let [e, i] of this._requiredElements) { let s = i ? !e.isValid : !e.checkValidity(); if (s) { (e?.shadowRoot?.lastElementChild || e).animate([{ transform: "translateX(-1rem)" }, { transform: "translateX(1rem)" }, { transform: "translateX(-0.5rem)" }, { transform: "translateX(0.5rem)" }, { transform: "translateX(0)" },], { duration: 300, easing: "ease" }), i ? e.focusIn() : e.focus(); break } } } } reset() { this.formElements.forEach(([t, e]) => { e ? t.reset() : t.value = "" }) } elementsChanged() { this.formElements = [...this.querySelectorAll(this.supportedElements)].map(t => [t, t.tagName.includes("-")]), this._requiredElements = this.formElements.filter(([t]) => t.hasAttribute("required")), this.submitButton = this.querySelector('[variant="primary"], [type="submit"]'), this.resetButton = this.querySelector('[type="reset"]'), this.resetButton && this.resetButton.addEventListener("click", this.reset), this._checkValidity() } connectedCallback() { let t = this.debounce(this.elementsChanged, 100); this.addEventListener("input", this.debounce(this._checkValidity, 100)), this.addEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.shadowRoot.querySelector("slot").addEventListener("slotchange", t), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { ("childList" === e.type && [...e.addedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements)) || [...e.removedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements))) && t() }) }), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } attributeChangedCallback(t, e, i) { "skip-submit" === t && (this.skipSubmit = null !== i) } disconnectedCallback() { this.removeEventListener("input", this.debounce(this._checkValidity, 100)), this.removeEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver.disconnect() } }); -const smInput = document.createElement("template"); smInput.innerHTML = '
    ', customElements.define("sm-input", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smInput.content.cloneNode(!0)), this.inputParent = this.shadowRoot.querySelector(".input"), this.input = this.shadowRoot.querySelector("input"), this.clearBtn = this.shadowRoot.querySelector(".clear"), this.label = this.shadowRoot.querySelector(".label"), this.feedbackText = this.shadowRoot.querySelector(".feedback-text"), this.outerContainer = this.shadowRoot.querySelector(".outer-container"), this.optionList = this.shadowRoot.querySelector(".datalist"), this._helperText = "", this._errorText = "", this.isRequired = !1, this.datalist = [], this.validationFunction = void 0, this.reflectedAttributes = ["value", "required", "disabled", "type", "inputmode", "readonly", "min", "max", "pattern", "minlength", "maxlength", "step", "list", "autocomplete"], this.reset = this.reset.bind(this), this.clear = this.clear.bind(this), this.focusIn = this.focusIn.bind(this), this.focusOut = this.focusOut.bind(this), this.fireEvent = this.fireEvent.bind(this), this.checkInput = this.checkInput.bind(this), this.showError = this.showError.bind(this), this.allowOnlyNum = this.allowOnlyNum.bind(this), this.handleOptionClick = this.handleOptionClick.bind(this), this.handleInputNavigation = this.handleInputNavigation.bind(this), this.handleDatalistNavigation = this.handleDatalistNavigation.bind(this), this.handleFocus = this.handleFocus.bind(this), this.handleBlur = this.handleBlur.bind(this) } static get observedAttributes() { return ["value", "placeholder", "required", "disabled", "type", "inputmode", "readonly", "min", "max", "pattern", "minlength", "maxlength", "step", "helper-text", "error-text", "list"] } get value() { return this.input.value } set value(t) { t !== this.input.value && (this.input.value = t, this.checkInput()) } get placeholder() { return this.getAttribute("placeholder") } set placeholder(t) { this.setAttribute("placeholder", t) } get type() { return this.getAttribute("type") } set type(t) { this.setAttribute("type", t) } get validity() { return this.input.validity } get disabled() { return this.hasAttribute("disabled") } set disabled(t) { t ? (this.inputParent.classList.add("disabled"), this.setAttribute("disabled", "")) : (this.inputParent.classList.remove("disabled"), this.removeAttribute("disabled")) } get readOnly() { return this.hasAttribute("readonly") } set readOnly(t) { t ? this.setAttribute("readonly", "") : this.removeAttribute("readonly") } set customValidation(t) { this.validationFunction = t } set errorText(t) { this._errorText = t } showError() { this.feedbackText.className = "feedback-text error", this.feedbackText.innerHTML = ` ${this._errorText}` } set helperText(t) { this._helperText = t } get isValid() { if ("" !== this.input.value) { const t = this.input.checkValidity(); let e = !0; return this.validationFunction && (e = Boolean(this.validationFunction(this.input.value))), t && e ? (this.feedbackText.className = "feedback-text success", this.feedbackText.textContent = "") : this._errorText && this.showError(), t && e } } reset() { this.value = "" } clear() { this.value = "", this.input.focus(), this.fireEvent() } focusIn() { this.input.focus() } focusOut() { this.input.blur() } fireEvent() { let t = new Event("input", { bubbles: !0, cancelable: !0, composed: !0 }); this.dispatchEvent(t) } searchDatalist(t) { const e = this.datalist.filter(e => e.toLowerCase().includes(t.toLowerCase())); if (e.sort((e, n) => { const i = e.toLowerCase().indexOf(t.toLowerCase()), s = n.toLowerCase().indexOf(t.toLowerCase()); return i - s }), e.length) { if (this.optionList.children.length > e.length) { const t = this.optionList.children.length - e.length; for (let e = 0; e < t; e++)this.optionList.removeChild(this.optionList.lastChild) } e.forEach((t, e) => { if (this.optionList.children[e]) this.optionList.children[e].textContent = t; else { const e = document.createElement("li"); e.textContent = t, e.classList.add("datalist-item"), e.setAttribute("tabindex", "0"), this.optionList.appendChild(e) } }), this.optionList.classList.remove("hidden") } else this.optionList.classList.add("hidden") } checkInput(t) { this.hasAttribute("readonly") || ("" !== this.input.value ? this.clearBtn.classList.remove("hidden") : this.clearBtn.classList.add("hidden")), this.hasAttribute("placeholder") && "" !== this.getAttribute("placeholder").trim() && ("" !== this.input.value ? (this.animate ? this.inputParent.classList.add("animate-placeholder") : this.label.classList.add("hidden"), this.datalist.length && (this.searchTimeout && clearTimeout(this.searchTimeout), this.searchTimeout = setTimeout(() => { this.searchDatalist(this.input.value.trim()) }, 100))) : (this.animate ? this.inputParent.classList.remove("animate-placeholder") : this.label.classList.remove("hidden"), this.feedbackText.textContent = "", this.datalist.length && (this.optionList.innerHTML = "", this.optionList.classList.add("hidden")))) } allowOnlyNum(t) { 1 === t.key.length && (("." !== t.key || !t.target.value.includes(".") && 0 !== t.target.value.length) && ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."].includes(t.key) || t.preventDefault()) } handleOptionClick(t) { this.input.value = t.target.textContent, this.optionList.classList.add("hidden"), this.input.focus() } handleInputNavigation(t) { "ArrowDown" === t.key ? (t.preventDefault(), this.optionList.children.length && this.optionList.children[0].focus()) : "ArrowUp" === t.key && (t.preventDefault(), this.optionList.children.length && this.optionList.children[this.optionList.children.length - 1].focus()) } handleDatalistNavigation(t) { "ArrowUp" === t.key ? (t.preventDefault(), this.shadowRoot.activeElement.previousElementSibling ? this.shadowRoot.activeElement.previousElementSibling.focus() : this.input.focus()) : "ArrowDown" === t.key ? (t.preventDefault(), this.shadowRoot.activeElement.nextElementSibling ? this.shadowRoot.activeElement.nextElementSibling.focus() : this.input.focus()) : "Enter" !== t.key && " " !== t.key || (t.preventDefault(), this.input.value = t.target.textContent, this.optionList.classList.add("hidden"), this.input.focus()) } handleFocus(t) { this.datalist.length && this.searchDatalist(this.input.value.trim()) } handleBlur(t) { this.datalist.length && this.optionList.classList.add("hidden") } connectedCallback() { this.animate = this.hasAttribute("animate"), this.setAttribute("role", "textbox"), this.input.addEventListener("input", this.checkInput), this.clearBtn.addEventListener("click", this.clear), this.datalist.length && (this.optionList.addEventListener("click", this.handleOptionClick), this.input.addEventListener("keydown", this.handleInputNavigation), this.optionList.addEventListener("keydown", this.handleDatalistNavigation)), this.input.addEventListener("focusin", this.handleFocus), this.addEventListener("focusout", this.handleBlur) } attributeChangedCallback(t, e, n) { e !== n && (this.reflectedAttributes.includes(t) && (this.hasAttribute(t) ? this.input.setAttribute(t, this.getAttribute(t) ? this.getAttribute(t) : "") : this.input.removeAttribute(t)), "placeholder" === t ? (this.label.textContent = n, this.setAttribute("aria-label", n)) : this.hasAttribute("value") ? this.checkInput() : "type" === t ? this.hasAttribute("type") && "number" === this.getAttribute("type") ? (this.input.setAttribute("inputmode", "decimal"), this.input.addEventListener("keydown", this.allowOnlyNum)) : this.input.removeEventListener("keydown", this.allowOnlyNum) : "helper-text" === t ? this._helperText = this.getAttribute("helper-text") : "error-text" === t ? this._errorText = this.getAttribute("error-text") : "required" === t ? (this.isRequired = this.hasAttribute("required"), this.isRequired ? this.setAttribute("aria-required", "true") : this.setAttribute("aria-required", "false")) : "readonly" === t ? this.hasAttribute("readonly") ? this.inputParent.classList.add("readonly") : this.inputParent.classList.remove("readonly") : "disabled" === t ? this.hasAttribute("disabled") ? this.inputParent.classList.add("disabled") : this.inputParent.classList.remove("disabled") : "list" === t && this.hasAttribute("list") && "" !== this.getAttribute("list").trim() && (this.datalist = this.getAttribute("list").split(","))) } disconnectedCallback() { this.input.removeEventListener("input", this.checkInput), this.clearBtn.removeEventListener("click", this.clear), this.input.removeEventListener("keydown", this.allowOnlyNum), this.optionList.removeEventListener("click", this.handleOptionClick), this.input.removeEventListener("keydown", this.handleInputNavigation), this.optionList.removeEventListener("keydown", this.handleDatalistNavigation), this.input.removeEventListener("focusin", this.handleFocus), this.removeEventListener("focusout", this.handleBlur) } }); +const smInput = document.createElement("template"); smInput.innerHTML = '
    ', customElements.define("sm-input", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smInput.content.cloneNode(!0)), this.inputParent = this.shadowRoot.querySelector(".input"), this.input = this.shadowRoot.querySelector("input"), this.clearBtn = this.shadowRoot.querySelector(".clear"), this.label = this.shadowRoot.querySelector(".label"), this.feedbackText = this.shadowRoot.querySelector(".feedback-text"), this.outerContainer = this.shadowRoot.querySelector(".outer-container"), this.optionList = this.shadowRoot.querySelector(".datalist"), this._helperText = "", this._errorText = "", this.isRequired = !1, this.datalist = [], this.validationFunction = void 0, this.reflectedAttributes = ["value", "required", "disabled", "type", "inputmode", "readonly", "min", "max", "pattern", "minlength", "maxlength", "step", "list", "autocomplete"], this.reset = this.reset.bind(this), this.clear = this.clear.bind(this), this.focusIn = this.focusIn.bind(this), this.focusOut = this.focusOut.bind(this), this.fireEvent = this.fireEvent.bind(this), this.checkInput = this.checkInput.bind(this), this.showError = this.showError.bind(this), this.allowOnlyNum = this.allowOnlyNum.bind(this), this.handleOptionClick = this.handleOptionClick.bind(this), this.handleInputNavigation = this.handleInputNavigation.bind(this), this.handleDatalistNavigation = this.handleDatalistNavigation.bind(this), this.handleFocus = this.handleFocus.bind(this), this.handleBlur = this.handleBlur.bind(this) } static get observedAttributes() { return ["value", "placeholder", "required", "disabled", "type", "inputmode", "readonly", "min", "max", "pattern", "minlength", "maxlength", "step", "helper-text", "error-text", "list"] } get value() { return this.input.value } set value(t) { t !== this.input.value && (this.input.value = t, this.checkInput()) } get placeholder() { return this.getAttribute("placeholder") } set placeholder(t) { this.setAttribute("placeholder", t) } get type() { return this.getAttribute("type") } set type(t) { this.setAttribute("type", t) } get validity() { return this.input.validity } get disabled() { return this.hasAttribute("disabled") } set disabled(t) { t ? (this.inputParent.classList.add("disabled"), this.setAttribute("disabled", "")) : (this.inputParent.classList.remove("disabled"), this.removeAttribute("disabled")) } get readOnly() { return this.hasAttribute("readonly") } set readOnly(t) { t ? this.setAttribute("readonly", "") : this.removeAttribute("readonly") } set customValidation(t) { this.validationFunction = t } set errorText(t) { this._errorText = t } showError() { this.feedbackText.className = "feedback-text error", this.feedbackText.innerHTML = ` ${this._errorText}` } set helperText(t) { this._helperText = t } get isValid() { if ("" !== this.input.value) { const t = this.input.checkValidity(); let e = !0; return this.validationFunction && (e = Boolean(this.validationFunction(this.input.value))), t && e ? (this.feedbackText.className = "feedback-text success", this.feedbackText.textContent = "") : this._errorText && this.showError(), t && e } } reset() { this.value = "" } clear() { this.value = "", this.input.focus(), this.fireEvent() } focusIn() { this.input.focus() } focusOut() { this.input.blur() } fireEvent() { let t = new Event("input", { bubbles: !0, cancelable: !0, composed: !0 }); this.dispatchEvent(t) } searchDatalist(t) { const e = this.datalist.filter(e => e.toLowerCase().includes(t.toLowerCase())); if (e.sort((e, n) => { const i = e.toLowerCase().indexOf(t.toLowerCase()), s = n.toLowerCase().indexOf(t.toLowerCase()); return i - s }), e.length) { if (this.optionList.children.length > e.length) { const t = this.optionList.children.length - e.length; for (let e = 0; e < t; e++)this.optionList.removeChild(this.optionList.lastChild) } e.forEach((t, e) => { if (this.optionList.children[e]) this.optionList.children[e].textContent = t; else { const e = document.createElement("li"); e.textContent = t, e.classList.add("datalist-item"), e.setAttribute("tabindex", "0"), this.optionList.appendChild(e) } }), this.optionList.classList.remove("hidden") } else this.optionList.classList.add("hidden") } checkInput(t) { this.hasAttribute("readonly") || ("" !== this.input.value ? this.clearBtn.classList.remove("hidden") : this.clearBtn.classList.add("hidden")), this.hasAttribute("placeholder") && "" !== this.getAttribute("placeholder").trim() && ("" !== this.input.value ? (this.animate ? this.inputParent.classList.add("animate-placeholder") : this.label.classList.add("hidden"), this.datalist.length && (this.searchTimeout && clearTimeout(this.searchTimeout), this.searchTimeout = setTimeout(() => { this.searchDatalist(this.input.value.trim()) }, 100))) : (this.animate ? this.inputParent.classList.remove("animate-placeholder") : this.label.classList.remove("hidden"), this.feedbackText.textContent = "", this.datalist.length && (this.optionList.innerHTML = "", this.optionList.classList.add("hidden")))) } allowOnlyNum(t) { 1 === t.key.length && (("." !== t.key || !t.target.value.includes(".") && 0 !== t.target.value.length) && ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."].includes(t.key) || t.preventDefault()) } handleOptionClick(t) { this.input.value = t.target.textContent, this.optionList.classList.add("hidden"), this.input.focus() } handleInputNavigation(t) { "ArrowDown" === t.key ? (t.preventDefault(), this.optionList.children.length && this.optionList.children[0].focus()) : "ArrowUp" === t.key && (t.preventDefault(), this.optionList.children.length && this.optionList.children[this.optionList.children.length - 1].focus()) } handleDatalistNavigation(t) { "ArrowUp" === t.key ? (t.preventDefault(), this.shadowRoot.activeElement.previousElementSibling ? this.shadowRoot.activeElement.previousElementSibling.focus() : this.input.focus()) : "ArrowDown" === t.key ? (t.preventDefault(), this.shadowRoot.activeElement.nextElementSibling ? this.shadowRoot.activeElement.nextElementSibling.focus() : this.input.focus()) : "Enter" !== t.key && " " !== t.key || (t.preventDefault(), this.input.value = t.target.textContent, this.optionList.classList.add("hidden"), this.input.focus()) } handleFocus(t) { this.datalist.length && this.searchDatalist(this.input.value.trim()) } handleBlur(t) { this.datalist.length && this.optionList.classList.add("hidden") } connectedCallback() { this.animate = this.hasAttribute("animate"), this.setAttribute("role", "textbox"), this.input.addEventListener("input", this.checkInput), this.clearBtn.addEventListener("click", this.clear), this.datalist.length && (this.optionList.addEventListener("click", this.handleOptionClick), this.input.addEventListener("keydown", this.handleInputNavigation), this.optionList.addEventListener("keydown", this.handleDatalistNavigation)), this.input.addEventListener("focusin", this.handleFocus), this.addEventListener("focusout", this.handleBlur) } attributeChangedCallback(t, e, n) { if (e !== n) switch (this.reflectedAttributes.includes(t) && (this.hasAttribute(t) ? this.input.setAttribute(t, this.getAttribute(t) ? this.getAttribute(t) : "") : this.input.removeAttribute(t)), t) { case "placeholder": this.label.textContent = n, this.setAttribute("aria-label", n); break; case "value": this.checkInput(); break; case "type": this.hasAttribute("type") && "number" === this.getAttribute("type") ? (this.input.setAttribute("inputmode", "decimal"), this.input.addEventListener("keydown", this.allowOnlyNum)) : this.input.removeEventListener("keydown", this.allowOnlyNum); break; case "helper-text": this._helperText = n; break; case "error-text": this._errorText = n; break; case "required": this.isRequired = this.hasAttribute("required"), this.isRequired ? this.setAttribute("aria-required", "true") : this.setAttribute("aria-required", "false"); break; case "readonly": this.hasAttribute("readonly") ? this.inputParent.classList.add("readonly") : this.inputParent.classList.remove("readonly"); break; case "disabled": this.hasAttribute("disabled") ? this.inputParent.classList.add("disabled") : this.inputParent.classList.remove("disabled"); break; case "list": this.hasAttribute("list") && "" !== this.getAttribute("list").trim() && (this.datalist = this.getAttribute("list").split(",")) } } disconnectedCallback() { this.input.removeEventListener("input", this.checkInput), this.clearBtn.removeEventListener("click", this.clear), this.input.removeEventListener("keydown", this.allowOnlyNum), this.optionList.removeEventListener("click", this.handleOptionClick), this.input.removeEventListener("keydown", this.handleInputNavigation), this.optionList.removeEventListener("keydown", this.handleDatalistNavigation), this.input.removeEventListener("focusin", this.handleFocus), this.removeEventListener("focusout", this.handleBlur) } }); 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(!0)), this.notificationPanel = this.shadowRoot.querySelector(".notification-panel"), this.animationOptions = { duration: 300, fill: "forwards", easing: "cubic-bezier(0.175, 0.885, 0.32, 1.275)" }, this.push = this.push.bind(this), this.createNotification = this.createNotification.bind(this), this.removeNotification = this.removeNotification.bind(this), this.clearAll = this.clearAll.bind(this), this.remove = this.remove.bind(this), this.handlePointerMove = this.handlePointerMove.bind(this), this.startX = 0, this.currentX = 0, this.endX = 0, this.swipeDistance = 0, this.swipeDirection = "", this.swipeThreshold = 0, this.startTime = 0, this.swipeTime = 0, this.swipeTimeThreshold = 200, this.currentTarget = null, this.mediaQuery = window.matchMedia("(min-width: 640px)"), this.handleOrientationChange = this.handleOrientationChange.bind(this), this.isLandscape = !1 } randString(n) { let t = ""; const e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (let i = 0; i < n; i++)t += e.charAt(Math.floor(Math.random() * e.length)); return t } createNotification(n, t = {}) { const { pinned: e = !1, icon: i = "", action: o } = t, r = document.createElement("div"); r.id = this.randString(8), r.className = `notification ${e ? "pinned" : ""}`; const a = document.createElement("div"); a.className = "icon-container", a.innerHTML = i; const s = document.createElement("output"); if (s.textContent = n, r.append(a, s), o) { const n = document.createElement("button"); n.className = "action", n.innerText = o.label, n.addEventListener("click", o.callback) } if (e) { const n = document.createElement("button"); n.className = "close", n.innerHTML = ' ', n.addEventListener("click", () => { this.remove(r.id) }), r.append(n) } return r } push(n, t = {}) { const e = this.createNotification(n, t); return this.isLandscape ? this.notificationPanel.append(e) : this.notificationPanel.prepend(e), this.notificationPanel.animate([{ transform: `translateY(${this.isLandscape ? "" : "-"}${e.clientHeight}px)` }, { transform: "none" }], this.animationOptions), e.animate([{ transform: "translateY(-1rem)", opacity: "0" }, { transform: "none", opacity: "1" }], this.animationOptions).onfinish = (n => { n.target.commitStyles(), n.target.cancel() }), e.querySelector(".action") && e.querySelector(".action").addEventListener("click", t.action.callback), e.id } removeNotification(n, t = "left") { if (!n) return; const e = "left" === t ? "-" : "+"; n.animate([{ transform: this.currentX ? `translateX(${this.currentX}px)` : "none", opacity: "1" }, { transform: `translateX(calc(${e}${Math.abs(this.currentX)}px ${e} 1rem))`, opacity: "0" }], this.animationOptions).onfinish = (() => { n.remove() }) } remove(n) { const t = this.notificationPanel.querySelector(`#${n}`); t && this.removeNotification(t) } clearAll() { Array.from(this.notificationPanel.children).forEach(n => { this.removeNotification(n) }) } handlePointerMove(n) { this.currentX = n.clientX - this.startX, this.currentTarget.style.transform = `translateX(${this.currentX}px)` } handleOrientationChange(n) { this.isLandscape = n.matches, n.matches } connectedCallback() { this.handleOrientationChange(this.mediaQuery), this.mediaQuery.addEventListener("change", this.handleOrientationChange), this.notificationPanel.addEventListener("pointerdown", n => { n.target.closest(".close") ? this.removeNotification(n.target.closest(".notification")) : n.target.closest(".notification") && (this.swipeThreshold = n.target.closest(".notification").getBoundingClientRect().width / 2, this.currentTarget = n.target.closest(".notification"), this.currentTarget.setPointerCapture(n.pointerId), this.startTime = Date.now(), this.startX = n.clientX, this.startY = n.clientY, this.notificationPanel.addEventListener("pointermove", this.handlePointerMove)) }), this.notificationPanel.addEventListener("pointerup", n => { this.endX = n.clientX, this.endY = n.clientY, this.swipeDistance = Math.abs(this.endX - this.startX), this.swipeTime = Date.now() - this.startTime, this.endX > this.startX ? this.swipeDirection = "right" : this.swipeDirection = "left", this.swipeTime < this.swipeTimeThreshold ? this.swipeDistance > 50 && this.removeNotification(this.currentTarget, this.swipeDirection) : this.swipeDistance > this.swipeThreshold ? this.removeNotification(this.currentTarget, this.swipeDirection) : this.currentTarget.animate([{ transform: `translateX(${this.currentX}px)` }, { transform: "none" }], this.animationOptions).onfinish = (n => { n.target.commitStyles(), n.target.cancel() }), this.notificationPanel.removeEventListener("pointermove", this.handlePointerMove), this.notificationPanel.releasePointerCapture(n.pointerId), this.currentX = 0 }); const n = new MutationObserver(n => { n.forEach(n => { "childList" === n.type && n.addedNodes.length && !n.addedNodes[0].classList.contains("pinned") && setTimeout(() => { this.removeNotification(n.addedNodes[0]) }, 5e3) }) }); n.observe(this.notificationPanel, { childList: !0 }) } disconnectedCallback() { mediaQueryList.removeEventListener("change", handleOrientationChange) } }); class Stack { constructor() { this.items = [] } push(t) { this.items.push(t) } pop() { return 0 == this.items.length ? "Underflow" : this.items.pop() } peek() { return this.items[this.items.length - 1] } } const popupStack = new Stack, smPopup = document.createElement("template"); smPopup.innerHTML = ` `, customElements.define("sm-popup", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(smPopup.content.cloneNode(!0)), this.allowClosing = !1, this.isOpen = !1, this.offset = 0, this.touchStartY = 0, this.touchEndY = 0, this.touchStartTime = 0, this.touchEndTime = 0, this.touchEndAnimation = void 0, this.focusable, this.autoFocus, this.mutationObserver, this.popupContainer = this.shadowRoot.querySelector(".popup-container"), this.backdrop = this.shadowRoot.querySelector(".backdrop"), this.dialogBox = this.shadowRoot.querySelector(".popup"), this.popupBodySlot = this.shadowRoot.querySelector(".popup-body slot"), this.popupHeader = this.shadowRoot.querySelector(".popup-top"), this.resumeScrolling = this.resumeScrolling.bind(this), this.setStateOpen = this.setStateOpen.bind(this), this.show = this.show.bind(this), this.hide = this.hide.bind(this), this.handleTouchStart = this.handleTouchStart.bind(this), this.handleTouchMove = this.handleTouchMove.bind(this), this.handleTouchEnd = this.handleTouchEnd.bind(this), this.detectFocus = this.detectFocus.bind(this), this.handleSoftDismiss = this.handleSoftDismiss.bind(this), this.debounce = this.debounce.bind(this) } static get observedAttributes() { return ["open"] } get open() { return this.isOpen } animateTo(t, e, i) { let s = t.animate(e, { ...i, fill: "both" }); return s.finished.then(() => { s.commitStyles(), s.cancel() }), s } resumeScrolling() { let t = document.body.style.top; window.scrollTo(0, -1 * parseInt(t || "0")), document.body.style.overflow = "", document.body.style.top = "initial" } setStateOpen() { if (!this.isOpen || this.offset) { let t = window.innerWidth > 640 ? "scale(1.1)" : `translateY(${this.offset ? `${this.offset}px` : "100%"})`; this.animateTo(this.dialogBox, [{ opacity: this.offset ? 1 : 0, transform: t }, { opacity: 1, transform: "none" },], { duration: 300, easing: "ease" }) } } show(t = {}) { let { pinned: e = !1, payload: i } = t; if (this.isOpen) return; let s = { duration: 300, easing: "ease" }; return this.payload = i, popupStack.push({ popup: this, permission: e }), popupStack.items.length > 1 && this.animateTo(popupStack.items[popupStack.items.length - 2].popup.shadowRoot.querySelector(".popup"), [{ transform: "none" }, { transform: window.innerWidth > 640 ? "scale(0.95)" : "translateY(-1.5rem)" },], s), this.popupContainer.classList.remove("hide"), this.offset || (this.backdrop.animate([{ opacity: 0 }, { opacity: 1 },], s).onfinish = () => { this.resolveOpen(this.payload) }, this.dispatchEvent(new CustomEvent("popupopened", { bubbles: !0, composed: !0, detail: { payload: this.payload } })), document.body.style.overflow = "hidden", document.body.style.top = `-${window.scrollY}px`), this.setStateOpen(), this.pinned = e, this.isOpen = !0, setTimeout(() => { let t = this.autoFocus || this.focusable?.[0] || this.dialogBox; t && (t.tagName.includes("-") ? t.focusIn() : t.focus()) }, 0), this.hasAttribute("open") || (this.setAttribute("open", ""), this.addEventListener("keydown", this.detectFocus), this.resizeObserver.observe(this), this.mutationObserver.observe(this, { attributes: !0, childList: !0, subtree: !0 }), this.popupHeader.addEventListener("touchstart", this.handleTouchStart, { passive: !0 }), this.backdrop.addEventListener("mousedown", this.handleSoftDismiss)), { opened: new Promise(t => { this.resolveOpen = t }), closed: new Promise(t => { this.resolveClose = t }) } } hide(t = {}) { let { payload: e } = t, i = { duration: 150, easing: "ease" }; this.backdrop.animate([{ opacity: 1 }, { opacity: 0 }], i), this.animateTo(this.dialogBox, [{ opacity: 1, transform: window.innerWidth > 640 ? "none" : `translateY(${this.offset ? `${this.offset}px` : "0"})` }, { opacity: 0, transform: window.innerWidth > 640 ? "scale(1.1)" : "translateY(100%)" },], i).finished.finally(() => { this.popupContainer.classList.add("hide"), this.dialogBox.style = "", this.removeAttribute("open"), this.forms.length && this.forms.forEach(t => t.reset()), this.dispatchEvent(new CustomEvent("popupclosed", { bubbles: !0, composed: !0, detail: { payload: e || this.payload } })), this.resolveClose(e || this.payload), this.isOpen = !1 }), popupStack.pop(), popupStack.items.length ? this.animateTo(popupStack.items[popupStack.items.length - 1].popup.shadowRoot.querySelector(".popup"), [{ transform: window.innerWidth > 640 ? "scale(0.95)" : "translateY(-1.5rem)" }, { transform: "none" },], i) : this.resumeScrolling(), this.resizeObserver.disconnect(), this.mutationObserver.disconnect(), this.removeEventListener("keydown", this.detectFocus), this.popupHeader.removeEventListener("touchstart", this.handleTouchStart, { passive: !0 }), this.backdrop.removeEventListener("mousedown", this.handleSoftDismiss) } handleTouchStart(t) { this.offset = 0, this.popupHeader.addEventListener("touchmove", this.handleTouchMove, { passive: !0 }), this.popupHeader.addEventListener("touchend", this.handleTouchEnd, { passive: !0 }), this.touchStartY = t.changedTouches[0].clientY, this.touchStartTime = t.timeStamp } handleTouchMove(t) { this.touchStartY < t.changedTouches[0].clientY && (this.offset = t.changedTouches[0].clientY - this.touchStartY, this.touchEndAnimation = window.requestAnimationFrame(() => { this.dialogBox.style.transform = `translateY(${this.offset}px)` })) } handleTouchEnd(t) { if (this.touchEndTime = t.timeStamp, cancelAnimationFrame(this.touchEndAnimation), this.touchEndY = t.changedTouches[0].clientY, this.threshold = .3 * this.dialogBox.getBoundingClientRect().height, this.touchEndTime - this.touchStartTime > 200) { if (this.touchEndY - this.touchStartY > this.threshold) { if (this.pinned) { this.setStateOpen(); return } this.hide() } else this.setStateOpen() } else if (this.touchEndY > this.touchStartY) { if (this.pinned) { this.setStateOpen(); return } this.hide() } this.popupHeader.removeEventListener("touchmove", this.handleTouchMove, { passive: !0 }), this.popupHeader.removeEventListener("touchend", this.handleTouchEnd, { passive: !0 }) } detectFocus(t) { if ("Tab" === t.key && this.focusable.length) { if (!this.firstFocusable) { for (let e = 0; e < this.focusable.length; e++)if (!this.focusable[e].disabled) { this.firstFocusable = this.focusable[e]; break } } if (!this.lastFocusable) { for (let i = this.focusable.length - 1; i >= 0; i--)if (!this.focusable[i].disabled) { this.lastFocusable = this.focusable[i]; break } } t.shiftKey && document.activeElement === this.firstFocusable ? (t.preventDefault(), this.lastFocusable.tagName.includes("SM-") ? this.lastFocusable.focusIn() : this.lastFocusable.focus()) : t.shiftKey || document.activeElement !== this.lastFocusable || (t.preventDefault(), this.firstFocusable.tagName.includes("SM-") ? this.firstFocusable.focusIn() : this.firstFocusable.focus()) } } updateFocusableList() { this.focusable = this.querySelectorAll('sm-button:not([disabled]), button:not([disabled]), [href], sm-input, input:not([readonly]), sm-select, select, sm-checkbox, sm-textarea, textarea, [tabindex]:not([tabindex="-1"])'), this.autoFocus = this.querySelector("[autofocus]"), this.firstFocusable = null, this.lastFocusable = null } handleSoftDismiss() { this.pinned ? this.dialogBox.animate([{ transform: "translateX(-1rem)" }, { transform: "translateX(1rem)" }, { transform: "translateX(-0.5rem)" }, { transform: "translateX(0.5rem)" }, { transform: "translateX(0)" },], { duration: 300, easing: "ease" }) : this.hide() } debounce(t, e) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { t.apply(null, s) }, e) } } connectedCallback() { this.popupBodySlot.addEventListener("slotchange", this.debounce(() => { this.forms = this.querySelectorAll("sm-form"), this.updateFocusableList() }, 0)), this.resizeObserver = new ResizeObserver(t => { t.forEach(t => { if (t.contentBoxSize) { let e = Array.isArray(t.contentBoxSize) ? t.contentBoxSize[0] : t.contentBoxSize; this.threshold = .3 * e.blockSize.height } else this.threshold = .3 * t.contentRect.height }) }), this.mutationObserver = new MutationObserver(t => { this.updateFocusableList() }) } disconnectedCallback() { this.resizeObserver.disconnect(), this.mutationObserver.disconnect(), this.removeEventListener("keydown", this.detectFocus), this.popupHeader.removeEventListener("touchstart", this.handleTouchStart, { passive: !0 }), this.backdrop.removeEventListener("mousedown", this.handleSoftDismiss) } attributeChangedCallback(t) { "open" === t && this.hasAttribute("open") && this.show() } }); const spinner = document.createElement("template"); spinner.innerHTML = ''; class SpinnerLoader extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(spinner.content.cloneNode(!0)) } } window.customElements.define("sm-spinner", SpinnerLoader);