diff --git a/public/assets/scripts/FileSelector.js b/public/assets/scripts/FileSelector.js new file mode 100644 index 0000000..b26bbb5 --- /dev/null +++ b/public/assets/scripts/FileSelector.js @@ -0,0 +1,63 @@ +export class FileSelector { + dropZones = []; + selectedFile = null; + onFileSelected = null; + + constructor(mimeType) { + this.mimeType = mimeType; + + window.ondrop = (e) => e.preventDefault(); + + window.ondragover = (e) => { + if (![...e.dataTransfer.items].some(it => it.kind === "file")) return; + + e.preventDefault(); + if (!this.dropZones.some(dropZone => dropZone.contains(e.target))) { + e.dataTransfer.dropEffect = "none"; + } + }; + } + + selectFile(file) { + this.selectedFile = file; + + if (this.onFileSelected) this.onFileSelected(file); + } + + addInput(elem) { + elem.oninput = () => { + setTimeout(() => this.selectFile(elem.files[0]), 200); + }; + } + + addDropZone(elem) { + this.dropZones.push(elem); + + elem.ondrop = (e) => { + e.preventDefault(); + + const file = [...e.dataTransfer.files].find(file => file.type.startsWith(this.mimeType)); + + if (file) { + this.selectFile(file); + } + }; + + elem.ondragover = (e) => { + const items = [...e.dataTransfer.items]; + + if (!items.some(it => it.kind === "file")) return; + + e.preventDefault(); + + if (items.some(it => it.type.startsWith(this.mimeType))) { + e.dataTransfer.dropEffect = "copy"; + elem.classList.add("dropping"); + } else { + e.dataTransfer.dropEffect = "copy"; + } + }; + + elem.ondragleave = () => elem.classList.remove("dropping"); + } +} diff --git a/public/assets/scripts/main.js b/public/assets/scripts/main.js index db6d4d6..9275d99 100644 --- a/public/assets/scripts/main.js +++ b/public/assets/scripts/main.js @@ -1,21 +1,33 @@ import { Notifier } from "./Notifier.js"; import { ProgressBar } from "./ProgressBar.js"; import { VideoCompressor } from "./VideoCompressor.js"; +import { FileSelector } from "./FileSelector.js"; const notifier = new Notifier(); const progressBar = new ProgressBar(); const videoCompressor = new VideoCompressor(progressBar); +const fileSelector = new FileSelector("video/"); -// Reset file input cache after reload -document.getElementById("file-input").value = ""; +fileSelector.addInput(document.getElementById("file-input")); +fileSelector.addDropZone(document.getElementById("file-drop-area")); + +fileSelector.onFileSelected = file => { + document.getElementById("uploaded-video").src = URL.createObjectURL(file); + document.getElementById("uploaded-video").load(); + + hideElements("#file-drop-area", "#file-input-spacing"); + showElements("#uploaded-video", "#change-file"); +}; async function compress(filesize, filesizeUnit) { document.getElementById("uploaded-video").pause(); - const file = document.getElementById("file-input").files[0]; + const sectionChangePromise = showSection("loading"); - if (!file) { + if (!fileSelector.selectedFile) { + await sectionChangePromise; // Avoid changing sections at the same time alert("Please select a file"); + return; } @@ -27,12 +39,11 @@ async function compress(filesize, filesizeUnit) { case "mb": targetFilesize = filesize * 1000000; break; } - showSection("loading"); - let result; try { - result = await videoCompressor.compress(file, targetFilesize, videoLength); + result = await videoCompressor.compress(fileSelector.selectedFile, targetFilesize, videoLength); } catch (e) { + await sectionChangePromise; // Avoid changing sections at the same time alert(e.message); showSection("file-picker"); @@ -80,45 +91,16 @@ document.getElementById("compress").onclick = async () => { await compress(filesize, filesizeUnit); } -for (const preset of document.getElementsByClassName("preset")) { - preset.onclick = () => compress(parseInt(preset.dataset.size), preset.dataset.unit); -} - document.getElementById("cancel").onclick = () => { videoCompressor.stop(); showSection("file-picker"); }; -function openFileSelector() { - document.getElementById("file-input").click(); -} -document.getElementById("file-drop-area").onclick = openFileSelector; -document.getElementById("change-file").onclick = openFileSelector; +document.getElementById("change-file").onclick = () => document.getElementById("file-input").click(); -for (const tabbable of document.querySelectorAll("[tabindex='0']")) { - tabbable.onkeydown = event => { - if (['Enter', 'Space'].includes(event.code)) - tabbable.click(); - } -} - -document.getElementById("file-input").oninput = () => { - setTimeout(() => { - const file = document.getElementById("file-input").files[0]; - - document.getElementById("uploaded-video").src = URL.createObjectURL(file); - document.getElementById("uploaded-video").load(); - - hideElements("#file-drop-area", "#file-input-spacing"); - showElements("#uploaded-video", "#change-file"); - }, 200); -}; - -document.getElementById("back").onclick = () => { - showSection("file-picker"); -}; +document.getElementById("back").onclick = () => showSection("file-picker"); window.onbeforeunload = event => { if (videoCompressor.inProgress) event.preventDefault(); @@ -130,3 +112,14 @@ function bytesToSizeString(bytes) { if (bytes >= 1000) return (bytes / 1000).toFixed(1) + " KB"; return bytes + " B"; } + +for (const tabbable of document.querySelectorAll("[tabindex='0']")) { + tabbable.onkeydown = event => { + if (['Enter', 'Space'].includes(event.code)) + tabbable.click(); + } +} + +for (const preset of document.getElementsByClassName("preset")) { + preset.onclick = () => compress(parseInt(preset.dataset.size), preset.dataset.unit); +} diff --git a/public/assets/scripts/ui.js b/public/assets/scripts/ui.js index 501c2a3..efac0c5 100644 --- a/public/assets/scripts/ui.js +++ b/public/assets/scripts/ui.js @@ -6,6 +6,8 @@ function showSection(section) { } showElements(`#${section}-section`); + + return new Promise(resolve => setTimeout(resolve, TRANSITION_TIME * 2)); } function hideElements() { diff --git a/public/assets/styles/file-picker-section.css b/public/assets/styles/file-picker-section.css index 544e1df..f3a53aa 100644 --- a/public/assets/styles/file-picker-section.css +++ b/public/assets/styles/file-picker-section.css @@ -29,6 +29,10 @@ outline: none; } +#file-drop-area.dropping { + border-color: black; +} + #uploaded-video { display: none; opacity: 0; diff --git a/public/index.html b/public/index.html index 436266f..74e7201 100644 --- a/public/index.html +++ b/public/index.html @@ -4,7 +4,7 @@ - Video Compressor + Compact.Video - Compress videos online @@ -19,10 +19,10 @@

Compress video files to a specific file size, so you can upload them to your favorite social media and messaging apps!

-
+
- + +