Files
soft_usite/resources/views/partials/file-upload.html
T
2025-04-24 02:07:47 +00:00

294 lines
12 KiB
HTML
Executable File

<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>&nbsp;<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>