JSFiddle - React, Tailwind, and code Playground

by Graham Dixon

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/localforage/1.7.1/localforage.js"></script>
<div class="container" role="main" class="box">

  <form method="post" id='file-catcher' action="?" enctype="multipart/form-data" novalidate fileStoreName="testing">


    <div class="box__input">
      <svg class="box__icon" xmlns="http://www.w3.org/2000/svg" width="50" height="43" viewBox="0 0 50 43"><path d="M48.4 26.5c-.9 0-1.7.7-1.7 1.7v11.6h-43.3v-11.6c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v13.2c0 .9.7 1.7 1.7 1.7h46.7c.9 0 1.7-.7 1.7-1.7v-13.2c0-1-.7-1.7-1.7-1.7zm-24.5 6.1c.3.3.8.5 1.2.5.4 0 .9-.2 1.2-.5l10-11.6c.7-.7.7-1.7 0-2.4s-1.7-.7-2.4 0l-7.1 8.3v-25.3c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v25.3l-7.1-8.3c-.7-.7-1.7-.7-2.4 0s-.7 1.7 0 2.4l10 11.6z"/></svg>
    <label for="file"><strong>Choose a file</strong><span class="box__dragndrop"> or drag it here</span>.<br/></label>
    
          <input id='file-input' type='file' multiple/>
          <button type='submit' class='box___button' >
          Submit
        </button>
    </div>
    
    <div class="box__uploading">Uploading&hellip;</div>
    <div class="box__success">Done! <a href="?" class="box__restart" role="button">Upload more?</a></div>
    <div class="box__error">Error! <span></span>. <a href="?" class="box__restart" role="button">Try again!</a></div>
  </form>
  <div id='file-list-display'></div>

</div>

CSS

html {}

		body {
		  font-family: Roboto, sans-serif;
		  color: #0f3c4b;
		  background-color: #e5edf1;
		  padding: 5rem 1.25rem;
		  /* 80 20 */
		}

		.container {
		  width: 100%;
		  max-width: 680px;
		  /* 800 */
		  text-align: center;
		  margin: 0 auto;
		}

		.container h1 {
		  font-size: 42px;
		  font-weight: 300;
		  color: #0f3c4b;
		  margin-bottom: 40px;
		}

		.container h1 a:hover,
		.container h1 a:focus {
		  color: #39bfd3;
		}

		.container nav {
		  margin-bottom: 40px;
		}

		.container nav a {
		  border-bottom: 2px solid #c8dadf;
		  display: inline-block;
		  padding: 4px 8px;
		  margin: 0 5px;
		}

		.container nav a.is-selected {
		  font-weight: 700;
		  color: #39bfd3;
		  border-bottom-color: currentColor;
		}

		.container nav a:not( .is-selected):hover,
		.container nav a:not( .is-selected):focus {
		  border-bottom-color: #0f3c4b;
		}

		.container footer {
		  color: #92b0b3;
		  margin-top: 40px;
		}

		.container footer p+p {
		  margin-top: 1em;
		}

		.container footer a:hover,
		.container footer a:focus {
		  color: #39bfd3;
		}

		.box {
		  font-size: 1.25rem;
		  /* 20 */
		  background-color: #c8dadf;
		  position: relative;
		  padding: 100px 20px;
		}

		.box.has-advanced-upload {
		  outline: 2px dashed #92b0b3;
		  outline-offset: -10px;
		  -webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear;
		  transition: outline-offset .15s ease-in-out, background-color .15s linear;
		}

		.box.is-dragover {
		  outline-offset: -20px;
		  outline-color: #c8dadf;
		  background-color: #fff;
		}

		.box__dragndrop,
		.box__icon {
		  display: none;
		}

		.box.has-advanced-upload .box__dragndrop {
		  display: inline;
		}

		.box.has-advanced-upload .box__icon {
		  width: 100%;
		  height: 80px;
		  fill: #92b0b3;
		  display: block;
		  margin-bottom: 40px;
		}
    
    .box__input {
       text-align: left
    }

		.box.is-uploading .box__input,
		.box.is-success...

JavaScript

/*!
 * Script: jQuery.fileCatcher.js
 * Description: jQuery.fileCatcher - A jQuery plugin...
 * Copyright: Copyright (c) 2018 Money Marketplace LTD
 * Author: GDixon
 * Email: [email protected]
 * Licensed: MIT
 * Requires: jQuery > 1.9
 * Version: 0.0.1
 */
;
(function(factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module depending on jQuery
        define(['jquery'], factory);
    } else {
        // No AMD. Register plugin with global jQuery object
        factory(jQuery);
    }
} (
  function($) {

      // defaults to align the repeatable instance with the dom situation
      var defaults = {
          dataAttr: "fileCatcher",
          storeAttr: "fileStoreName"
      };

      $.fn.fileCatcher = (function(options, data) {
          // the options must be passed to the init func as an array - convert any strings
          var optionsArray = (typeof options == "string" ? [options] : options);
          // only create when the giving el exists
          if ($(this).length) {
              // extend defaults with options and set to settings
              optionsArray = $.extend({}, defaults, (typeof optionsArray !== "undefined" ? optionsArray : []));
              // check that repeatable instance hasnt already been created against instance
              if (typeof $(this).data(optionsArray.dataAttr) == "undefined") {
                  // create a new instance
                  var instance = $(this).data(optionsArray.dataAttr, new FileCatcher(this, optionsArray, data)).data(optionsArray.dataAttr);
              } else {
                  // do option based functions
                  if (typeof options !== 'undefined' && options == "destroy") {
                      // fully destroys the repeatable instance
                      var instance = this.data(optionsArray.dataAttr).destroy();
                  } else if (typeof options !== 'undefined') {
                      // recreates the...