JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://cdn.jsdelivr.net/mithril/0.1.22/mithril.min.js"></script>

CSS

.uploader {background:#eee;height:200px;width:300px;}

JavaScript

//drag and drop micro-library
function dragdrop(element, options) {
	options = options || {}
	
	element.addEventListener("dragover", activate)
	element.addEventListener("dragleave", deactivate)
	element.addEventListener("dragend", deactivate)
	element.addEventListener("drop", deactivate)
	element.addEventListener("drop", update)
	window.addEventListener("blur", deactivate)

	function activate(e) {
		e.preventDefault()
	}
	function deactivate() {}
	function update(e) {
		e.preventDefault()
		if (typeof options.onchange == "function") {
			options.onchange((e.dataTransfer || e.target).files)
		}
	}
}

//model entity
var Uploader = {}

Uploader.upload = function(files) {
	var formData = new FormData
	for (var i = 0; i < files.length; i++) {
		formData.append("file" + i, files[i])
	}
	
	return m.request({
		method: "POST",
		url: "/echo/json",
		data: formData,
		//simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it
		serialize: function(value) {return value}
	})
}

//an uploader module
var uploader = {}

uploader.controller = function(options) {
	options = options || {}
	return {
		onchange: options.onchange
	}
}

uploader.view = function(ctrl) {
	return m(".uploader", {
		config: function(element, isInitialized) {
			if (!isInitialized) {
				dragdrop(element, {onchange: ctrl.onchange})
			}
		}
	})
}

//demo module
var demo = {}

demo.controller = function() {
	return {
		title: m.prop("Upload something"),
		uploader: submodule(uploader, {
            onchange: function(files) {
                Uploader.upload(files).then(function() {
                    alert("uploaded!")
                })
            }
		})
	}
}

demo.view = function(ctrl) {
	return [
		m("h1", ctrl.title()),
		ctrl.uploader(),
		m("p", "drop a file in the box above")
	]
}

m.module(document.body, demo)

//submodule helper
function submodule(module, args) {
	return module.view.bind(this, new module.controller(args))
}