Last active
June 10, 2022 21:55
-
-
Save gtindo/32a59f8a2fd530973ef3619349a6d25d to your computer and use it in GitHub Desktop.
File Upload Web Component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FileUpload extends HTMLElement { | |
constructor(){ | |
super(); | |
this.handleDropEvent = this.handleDropEvent.bind(this); | |
this._root = this.attachShadow({mode: "open"}); | |
this._root.addEventListener("drop", this.handleDropEvent); | |
this._root.addEventListener("dragover", this.handleDragOver); | |
this.maxSize = 0; | |
this.maxFiles = 1; | |
this.filesTypes = []; | |
this.render(); | |
this.error = this._root.querySelector("#error"); | |
this.dropZone = this._root.querySelector("#drop-zone"); | |
this.dropZone.addEventListener("click", () => { | |
const input = document.createElement("input"); | |
input.type = "file"; | |
input.multiple = true; | |
input.click(); | |
input.onchange = () => { | |
this.handleFilesSelection(input.files); | |
} | |
}); | |
} | |
static get observedAttributes(){ | |
return [ | |
"max-files", | |
"max-size", | |
"files-types" | |
] | |
} | |
attributeChangedCallback(name, oldValue, newValue){ | |
switch(name){ | |
case "max-files": | |
this.maxFiles = parseInt(newValue ?? "1"); | |
break; | |
case "max-size": | |
this.maxSize = parseInt(newValue ?? "0"); | |
break; | |
case "files-types": | |
this.filesTypes = newValue ? newValue.split(",") : []; | |
break; | |
} | |
} | |
/** | |
* | |
* @param {FileList} files | |
*/ | |
validateFiles(files){ | |
let status = { | |
err: false, | |
msg: "" | |
} | |
const nbFiles = files.length; | |
if(nbFiles > this.maxFiles) { | |
status.err = true; | |
status.msg += `There are ${nbFiles - this.maxFiles} more files than required <br />`; | |
} | |
for(let i = 0; i < nbFiles; i++){ | |
const file = files.item(i); | |
if(file.size > this.maxSize){ | |
status.err = true; | |
status.msg += `${file.name} size exceed ${this.maxSize} limit <br />`; | |
} | |
if(this.filesTypes.length > 0 && !this.filesTypes.includes(file.type)){ | |
status.err = true; | |
status.msg += `${file.name} mime type is not supported <br />`; | |
} | |
} | |
return status; | |
} | |
/** | |
* | |
* @param {DragEvent} e | |
*/ | |
handleDropEvent(e){ | |
e.preventDefault(); | |
const files = e.dataTransfer.files; | |
this.handleFilesSelection(files); | |
} | |
/** | |
* | |
* @param {FileList} files | |
*/ | |
handleFilesSelection(files) { | |
const validation = this.validateFiles(files); | |
if(validation.err) { | |
this.error.innerHTML = validation.msg; | |
return; | |
} | |
this.error.innerHTML = ""; | |
this.dispatchEvent(new CustomEvent("files", { | |
detail: { | |
files | |
} | |
})); | |
} | |
handleDragOver(e) { | |
e.preventDefault(); | |
} | |
render(){ | |
this._root.innerHTML = ` | |
<style> | |
:host { | |
display: block; | |
width: 350px; | |
height: 350px; | |
border: solid 1px black; | |
text-align: center; | |
background: #eee; | |
} | |
.drop-zone { | |
display: flex; | |
width: 100%; | |
height: 100%; | |
justify-content: center; | |
align-items: center; | |
cursor: pointer; | |
} | |
.drop-zone:hover { | |
background: #ddd; | |
} | |
#error { | |
font-size: 12px; | |
color: #aaa; | |
} | |
</style> | |
<div class="drop-zone" id="drop-zone"> | |
<div> | |
Drag and Drop your files here or click to upload! <br /> | |
<span id="error"></span> | |
</div> | |
</div>` | |
; | |
} | |
} | |
customElements.define("file-upload", FileUpload); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>File Upload Demo</title> | |
<script src="file-upload.js" defer></script> | |
<style> | |
</style> | |
</head> | |
<body> | |
<file-upload id="file-upload" | |
max-files="2" | |
max-size="5000000" | |
files-types="image/jpeg,image/jpg,image/png" | |
></file-upload> | |
<script> | |
const fileUpload = document.querySelector("file-upload"); | |
fileUpload.addEventListener("files", (e) => { | |
console.log(e.detail); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment