UI update
FLO torrent uploader UI revamp
This commit is contained in:
parent
78b98995c4
commit
d6a7970a51
681
components.js
681
components.js
@ -76,6 +76,7 @@ smButton.innerHTML = `
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
color: inherit;
|
||||
align-items: center;
|
||||
}
|
||||
:host(:not([disabled])) .button:focus-visible{
|
||||
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color);
|
||||
@ -370,24 +371,32 @@ input{
|
||||
`;
|
||||
customElements.define('sm-input',
|
||||
class extends HTMLElement {
|
||||
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smInput.content.cloneNode(true))
|
||||
|
||||
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.validationFunction
|
||||
this.observeList = ['type', 'required', 'disabled', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step']
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['placeholder']
|
||||
return ['placeholder', 'type', 'required', 'disabled', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step']
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.shadowRoot.querySelector('input').value
|
||||
return this.input.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.shadowRoot.querySelector('input').value = val;
|
||||
this.input.value = val;
|
||||
this.checkInput()
|
||||
this.fireEvent()
|
||||
}
|
||||
@ -404,29 +413,42 @@ customElements.define('sm-input',
|
||||
return this.getAttribute('type')
|
||||
}
|
||||
|
||||
set type(val) {
|
||||
this.setAttribute('type', val)
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
return this.shadowRoot.querySelector('input').checkValidity()
|
||||
if (this.hasAttribute('data-flo-id') || this.hasAttribute('data-private-key')) {
|
||||
return this.validationFunction(this.input.value)
|
||||
}
|
||||
else {
|
||||
return this.input.checkValidity()
|
||||
}
|
||||
}
|
||||
|
||||
get validity() {
|
||||
return this.shadowRoot.querySelector('input').validity
|
||||
return this.input.validity
|
||||
}
|
||||
|
||||
set disabled(value) {
|
||||
if (value)
|
||||
this.shadowRoot.querySelector('.input').classList.add('disabled')
|
||||
this.inputParent.classList.add('disabled')
|
||||
else
|
||||
this.shadowRoot.querySelector('.input').classList.remove('disabled')
|
||||
this.inputParent.classList.remove('disabled')
|
||||
}
|
||||
set readOnly(value) {
|
||||
if (value) {
|
||||
this.shadowRoot.querySelector('input').setAttribute('readonly', '')
|
||||
this.shadowRoot.querySelector('.input').classList.add('readonly')
|
||||
this.setAttribute('readonly', '')
|
||||
} else {
|
||||
this.shadowRoot.querySelector('input').removeAttribute('readonly')
|
||||
this.shadowRoot.querySelector('.input').classList.remove('readonly')
|
||||
this.removeAttribute('readonly')
|
||||
}
|
||||
}
|
||||
set customValidation(val) {
|
||||
this.validationFunction = val
|
||||
}
|
||||
reset = () => {
|
||||
this.value = ''
|
||||
}
|
||||
|
||||
setValidity = (message) => {
|
||||
this.feedbackText.textContent = message
|
||||
@ -435,7 +457,7 @@ customElements.define('sm-input',
|
||||
showValidity = () => {
|
||||
this.feedbackText.classList.remove('hide-completely')
|
||||
}
|
||||
|
||||
|
||||
hideValidity = () => {
|
||||
this.feedbackText.classList.add('hide-completely')
|
||||
}
|
||||
@ -458,7 +480,7 @@ customElements.define('sm-input',
|
||||
}
|
||||
|
||||
checkInput = (e) => {
|
||||
if (!this.readonly) {
|
||||
if (!this.hasAttribute('readonly')) {
|
||||
if (this.input.value !== '') {
|
||||
this.clearBtn.classList.remove('hide')
|
||||
} else {
|
||||
@ -481,83 +503,58 @@ customElements.define('sm-input',
|
||||
|
||||
|
||||
connectedCallback() {
|
||||
this.inputParent = this.shadowRoot.querySelector('.input')
|
||||
this.clearBtn = this.shadowRoot.querySelector('.clear')
|
||||
this.label = this.shadowRoot.querySelector('.label')
|
||||
this.feedbackText = this.shadowRoot.querySelector('.feedback-text')
|
||||
this.valueChanged = false;
|
||||
this.readonly = false
|
||||
this.isNumeric = false
|
||||
this.min
|
||||
this.max
|
||||
this.animate = this.hasAttribute('animate')
|
||||
this.input = this.shadowRoot.querySelector('input')
|
||||
this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder')
|
||||
if (this.hasAttribute('value')) {
|
||||
this.input.value = this.getAttribute('value')
|
||||
this.checkInput()
|
||||
}
|
||||
if (this.hasAttribute('required')) {
|
||||
this.input.setAttribute('required', '')
|
||||
}
|
||||
if (this.hasAttribute('min')) {
|
||||
let minValue = this.getAttribute('min')
|
||||
this.input.setAttribute('min', minValue)
|
||||
this.min = parseInt(minValue)
|
||||
}
|
||||
if (this.hasAttribute('max')) {
|
||||
let maxValue = this.getAttribute('max')
|
||||
this.input.setAttribute('max', maxValue)
|
||||
this.max = parseInt(maxValue)
|
||||
}
|
||||
if (this.hasAttribute('minlength')) {
|
||||
const minValue = this.getAttribute('minlength')
|
||||
this.input.setAttribute('minlength', minValue)
|
||||
}
|
||||
if (this.hasAttribute('maxlength')) {
|
||||
const maxValue = this.getAttribute('maxlength')
|
||||
this.input.setAttribute('maxlength', maxValue)
|
||||
}
|
||||
if (this.hasAttribute('step')) {
|
||||
const steps = this.getAttribute('step')
|
||||
this.input.setAttribute('step', steps)
|
||||
}
|
||||
if (this.hasAttribute('pattern')) {
|
||||
this.input.setAttribute('pattern', this.getAttribute('pattern'))
|
||||
}
|
||||
if (this.hasAttribute('readonly')) {
|
||||
this.input.setAttribute('readonly', '')
|
||||
this.readonly = true
|
||||
}
|
||||
if (this.hasAttribute('disabled')) {
|
||||
this.inputParent.classList.add('disabled')
|
||||
}
|
||||
if (this.hasAttribute('error-text')) {
|
||||
this.feedbackText.textContent = this.getAttribute('error-text')
|
||||
}
|
||||
if (this.hasAttribute('type')) {
|
||||
if (this.getAttribute('type') === 'number') {
|
||||
this.input.setAttribute('inputmode', 'numeric')
|
||||
this.input.setAttribute('type', 'number')
|
||||
this.isNumeric = true
|
||||
} else
|
||||
this.input.setAttribute('type', this.getAttribute('type'))
|
||||
} else
|
||||
this.input.setAttribute('type', 'text')
|
||||
if (!this.hasAttribute('type')) {
|
||||
this.setAttribute('type', 'text')
|
||||
}
|
||||
|
||||
this.input.addEventListener('input', e => {
|
||||
this.checkInput(e)
|
||||
})
|
||||
this.clearBtn.addEventListener('click', e => {
|
||||
this.value = ''
|
||||
})
|
||||
this.clearBtn.addEventListener('click', this.reset)
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (oldValue !== newValue) {
|
||||
if (this.observeList.includes(name)) {
|
||||
if (this.hasAttribute(name)) {
|
||||
this.input.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : '')
|
||||
}
|
||||
else {
|
||||
this.input.removeAttribute(name)
|
||||
}
|
||||
}
|
||||
if (name === 'placeholder') {
|
||||
this.shadowRoot.querySelector('.label').textContent = newValue;
|
||||
this.label.textContent = newValue;
|
||||
this.setAttribute('aria-label', newValue);
|
||||
}
|
||||
else if (name === 'type') {
|
||||
if (this.hasAttribute('type') && this.getAttribute('type') === 'number') {
|
||||
this.input.setAttribute('inputmode', 'numeric')
|
||||
}
|
||||
}
|
||||
else if (name === 'readonly') {
|
||||
if (this.hasAttribute('readonly')) {
|
||||
this.inputParent.classList.add('readonly')
|
||||
} else {
|
||||
this.inputParent.classList.remove('readonly')
|
||||
}
|
||||
}
|
||||
else if (name === 'disabled') {
|
||||
if (this.hasAttribute('disabled')) {
|
||||
this.inputParent.classList.add('disabled')
|
||||
}
|
||||
else {
|
||||
this.inputParent.classList.remove('disabled')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -582,7 +579,7 @@ smTextarea.innerHTML = `
|
||||
}
|
||||
:host{
|
||||
display: grid;
|
||||
--border-radius: 0.3s;
|
||||
--border-radius: 0.3rem;
|
||||
--background: rgba(var(--text-color), 0.06);
|
||||
--padding-right: initial;
|
||||
--padding-left: initial;
|
||||
@ -652,6 +649,20 @@ textarea{
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
@media (any-hover: hover){
|
||||
::-webkit-scrollbar{
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb{
|
||||
background: rgba(var(--text-color), 0.3);
|
||||
border-radius: 1rem;
|
||||
&:hover{
|
||||
background: rgba(var(--text-color), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<label class="textarea" part="textarea">
|
||||
<span class="placeholder"></span>
|
||||
@ -665,21 +676,32 @@ customElements.define('sm-textarea',
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smTextarea.content.cloneNode(true))
|
||||
|
||||
this.textarea = this.shadowRoot.querySelector('textarea')
|
||||
this.textareaBox = this.shadowRoot.querySelector('.textarea')
|
||||
this.placeholder = this.shadowRoot.querySelector('.placeholder')
|
||||
this.observeList = ['required', 'readonly', 'rows', 'minlength', 'maxlength']
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['value', 'placeholder', 'required', 'readonly', 'rows', 'minlength', 'maxlength']
|
||||
}
|
||||
get value() {
|
||||
return this.textarea.value
|
||||
}
|
||||
set value(val) {
|
||||
this.textarea.value = val;
|
||||
this.textareaBox.dataset.value = val
|
||||
this.checkInput()
|
||||
this.setAttribute('value', val)
|
||||
this.fireEvent()
|
||||
}
|
||||
get isValid() {
|
||||
return this.textarea.checkValidity()
|
||||
}
|
||||
reset = () => {
|
||||
this.setAttribute('value', '')
|
||||
}
|
||||
focusIn = () => {
|
||||
this.textarea.focus()
|
||||
}
|
||||
fireEvent() {
|
||||
fireEvent = () => {
|
||||
let event = new Event('input', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
@ -697,30 +719,29 @@ customElements.define('sm-textarea',
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this.textareaBox = this.shadowRoot.querySelector('.textarea')
|
||||
this.placeholder = this.shadowRoot.querySelector('.placeholder')
|
||||
|
||||
if(this.hasAttribute('placeholder'))
|
||||
this.placeholder.textContent = this.getAttribute('placeholder')
|
||||
|
||||
if (this.hasAttribute('value')) {
|
||||
this.textarea.value = this.getAttribute('value')
|
||||
this.checkInput()
|
||||
}
|
||||
if (this.hasAttribute('required')) {
|
||||
this.textarea.setAttribute('required', '')
|
||||
}
|
||||
if (this.hasAttribute('readonly')) {
|
||||
this.textarea.setAttribute('readonly', '')
|
||||
}
|
||||
if (this.hasAttribute('rows')) {
|
||||
this.textarea.setAttribute('rows', this.getAttribute('rows'))
|
||||
}
|
||||
this.textarea.addEventListener('input', e => {
|
||||
this.textareaBox.dataset.value = this.textarea.value
|
||||
this.checkInput()
|
||||
})
|
||||
}
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (this.observeList.includes(name)) {
|
||||
if (this.hasAttribute(name)) {
|
||||
this.textarea.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : '')
|
||||
}
|
||||
else {
|
||||
this.input.removeAttribute(name)
|
||||
}
|
||||
}
|
||||
else if (name === 'placeholder') {
|
||||
this.placeholder.textContent = this.getAttribute('placeholder')
|
||||
}
|
||||
else if (name === 'value') {
|
||||
this.textarea.value = newValue;
|
||||
this.textareaBox.dataset.value = newValue
|
||||
this.checkInput()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// tab
|
||||
@ -1246,6 +1267,7 @@ smSelect.innerHTML = `
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
--max-height: auto;
|
||||
--min-width: 100%;
|
||||
}
|
||||
.hide{
|
||||
display: none !important;
|
||||
@ -1311,7 +1333,7 @@ smSelect.innerHTML = `
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
min-width: 100%;
|
||||
min-width: var(--min-width);
|
||||
max-height: var(--max-height);
|
||||
background: rgba(var(--foreground-color), 1);
|
||||
border: solid 1px rgba(var(--text-color), 0.2);
|
||||
@ -1325,6 +1347,20 @@ smSelect.innerHTML = `
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg)
|
||||
}
|
||||
@media (any-hover: hover){
|
||||
::-webkit-scrollbar{
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb{
|
||||
background: rgba(var(--text-color), 0.3);
|
||||
border-radius: 1rem;
|
||||
&:hover{
|
||||
background: rgba(var(--text-color), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="select" >
|
||||
<div class="selection" tabindex="0">
|
||||
@ -1354,6 +1390,10 @@ customElements.define('sm-select', class extends HTMLElement {
|
||||
this.setAttribute('value', val)
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
|
||||
}
|
||||
|
||||
collapse() {
|
||||
this.chevron.classList.remove('rotate')
|
||||
this.optionList.animate(this.slideUp, this.animationOptions)
|
||||
@ -3287,4 +3327,449 @@ customElements.define('sm-menu-option', class extends HTMLElement {
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// tags input
|
||||
const tagsInput = document.createElement('template')
|
||||
tagsInput.innerHTML = `
|
||||
<style>
|
||||
*{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host{
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
.hide{
|
||||
display: none !important;
|
||||
}
|
||||
.tags-wrapper{
|
||||
position: relative;
|
||||
display: flex;
|
||||
cursor: text;
|
||||
flex-wrap: wrap;
|
||||
justify-items: flex-start;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem 0 0.5rem;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
.tags-wrapper:focus-within{
|
||||
box-shadow: 0 0 0 0.1rem var(--accent-color) inset !important;
|
||||
}
|
||||
|
||||
.tag {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
margin: 0 0.5rem 0.5rem 0;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
margin-left: 0.3rem;
|
||||
fill: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
|
||||
input,
|
||||
input:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
color: inherit;
|
||||
max-width: inherit;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
padding: 0.4rem 0.5rem;
|
||||
margin: 0 0.5rem 0.5rem 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.placeholder{
|
||||
position: absolute;
|
||||
padding: 0 0.5rem;
|
||||
top: 50%;
|
||||
font-weight: 500;
|
||||
transform: translateY(-50%);
|
||||
color: rgba(var(--text-color), 0.6);
|
||||
}
|
||||
</style>
|
||||
<div class="tags-wrapper">
|
||||
<input type="text" size="3"/>
|
||||
<p class="placeholder"></p>
|
||||
</div>
|
||||
`
|
||||
|
||||
customElements.define('tags-input', class extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(tagsInput.content.cloneNode(true))
|
||||
this.input = this.shadowRoot.querySelector('input')
|
||||
this.tagsWrapper = this.shadowRoot.querySelector('.tags-wrapper')
|
||||
this.placeholder = this.shadowRoot.querySelector('.placeholder')
|
||||
this.observeList = ['placeholder', 'limit']
|
||||
this.limit = undefined
|
||||
this.tags = new Set()
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['placeholder', 'limit']
|
||||
}
|
||||
get value() {
|
||||
return [...this.tags].join()
|
||||
}
|
||||
reset = () => {
|
||||
this.input.value = ''
|
||||
this.tags.clear()
|
||||
while (this.input.previousElementSibling) {
|
||||
this.input.previousElementSibling.remove()
|
||||
}
|
||||
}
|
||||
handleInput = e => {
|
||||
const inputValueLength = e.target.value.trim().length
|
||||
e.target.setAttribute('size', inputValueLength ? inputValueLength : '3')
|
||||
if (inputValueLength) {
|
||||
this.placeholder.classList.add('hide')
|
||||
}
|
||||
else if (!inputValueLength && !this.tags.size) {
|
||||
this.placeholder.classList.remove('hide')
|
||||
}
|
||||
}
|
||||
handleKeydown = e => {
|
||||
if (e.key === ',' || e.key === '/') {
|
||||
e.preventDefault()
|
||||
}
|
||||
if (e.target.value.trim() !== '') {
|
||||
if (e.key === 'Enter' || e.key === ',' || e.key === '/' || e.code === 'Space') {
|
||||
const tagValue = e.target.value.trim()
|
||||
if (this.tags.has(tagValue)) {
|
||||
this.tagsWrapper.querySelector(`[data-value="${tagValue}"]`).animate([
|
||||
{
|
||||
backgroundColor: 'initial'
|
||||
},
|
||||
{
|
||||
backgroundColor: 'var(--accent-color)'
|
||||
},
|
||||
{
|
||||
backgroundColor: 'initial'
|
||||
},
|
||||
], {
|
||||
duration: 300
|
||||
})
|
||||
}
|
||||
else {
|
||||
const tag = document.createElement('span')
|
||||
tag.dataset.value = tagValue
|
||||
tag.className = 'tag'
|
||||
tag.innerHTML = `
|
||||
<span class="tag-text">${tagValue}</span>
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
|
||||
`
|
||||
this.input.before(tag)
|
||||
this.tags.add(tagValue)
|
||||
}
|
||||
e.target.value = ''
|
||||
e.target.setAttribute('size', '3')
|
||||
if (this.limit && this.limit < this.tags.size + 1) {
|
||||
this.input.readOnly = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (e.key === 'Backspace' && this.input.previousElementSibling) {
|
||||
this.removeTag(this.input.previousElementSibling)
|
||||
}
|
||||
if (this.limit && this.limit > this.tags.size){
|
||||
this.input.readOnly = false
|
||||
}
|
||||
}
|
||||
}
|
||||
handleClick = e => {
|
||||
if (e.target.closest('.tag')) {
|
||||
this.removeTag(e.target.closest('.tag'))
|
||||
}
|
||||
else {
|
||||
this.input.focus()
|
||||
}
|
||||
}
|
||||
removeTag = (tag) => {
|
||||
this.tags.delete(tag.dataset.value)
|
||||
tag.remove()
|
||||
if (!this.tags.size) {
|
||||
this.placeholder.classList.remove('hide')
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this.input.addEventListener('input', this.handleInput)
|
||||
this.input.addEventListener('keydown', this.handleKeydown)
|
||||
this.tagsWrapper.addEventListener('click', this.handleClick)
|
||||
}
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name === 'placeholder') {
|
||||
this.placeholder.textContent = newValue
|
||||
}
|
||||
if (name === 'limit') {
|
||||
this.limit = parseInt(newValue)
|
||||
}
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.input.removeEventListener('input', this.handleInput)
|
||||
this.input.removeEventListener('keydown', this.handleKeydown)
|
||||
this.tagsWrapper.removeEventListener('click', this.handleClick)
|
||||
}
|
||||
})
|
||||
|
||||
// file input
|
||||
const fileInput = document.createElement('template')
|
||||
fileInput.innerHTML = `
|
||||
<style>
|
||||
*{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host{
|
||||
--border-radius: 0.3rem;
|
||||
--button-color: inherit;
|
||||
--button-font-weight: 500;
|
||||
--button-background-color: var(--accent-color);
|
||||
}
|
||||
.file-input {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.file-picker {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.8rem;
|
||||
color: var(--button-color);
|
||||
border-radius: var(--border-radius);
|
||||
font-weight: var(--button-font-weigh);
|
||||
background-color: var(--button-background-color);
|
||||
}
|
||||
.files-preview-wrapper{
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
list-style: none;
|
||||
}
|
||||
.files-preview-wrapper:not(:empty){
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.file-preview{
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.8rem;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: rgba(var(--text-color), 0.06)
|
||||
}
|
||||
.file-name{
|
||||
}
|
||||
.file-size{
|
||||
font-size: 0.8rem;
|
||||
font-weight: 400;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
input[type=file] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<ul class="files-preview-wrapper"></ul>
|
||||
<label tabindex="0" class="file-input">
|
||||
<div class="file-picker"><slot>Choose file</slot></div>
|
||||
<input type="file">
|
||||
</label>
|
||||
`
|
||||
|
||||
customElements.define('file-input', class extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(fileInput.content.cloneNode(true))
|
||||
this.input = this.shadowRoot.querySelector('input')
|
||||
this.fileInput = this.shadowRoot.querySelector('.file-input')
|
||||
this.filesPreviewWraper = this.shadowRoot.querySelector('.files-preview-wrapper')
|
||||
this.observeList = ['accept', 'multiple', 'capture']
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['accept', 'multiple', 'capture']
|
||||
}
|
||||
get files() {
|
||||
return this.input.files
|
||||
}
|
||||
set accept(val) {
|
||||
this.setAttribute('accept', val)
|
||||
}
|
||||
set multiple(val) {
|
||||
if (val) {
|
||||
this.setAttribute('mutiple', '')
|
||||
}
|
||||
else {
|
||||
this.removeAttribute('mutiple')
|
||||
}
|
||||
}
|
||||
set capture(val) {
|
||||
this.setAttribute('capture', val)
|
||||
}
|
||||
set value(val) {
|
||||
this.input.value = val
|
||||
}
|
||||
get isValid() {
|
||||
return this.input.value !== ''
|
||||
}
|
||||
reset = () => {
|
||||
this.input.value = ''
|
||||
this.filesPreviewWraper.innerHTML = ''
|
||||
}
|
||||
formatBytes = (a,b=2) => {if(0===a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}
|
||||
createFilePreview = (file) => {
|
||||
const filePreview = document.createElement('li')
|
||||
const {name, size} = file
|
||||
filePreview.className = 'file-preview'
|
||||
filePreview.innerHTML = `
|
||||
<div class="file-name">${name}</div>
|
||||
<h5 class="file-size">${this.formatBytes(size)}</h5>
|
||||
`
|
||||
return filePreview
|
||||
}
|
||||
handleChange = (e) => {
|
||||
this.filesPreviewWraper.innerHTML = ''
|
||||
const frag = document.createDocumentFragment()
|
||||
Array.from(e.target.files).forEach(file => {
|
||||
frag.append(
|
||||
this.createFilePreview(file)
|
||||
)
|
||||
});
|
||||
this.filesPreviewWraper.append(frag)
|
||||
}
|
||||
handleKeyDown = e => {
|
||||
if (e.key === 'Enter' || e.code === 'Space') {
|
||||
e.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(name) {
|
||||
if (this.observeList.includes(name)){
|
||||
if (this.hasAttribute(name)) {
|
||||
this.input.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : '')
|
||||
}
|
||||
else {
|
||||
this.input.removeAttribute(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.input.removeEventListener('change', this.handleChange)
|
||||
this.fileInput.removeEventListener('keydown', this.handleKeyDown)
|
||||
}
|
||||
})
|
||||
|
||||
// sm-form
|
||||
const smForm = document.createElement('template')
|
||||
smForm.innerHTML = `
|
||||
<style>
|
||||
*{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host{
|
||||
--gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
form{
|
||||
display: grid;
|
||||
gap: var(--gap);
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<form onsubmit="return false">
|
||||
<slot></slot>
|
||||
</form>
|
||||
`
|
||||
|
||||
customElements.define('sm-form', class extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smForm.content.cloneNode(true))
|
||||
|
||||
this.form = this.shadowRoot.querySelector('form')
|
||||
this.formElements
|
||||
this.requiredElements
|
||||
this.submitButton
|
||||
this.resetButton
|
||||
this.allRequiredValid = false
|
||||
}
|
||||
debounce = (callback, wait) => {
|
||||
let timeoutId = null;
|
||||
return (...args) => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = window.setTimeout(() => {
|
||||
callback.apply(null, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
handleInput = this.debounce((e) => {
|
||||
this.allRequiredValid = this.requiredElements.every(elem => elem.isValid)
|
||||
if (!this.submitButton) return;
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.disabled = false;
|
||||
}
|
||||
else {
|
||||
this.submitButton.disabled = true;
|
||||
}
|
||||
}, 100)
|
||||
handleKeydown = this.debounce((e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.click()
|
||||
}
|
||||
else {
|
||||
// implement show validity logic
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
reset = () => {
|
||||
this.formElements.forEach(elem => elem.reset())
|
||||
}
|
||||
connectedCallback() {
|
||||
const slot = this.shadowRoot.querySelector('slot')
|
||||
slot.addEventListener('slotchange', e => {
|
||||
this.formElements = [...this.querySelectorAll('sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-checkbox')]
|
||||
this.requiredElements = this.formElements.filter(elem => elem.hasAttribute('required'))
|
||||
this.submitButton = e.target.assignedElements().find(elem => elem.getAttribute('variant') === 'primary' || elem.getAttribute('type') === 'submit');
|
||||
this.resetButton = e.target.assignedElements().find(elem => elem.getAttribute('type') === 'reset');
|
||||
if (this.resetButton) {
|
||||
this.resetButton.addEventListener('click', this.reset)
|
||||
}
|
||||
})
|
||||
this.addEventListener('input', this.handleInput)
|
||||
this.addEventListener('keydown', this.handleKeydown)
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener('input', this.handleInput)
|
||||
}
|
||||
})
|
||||
555
css/uploader-style.css
Normal file
555
css/uploader-style.css
Normal file
@ -0,0 +1,555 @@
|
||||
* {
|
||||
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: #0eaf8f;
|
||||
--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: #F6f6f6;
|
||||
--error-color: red;
|
||||
--green: #00843b;
|
||||
color: rgba(var(--text-color), 1);
|
||||
background: var(--background-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
body[data-theme=dark] {
|
||||
--accent-color: #1abc9c;
|
||||
--green: #13ff5a;
|
||||
--text-color: 240, 240, 240;
|
||||
--text-color-light: 170, 170, 170;
|
||||
--foreground-color: 20, 20, 20;
|
||||
--background-color: #0a0a0a;
|
||||
--error-color: rgb(255, 106, 106);
|
||||
}
|
||||
|
||||
.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: 0.8rem;
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.8;
|
||||
max-width: 60ch;
|
||||
line-height: 1.7;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
p:not(:last-of-type) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:focus-visible {
|
||||
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
||||
}
|
||||
|
||||
button {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button:focus-visible {
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
|
||||
a.button:any-link {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
align-self: flex-start;
|
||||
text-decoration: none;
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
a.button:any-link .icon {
|
||||
margin-right: 0.3rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
a:any-link:focus-visible {
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
|
||||
sm-button {
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
sm-popup {
|
||||
--width: min(24rem, 100%);
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hide-completely {
|
||||
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;
|
||||
-ms-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.flow-column {
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
|
||||
.gap-0-5 {
|
||||
gap: 0.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;
|
||||
}
|
||||
|
||||
.justify-self-end {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.direction-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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: transparent;
|
||||
}
|
||||
|
||||
.observe-empty-state:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.observe-empty-state:not(:empty) ~ .empty-state {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
fill: rgba(var(--text-color), 0.9);
|
||||
}
|
||||
|
||||
.button__icon {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
}
|
||||
.button__icon--left {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.button__icon--right {
|
||||
margin-left: 0.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: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0 1.5rem 0 0.5rem;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
|
||||
.popup__header__close {
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1.2rem;
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
#confirmation_popup,
|
||||
#prompt_popup {
|
||||
flex-direction: column;
|
||||
}
|
||||
#confirmation_popup h4,
|
||||
#prompt_popup h4 {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#confirmation_popup sm-button,
|
||||
#prompt_popup sm-button {
|
||||
margin: 0;
|
||||
}
|
||||
#confirmation_popup .flex,
|
||||
#prompt_popup .flex {
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#confirmation_popup .flex sm-button:first-of-type,
|
||||
#prompt_popup .flex sm-button:first-of-type {
|
||||
margin-right: 0.6rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#main_header {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
}
|
||||
#main_header a.button {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
border: solid thin var(--accent-color);
|
||||
}
|
||||
|
||||
#main_header__logo {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
}
|
||||
|
||||
.theme-switcher {
|
||||
position: relative;
|
||||
justify-self: flex-end;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.theme-switcher .icon {
|
||||
position: absolute;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
|
||||
.theme-switcher__checkbox {
|
||||
display: none;
|
||||
}
|
||||
.theme-switcher__checkbox:checked ~ .moon-icon {
|
||||
transform: scale(0) rotate(90deg);
|
||||
}
|
||||
.theme-switcher__checkbox:not(:checked) ~ .sun-icon {
|
||||
transform: scale(0) rotate(-90deg);
|
||||
}
|
||||
|
||||
.fieldset {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
|
||||
tags-input {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.torrent-upload-section {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
flex: 1;
|
||||
padding: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
#torrent_form {
|
||||
display: grid;
|
||||
padding: 0 1rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.loader {
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
stroke-width: 8;
|
||||
overflow: visible;
|
||||
stroke: var(--accent-color);
|
||||
fill: none;
|
||||
stroke-dashoffset: 180;
|
||||
stroke-dasharray: 180;
|
||||
animation: load 3.6s linear infinite, spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes load {
|
||||
50% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: -180;
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
height: 0.5rem;
|
||||
border-radius: 2rem;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
border-radius: 2rem;
|
||||
transition: width 0.3s;
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
#overlay_content,
|
||||
#overlay_content > * {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#upload_torrent_button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
sm-select {
|
||||
--max-height: 40vh;
|
||||
}
|
||||
|
||||
file-input {
|
||||
font-size: 0.9rem;
|
||||
--button-color: var(--background-color);
|
||||
}
|
||||
file-input .icon {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
fill: var(--background-color);
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
justify-items: center;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
.page-footer .icon {
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
margin-left: -0.4rem;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 640px) {
|
||||
#main_header {
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
|
||||
#torrent_form {
|
||||
width: 32rem;
|
||||
}
|
||||
}
|
||||
@media (any-hover: hover) {
|
||||
::-webkit-scrollbar {
|
||||
width: 0.5rem;
|
||||
height: 0.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);
|
||||
}
|
||||
}
|
||||
1
css/uploader-style.min.css
vendored
Normal file
1
css/uploader-style.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
483
css/uploader-style.scss
Normal file
483
css/uploader-style.scss
Normal file
@ -0,0 +1,483 @@
|
||||
*{
|
||||
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: #0eaf8f;
|
||||
--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: #F6f6f6;
|
||||
--error-color: red;
|
||||
--green: #00843b;
|
||||
color: rgba(var(--text-color), 1);
|
||||
background: var(--background-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
body[data-theme='dark']{
|
||||
--accent-color: #1abc9c;
|
||||
--green: #13ff5a;
|
||||
--text-color: 240, 240, 240;
|
||||
--text-color-light: 170, 170, 170;
|
||||
--foreground-color: 20, 20, 20;
|
||||
--background-color: #0a0a0a;
|
||||
--error-color: rgb(255, 106, 106);
|
||||
}
|
||||
.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: 0.8rem;
|
||||
}
|
||||
|
||||
.uppercase{
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.capitalize{
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.8;
|
||||
max-width: 60ch;
|
||||
line-height: 1.7;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
&:not(:last-of-type){
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
img{
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
a{
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
&:focus-visible{
|
||||
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
||||
}
|
||||
}
|
||||
button{
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
button:focus-visible{
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
a.button:any-link{
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
align-self: flex-start;
|
||||
text-decoration: none;
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
.icon{
|
||||
margin-right: 0.3rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
}
|
||||
a:any-link:focus-visible{
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
.button{
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
sm-button{
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
sm-popup{
|
||||
--width: min(24rem, 100%);
|
||||
}
|
||||
ul{
|
||||
list-style: none;
|
||||
}
|
||||
.flex{
|
||||
display: flex;
|
||||
}
|
||||
.grid{
|
||||
display: grid;
|
||||
}
|
||||
.hide{
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hide-completely{
|
||||
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;
|
||||
-ms-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
.flex{
|
||||
display: flex;
|
||||
}
|
||||
.grid{
|
||||
display: grid;
|
||||
}
|
||||
.flow-column{
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
.gap-0-5{
|
||||
gap: 0.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;
|
||||
}
|
||||
.justify-self-end{
|
||||
justify-self: end;
|
||||
}
|
||||
.direction-column{
|
||||
flex-direction: column;
|
||||
}
|
||||
.space-between{
|
||||
justify-content: space-between;
|
||||
}
|
||||
.w-100{
|
||||
width: 100%;
|
||||
}
|
||||
.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: transparent;
|
||||
}
|
||||
.observe-empty-state:empty{
|
||||
display: none;
|
||||
}
|
||||
.observe-empty-state:not(:empty) ~ .empty-state{
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.icon{
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
fill: rgba(var(--text-color), 0.9);
|
||||
}
|
||||
.button__icon{
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
&--left{
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
&--right{
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-layout{
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
|
||||
& > * {
|
||||
grid-column: 2/3;
|
||||
}
|
||||
}
|
||||
.popup__header{
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0 1.5rem 0 0.5rem;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
.popup__header__close{
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button--primary{
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1.2rem;
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
#confirmation_popup,
|
||||
#prompt_popup {
|
||||
flex-direction: column;
|
||||
h4 {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
sm-button{
|
||||
margin: 0;
|
||||
}
|
||||
.flex {
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
sm-button:first-of-type {
|
||||
margin-right: 0.6rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#main_header{
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
a.button{
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
border: solid thin var(--accent-color);
|
||||
}
|
||||
}
|
||||
#main_header__logo{
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
}
|
||||
|
||||
.theme-switcher{
|
||||
position: relative;
|
||||
justify-self: flex-end;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
.icon{
|
||||
position: absolute;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
}
|
||||
.theme-switcher__checkbox{
|
||||
display: none;
|
||||
&:checked ~ .moon-icon{
|
||||
transform: scale(0) rotate(90deg);
|
||||
}
|
||||
&:not(:checked) ~ .sun-icon{
|
||||
transform: scale(0) rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.fieldset{
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
|
||||
tags-input{
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.torrent-upload-section{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
flex: 1;
|
||||
padding: 0 0 2rem 0;
|
||||
}
|
||||
#torrent_form{
|
||||
display: grid;
|
||||
padding: 0 1rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.loader {
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
stroke-width: 8;
|
||||
overflow: visible;
|
||||
stroke: var(--accent-color);
|
||||
fill: none;
|
||||
stroke-dashoffset: 180;
|
||||
stroke-dasharray: 180;
|
||||
animation: load 3.6s linear infinite, spin 1s linear infinite;
|
||||
}
|
||||
@keyframes load {
|
||||
50% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100%{
|
||||
stroke-dashoffset: -180;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.progress-bar{
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
height: 0.5rem;
|
||||
border-radius: 2rem;
|
||||
}
|
||||
.progress{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
border-radius: 2rem;
|
||||
transition: width 0.3s;
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
#overlay_content,
|
||||
#overlay_content > *{
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
#upload_torrent_button{
|
||||
width: 100%;
|
||||
}
|
||||
sm-select{
|
||||
--max-height: 40vh;
|
||||
}
|
||||
file-input{
|
||||
font-size: 0.9rem;
|
||||
--button-color: var(--background-color);
|
||||
.icon{
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
fill: var(--background-color);
|
||||
}
|
||||
}
|
||||
.page-footer{
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
justify-items: center;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
.icon{
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
margin-left: -0.4rem;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 640px) {
|
||||
#main_header{
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
#torrent_form{
|
||||
width: 32rem;
|
||||
}
|
||||
}
|
||||
@media (any-hover: hover){
|
||||
::-webkit-scrollbar{
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb{
|
||||
background: rgba(var(--text-color), 0.3);
|
||||
border-radius: 1rem;
|
||||
&:hover{
|
||||
background: rgba(var(--text-color), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
5415
uploader.html
Normal file
5415
uploader.html
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user