From 50083bfe6502f5cbd4c084889fb165168fc301d5 Mon Sep 17 00:00:00 2001 From: sairaj mote Date: Mon, 16 Jan 2023 20:18:04 +0530 Subject: [PATCH] UI improvements and bug fixes --- css/main.css | 4 +++- css/main.min.css | 2 +- css/main.scss | 8 +++++++- index.html | 6 +++--- scripts/components.js | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/css/main.css b/css/main.css index 12dd96d..2a4a786 100644 --- a/css/main.css +++ b/css/main.css @@ -339,10 +339,12 @@ ul { } .ripple { + height: 8rem; + width: 8rem; position: absolute; border-radius: 50%; transform: scale(0); - background: rgba(var(--text-color), 0.16); + background: radial-gradient(circle, rgba(var(--text-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%); pointer-events: none; } diff --git a/css/main.min.css b/css/main.min.css index 01df9cf..e7cd776 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1 +1 @@ -*{padding:0;margin:0;box-sizing:border-box;font-family:"Inter",sans-serif}:root{font-size:clamp(1rem,1.2vmax,3rem)}html,body{height:100%;scroll-behavior:smooth}body{--accent-color: #304ffe;--light-shade: rgba(var(--text-color), 0.06);--text-color: 17, 17, 17;--text-color-light: 100, 100, 100;--foreground-color: 255, 255, 255;--background-color: 243, 245, 250;--error-color: red;--green: #00843b;color:rgba(var(--text-color), 1);background:rgba(var(--background-color), 1);display:flex;flex-direction:column}body[data-theme=dark]{--accent-color: #a6b9ff;--green: #13ff5a;--text-color: 240, 240, 240;--text-color-light: 170, 170, 170;--foreground-color: 27, 28, 29;--background-color: 21, 22, 22;--error-color: rgb(255, 106, 106)}main{flex:1}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(--foreground-color), 1)}.full-bleed{grid-column:1/4}.h1{font-size:2.5rem}.h2{font-size:2rem}.h3{font-size:1.4rem}.h4{font-size:1rem}.h5{font-size:.8rem}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}p{font-size:.8;max-width:60ch;line-height:1.7;color:rgba(var(--text-color), 0.8)}p:not(:last-of-type){margin-bottom:1rem}img{-o-object-fit:cover;object-fit:cover}a{color:inherit;text-decoration:none}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;-webkit-tap-highlight-color:rgba(0,0,0,0);align-items:center;font-size:.9rem;font-weight:500;white-space:nowrap;padding:.8rem;border-radius:.5rem;justify-content:center;color:inherit;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content}.button:not(:disabled),button:not(:disabled){cursor:pointer}.button{color:var(--accent-color);background-color:var(--blue-accent-1)}.button .icon{fill:var(--accent-color)}.button--primary,.button--danger{color:rgba(var(--background-color), 1) !important}.button--primary .icon,.button--danger .icon{fill:rgba(var(--background-color), 1)}.button--primary{width:100%;background-color:var(--accent-color)}.button--danger{background-color:var(--danger-color)}.button--small{padding:.4rem .6rem}.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{padding:.5rem;border-radius:.3rem}button:disabled{cursor:not-allowed;filter:saturate(0.5);opacity:.8}ul{list-style:none}.flex{display:flex}.grid{display:grid}.hide{opacity:0;pointer-events:none}.hidden{display:none !important}.no-transformations{transform:none !important}.overflow-ellipsis{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.breakable{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-word;-webkit-hyphens:auto;hyphens:auto}.flex{display:flex}.grid{display:grid}.flow-column{grid-auto-flow:column}.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-items:flex-start}.align-center{align-items:center}.text-center{text-align:center}.justify-start{justify-content:start}.justify-center{justify-content:center}.justify-right{margin-left:auto}.align-self-center{align-self:center}.justify-self-center{justify-self:center}.justify-self-start{justify-self:start}.direction-column{flex-direction:column}.space-between{justify-content:space-between}.w-100{width:100%}.margin-left-0-3{margin-left:.3rem}.margin-left-0-5{margin-left:.5rem}.margin-right-0-3{margin-right:.3rem}.margin-right-0-5{margin-right:.5rem}.ripple{position:absolute;border-radius:50%;transform:scale(0);background:rgba(var(--text-color), 0.16);pointer-events:none}.interact{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}.button__icon--left{margin-right:.5rem}.button__icon--right{margin-left:.5rem}.page-layout{position:relative;display:grid;grid-template-columns:1rem minmax(0, 1fr) 1rem}.page-layout>*{grid-column:2/3}.popup__header{display:grid;gap:.5rem;width:100%;padding:0 1.5rem 0 .5rem;align-items:center;grid-template-columns:auto 1fr}.popup__header__close{padding:.5rem;cursor:pointer}.auto-grid-2{gap:2rem;grid-template-columns:1fr}#loading_page,#error_page{position:relative;display:grid;height:100%;place-content:center;justify-items:center}#logo{grid-area:logo;color:inherit}theme-toggle{grid-area:theme;justify-self:end;align-self:center}#search_wrapper{grid-area:search;width:100%;position:relative}#main_header{position:relative;display:grid;gap:1rem;padding:1rem;align-items:center;grid-template-columns:1fr auto auto;grid-template-areas:"logo info theme" "search search search"}#main_header a.button{grid-area:info;font-size:.9rem;font-weight:500;border:solid thin var(--accent-color)}.header__company-name{grid-area:logo;font-weight:500}#main_header__logo{height:1.3rem;width:1.3rem}.page{padding-bottom:3rem}.page__title{font-size:2rem}.app-icon{height:3rem;width:3rem}.app-icon-loader{fill:none;stroke-width:2;justify-self:center;stroke-dasharray:202;margin:2rem 0;stroke:rgba(var(--text-color), 1);-webkit-animation:stroke-anim 2s infinite alternate;animation:stroke-anim 2s infinite alternate}@-webkit-keyframes stroke-anim{0%{stroke-dashoffset:202}100%{stroke-dashoffset:0}}@keyframes stroke-anim{0%{stroke-dashoffset:202}100%{stroke-dashoffset:0}}.search-torrent{flex:1;width:100%;--border-radius: 0.5rem;--background: rgba(var(--text-color), 0.06);--padding: 0 0.3rem 0 0.8rem}.search-torrent .icon{fill:rgba(var(--text-color), 0.7)}#filter_button{position:relative;border-radius:.5rem}#filter_button[data-active]::after{display:flex;content:attr(data-active);position:absolute;top:0;right:0;padding:.1em .3rem;background-color:var(--accent-color);color:rgba(var(--background-color), 1);border-radius:.5rem;font-size:.8rem;font-weight:500}.search-suggestions-container{top:100%;position:absolute;z-index:1;width:100%;border-radius:1rem;margin-top:.5rem;box-shadow:0 .5rem 1rem -0.5rem rgba(0,0,0,.2);background-color:rgba(var(--foreground-color), 1)}.search-suggestions-container:not(:empty){padding:.5rem 0}.search-suggestion{display:flex;cursor:pointer;font-weight:700;font-size:.9rem;padding:.8rem 1rem;color:rgba(var(--text-color), 0.8);outline:none}.search-suggestion:focus,.search-suggestion:active{outline:none;border:0}.search-suggestion:focus,.search-suggestion:focus-visible{outline:rgba(0,0,0,0);background-color:rgba(var(--text-color), 0.1)}.search-suggestion span{font-weight:450}.search-suggestion pre{white-space:pre-wrap}.torrent-container{padding:1.5rem 0;display:grid;grid-template-columns:repeat(auto-fill, minmax(16rem, 1fr));gap:.8rem;padding-bottom:4rem}.torrent-card{display:flex;flex-direction:column;border-radius:.5rem;-webkit-tap-highlight-color:rgba(0,0,0,0);background-color:rgba(var(--foreground-color), 1);transition:box-shadow .3s,transform .3s}.torrent-card:hover{transform:translateY(-0.2rem);box-shadow:0 .5rem 1rem -0.5rem rgba(0,0,0,.2)}.torrent-card .torrent-info{flex:1;gap:.5rem 1rem;padding:1rem;padding-bottom:0}.torrent-card .torrent-type-icon{padding:.8rem}.torrent-card__icon{height:4rem;width:4rem;margin-bottom:1rem}.torrent-card__icon .icon{fill:rgba(var(--background-color), 1)}.torrent-card__title{font-weight:600;font-size:1.1rem}.torrent-card__tags,.torrent-card__uploader{font-size:.85rem;color:rgba(var(--text-color), 0.7)}.torrent-card__uploader{overflow-wrap:break-word;word-break:break-word;line-height:1.5;margin-top:auto}.torrent-card__download-button{background-color:rgba(var(--text-color), 0.1);margin-top:auto;margin:1rem;transition:background-color .3s,color .3s}.torrent-card__download-button:hover{background-color:var(--accent-color);color:rgba(var(--background-color), 1)}.torrent-card__download-button[data-collecting]::after{content:"";position:absolute;bottom:0;left:0;width:100%;transform:translateX(calc(-100% + var(--progress, 0%)));height:.3rem;background-color:rgba(var(--foreground-color), 1);z-index:1;transition:all .3s;border-radius:.5rem}.torrent-preview{display:grid;justify-content:center}.torrent-preview__info-section{display:flex;flex-direction:column;align-content:flex-start}.torrent-type-icon{display:flex;padding:1rem;border-radius:50%;align-items:center;justify-content:center;align-self:flex-start;aspect-ratio:1/1;flex-shrink:0;background-color:var(--accent-color)}#torrent_type_icon{align-self:center;justify-self:center;padding:2rem;margin:3rem 0 4rem 0;background-color:rgba(var(--text-color), 0.06)}#torrent_type_icon .icon{height:3rem;width:3rem;fill:rgba(var(--text-color), 0.3)}#torrent_tags{text-transform:capitalize}#torrent_tags,#torrent_uploader{display:flex;width:100%;font-size:.85rem;margin-bottom:.5rem;color:rgba(var(--text-color), 0.8)}#torrent_name{line-height:1.1;font-size:1.8rem;margin-bottom:2rem}#torrent_description{font-size:1rem;color:rgba(var(--text-color), 0.8)}#torrent_uploader{flex-direction:column;font-weight:500;margin:1.5rem 0 1rem 0;width:auto;border-radius:.5rem;padding:.8rem;gap:.3rem;background-color:rgba(var(--text-color), 0.06)}#torrent_download_button{position:relative;padding:1rem;margin-top:1.5rem;overflow:hidden}#torrent_download_button[data-collecting]::after{content:"";position:absolute;bottom:0;left:0;width:100%;transform:translateX(calc(-100% + var(--progress, 0%)));height:.3rem;background-color:rgba(var(--foreground-color), 1);z-index:1;transition:all .3s;border-radius:.5rem}#torrent_download_button .icon{margin-right:.5rem}#torrent_index_tx{padding:.4rem;font-size:.9rem;border-radius:.5rem;align-self:flex-start;border:solid var(--accent-color) thin}#torrent_index_tx .icon{margin-right:.5rem}#advance_search_section{align-items:flex-start;padding:1rem 0;margin-bottom:1rem}#filters_bar{padding:.5rem 0}sm-option{font-size:.9rem}#filter_popup{--width: min(32rem, 100%)}.option-selector{display:flex;flex-wrap:wrap;gap:.8rem;padding:1rem 0}.filter-option{display:inline-flex;-webkit-tap-highlight-color:rgba(0,0,0,0)}.filter-option input{display:none}.filter-option input:checked~.option-text{color:rgba(var(--background-color), 1);background-color:var(--accent-color);border:solid var(--accent-color) thin}.filter-option .option-text{cursor:pointer;font-weight:500;font-size:.95rem;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-radius:.3rem;padding:.3rem .6rem;transition:background-color .3s;color:rgba(var(--text-color), 0.8);border:solid rgba(var(--text-color), 0.2) thin}#filters_bar{display:flex;flex-wrap:wrap;justify-content:space-between;gap:1rem;margin-top:1rem}#selected_filters_container{display:flex;flex-wrap:wrap;gap:.5rem}.selected-filter{display:flex;align-items:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;gap:.3rem;padding:.4rem .5rem;border:solid rgba(var(--text-color), 0.2) thin;border-radius:.3rem}#page_selector sm-chip,#search_page_selector sm-chip{--border-radius: 0.3rem;--active-option-color: white;--active-option-backgroud-color: var(--accent-color)}#how_it_works{padding-bottom:8rem}#how_it_works .page__title{margin:6rem 0 1rem 0}.info-section{display:grid;gap:1.5rem;margin-top:3rem;align-items:center;grid-template-columns:1fr}.info__title{margin-bottom:1rem}#main_footer{padding:2rem 0;background-color:rgba(var(--text-color), 0.06)}@media only screen and (min-width: 640px){.popup__header{padding:1.5rem 1.5rem 0 .5rem}.auto-grid-2{grid-template-columns:1fr 1fr}.page-layout{grid-template-columns:1fr 90vw 1fr}#main_header{padding:1rem 1.5rem;grid-template-areas:"logo info theme"}#search_wrapper{grid-area:1/1/2/-1;margin:0 auto;width:28rem;max-width:calc(100% - 22rem)}.page__title{font-size:3rem}.torrent-card .torrent-info{padding:1.5rem}.torrent-preview{gap:3rem}#torrent_name{font-size:4rem;max-width:16ch}.info-section{grid-template-columns:1fr 1fr}.info-section:nth-of-type(even) .info__image{grid-column:2/3}.info-section:nth-of-type(even) .textual-info{grid-row:1/2;grid-column:1/2}}@media only screen and (min-width: 1280px){.page-layout{grid-template-columns:1fr 80vw 1fr}}@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)}.search-suggestion:hover{background-color:rgba(var(--text-color), 0.1)}} \ No newline at end of file +*{padding:0;margin:0;box-sizing:border-box;font-family:"Inter",sans-serif}:root{font-size:clamp(1rem,1.2vmax,3rem)}html,body{height:100%;scroll-behavior:smooth}body{--accent-color: #304ffe;--light-shade: rgba(var(--text-color), 0.06);--text-color: 17, 17, 17;--text-color-light: 100, 100, 100;--foreground-color: 255, 255, 255;--background-color: 243, 245, 250;--error-color: red;--green: #00843b;color:rgba(var(--text-color), 1);background:rgba(var(--background-color), 1);display:flex;flex-direction:column}body[data-theme=dark]{--accent-color: #a6b9ff;--green: #13ff5a;--text-color: 240, 240, 240;--text-color-light: 170, 170, 170;--foreground-color: 27, 28, 29;--background-color: 21, 22, 22;--error-color: rgb(255, 106, 106)}main{flex:1}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(--foreground-color), 1)}.full-bleed{grid-column:1/4}.h1{font-size:2.5rem}.h2{font-size:2rem}.h3{font-size:1.4rem}.h4{font-size:1rem}.h5{font-size:.8rem}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}p{font-size:.8;max-width:60ch;line-height:1.7;color:rgba(var(--text-color), 0.8)}p:not(:last-of-type){margin-bottom:1rem}img{-o-object-fit:cover;object-fit:cover}a{color:inherit;text-decoration:none}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;-webkit-tap-highlight-color:rgba(0,0,0,0);align-items:center;font-size:.9rem;font-weight:500;white-space:nowrap;padding:.8rem;border-radius:.5rem;justify-content:center;color:inherit;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content}.button:not(:disabled),button:not(:disabled){cursor:pointer}.button{color:var(--accent-color);background-color:var(--blue-accent-1)}.button .icon{fill:var(--accent-color)}.button--primary,.button--danger{color:rgba(var(--background-color), 1) !important}.button--primary .icon,.button--danger .icon{fill:rgba(var(--background-color), 1)}.button--primary{width:100%;background-color:var(--accent-color)}.button--danger{background-color:var(--danger-color)}.button--small{padding:.4rem .6rem}.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{padding:.5rem;border-radius:.3rem}button:disabled{cursor:not-allowed;filter:saturate(0.5);opacity:.8}ul{list-style:none}.flex{display:flex}.grid{display:grid}.hide{opacity:0;pointer-events:none}.hidden{display:none !important}.no-transformations{transform:none !important}.overflow-ellipsis{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.breakable{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-word;-webkit-hyphens:auto;hyphens:auto}.flex{display:flex}.grid{display:grid}.flow-column{grid-auto-flow:column}.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-items:flex-start}.align-center{align-items:center}.text-center{text-align:center}.justify-start{justify-content:start}.justify-center{justify-content:center}.justify-right{margin-left:auto}.align-self-center{align-self:center}.justify-self-center{justify-self:center}.justify-self-start{justify-self:start}.direction-column{flex-direction:column}.space-between{justify-content:space-between}.w-100{width:100%}.margin-left-0-3{margin-left:.3rem}.margin-left-0-5{margin-left:.5rem}.margin-right-0-3{margin-right:.3rem}.margin-right-0-5{margin-right:.5rem}.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}.interact{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}.button__icon--left{margin-right:.5rem}.button__icon--right{margin-left:.5rem}.page-layout{position:relative;display:grid;grid-template-columns:1rem minmax(0, 1fr) 1rem}.page-layout>*{grid-column:2/3}.popup__header{display:grid;gap:.5rem;width:100%;padding:0 1.5rem 0 .5rem;align-items:center;grid-template-columns:auto 1fr}.popup__header__close{padding:.5rem;cursor:pointer}.auto-grid-2{gap:2rem;grid-template-columns:1fr}#loading_page,#error_page{position:relative;display:grid;height:100%;place-content:center;justify-items:center}#logo{grid-area:logo;color:inherit}theme-toggle{grid-area:theme;justify-self:end;align-self:center}#search_wrapper{grid-area:search;width:100%;position:relative}#main_header{position:relative;display:grid;gap:1rem;padding:1rem;align-items:center;grid-template-columns:1fr auto auto;grid-template-areas:"logo info theme" "search search search"}#main_header a.button{grid-area:info;font-size:.9rem;font-weight:500;border:solid thin var(--accent-color)}.header__company-name{grid-area:logo;font-weight:500}#main_header__logo{height:1.3rem;width:1.3rem}.page{padding-bottom:3rem}.page__title{font-size:2rem}.app-icon{height:3rem;width:3rem}.app-icon-loader{fill:none;stroke-width:2;justify-self:center;stroke-dasharray:202;margin:2rem 0;stroke:rgba(var(--text-color), 1);-webkit-animation:stroke-anim 2s infinite alternate;animation:stroke-anim 2s infinite alternate}@-webkit-keyframes stroke-anim{0%{stroke-dashoffset:202}100%{stroke-dashoffset:0}}@keyframes stroke-anim{0%{stroke-dashoffset:202}100%{stroke-dashoffset:0}}.search-torrent{flex:1;width:100%;--border-radius: 0.5rem;--background: rgba(var(--text-color), 0.06);--padding: 0 0.3rem 0 0.8rem}.search-torrent .icon{fill:rgba(var(--text-color), 0.7)}#filter_button{position:relative;border-radius:.5rem}#filter_button[data-active]::after{display:flex;content:attr(data-active);position:absolute;top:0;right:0;padding:.1em .3rem;background-color:var(--accent-color);color:rgba(var(--background-color), 1);border-radius:.5rem;font-size:.8rem;font-weight:500}.search-suggestions-container{top:100%;position:absolute;z-index:1;width:100%;border-radius:1rem;margin-top:.5rem;box-shadow:0 .5rem 1rem -0.5rem rgba(0,0,0,.2);background-color:rgba(var(--foreground-color), 1)}.search-suggestions-container:not(:empty){padding:.5rem 0}.search-suggestion{display:flex;cursor:pointer;font-weight:700;font-size:.9rem;padding:.8rem 1rem;color:rgba(var(--text-color), 0.8);outline:none}.search-suggestion:focus,.search-suggestion:active{outline:none;border:0}.search-suggestion:focus,.search-suggestion:focus-visible{outline:rgba(0,0,0,0);background-color:rgba(var(--text-color), 0.1)}.search-suggestion span{font-weight:450}.search-suggestion pre{white-space:pre-wrap}.torrent-container{padding:1.5rem 0;display:grid;grid-template-columns:repeat(auto-fill, minmax(16rem, 1fr));gap:.8rem;padding-bottom:4rem}.torrent-card{display:flex;flex-direction:column;border-radius:.5rem;-webkit-tap-highlight-color:rgba(0,0,0,0);background-color:rgba(var(--foreground-color), 1);transition:box-shadow .3s,transform .3s}.torrent-card:hover{transform:translateY(-0.2rem);box-shadow:0 .5rem 1rem -0.5rem rgba(0,0,0,.2)}.torrent-card .torrent-info{flex:1;gap:.5rem 1rem;padding:1rem;padding-bottom:0}.torrent-card .torrent-type-icon{padding:.8rem}.torrent-card__icon{height:4rem;width:4rem;margin-bottom:1rem}.torrent-card__icon .icon{fill:rgba(var(--background-color), 1)}.torrent-card__title{font-weight:600;font-size:1.1rem}.torrent-card__tags,.torrent-card__uploader{font-size:.85rem;color:rgba(var(--text-color), 0.7)}.torrent-card__uploader{overflow-wrap:break-word;word-break:break-word;line-height:1.5;margin-top:auto}.torrent-card__download-button{background-color:rgba(var(--text-color), 0.1);margin-top:auto;margin:1rem;transition:background-color .3s,color .3s}.torrent-card__download-button:hover{background-color:var(--accent-color);color:rgba(var(--background-color), 1)}.torrent-card__download-button[data-collecting]::after{content:"";position:absolute;bottom:0;left:0;width:100%;transform:translateX(calc(-100% + var(--progress, 0%)));height:.3rem;background-color:rgba(var(--foreground-color), 1);z-index:1;transition:all .3s;border-radius:.5rem}.torrent-preview{display:grid;justify-content:center}.torrent-preview__info-section{display:flex;flex-direction:column;align-content:flex-start}.torrent-type-icon{display:flex;padding:1rem;border-radius:50%;align-items:center;justify-content:center;align-self:flex-start;aspect-ratio:1/1;flex-shrink:0;background-color:var(--accent-color)}#torrent_type_icon{align-self:center;justify-self:center;padding:2rem;margin:3rem 0 4rem 0;background-color:rgba(var(--text-color), 0.06)}#torrent_type_icon .icon{height:3rem;width:3rem;fill:rgba(var(--text-color), 0.3)}#torrent_tags{text-transform:capitalize}#torrent_tags,#torrent_uploader{display:flex;width:100%;font-size:.85rem;margin-bottom:.5rem;color:rgba(var(--text-color), 0.8)}#torrent_name{line-height:1.1;font-size:1.8rem;margin-bottom:2rem}#torrent_description{font-size:1rem;color:rgba(var(--text-color), 0.8)}#torrent_uploader{flex-direction:column;font-weight:500;margin:1.5rem 0 1rem 0;width:auto;border-radius:.5rem;padding:.8rem;gap:.3rem;background-color:rgba(var(--text-color), 0.06)}#torrent_download_button{position:relative;padding:1rem;margin-top:1.5rem;overflow:hidden}#torrent_download_button[data-collecting]::after{content:"";position:absolute;bottom:0;left:0;width:100%;transform:translateX(calc(-100% + var(--progress, 0%)));height:.3rem;background-color:rgba(var(--foreground-color), 1);z-index:1;transition:all .3s;border-radius:.5rem}#torrent_download_button .icon{margin-right:.5rem}#torrent_index_tx{padding:.4rem;font-size:.9rem;border-radius:.5rem;align-self:flex-start;border:solid var(--accent-color) thin}#torrent_index_tx .icon{margin-right:.5rem}#advance_search_section{align-items:flex-start;padding:1rem 0;margin-bottom:1rem}#filters_bar{padding:.5rem 0}sm-option{font-size:.9rem}#filter_popup{--width: min(32rem, 100%)}.option-selector{display:flex;flex-wrap:wrap;gap:.8rem;padding:1rem 0}.filter-option{display:inline-flex;-webkit-tap-highlight-color:rgba(0,0,0,0)}.filter-option input{display:none}.filter-option input:checked~.option-text{color:rgba(var(--background-color), 1);background-color:var(--accent-color);border:solid var(--accent-color) thin}.filter-option .option-text{cursor:pointer;font-weight:500;font-size:.95rem;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-radius:.3rem;padding:.3rem .6rem;transition:background-color .3s;color:rgba(var(--text-color), 0.8);border:solid rgba(var(--text-color), 0.2) thin}#filters_bar{display:flex;flex-wrap:wrap;justify-content:space-between;gap:1rem;margin-top:1rem}#selected_filters_container{display:flex;flex-wrap:wrap;gap:.5rem}.selected-filter{display:flex;align-items:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;gap:.3rem;padding:.4rem .5rem;border:solid rgba(var(--text-color), 0.2) thin;border-radius:.3rem}#page_selector sm-chip,#search_page_selector sm-chip{--border-radius: 0.3rem;--active-option-color: white;--active-option-backgroud-color: var(--accent-color)}#how_it_works{padding-bottom:8rem}#how_it_works .page__title{margin:6rem 0 1rem 0}.info-section{display:grid;gap:1.5rem;margin-top:3rem;align-items:center;grid-template-columns:1fr}.info__title{margin-bottom:1rem}#main_footer{padding:2rem 0;background-color:rgba(var(--text-color), 0.06)}@media only screen and (min-width: 640px){.popup__header{padding:1.5rem 1.5rem 0 .5rem}.auto-grid-2{grid-template-columns:1fr 1fr}.page-layout{grid-template-columns:1fr 90vw 1fr}#main_header{padding:1rem 1.5rem;grid-template-areas:"logo info theme"}#search_wrapper{grid-area:1/1/2/-1;margin:0 auto;width:28rem;max-width:calc(100% - 22rem)}.page__title{font-size:3rem}.torrent-card .torrent-info{padding:1.5rem}.torrent-preview{gap:3rem}#torrent_name{font-size:4rem;max-width:16ch}.info-section{grid-template-columns:1fr 1fr}.info-section:nth-of-type(even) .info__image{grid-column:2/3}.info-section:nth-of-type(even) .textual-info{grid-row:1/2;grid-column:1/2}}@media only screen and (min-width: 1280px){.page-layout{grid-template-columns:1fr 80vw 1fr}}@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)}.search-suggestion:hover{background-color:rgba(var(--text-color), 0.1)}} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index f224ee4..ce640d9 100644 --- a/css/main.scss +++ b/css/main.scss @@ -282,10 +282,16 @@ ul { margin-right: 0.5rem; } .ripple { + height: 8rem; + width: 8rem; position: absolute; border-radius: 50%; transform: scale(0); - background: rgba(var(--text-color), 0.16); + background: radial-gradient( + circle, + rgba(var(--text-color), 0.3) 0%, + rgba(0, 0, 0, 0) 50% + ); pointer-events: none; } .interact { diff --git a/index.html b/index.html index af3d614..5aef926 100644 --- a/index.html +++ b/index.html @@ -841,12 +841,12 @@ const rippleAnimation = circle.animate( [ { - transform: "scale(3)", + transform: "scale(4)", opacity: 0, }, ], { - duration: 1000, + duration: uiGlobals.prefersReducedMotion ? 0 : 600, fill: "forwards", easing: "ease-out", } @@ -916,7 +916,7 @@
  • ${icon}
    -

    ${filename}

    +

    ${filename}

    ${tags.replace(/[\/,]/g, ' • ')}

    ${`by ${uploader}`}
    diff --git a/scripts/components.js b/scripts/components.js index d13ea2f..c805ea7 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -1,6 +1,6 @@ /*jshint esversion: 6 */ // Components downloaded: chips,file-input,form,input,notifications,popup,select,spinner,tags-input,textarea,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(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 => { 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])) : (this.navButtonLeft.classList.add("hide"), this.navButtonRight.classList.add("hide"), this.coverLeft.classList.add("hide"), this.coverRight.classList.add("hide")) }) }, { 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 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 fileInput = document.createElement("template"); fileInput.innerHTML = ' \t\t \t', customElements.define("file-input", class extends HTMLElement { constructor() { super(), this.attachShadow({ mode: "open" }).append(fileInput.content.cloneNode(!0)), this.input = this.shadowRoot.querySelector("input"), this.fileInput = this.shadowRoot.querySelector(".file-input"), this.filesPreviewWrapper = this.shadowRoot.querySelector(".files-preview-wrapper"), this.reflectedAttributes = ["accept", "multiple", "capture", "type"], this.reset = this.reset.bind(this), this.formatBytes = this.formatBytes.bind(this), this.createFilePreview = this.createFilePreview.bind(this), this.handleChange = this.handleChange.bind(this), this.handleKeyDown = this.handleKeyDown.bind(this) } static get observedAttributes() { return ["accept", "multiple", "capture", "type"] } get files() { return this.input.files } set accept(t) { this.setAttribute("accept", t) } set multiple(t) { t ? this.setAttribute("multiple", "") : this.removeAttribute("multiple") } set capture(t) { this.setAttribute("capture", t) } set value(t) { this.input.value = t } get isValid() { return "" !== this.input.value } reset() { this.input.value = "", this.filesPreviewWrapper.innerHTML = "" } formatBytes(t, e = 2) { if (0 === t) return "0 Bytes"; const n = 0 > e ? 0 : e, i = Math.floor(Math.log(t) / Math.log(1024)); return parseFloat((t / Math.pow(1024, i)).toFixed(n)) + " " + ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][i] } createFilePreview(t) { const e = document.createElement("li"), { name: n, size: i } = t; return e.className = "file-preview", e.innerHTML = `\t\t\t
    ${n}
    ${this.formatBytes(i)}
    \t\t`, e } handleChange(t) { this.filesPreviewWrapper.innerHTML = ""; const e = document.createDocumentFragment(); Array.from(t.target.files).forEach(t => { e.append(this.createFilePreview(t)) }), this.filesPreviewWrapper.append(e) } handleKeyDown(t) { "Enter" !== t.key && " " !== t.key || (t.preventDefault(), this.input.click()) } connectedCallback() { this.setAttribute("role", "button"), this.setAttribute("aria-label", "File upload"), this.input.addEventListener("change", this.handleChange), this.fileInput.addEventListener("keydown", this.handleKeyDown) } attributeChangedCallback(t) { this.reflectedAttributes.includes(t) && (this.hasAttribute(t) ? this.input.setAttribute(t, this.getAttribute(t) ? this.getAttribute(t) : "") : this.input.removeAttribute(t)) } disconnectedCallback() { this.input.removeEventListener("change", this.handleChange), this.fileInput.removeEventListener("keydown", this.handleKeyDown) } }); 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.invalidFields = !1, this.skipSubmit = !1, this.isFormValid = !1, this.supportedElements = new Set(["INPUT", "SM-INPUT", "SM-TEXTAREA", "SM-CHECKBOX", "TAGS-INPUT", "FILE-INPUT", "SM-SWITCH", "SM-RADIO"]), 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 && (this.invalidFields = this._requiredElements.filter(([t, e]) => e ? !t.isValid : !t.checkValidity()), this.isFormValid = 0 === this.invalidFields.length, this.skipSubmit || (this.submitButton.disabled = !this.isFormValid), this.isFormValid ? this.dispatchEvent(new CustomEvent("valid", { bubbles: !0, composed: !0 })) : this.dispatchEvent(new CustomEvent("invalid", { bubbles: !0, composed: !0 }))) } handleKeydown(t) { if ("Enter" === t.key && t.target.tagName.includes("INPUT")) { if (this.invalidFields.length) 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 } } else this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })) } } reset() { this.formElements.forEach(([t, e]) => { e ? t.reset() : t.value = "" }) } elementsChanged() { this.formElements = [...this.querySelectorAll("input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio")].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.shadowRoot.querySelector("slot").addEventListener("slotchange", t), this.addEventListener("input", this.debounce(this._checkValidity, 100)), this.addEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { ("childList" === e.type && [...e.addedNodes].some(t => this.supportedElements.has(t.tagName)) || [...e.removedNodes].some(t => this.supportedElements.has(t.tagName))) && 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.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.inputParent.classList.remove("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 } 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.classList.remove("error"), this.feedbackText.classList.add("success"), this.feedbackText.textContent = "") : this._errorText && (this.feedbackText.classList.add("error"), this.feedbackText.classList.remove("success"), this.feedbackText.innerHTML = ` ${this._errorText}`), 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) } });