294 lines
12 KiB
HTML
294 lines
12 KiB
HTML
<div class="float-right flex-row">
|
|
<main class="max-w-screen-lg h-full">
|
|
<!-- file upload modal -->
|
|
<article aria-label="File Upload Modal" class="relative h-full flex flex-col bg-white shadow-xl rounded-md"
|
|
ondrop="dropHandler(event);" ondragover="dragOverHandler(event);"
|
|
ondragleave="dragLeaveHandler(event);" ondragenter="dragEnterHandler(event);">
|
|
<!-- overlay -->
|
|
<div id="overlay"
|
|
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md">
|
|
<i>
|
|
<svg class="fill-current w-12 h-12 mb-3 text-blue-700" xmlns="http://www.w3.org/2000/svg" width="24"
|
|
height="24" viewBox="0 0 24 24">
|
|
<path d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z"/>
|
|
</svg>
|
|
</i>
|
|
<p class="text-lg text-blue-700">Drop files to upload</p>
|
|
</div>
|
|
|
|
<!-- scroll area -->
|
|
<section class="h-full overflow-auto p-8 w-full h-full flex flex-col">
|
|
<header class="border-dashed border-2 border-gray-400 py-12 flex flex-col justify-center items-center">
|
|
<p class="mb-3 font-semibold text-gray-900 flex flex-wrap justify-center p-2">
|
|
<span>Drag and drop your</span> <span>files anywhere or</span>
|
|
</p>
|
|
<input id="hidden-input" type="file" multiple class="hidden" accept=".csv"/>
|
|
<button id="button"
|
|
class="mt-2 rounded-sm px-3 py-1 bg-gray-200 hover:bg-gray-300 focus:shadow-outline focus:outline-none">
|
|
Upload a file
|
|
</button>
|
|
</header>
|
|
|
|
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">
|
|
To Upload
|
|
</h1>
|
|
|
|
<ul id="gallery" class="flex flex-1 flex-wrap -m-1">
|
|
<li id="empty"
|
|
class="h-full w-full text-center flex flex-col items-center justify-center items-center">
|
|
<img class="mx-auto w-32"
|
|
src="https://user-images.githubusercontent.com/507615/54591670-ac0a0180-4a65-11e9-846c-e55ffce0fe7b.png"
|
|
alt="no data"/>
|
|
<span class="text-small text-gray-500">No files selected</span>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- sticky footer -->
|
|
<footer class="flex justify-end px-8 pb-8 pt-4">
|
|
<button id="submit"
|
|
class="rounded-sm px-3 py-1 bg-blue-700 hover:bg-blue-500 text-white focus:shadow-outline focus:outline-none">
|
|
Upload now
|
|
</button>
|
|
<button id="cancel"
|
|
class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none">
|
|
Cancel
|
|
</button>
|
|
</footer>
|
|
</article>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- using two similar templates for simplicity in js code -->
|
|
<template id="file-template">
|
|
<li class="block p-1 w-full h-24">
|
|
<article tabindex="0"
|
|
class="group w-full h-full rounded-md focus:outline-none focus:shadow-outline elative bg-gray-100 cursor-pointer relative shadow-sm">
|
|
<img alt="upload preview" class="img-preview hidden w-full h-full sticky object-cover rounded-md bg-fixed"/>
|
|
|
|
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
|
<h1 class="flex-1 group-hover:text-blue-800"></h1>
|
|
<div class="flex">
|
|
<span class="p-1 text-blue-800">
|
|
<i>
|
|
<svg class="fill-current w-4 h-4 ml-auto pt-1" xmlns="http://www.w3.org/2000/svg" width="24"
|
|
height="24" viewBox="0 0 24 24">
|
|
<path d="M15 2v5h5v15h-16v-20h11zm1-2h-14v24h20v-18l-6-6z"/>
|
|
</svg>
|
|
</i>
|
|
</span>
|
|
<p class="p-1 size text-xs text-gray-700"></p>
|
|
<button class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800">
|
|
<svg class="pointer-events-none fill-current w-4 h-4 ml-auto" xmlns="http://www.w3.org/2000/svg"
|
|
width="24" height="24" viewBox="0 0 24 24">
|
|
<path class="pointer-events-none"
|
|
d="M3 6l3 18h12l3-18h-18zm19-4v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.316c0 .901.73 2 1.631 2h5.711z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
</li>
|
|
</template>
|
|
|
|
<template id="image-template">
|
|
<li class="block p-1 w-full h-24">
|
|
<article tabindex="0"
|
|
class="group hasImage w-full h-full rounded-md focus:outline-none focus:shadow-outline bg-gray-100 cursor-pointer relative text-transparent hover:text-white shadow-sm">
|
|
<img alt="upload preview" class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed"/>
|
|
|
|
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
|
<h1 class="flex-1"></h1>
|
|
<div class="flex">
|
|
<span class="p-1">
|
|
<i>
|
|
<svg class="fill-current w-4 h-4 ml-auto pt-" xmlns="http://www.w3.org/2000/svg" width="24"
|
|
height="24" viewBox="0 0 24 24">
|
|
<path d="M5 8.5c0-.828.672-1.5 1.5-1.5s1.5.672 1.5 1.5c0 .829-.672 1.5-1.5 1.5s-1.5-.671-1.5-1.5zm9 .5l-2.519 4-2.481-1.96-4 5.96h14l-5-8zm8-4v14h-20v-14h20zm2-2h-24v18h24v-18z"/>
|
|
</svg>
|
|
</i>
|
|
</span>
|
|
|
|
<p class="p-1 size text-xs"></p>
|
|
<button class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md">
|
|
<svg class="pointer-events-none fill-current w-4 h-4 ml-auto" xmlns="http://www.w3.org/2000/svg"
|
|
width="24" height="24" viewBox="0 0 24 24">
|
|
<path class="pointer-events-none"
|
|
d="M3 6l3 18h12l3-18h-18zm19-4v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.316c0 .901.73 2 1.631 2h5.711z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
</li>
|
|
</template>
|
|
|
|
<script>
|
|
const fileTempl = document.getElementById("file-template"),
|
|
imageTempl = document.getElementById("image-template"),
|
|
empty = document.getElementById("empty");
|
|
|
|
// use to store pre selected files
|
|
let FILES = {};
|
|
|
|
// check if file is of type image and prepend the initialied
|
|
// template to the target element
|
|
function addFile(target, file) {
|
|
const isImage = file.type.match("image.*"),
|
|
objectURL = URL.createObjectURL(file);
|
|
|
|
const clone = isImage
|
|
? imageTempl.content.cloneNode(true)
|
|
: fileTempl.content.cloneNode(true);
|
|
|
|
clone.querySelector("h1").textContent = file.name;
|
|
clone.querySelector("li").id = objectURL;
|
|
clone.querySelector(".delete").dataset.target = objectURL;
|
|
clone.querySelector(".size").textContent =
|
|
file.size > 1024
|
|
? file.size > 1048576
|
|
? Math.round(file.size / 1048576) + "mb"
|
|
: Math.round(file.size / 1024) + "kb"
|
|
: file.size + "b";
|
|
|
|
isImage &&
|
|
Object.assign(clone.querySelector("img"), {
|
|
src: objectURL,
|
|
alt: file.name
|
|
});
|
|
|
|
empty.classList.add("hidden");
|
|
target.prepend(clone);
|
|
|
|
FILES[objectURL] = file;
|
|
}
|
|
|
|
const gallery = document.getElementById("gallery"),
|
|
overlay = document.getElementById("overlay");
|
|
|
|
// click the hidden input of type file if the visible button is clicked
|
|
// and capture the selected files
|
|
const hidden = document.getElementById("hidden-input");
|
|
document.getElementById("button").onclick = () => hidden.click();
|
|
hidden.onchange = (e) => {
|
|
for (const file of e.target.files) {
|
|
addFile(gallery, file);
|
|
}
|
|
};
|
|
|
|
// use to check if a file is being dragged
|
|
const hasFiles = ({dataTransfer: {types = []}}) =>
|
|
types.indexOf("Files") > -1;
|
|
|
|
// use to drag dragenter and dragleave events.
|
|
// this is to know if the outermost parent is dragged over
|
|
// without issues due to drag events on its children
|
|
let counter = 0;
|
|
|
|
// reset counter and append file to gallery when file is dropped
|
|
function dropHandler(ev) {
|
|
ev.preventDefault();
|
|
for (const file of ev.dataTransfer.files) {
|
|
addFile(gallery, file);
|
|
overlay.classList.remove("draggedover");
|
|
counter = 0;
|
|
}
|
|
}
|
|
|
|
// only react to actual files being dragged
|
|
function dragEnterHandler(e) {
|
|
e.preventDefault();
|
|
if (!hasFiles(e)) {
|
|
return;
|
|
}
|
|
++counter && overlay.classList.add("draggedover");
|
|
}
|
|
|
|
function dragLeaveHandler(e) {
|
|
1 > --counter && overlay.classList.remove("draggedover");
|
|
}
|
|
|
|
function dragOverHandler(e) {
|
|
if (hasFiles(e)) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
// event delegation to caputre delete events
|
|
// fron the waste buckets in the file preview cards
|
|
gallery.onclick = ({target}) => {
|
|
if (target.classList.contains("delete")) {
|
|
const ou = target.dataset.target;
|
|
document.getElementById(ou).remove(ou);
|
|
gallery.children.length === 1 && empty.classList.remove("hidden");
|
|
delete FILES[ou];
|
|
}
|
|
};
|
|
|
|
// print all selected files
|
|
document.getElementById("submit").onclick = () => {
|
|
var data = new FormData();
|
|
let files = Object.values(FILES);
|
|
for (var x = 0; x < files.length; x++) {
|
|
|
|
data.append('files', files[x], files[x].name);
|
|
}
|
|
var request = new XMLHttpRequest();
|
|
request.open('post', '/account/file-manager/upload', true);
|
|
// upload progress event
|
|
request.upload.addEventListener('progress', function (e) {
|
|
var percent_complete = (e.loaded / e.total) * 100;
|
|
|
|
// Percentage of upload completed
|
|
console.log(percent_complete);
|
|
});
|
|
|
|
// AJAX request finished event
|
|
request.addEventListener('load', function (e) {
|
|
clearUploads()
|
|
});
|
|
request.send(data);
|
|
};
|
|
|
|
// clear entire selection
|
|
document.getElementById("cancel").onclick = () => {
|
|
clearUploads()
|
|
};
|
|
|
|
function clearUploads() {
|
|
while (gallery.children.length > 0) {
|
|
gallery.lastChild.remove();
|
|
}
|
|
FILES = {};
|
|
empty.classList.remove("hidden");
|
|
gallery.append(empty);
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.hasImage:hover section {
|
|
background-color: rgba(5, 5, 5, 0.4);
|
|
}
|
|
|
|
.hasImage:hover button:hover {
|
|
background: rgba(5, 5, 5, 0.45);
|
|
}
|
|
|
|
#overlay p,
|
|
i {
|
|
opacity: 0;
|
|
}
|
|
|
|
#overlay.draggedover {
|
|
background-color: rgba(255, 255, 255, 0.7);
|
|
}
|
|
|
|
#overlay.draggedover p,
|
|
#overlay.draggedover i {
|
|
opacity: 1;
|
|
}
|
|
|
|
.group:hover .group-hover\:text-blue-800 {
|
|
color: #2b6cb0;
|
|
}
|
|
</style> |