const fileInput = document.createElement('template') fileInput.innerHTML = ` ` 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.reflectedAttributes = ['accept', 'multiple', 'capture'] 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'] } 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 = `
${name}
${this.formatBytes(size)}
` 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.reflectedAttributes.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) } })