From 314ad62005b97e6702f312aefc4231f15501d5a5 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Thu, 30 Nov 2023 02:59:54 +0530 Subject: [PATCH] Adding dapp filtering --- css/main.css | 74 ++++++++++++++++++- css/main.min.css | 2 +- css/main.scss | 71 +++++++++++++++++- index.html | 151 ++++++++++++++++++++++++++------------ scripts/components.min.js | 3 +- 5 files changed, 246 insertions(+), 55 deletions(-) diff --git a/css/main.css b/css/main.css index 8925a87..fe67a7b 100644 --- a/css/main.css +++ b/css/main.css @@ -272,8 +272,8 @@ sm-chip { user-select: none; font-weight: 500; } -sm-chip[selected] { - --background: var(--accent-color); +sm-chip[selected]::part(chip) { + background: rgba(var(--text-color), 1); color: rgba(var(--background-color), 1); } @@ -812,6 +812,13 @@ theme-toggle { border-radius: 0.5rem; } +#category_selector { + view-transition-name: category-selector; +} +#category_selector sm-chip { + text-transform: capitalize; +} + #dapp_list { display: grid; gap: 1rem; @@ -834,10 +841,19 @@ theme-toggle { object-fit: cover; background-color: rgba(var(--text-color), 0.06); } +.dapp-card__category { + font-size: 0.8rem; + font-weight: 500; + color: rgba(var(--text-color), 0.8); + padding: 0.1rem 0.5rem; + border-radius: 0.5rem; + background-color: rgba(var(--text-color), 0.06); + align-self: flex-start; +} .dapp-card__title__wrapper { color: rgba(var(--text-color), 1); } -.dapp-card > .flex { +.dapp-card > .flex:last-of-type { margin-top: 1rem; margin-left: auto; } @@ -853,6 +869,11 @@ theme-toggle { view-transition-name: dapps-menu; } +#hamburger_menu_trigger { + padding: 0.5rem; + margin-left: -0.5rem; +} + .hamburger-menu__item { display: flex; align-items: center; @@ -875,8 +896,55 @@ theme-toggle { .hide-on-small { display: none; } + #category_selector { + margin-left: -1rem; + max-width: initial; + width: calc(100% + 2rem); + } + #category_selector::part(chips-wrapper) { + padding: 0 1rem; + } + .hamburger-menu-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; + pointer-events: none; + transform: translateX(-100%); + transition: transform 0.3s; + } + .hamburger-menu-wrapper.hamburger-menu--open { + pointer-events: auto; + transform: translateX(0); + } + .hamburger-menu { + z-index: 1; + position: absolute; + top: 0; + left: 0; + width: calc(100% - 4rem); + height: 100%; + background-color: rgba(var(--foreground-color), 1); + padding: 1rem; + box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1); + } + .hamburger-menu__overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(var(--text-color), 0.1); + -webkit-backdrop-filter: blur(0.5rem); + backdrop-filter: blur(0.5rem); + } } @media only screen and (min-width: 640px) { + .hide-on-large { + display: none; + } sm-popup { --width: 24rem; } diff --git a/css/main.min.css b/css/main.min.css index faecdb6..8608ddc 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1 +1 @@ -*{padding:0;margin:0;box-sizing:border-box;font-family:"IBM Plex Sans",sans-serif}:root{font-size:clamp(1rem,1.2vmax,1.5rem)}html,body{height:100%}body{--accent-color: #3d5afe;--accent-color-rgb: 77, 119, 255;--secondary-color: #ffac2e;--text-color: 34, 34, 34;--foreground-color: 252, 253, 255;--background-color: 241, 243, 248;--danger-color: rgb(255, 75, 75);--green: #1cad59;--yellow: rgb(220, 165, 0);color:rgba(var(--text-color), 1);background-color:rgba(var(--foreground-color), 1)}body[data-theme=dark]{--accent-color: #92a2ff;--accent-color-rgb: 160, 182, 255;--secondary-color: #d60739;--text-color: 210, 210, 210;--foreground-color: 27, 28, 29;--background-color: 21, 22, 22;--danger-color: rgb(255, 106, 106);--green: #00e676;--yellow: rgb(255, 213, 5)}body[data-theme=dark] ::-webkit-calendar-picker-indicator{filter:invert(1)}h1,h2,h3,h4,h5,h6{letter-spacing:-0.01em;font-weight:700}p,strong{color:rgba(var(--text-color), 0.9);max-width:70ch;font-size:.9rem}img{-o-object-fit:cover;object-fit:cover}a:where([class]){color:inherit;text-decoration:none}a:where([class]):focus-visible{box-shadow:0 0 0 .1rem rgba(var(--text-color), 1) inset}a{color:var(--accent-color);text-decoration:none}a:hover{-webkit-text-decoration:underline solid currentColor;text-decoration:underline solid currentColor}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}input[type=datetime-local]{width:100%;padding:.8rem .6rem;border:none;border-radius:.5rem;font-weight:500;font-family:inherit;font-size:inherit;color:inherit;background-color:rgba(var(--text-color), 0.06)}input[type=datetime-local]:focus{outline:none;box-shadow:0 0 0 .1rem var(--accent-color)}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:.9rem;font-weight:500;white-space:nowrap;padding:.5rem 1rem;border-radius:.5rem;justify-content:center;flex-shrink:0}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{padding:.8rem 1rem;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 var(--accent-color) 1px;background-color:rgba(0,0,0,0);color:var(--accent-color)}.button--outlined .icon{fill:var(--accent-color)}.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:.8rem;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{height:100%;padding:0;padding:.4rem;border-radius:.3rem;aspect-ratio:1/1}.icon-only .icon{height:1em;width:1em}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 summary{display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;align-items:center;gap:1rem;color:var(--accent-color)}details[open] summary{margin-bottom:1rem}details[open]>summary .down-arrow{transform:rotate(180deg)}fieldset{border:none}sm-input{--border-radius: 0.5rem;--background-color: rgba(var(--foreground-color), 1)}sm-spinner{--size: 1.3rem;--stroke-width: 0.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;font-weight:500}sm-chip[selected]{--background: var(--accent-color);color:rgba(var(--background-color), 1)}sm-select{font-size:.9rem;font-weight:500;--padding: 0.6rem 0.3rem 0.6rem 0.6rem}sm-option{font-size:.9rem}ul{list-style:none}.interact{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.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}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.sticky{position:-webkit-sticky;position:sticky}.top-0{top:0}.flex{display:flex}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.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}.text-align-left{text-align:left}.align-items-start{align-items:flex-start}.align-items-center{align-items:center}.align-content-start{align-content:flex-start}.align-start{align-content:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.text-center{text-align:center}.justify-start{justify-items:start}.justify-content-start{justify-content:start}.justify-content-center{justify-content:center}.justify-items-center{justify-items:center}.justify-right{margin-left:auto}.align-self-start{align-self:start}.align-self-center{align-self:center}.align-self-end{align-self:end}.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}.space-evenly{justify-content:space-evenly}.w-100{width:100%}.h-100{height:100%}.padding-block-1{padding-block:1rem}.margin-right-0-3{margin-right:.3rem}.margin-right-0-5{margin-right:.5rem}.margin-right-1{margin-right:1rem}.margin-left-0-5{margin-left:.5rem}.margin-left-auto{margin-left:auto}.margin-right-auto{margin-right:auto}.margin-top-1{margin-top:1rem}.margin-bottom-0-5{margin-bottom:.5rem}.margin-bottom-1{margin-bottom:1rem}.margin-bottom-2{margin-bottom:2rem}.margin-block-0-5{margin-block:.5rem}.margin-block-1{margin-block:1rem}.margin-block-1-5{margin-block:1.5rem}.margin-inline-1{margin-inline:1rem}.margin-inline-1-5{margin-inline:1.5rem}.hidden{display:none !important}.h1{font-size:2.5rem}.h2{font-size:2rem}.h3{font-size:1.4rem}.h4{font-size:1rem}.h5{font-size:.8rem}.grid-3{grid-template-columns:1fr auto auto}.flow-column{grid-auto-flow:column}.w-100{width:100%}.color-0-8{color:rgba(var(--text-color), 0.8)}.weight-400{font-weight:400}.weight-500{font-weight:500}.ws-pre-line{white-space:pre-line}.card{background-color:rgba(var(--foreground-color), 1);border-radius:.5rem;padding:max(1rem,3vw)}.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}.interactive{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.observe-empty-state:empty{display:none}.observe-empty-state:not(:empty)~.empty-state{display:none}.password-field label{display:flex;flex-shrink:0}.password-field label input:checked~.visible{display:none}.password-field label input:not(:checked)~.invisible{display:none}.multi-state-button{display:grid;text-align:center;align-items:center;justify-items:center;isolation:isolate}.multi-state-button>*{grid-area:1/1/2/2}.multi-state-button button{z-index:1;width:100%}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{margin-bottom:1rem}#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}#prompt_message{margin-bottom:1.5rem}.popup__header{position:relative;display:grid;gap:.5rem;width:100%;padding:0 1.5rem;align-items:center}.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;margin-left:-1rem;justify-self:flex-start}ul[type=circle],menu[type=circle]{padding:1.5rem 2.5rem;list-style:circle}ul[type=circle] li,menu[type=circle] li{margin-bottom:1rem}ul[type=circle] li:last-of-type,menu[type=circle] li:last-of-type{margin-bottom:0}ul,menu{list-style:none}#main_header{grid-area:header;display:grid;align-items:center;grid-template-columns:1fr auto;gap:1rem;padding:max(1rem,3vw);grid-column:1/-1;view-transition-name:main-header}#main_header h4{font-weight:600;color:rgba(var(--text-color), 1)}#logo{color:inherit;margin-right:auto}.app-brand{display:flex;gap:.3rem;align-items:center}.app-brand .icon{height:1.2rem;width:1.2rem}.app-name__company{font-size:.8rem;font-weight:500;color:rgba(var(--text-color), 0.8)}theme-toggle{justify-self:end;align-self:center}.label{text-transform:capitalize;font-size:.8rem;margin-bottom:.3rem;color:rgba(var(--text-color), 0.8);margin-top:1.5rem;font-weight:500}.label:first-of-type{margin-top:0}.label+:is(h1,h2,h3,h4,h5,h6,p,span,sm-copy,a){font-weight:700}#page_container{overflow:auto;padding:1rem max(1rem,3vw);gap:1rem}#page_container>*{margin:0 auto;width:min(100%,72rem)}#page_container[data-page=dapps] h1{font-size:max(4vw,1.5rem)}#hero_section{border-radius:1rem;padding:max(1rem,6vw);background-color:#ecfffa}#hero_section__svg{width:auto;height:max(4rem,8vw);margin-bottom:1rem}#hero_section h1{color:#2f9b7e;font-size:max(3vw,1.5rem)}#dapp_search_input{width:min(100%,20rem);--background: transparent;border:solid thin rgba(var(--text-color), 0.3);border-radius:.5rem}#dapp_list{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill, minmax(20rem, 1fr))}.dapp-card{display:grid;gap:1rem;padding:max(1rem,1.5vw);border:solid thin rgba(var(--text-color), 0.1);background-color:rgba(var(--text-color), 0.02);border-radius:1rem}.dapp-card__icon{height:4rem;aspect-ratio:1/1;border-radius:.5rem;-o-object-fit:cover;object-fit:cover;background-color:rgba(var(--text-color), 0.06)}.dapp-card__title__wrapper{color:rgba(var(--text-color), 1)}.dapp-card>.flex{margin-top:1rem;margin-left:auto}.dapp-card__link--primary{background-color:rgba(var(--text-color), 1);color:rgba(var(--background-color), 1)}.dapp-card__link--primary .icon{fill:rgba(var(--background-color), 1)}#dapps_menu{view-transition-name:dapps-menu}.hamburger-menu__item{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.9rem;color:rgba(var(--text-color), 0.8)}.hamburger-menu__item:hover{background-color:rgba(var(--text-color), 0.06);text-decoration:none}.hamburger-menu__item--active{background-color:rgba(var(--text-color), 0.06);color:rgba(var(--text-color), 1)}@media only screen and (max-width: 640px){.hide-on-small{display:none}}@media only screen and (min-width: 640px){sm-popup{--width: 24rem}.popup__header{padding:1rem 1.5rem 0 1.5rem}#page_container[data-page=dapps]{display:grid;grid-template-columns:12rem 1fr}}@media only screen and (min-width: 1280px){.page{margin:0 8vw}}@media(hover: hover){.hover{cursor:pointer}::-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],.button--primary){transition:background-color .3s}.interact:not([disabled],.button--primary):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)}}@media(prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){-webkit-animation:none !important;animation:none !important}} \ No newline at end of file +*{padding:0;margin:0;box-sizing:border-box;font-family:"IBM Plex Sans",sans-serif}:root{font-size:clamp(1rem,1.2vmax,1.5rem)}html,body{height:100%}body{--accent-color: #3d5afe;--accent-color-rgb: 77, 119, 255;--secondary-color: #ffac2e;--text-color: 34, 34, 34;--foreground-color: 252, 253, 255;--background-color: 241, 243, 248;--danger-color: rgb(255, 75, 75);--green: #1cad59;--yellow: rgb(220, 165, 0);color:rgba(var(--text-color), 1);background-color:rgba(var(--foreground-color), 1)}body[data-theme=dark]{--accent-color: #92a2ff;--accent-color-rgb: 160, 182, 255;--secondary-color: #d60739;--text-color: 210, 210, 210;--foreground-color: 27, 28, 29;--background-color: 21, 22, 22;--danger-color: rgb(255, 106, 106);--green: #00e676;--yellow: rgb(255, 213, 5)}body[data-theme=dark] ::-webkit-calendar-picker-indicator{filter:invert(1)}h1,h2,h3,h4,h5,h6{letter-spacing:-0.01em;font-weight:700}p,strong{color:rgba(var(--text-color), 0.9);max-width:70ch;font-size:.9rem}img{-o-object-fit:cover;object-fit:cover}a:where([class]){color:inherit;text-decoration:none}a:where([class]):focus-visible{box-shadow:0 0 0 .1rem rgba(var(--text-color), 1) inset}a{color:var(--accent-color);text-decoration:none}a:hover{-webkit-text-decoration:underline solid currentColor;text-decoration:underline solid currentColor}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}input[type=datetime-local]{width:100%;padding:.8rem .6rem;border:none;border-radius:.5rem;font-weight:500;font-family:inherit;font-size:inherit;color:inherit;background-color:rgba(var(--text-color), 0.06)}input[type=datetime-local]:focus{outline:none;box-shadow:0 0 0 .1rem var(--accent-color)}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:.9rem;font-weight:500;white-space:nowrap;padding:.5rem 1rem;border-radius:.5rem;justify-content:center;flex-shrink:0}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{padding:.8rem 1rem;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 var(--accent-color) 1px;background-color:rgba(0,0,0,0);color:var(--accent-color)}.button--outlined .icon{fill:var(--accent-color)}.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:.8rem;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{height:100%;padding:0;padding:.4rem;border-radius:.3rem;aspect-ratio:1/1}.icon-only .icon{height:1em;width:1em}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 summary{display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;align-items:center;gap:1rem;color:var(--accent-color)}details[open] summary{margin-bottom:1rem}details[open]>summary .down-arrow{transform:rotate(180deg)}fieldset{border:none}sm-input{--border-radius: 0.5rem;--background-color: rgba(var(--foreground-color), 1)}sm-spinner{--size: 1.3rem;--stroke-width: 0.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;font-weight:500}sm-chip[selected]::part(chip){background:rgba(var(--text-color), 1);color:rgba(var(--background-color), 1)}sm-select{font-size:.9rem;font-weight:500;--padding: 0.6rem 0.3rem 0.6rem 0.6rem}sm-option{font-size:.9rem}ul{list-style:none}.interact{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.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}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.sticky{position:-webkit-sticky;position:sticky}.top-0{top:0}.flex{display:flex}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.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}.text-align-left{text-align:left}.align-items-start{align-items:flex-start}.align-items-center{align-items:center}.align-content-start{align-content:flex-start}.align-start{align-content:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.text-center{text-align:center}.justify-start{justify-items:start}.justify-content-start{justify-content:start}.justify-content-center{justify-content:center}.justify-items-center{justify-items:center}.justify-right{margin-left:auto}.align-self-start{align-self:start}.align-self-center{align-self:center}.align-self-end{align-self:end}.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}.space-evenly{justify-content:space-evenly}.w-100{width:100%}.h-100{height:100%}.padding-block-1{padding-block:1rem}.margin-right-0-3{margin-right:.3rem}.margin-right-0-5{margin-right:.5rem}.margin-right-1{margin-right:1rem}.margin-left-0-5{margin-left:.5rem}.margin-left-auto{margin-left:auto}.margin-right-auto{margin-right:auto}.margin-top-1{margin-top:1rem}.margin-bottom-0-5{margin-bottom:.5rem}.margin-bottom-1{margin-bottom:1rem}.margin-bottom-2{margin-bottom:2rem}.margin-block-0-5{margin-block:.5rem}.margin-block-1{margin-block:1rem}.margin-block-1-5{margin-block:1.5rem}.margin-inline-1{margin-inline:1rem}.margin-inline-1-5{margin-inline:1.5rem}.hidden{display:none !important}.h1{font-size:2.5rem}.h2{font-size:2rem}.h3{font-size:1.4rem}.h4{font-size:1rem}.h5{font-size:.8rem}.grid-3{grid-template-columns:1fr auto auto}.flow-column{grid-auto-flow:column}.w-100{width:100%}.color-0-8{color:rgba(var(--text-color), 0.8)}.weight-400{font-weight:400}.weight-500{font-weight:500}.ws-pre-line{white-space:pre-line}.card{background-color:rgba(var(--foreground-color), 1);border-radius:.5rem;padding:max(1rem,3vw)}.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}.interactive{position:relative;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.observe-empty-state:empty{display:none}.observe-empty-state:not(:empty)~.empty-state{display:none}.password-field label{display:flex;flex-shrink:0}.password-field label input:checked~.visible{display:none}.password-field label input:not(:checked)~.invisible{display:none}.multi-state-button{display:grid;text-align:center;align-items:center;justify-items:center;isolation:isolate}.multi-state-button>*{grid-area:1/1/2/2}.multi-state-button button{z-index:1;width:100%}#confirmation_popup,#prompt_popup{flex-direction:column}#confirmation_popup h4,#prompt_popup h4{margin-bottom:1rem}#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}#prompt_message{margin-bottom:1.5rem}.popup__header{position:relative;display:grid;gap:.5rem;width:100%;padding:0 1.5rem;align-items:center}.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;margin-left:-1rem;justify-self:flex-start}ul[type=circle],menu[type=circle]{padding:1.5rem 2.5rem;list-style:circle}ul[type=circle] li,menu[type=circle] li{margin-bottom:1rem}ul[type=circle] li:last-of-type,menu[type=circle] li:last-of-type{margin-bottom:0}ul,menu{list-style:none}#main_header{grid-area:header;display:grid;align-items:center;grid-template-columns:1fr auto;gap:1rem;padding:max(1rem,3vw);grid-column:1/-1;view-transition-name:main-header}#main_header h4{font-weight:600;color:rgba(var(--text-color), 1)}#logo{color:inherit;margin-right:auto}.app-brand{display:flex;gap:.3rem;align-items:center}.app-brand .icon{height:1.2rem;width:1.2rem}.app-name__company{font-size:.8rem;font-weight:500;color:rgba(var(--text-color), 0.8)}theme-toggle{justify-self:end;align-self:center}.label{text-transform:capitalize;font-size:.8rem;margin-bottom:.3rem;color:rgba(var(--text-color), 0.8);margin-top:1.5rem;font-weight:500}.label:first-of-type{margin-top:0}.label+:is(h1,h2,h3,h4,h5,h6,p,span,sm-copy,a){font-weight:700}#page_container{overflow:auto;padding:1rem max(1rem,3vw);gap:1rem}#page_container>*{margin:0 auto;width:min(100%,72rem)}#page_container[data-page=dapps] h1{font-size:max(4vw,1.5rem)}#hero_section{border-radius:1rem;padding:max(1rem,6vw);background-color:#ecfffa}#hero_section__svg{width:auto;height:max(4rem,8vw);margin-bottom:1rem}#hero_section h1{color:#2f9b7e;font-size:max(3vw,1.5rem)}#dapp_search_input{width:min(100%,20rem);--background: transparent;border:solid thin rgba(var(--text-color), 0.3);border-radius:.5rem}#category_selector{view-transition-name:category-selector}#category_selector sm-chip{text-transform:capitalize}#dapp_list{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill, minmax(20rem, 1fr))}.dapp-card{display:grid;gap:1rem;padding:max(1rem,1.5vw);border:solid thin rgba(var(--text-color), 0.1);background-color:rgba(var(--text-color), 0.02);border-radius:1rem}.dapp-card__icon{height:4rem;aspect-ratio:1/1;border-radius:.5rem;-o-object-fit:cover;object-fit:cover;background-color:rgba(var(--text-color), 0.06)}.dapp-card__category{font-size:.8rem;font-weight:500;color:rgba(var(--text-color), 0.8);padding:.1rem .5rem;border-radius:.5rem;background-color:rgba(var(--text-color), 0.06);align-self:flex-start}.dapp-card__title__wrapper{color:rgba(var(--text-color), 1)}.dapp-card>.flex:last-of-type{margin-top:1rem;margin-left:auto}.dapp-card__link--primary{background-color:rgba(var(--text-color), 1);color:rgba(var(--background-color), 1)}.dapp-card__link--primary .icon{fill:rgba(var(--background-color), 1)}#dapps_menu{view-transition-name:dapps-menu}#hamburger_menu_trigger{padding:.5rem;margin-left:-0.5rem}.hamburger-menu__item{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.9rem;color:rgba(var(--text-color), 0.8)}.hamburger-menu__item:hover{background-color:rgba(var(--text-color), 0.06);text-decoration:none}.hamburger-menu__item--active{background-color:rgba(var(--text-color), 0.06);color:rgba(var(--text-color), 1)}@media only screen and (max-width: 640px){.hide-on-small{display:none}#category_selector{margin-left:-1rem;max-width:initial;width:calc(100% + 2rem)}#category_selector::part(chips-wrapper){padding:0 1rem}.hamburger-menu-wrapper{position:fixed;top:0;left:0;width:100%;height:100%;z-index:100;pointer-events:none;transform:translateX(-100%);transition:transform .3s}.hamburger-menu-wrapper.hamburger-menu--open{pointer-events:auto;transform:translateX(0)}.hamburger-menu{z-index:1;position:absolute;top:0;left:0;width:calc(100% - 4rem);height:100%;background-color:rgba(var(--foreground-color), 1);padding:1rem;box-shadow:0 0 1rem rgba(0,0,0,.1)}.hamburger-menu__overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(var(--text-color), 0.1);-webkit-backdrop-filter:blur(0.5rem);backdrop-filter:blur(0.5rem)}}@media only screen and (min-width: 640px){.hide-on-large{display:none}sm-popup{--width: 24rem}.popup__header{padding:1rem 1.5rem 0 1.5rem}#page_container[data-page=dapps]{display:grid;grid-template-columns:12rem 1fr}}@media only screen and (min-width: 1280px){.page{margin:0 8vw}}@media(hover: hover){.hover{cursor:pointer}::-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],.button--primary){transition:background-color .3s}.interact:not([disabled],.button--primary):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)}}@media(prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){-webkit-animation:none !important;animation:none !important}} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index 557ee93..1f4a2b5 100644 --- a/css/main.scss +++ b/css/main.scss @@ -246,8 +246,8 @@ sm-chip { --background: rgba(var(--text-color), 0.06); user-select: none; font-weight: 500; - &[selected] { - --background: var(--accent-color); + &[selected]::part(chip) { + background: rgba(var(--text-color), 1); color: rgba(var(--background-color), 1); } } @@ -754,6 +754,12 @@ theme-toggle { border: solid thin rgba(var(--text-color), 0.3); border-radius: 0.5rem; } +#category_selector { + view-transition-name: category-selector; + sm-chip { + text-transform: capitalize; + } +} #dapp_list { display: grid; gap: 1rem; @@ -773,10 +779,19 @@ theme-toggle { object-fit: cover; background-color: rgba(var(--text-color), 0.06); } + &__category { + font-size: 0.8rem; + font-weight: 500; + color: rgba(var(--text-color), 0.8); + padding: 0.1rem 0.5rem; + border-radius: 0.5rem; + background-color: rgba(var(--text-color), 0.06); + align-self: flex-start; + } &__title__wrapper { color: rgba(var(--text-color), 1); } - & > .flex { + & > .flex:last-of-type { margin-top: 1rem; margin-left: auto; } @@ -793,6 +808,10 @@ theme-toggle { #dapps_menu { view-transition-name: dapps-menu; } +#hamburger_menu_trigger { + padding: 0.5rem; + margin-left: -0.5rem; +} .hamburger-menu { } .hamburger-menu__item { @@ -820,8 +839,54 @@ theme-toggle { &[data-page="dapps"] { } } + #category_selector { + margin-left: -1rem; + max-width: initial; + width: calc(100% + 2rem); + &::part(chips-wrapper) { + padding: 0 1rem; + } + } + .hamburger-menu-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; + pointer-events: none; + transform: translateX(-100%); + transition: transform 0.3s; + &.hamburger-menu--open { + pointer-events: auto; + transform: translateX(0); + } + } + .hamburger-menu { + z-index: 1; + position: absolute; + top: 0; + left: 0; + width: calc(100% - 4rem); + height: 100%; + background-color: rgba(var(--foreground-color), 1); + padding: 1rem; + box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1); + } + .hamburger-menu__overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(var(--text-color), 0.1); + backdrop-filter: blur(0.5rem); + } } @media only screen and (min-width: 640px) { + .hide-on-large { + display: none; + } sm-popup { --width: 24rem; } diff --git a/index.html b/index.html index 8d2b5df..c7d9bf1 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,13 @@
- + +

RanchiMall Dapps

@@ -314,7 +320,7 @@ appLink: 'https://ranchimall.github.io/messenger', moreLink: '#/dapps/messenger', tags: ['messenger', 'chat', 'bitcoin', 'ethereum', 'flo', 'multisig'], - category: 'Social' + category: 'social' }, { name: 'FLO Wallet', @@ -323,7 +329,7 @@ appLink: 'https://ranchimall.github.io/flowallet', moreLink: '#/dapps/flo-wallet', tags: ['flo', 'wallet'], - category: 'Wallet' + category: 'wallet' }, { name: 'FLO Scout', @@ -332,7 +338,7 @@ appLink: 'https://ranchimall.github.io/floscout', moreLink: '#/dapps/flo-scout', tags: ['flo', 'scout', 'smart contracts'], - category: 'Blockchain Explorer' + category: 'blockchain-explorer' }, { name: 'BTC Wallet', @@ -341,7 +347,7 @@ appLink: 'https://ranchimall.github.io/btcwallet', moreLink: '#/dapps/btc-wallet', tags: ['bitcoin', 'wallet'], - category: 'Wallet' + category: 'wallet' }, { name: 'Taproot wallet', @@ -350,7 +356,7 @@ appLink: 'https://ranchimall.github.io/taprootwallet', moreLink: '#/dapps/taproot-wallet', tags: ['bitcoin', 'wallet', 'taproot'], - category: 'Wallet' + category: 'wallet' }, { name: 'FLO Ethereum', @@ -359,7 +365,7 @@ appLink: 'https://ranchimall.github.io/floethereum', moreLink: '#/dapps/flo-ethereum', tags: ['ethereum', 'wallet', 'flo'], - category: 'Wallet' + category: 'wallet' }, { name: 'KYC', @@ -368,7 +374,7 @@ appLink: 'https://ranchimall.github.io/kyc', moreLink: '#/dapps/kyc', tags: ['kyc', 'identity'], - category: 'Identity' + category: 'identity' }, { name: 'BTC Mortgage', @@ -377,7 +383,7 @@ appLink: 'https://ranchimall.github.io/btcmortgage', moreLink: '#/dapps/btc-mortgage', tags: ['bitcoin', 'mortgage', 'lend', 'borrow'], - category: 'Finance' + category: 'finance' }, { name: 'FLOpay', @@ -386,7 +392,7 @@ appLink: 'https://ranchimall.github.io/flopay', moreLink: '#/dapps/flopay', tags: ['flo', 'payments', 'rupee'], - category: 'Wallet' + category: 'wallet' }, { name: 'Content Collaboration', @@ -395,7 +401,7 @@ appLink: 'https://ranchimall.github.io/cc', moreLink: '#/dapps/content-collaboration', tags: ['collaboration', 'writing', 'content'], - category: 'Content' + category: 'content' }, { name: 'RIBC', @@ -404,7 +410,7 @@ appLink: 'https://ranchimall.github.io/ribc', moreLink: '#/dapps/ribc', tags: ['internship', 'contract', 'ribc'], - category: 'Management' + category: 'management' }, { name: 'LogSheet', @@ -413,7 +419,7 @@ appLink: 'https://ranchimall.github.io/logsheet', moreLink: '#/dapps/log-sheet', tags: ['log', 'sheet', 'logsheets'], - category: 'Content' + category: 'content' }, { name: 'Exchange', @@ -422,7 +428,7 @@ appLink: 'https://ranchimall.github.io/exchangemarket', moreLink: '#/dapps/exchange', tags: ['exchange', 'trade', 'flo', 'rupee'], - category: 'Finance' + category: 'finance' }, { name: 'RanchiMall Times', @@ -431,17 +437,22 @@ appLink: 'https://ranchimall.github.io/rmtimes', moreLink: '#/dapps/ranchimall-times', tags: ['news', 'articles', 'content'], - category: 'Content' + category: 'content' }, ].sort((a, b) => a.name.localeCompare(b.name)) router.addRoute('home', renderHome) router.addRoute('', renderHome) function renderDappCard(dapp) { - const { name, description, icon, appLink, moreLink } = dapp; + const { name, description, icon, appLink, moreLink, tags, category } = dapp; return html`
  • -
    - ${icon} +
    @@ -463,9 +474,10 @@ ` } function renderHome(state) { - const { wildcards: [page], viewTransition } = state + const { params: { category = 'all', query = '' }, page, viewTransition, lastPage } = state getRef('page_container').dataset.page = 'home'; - const renderedDappsList = dappsList.map((dapp) => renderDappCard(dapp)) + const renderedDappsList = filterDapps(query, category) + .map((dapp) => renderDappCard(dapp)) renderElem(getRef('page_container'), html`
    @@ -474,15 +486,28 @@ exciting new blockchain apps
    + +

    Our offerings

    - +
    -
      ${renderedDappsList}
    + + ${['all', 'blockchain-explorer', 'content', 'finance', 'identity', 'management', 'social', 'wallet'].map((value, index) => html` + ${value.replace('-', ' ')} + `)} + + ${renderedDappsList.length === 0 ? html` +
    + No dapps related to '${query}' ${category === 'all' ? '' : `in ${category.replace('-', ' ')}`} found. +
    + ` : html` +
      ${renderedDappsList}
    + `} `) - if (!viewTransition) return + if (!viewTransition || lastPage === page) return viewTransition.ready.then(() => { document.documentElement.animate( [ @@ -508,40 +533,72 @@ ); }) } - function searchDapps() { - const searchQuery = getRef('dapp_search_input').value.trim().toLowerCase() - const filtered = dappsList.filter(dapp => dapp.name.toLowerCase().includes(searchQuery)) - if (filtered.length === 0) { - renderElem(getRef('dapp_list'), html` -

    No Dapp related to '${searchQuery}'

    - `) - } else { - const renderedDappsList = filtered.map((dapp) => renderDappCard(dapp)) - renderElem(getRef('dapp_list'), html`${renderedDappsList}`) - } + function handleFilterCategory(e) { + const query = getRef('dapp_search_input')?.value.trim().toLowerCase() || ''; + location.hash = `#/home?category=${e.target.value}${query ? `&query=${query}` : ''}` + } + function filterDapps(query = '', category = 'all') { + const filtered = dappsList.filter(dapp => { + if (category === 'all') { + return dapp.name.toLowerCase().includes(query) || dapp.description.toLowerCase().includes(query) || dapp.tags.some(tag => tag.includes(query)) + } else { + return dapp.category.toLowerCase().includes(category) && (dapp.name.toLowerCase().includes(query) || dapp.description.toLowerCase().includes(query) || dapp.tags.some(tag => tag.includes(query))) + } + }) + return filtered + } + function searchDapps(e) { + const query = e.target.value.trim().toLowerCase() || ''; + const category = getRef('category_selector').value || 'all'; + location.hash = `#/home?category=${category}&query=${query}` } const appHamburgerMenu = (activePage) => html` -
    + ` + function openHamburgerMenu() { + if (getRef('dapps_menu')) + getRef('dapps_menu').parentElement.classList.add('hamburger-menu--open') + } + function closeHamburgerMenu() { + if (getRef('dapps_menu')) + getRef('dapps_menu').parentElement.classList.remove('hamburger-menu--open') + } router.addRoute('dapps', (state) => { - const { wildcards: [page], viewTransition } = state + const { wildcards: [page], viewTransition } = state; getRef('page_container').dataset.page = 'dapps'; - const { name, description, icon, appLink, moreLink } = dappsList.find(dapp => dapp.moreLink.includes(page)) + const { name, description, icon, appLink, moreLink } = dappsList.find(dapp => dapp.moreLink.includes(page)); + closeHamburgerMenu() let dappPage = [ appHamburgerMenu(page), html`
    -

    ${name}

    + +
    + + + Home + +

    ${name}

    +

    ${description}

    + + Try it + +
    ` ] diff --git a/scripts/components.min.js b/scripts/components.min.js index 17e2d8f..68994a9 100644 --- a/scripts/components.min.js +++ b/scripts/components.min.js @@ -1,4 +1,5 @@ -// Components downloaded: copy,input,notifications,popup,spinner,theme-toggle +// Components downloaded: chips,copy,input,notifications,popup,spinner,theme-toggle +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(val) { this.setSelectedOption(val) } scrollLeft() { this.chipsWrapper.scrollBy({ left: -this.scrollDistance, behavior: "smooth" }) } scrollRight() { this.chipsWrapper.scrollBy({ left: this.scrollDistance, behavior: "smooth" }) } setSelectedOption(value) { this._value !== value && (this._value = value, this.assignedElements.forEach((elem => { elem.value == value ? (elem.setAttribute("selected", ""), elem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" })) : elem.removeAttribute("selected") }))) } fireEvent() { this.dispatchEvent(new CustomEvent("change", { bubbles: !0, composed: !0, detail: { value: this._value } })) } connectedCallback() { this.setAttribute("role", "listbox"); const slot = this.shadowRoot.querySelector("slot"); slot.addEventListener("slotchange", (e => { firstOptionObserver.disconnect(), lastOptionObserver.disconnect(), this.observeSelf.disconnect(), clearTimeout(this.slotChangeTimeout), this.slotChangeTimeout = setTimeout((() => { this.assignedElements = slot.assignedElements(), this.observeSelf.observe(this) }), 0) })); new ResizeObserver((entries => { entries.forEach((entry => { if (entry.contentBoxSize) { const contentBoxSize = Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize; this.scrollDistance = .6 * contentBoxSize.inlineSize } else this.scrollDistance = .6 * entry.contentRect.width })) })).observe(this), this.observeSelf = new IntersectionObserver(((entries, observer) => { entries.forEach((entry => { entry.isIntersecting && !this.hasAttribute("multiline") && this.assignedElements.length > 0 && (firstOptionObserver.observe(this.assignedElements[0]), lastOptionObserver.observe(this.assignedElements[this.assignedElements.length - 1]), observer.unobserve(this)) })) }), { threshold: 1 }), this.chipsWrapper.addEventListener("option-clicked", (e => { this._value !== e.target.value && (this.setSelectedOption(e.target.value), this.fireEvent()) })); const firstOptionObserver = new IntersectionObserver((entries => { entries.forEach((entry => { entry.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 }), lastOptionObserver = new IntersectionObserver((entries => { entries.forEach((entry => { entry.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) } static get observedAttributes() { return ["selected"] } get value() { return this._value } fireEvent() { this.dispatchEvent(new CustomEvent("option-clicked", { bubbles: !0, composed: !0, detail: { value: this._value } })) } handleKeyDown(e) { "Enter" !== e.key && "Space" !== e.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) } attributeChangedCallback(name, oldValue, newValue) { "selected" === name && (null !== newValue ? (this.fireEvent(), this.setAttribute("aria-selected", "true")) : this.removeAttribute("aria-selected")) } 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.getAttribute("value")).then(t => this.fireEvent()).catch(t => console.error(t)) } connectedCallback() { this.copyButton.addEventListener("click", this.copy) } attributeChangedCallback(t, n, o) { if ("value" === t) { const t = this.copyContent.querySelector("slot"); if (!t) return; const n = t.assignedNodes(); n && n.length || (t.textContent = o) } } disconnectedCallback() { this.copyButton.removeEventListener("click", this.copy) } }); const smInput = document.createElement("template"); smInput.innerHTML = '
    ', customElements.define("sm-input", class SmInput extends HTMLElement { static hasAppendedStyles = !1; #validationState = { validatedFor: void 0, isValid: !1, errorMessage: "Please fill out this field." }; 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.placeholderElement = this.shadowRoot.querySelector(".placeholder"), this.outerContainer = this.shadowRoot.querySelector(".outer-container"), this.optionList = this.shadowRoot.querySelector(".datalist"), this._helperText = "", 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"] } 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(val) { val !== this.input.value && (this.input.value = val, this._value = val, this.checkInput()) } get placeholder() { return this.getAttribute("placeholder") } set placeholder(val) { this.setAttribute("placeholder", val) } get type() { return this.getAttribute("type") } set type(val) { this.setAttribute("type", val) } get validity() { return this.input.validity } get disabled() { return this.hasAttribute("disabled") } set disabled(value) { value ? (this.inputParent.classList.add("disabled"), this.setAttribute("disabled", "")) : (this.inputParent.classList.remove("disabled"), this.removeAttribute("disabled")) } get readOnly() { return this.hasAttribute("readonly") } set readOnly(value) { value ? this.setAttribute("readonly", "") : this.removeAttribute("readonly") } set customValidation(val) { val && (this.validationFunction = val) } set errorText(val) { this.#validationState.errorText = val } showError = (errorText = this.#validationState.errorText) => { const appendedNew = this.appendFeedbackElement(); this.feedbackPopover.innerHTML = ` ${errorText} `, this.feedbackPopover.dataset.state = "error", appendedNew && this.feedbackPopover.animate([{ transform: "scale(0.95)", opacity: 0 }, { transform: "scale(1)", opacity: 1 }], { duration: 200, easing: "ease", fill: "forwards" }) }; set helperText(val) { this._helperText = val } get isValid() { if (this.#validationState.validatedFor === this.input.value) return this.#validationState.isValid; const _isValid = this.input.checkValidity(); let _validity = { isValid: !0, errorText: "" }; return this.validationFunction && (_validity = this.validationFunction(this.input.value)), _isValid && _validity.isValid ? (this.setAttribute("valid", ""), this.removeAttribute("invalid"), this.hideFeedback()) : (this.removeAttribute("valid"), this.setAttribute("invalid", ""), "" !== this.value.trim() && (_validity.errorText || this.#validationState.errorText) && this.showError(_validity.errorText || this.#validationState.errorText)), this.#validationState.validatedFor = this.input.value, this.#validationState.isValid = _isValid && _validity.isValid, this.#validationState.errorText = _validity.errorText || this.#validationState.errorText, this.#validationState.isValid } reset = () => { this.value = "" }; clear = () => { this.value = "", this.input.focus(), this.fireEvent() }; focusIn = () => { this.input.focus() }; focusOut = () => { this.input.blur() }; fireEvent = () => { let event = new Event("input", { bubbles: !0, cancelable: !0, composed: !0 }); this.dispatchEvent(event) }; searchDatalist = searchKey => { const filteredData = this.datalist.filter((item => item.toLowerCase().includes(searchKey.toLowerCase()))); if (filteredData.sort(((a, b) => a.toLowerCase().indexOf(searchKey.toLowerCase()) - b.toLowerCase().indexOf(searchKey.toLowerCase()))), filteredData.length) { if (this.optionList.children.length > filteredData.length) { const optionsToRemove = this.optionList.children.length - filteredData.length; for (let i = 0; i < optionsToRemove; i++)this.optionList.removeChild(this.optionList.lastChild) } filteredData.forEach(((item, index) => { if (this.optionList.children[index]) this.optionList.children[index].textContent = item; else { const option = document.createElement("li"); option.textContent = item, option.classList.add("datalist-item"), option.setAttribute("tabindex", "0"), this.optionList.appendChild(option) } })), this.optionList.classList.remove("hidden") } else this.optionList.classList.add("hidden") }; checkInput = e => { 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.shouldAnimatePlaceholder && this.inputParent.classList.add("animate-placeholder"), this.placeholderElement.classList.toggle("hidden", !this.shouldAnimatePlaceholder), this.datalist.length && (this.searchTimeout && clearTimeout(this.searchTimeout), this.searchTimeout = setTimeout((() => { this.searchDatalist(this.input.value.trim()) }), 100))) : (this.shouldAnimatePlaceholder && this.inputParent.classList.remove("animate-placeholder"), this.placeholderElement.classList.remove("hidden"), this.hideFeedback(), this.datalist.length && (this.optionList.innerHTML = "", this.optionList.classList.add("hidden")))) }; allowOnlyNum = e => { e.ctrlKey || 1 === e.key.length && (("." !== e.key || !e.target.value.includes(".") && 0 !== e.target.value.length) && ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."].includes(e.key) || e.preventDefault()) }; handleOptionClick = e => { this.input.value = e.target.textContent, this.optionList.classList.add("hidden"), this.input.focus() }; handleInputNavigation = e => { "ArrowDown" === e.key ? (e.preventDefault(), this.optionList.children.length && this.optionList.children[0].focus()) : "ArrowUp" === e.key && (e.preventDefault(), this.optionList.children.length && this.optionList.children[this.optionList.children.length - 1].focus()) }; handleDatalistNavigation = e => { "ArrowUp" === e.key ? (e.preventDefault(), this.shadowRoot.activeElement.previousElementSibling ? this.shadowRoot.activeElement.previousElementSibling.focus() : this.input.focus()) : "ArrowDown" === e.key ? (e.preventDefault(), this.shadowRoot.activeElement.nextElementSibling ? this.shadowRoot.activeElement.nextElementSibling.focus() : this.input.focus()) : "Enter" !== e.key && " " !== e.key || (e.preventDefault(), this.input.value = e.target.textContent, this.optionList.classList.add("hidden"), this.input.focus()) }; handleFocus = e => { this.datalist.length && this.searchDatalist(this.input.value.trim()) }; handleBlur = e => { this.datalist.length && this.optionList.classList.add("hidden") }; applyGlobalCustomValidation = () => { if (void 0 !== window.smCompConfig && window.smCompConfig["sm-input"]) { const config = window.smCompConfig["sm-input"].find((config => this.matches(config.selector))); this.customValidation = config?.customValidation } }; updatePosition = () => { requestAnimationFrame((() => { if (this.dimensions = this.getBoundingClientRect(), this.scrollingParentDimensions = this.scrollingParent.getBoundingClientRect(), 0 === this.dimensions.width || 0 === this.dimensions.height) return; let topOffset = this.dimensions.top - this.scrollingParentDimensions.top + this.dimensions.height, leftOffset = this.dimensions.left - this.scrollingParentDimensions.left; const maxWidth = this.dimensions.width; this.feedbackPopover.style = `top: ${topOffset}px; left: ${leftOffset}px; max-width: ${maxWidth}px;` })) }; appendFeedbackElement = () => { if (this.feedbackPopover) return !1; this.feedbackPopover = document.createElement("div"), this.feedbackPopover.className = "feedback-popover", this.feedbackPopover.setAttribute("aria-live", "polite"), this.containment = this.closest("[data-sm-containment]"), this.scrollingParent = this.getNearestScrollingParent(this); return (this.containment || this.scrollingParent).appendChild(this.feedbackPopover), "" === this.scrollingParent.style.position && (this.scrollingParent.style.position = "relative"), this.containment || (this.observerHidFeedback = !1, this.intersectionObserver = new IntersectionObserver((entries => { if (this.feedbackPopover) if (entries[0].isIntersecting) { if (!this.observerHidFeedback) return; this.feedbackPopover.classList.remove("hidden"), this.observerHidFeedback = !1 } else this.feedbackPopover.classList.add("hidden"), this.observerHidFeedback = !0 })).observe(this)), this.updatePosition(), window.addEventListener("resize", this.updatePosition, { passive: !0 }), !0 }; getNearestScrollingParent = element => { let parent = element.parentNode; for (; parent;) { if (parent.scrollHeight > parent.clientHeight || parent.scrollWidth > parent.clientWidth || parent.tagName.includes("SM-") || parent.hasAttribute("data-scrollable")) return parent; parent = parent.parentNode } return document.body }; hideFeedback = () => { this.feedbackPopover && (this.feedbackPopover.animate([{ transform: "none", opacity: 1 }, { transform: "scale(0.95)", opacity: 0 }], { duration: 100, easing: "ease-in-out", fill: "forwards" }).onfinish = () => { this.intersectionObserver?.disconnect(), this.feedbackPopover.remove(), this.feedbackPopover = null, window.removeEventListener("resize", this.updatePosition, { passive: !0 }) }) }; connectedCallback() { SmInput.hasAppendedStyles || (document.head.insertAdjacentHTML("beforeend", ""), SmInput.hasAppendedStyles = !0), this.shouldAnimatePlaceholder = this.hasAttribute("animate"), this.shouldAnimatePlaceholder && "" !== this.placeholderElement && this.value && (this.inputParent.classList.add("animate-placeholder"), this.placeholderElement.classList.remove("hidden")), this.setAttribute("role", "textbox"), "loading" === document.readyState ? window.addEventListener("load", this.applyGlobalCustomValidation, { once: !0 }) : this.applyGlobalCustomValidation(), 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(name, oldValue, newValue) { if (oldValue !== newValue) switch (this.reflectedAttributes.includes(name) && (this.hasAttribute(name) ? this.input.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : "") : this.input.removeAttribute(name)), name) { case "placeholder": this.placeholderElement.textContent = newValue, this.setAttribute("aria-label", newValue); 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 = newValue; break; case "error-text": this.#validationState.errorText = newValue; 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), window.removeEventListener("resize", this.updatePosition, { passive: !0 }), this.feedbackPopover && this.feedbackPopover.remove(), this.intersectionObserver && this.intersectionObserver.disconnect() } }); 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.handleTouchMove = this.handleTouchMove.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.notificationTimeout = 5e3, this.mediaQuery = window.matchMedia("(min-width: 640px)"), this.handleOrientationChange = this.handleOrientationChange.bind(this), this.isBigViewport = !1 } set timeout(value) { isNaN(value) || (this.notificationTimeout = value) } randString(length) { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (let i = 0; i < length; i++)result += characters.charAt(Math.floor(52 * Math.random())); return result } createNotification(message, options = {}) { const { pinned: pinned = !1, icon: icon, action: action, timeout: timeout = this.notificationTimeout } = options, notification = document.createElement("div"); return notification.id = this.randString(8), notification.className = "notification " + (pinned ? "pinned" : ""), notification.style.setProperty("--timeout", `${timeout}ms`), notification.innerHTML = ` ${icon ? `
    ${icon}
    ` : ""} ${message} ${action ? `` : ""} `, action && notification.querySelector(".action").addEventListener("click", action.callback), notification.querySelector(".close").addEventListener("click", (() => { this.removeNotification(notification) })), pinned || setTimeout((() => { this.removeNotification(notification, this.isBigViewport ? "left" : "top") }), timeout), notification } push(message, options = {}) { const notification = this.createNotification(message, options); return this.isBigViewport ? this.notificationPanel.append(notification) : this.notificationPanel.prepend(notification), notification.scrollIntoView({ behavior: "smooth" }), this.notificationPanel.animate([{ transform: `translateY(${this.isBigViewport ? "" : "-"}${notification.clientHeight}px)` }, { transform: "none" }], this.animationOptions), notification.animate([{ transform: "translateY(-1rem)", opacity: "0" }, { transform: "none", opacity: "1" }], this.animationOptions).onfinish = e => { e.target.commitStyles(), e.target.cancel() }, notification.id } removeNotification(notification, direction = "left") { if (!notification) return; const sign = "left" === direction || "top" === direction ? "-" : "+"; this.isBigViewport || "top" !== direction ? notification.animate([{ transform: this.currentX ? `translateX(${this.currentX}px)` : "none", opacity: "1" }, { transform: `translateX(calc(${sign}${Math.abs(this.currentX)}px ${sign} 1rem))`, opacity: "0" }], this.animationOptions).onfinish = () => { notification.remove() } : notification.animate([{ transform: this.currentX ? `translateY(${this.currentX}px)` : "none", opacity: "1" }, { transform: `translateY(calc(${sign}${Math.abs(this.currentX)}px ${sign} 1rem))`, opacity: "0" }], this.animationOptions).onfinish = () => { notification.remove() } } remove(id) { const notification = this.notificationPanel.querySelector(`#${id}`); notification && this.removeNotification(notification) } clearAll() { Array.from(this.notificationPanel.children).forEach((child => { this.removeNotification(child) })) } handleTouchMove(e) { this.currentX = e.touches[0].clientX - this.startX, this.currentTarget.style.transform = `translateX(${this.currentX}px)` } handleOrientationChange(e) { this.isBigViewport = e.matches, e.matches } connectedCallback() { this.handleOrientationChange(this.mediaQuery), this.mediaQuery.addEventListener("change", this.handleOrientationChange), this.notificationPanel.addEventListener("touchstart", (e => { e.target.closest(".close") ? this.removeNotification(e.target.closest(".notification")) : e.target.closest(".notification") && (this.swipeThreshold = e.target.closest(".notification").getBoundingClientRect().width / 2, this.currentTarget = e.target.closest(".notification"), this.startTime = Date.now(), this.startX = e.touches[0].clientX, this.startY = e.touches[0].clientY, this.notificationPanel.addEventListener("touchmove", this.handleTouchMove, { passive: !0 })) }), { passive: !0 }), this.notificationPanel.addEventListener("touchend", (e => { this.endX = e.changedTouches[0].clientX, this.endY = e.changedTouches[0].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 = e => { e.target.commitStyles(), e.target.cancel() }, this.notificationPanel.removeEventListener("touchmove", this.handleTouchMove), this.currentX = 0 })) } disconnectedCallback() { mediaQueryList.removeEventListener("change", handleOrientationChange) } });