JSFiddle - React, Tailwind, and code Playground

by Joe

HTML

<main>
  <section>
    <div id="drop-area">
      <form class="my-form" enctype="multipart/form-data">
        <p>
          You can drag an drop multiple files onto this area to upload them.
        </p>
        <input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
        <label for="fileElem" class="button">
          Select some files
        </label>
        <progress id="progress-bar" max=100 value=0></progress>
        <div id="gallery"></div>
      </form>
    </div>
    <p id="formError"></p>
  </section>
</main>

CSS

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  font-size: 18px;
  height: 100%;
  overflow: hidden;
}

body {
  background-color: #d9d9d9;
  font-family: Helvetica;
}

header,
footer {
  background-color: #000000;
  height: 20vh;
  padding: 7vh 10vw;
  box-shadow: 0 0 5px 5px;
}

header h1 {
  color: #ffffff;
  margin: 0;
  padding: 0;
}

footer {
  height: 10vh;
  padding: 1vh 3vw;
}

footer small {
  color: #ffffff;
  font-size: 12px;
}

footer a {
  color: #dfdfdf;
}

main {
  height: 70vh;
  padding-top: 10vh;
}

#drop-area {
  border: 2px dashed #ccc;
  border-radius: 10px;
  width: 80vw;
  max-width: 480px;
  font-family: sans-serif;
  margin: auto;
  padding: 20px;
}

#drop-area.highlight {
  border-color: purple;
}

p {
  margin: 0;
}

p#formError {
  visibility: hidden;
  background-color: #cc0000;
  border-radius: 6px;
  color: #ffffff;
  text-align: center;
  padding: 8px 0;
  width: 80vw;
  max-width: 480px;
  margin: 1rem auto 0;
}

.my-form {
  margin-bottom: 10px;
}

#progress-bar {
  display: block;
  margin: 1rem auto 0;
  width: 90%;
}

#gallery {
  margin-top: 10px;
}

#gallery img {
  width: 150px;
  margin-bottom: 10px;
  margin-right: 10px;
  vertical-align: middle;
}

.button {
  display: inline-block;
  padding: 10px;
  background: #ccc;
  cursor: pointer;
  border-radius: 5px;
  border: 1px solid #ccc;
}

.button:hover {
  background: #ddd;
}

#fileElem {
  display: none;
}

JavaScript

let filesDone = 0;
let filesToDo = 0;
let uploadProgress = [];
let progressBar = document.getElementById("progress-bar");

const dropArea = document.getElementById("drop-area");

const preventDefaults = e => {
  e.preventDefault();
  e.stopPropagation();
};

const highlight = e => {
  dropArea.classList.add("highlight");
};

const unhighlight = e => {
  dropArea.classList.remove("highlight");
};

const handleDrop = e => {
  // shorthand version
  // ([...e.dataTransfer.files]).forEach((file)=>{console.log("file...",file)});

  const dt = e.dataTransfer;
  const files = dt.files;

  handleFiles(files);
};

const handleFiles = files => {
  const filesArray = [...files];
  initializeProgress(filesArray.length);
  filesArray.forEach(uploadFile);
  filesArray.forEach(previewFile);
};

const uploadFile = (file, i) => {
  const url = "http://uploader.localhost/uploadMe.php";
  let xhr = new XMLHttpRequest();
  let formData = new FormData();

  xhr.open("POST", url, true);

  xhr.upload.addEventListener("progress", e => {
    updateProgress(i, (e.loaded * 100.0 / e.total) || 100);
  });

  xhr.addEventListener(
    "readystatechange",
    function(resp) {
      if (xhr.readyState == 4 && xhr.status == 200) {
        // Upload correct
      } else if (xhr.readyState == 4 && xhr.status != 200) {
        let formError = document.getElementById("formError");
        formError.innerHTML = "Sorry, but there was an error with the upload";
        formError.style.visibility = "visible";
      }
    },
    false
  );

  formData.append("file", file);
  xhr.send(formData);
};

["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => {
  dropArea.addEventListener(eventName, preventDefaults, false);
});
["dragenter", "dragover"].forEach(eventName => {
  dropArea.addEventListener(eventName, highlight, false);
});
["dragleave", "drop"].forEach(eventName => {
  dropArea.addEventListener(eventName, unhighlight, false);
});

dropArea.addEventListener("drop", handleDrop,...