FEAT: Working on the uploading modal, most everything on the frontend is complete. Just need some handling and a backend!

This commit is contained in:
Hayden Hargreaves 2025-03-06 21:23:03 -07:00
parent cf886b8983
commit d9ade304ba
3 changed files with 157 additions and 6 deletions

View File

@ -39,12 +39,13 @@ function DownloadButton({downloadFiles}) {
/** /**
* Upload button * Upload button
* @param uploadFiles {function}
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @constructor
*/ */
function UploadButton() { function UploadButton({uploadFiles}) {
return ( return (
<button className="text-black" title="Upload files"> <button className="text-black" title="Upload files" onClick={uploadFiles}>
<svg className="hover:bg-gray-300 mx-1 transition-colors duration-200 p-1.5 rounded-full font-semibold h-8" <svg className="hover:bg-gray-300 mx-1 transition-colors duration-200 p-1.5 rounded-full font-semibold h-8"
viewBox="0 0 24 24" fill="currentColor" viewBox="0 0 24 24" fill="currentColor"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
@ -140,7 +141,7 @@ function SearchBar() {
) )
} }
export default function Navbar({downloadFiles}) { export default function Navbar({downloadFiles, uploadFiles}) {
return <nav className="absolute w-full p-2 flex items-center border-b-1 border-gray-400 bg-gray-100"> return <nav className="absolute w-full p-2 flex items-center border-b-1 border-gray-400 bg-gray-100">
<MainIcon/> <MainIcon/>
@ -149,7 +150,7 @@ export default function Navbar({downloadFiles}) {
<div className="min-h-fit ml-auto flex"> <div className="min-h-fit ml-auto flex">
<DownloadButton downloadFiles={downloadFiles}/> <DownloadButton downloadFiles={downloadFiles}/>
<UploadButton/> <UploadButton uploadFiles={uploadFiles}/>
<InfoButton/> <InfoButton/>
<LogoutButton/> <LogoutButton/>
</div> </div>

View File

@ -0,0 +1,127 @@
import "../index.css"
import {useEffect, useRef, useState} from "react";
/**
* The list of files that the user is attempting to upload.
* @param files {object[]}
* @returns {JSX.Element}
* @constructor
*/
function FileList({files}) {
return (
<ul className="text-xs overflow-auto max-h-[200px] italic">
{files.map((file) => <li className="py-0.5">{file.name}</li>)}
</ul>
);
}
export default function Uploader({close, upload}) {
const [files, setFiles] = useState([]);
/**
* References to the elements.
* @type {React.RefObject<null>}
*/
const inputElement = useRef(null);
/**
* Prevent the default drag behavior and apply visual classes.
* @param e {object} Event object from the div wrapper.
*/
const dragEnter = (e) => {
e.preventDefault();
e.target.classList.add('border-blue-500', 'bg-blue-100');
};
/**
* Prevent the default drag behavior and apply visual classes.
* @param e {object} Event object from the div wrapper.
*/
const dragLeave = (e) => {
e.preventDefault();
e.target.classList.remove('border-blue-500', 'bg-blue-100');
};
/**
* Prevent the default drag behavior.
* @param e {object} Event object from the div wrapper.
*/
const dragOver = (e) => {
e.preventDefault();
};
/**
* When files are dropped into the div, this will be called, to append the files.
* @param e {object} Event object from the div wrapper.
*/
const drop = (e) => {
e.preventDefault();
e.target.classList.remove('border-blue-500', 'bg-blue-100');
setFiles([...files, ...e.dataTransfer.files]);
};
/**
* Click event for the input element.
* Uses the ref hook to access the element.
*/
const click = () => {
inputElement.current.click();
};
/**
* When the input changes, append the new files.
* @param e {object} Event object from the input.
*/
const inputChange = (e) => {
setFiles([...files, ...e.target.files]);
};
const uploadFiles = () => {
upload(files);
};
/**
* Debugging
*/
useEffect(() => {
files.forEach((file) => {
console.log(file)
});
}, [files]);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="fixed -inset-10 bg-black opacity-50 blur-lg"></div>
<div className="relative z-50 bg-white p-8 rounded-lg shadow-lg w-2/5 border-1 border-gray-400">
<h2 className="text-2xl font-semibold mb-2 text-blue-400">Upload Files</h2>
<p className="text-sm">
Files uploaded will be added to the current directory. Currently, directory
uploads are not supported. If you want to upload a directory you can create a new one and upload the
files into it.
</p>
<div
className="my-5 border-2 border-dashed p-8 rounded-md text-center cursor-pointer border-gray-400 hover:bg-blue-100 hover:border-blue-500 transition-all duration-100"
onDragEnter={dragEnter} onDragLeave={dragLeave} onDragOver={dragOver} onDrop={drop} onClick={click}
>
<input multiple type="file" className="hidden" ref={inputElement} onChange={inputChange}/>
<p className="italic">Drag and drop files here or click to select</p>
</div>
<FileList files={files}/>
<div className="flex justify-end">
<button
onClick={close}
title="Close without uploading"
className="bg-red-500 hover:bg-red-600 duration-100 text-white text-sm font-semibold py-1.5 px-3 mt-2 mx-2 rounded hover:cursor-pointer">
Close
</button>
<button
onClick={uploadFiles}
title="Upload times"
className="bg-blue-400 hover:bg-blue-500 duration-100 text-white text-sm font-semibold py-1.5 px-3 mt-2 rounded hover:cursor-pointer">
Upload
</button>
</div>
</div>
</div>
);
}

View File

@ -7,6 +7,7 @@ import Error from "../components/Error.jsx";
import Editor from "../components/Editor.jsx"; import Editor from "../components/Editor.jsx";
import ChildrenLoading from "../components/ChildrenLoading.jsx"; import ChildrenLoading from "../components/ChildrenLoading.jsx";
import DownloadLoading from "../components/DownloadLoading.jsx"; import DownloadLoading from "../components/DownloadLoading.jsx";
import Uploader from "../components/Uploader.jsx";
export default function Dashboard() { export default function Dashboard() {
// Store the default path // Store the default path
@ -28,6 +29,7 @@ export default function Dashboard() {
const [files, setFiles] = useState([]); const [files, setFiles] = useState([]);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [editing, setEditing] = useState(""); const [editing, setEditing] = useState("");
const [uploading, setUploading] = useState(false);
const [childrenLoading, setChildrenLoading] = useState(false); const [childrenLoading, setChildrenLoading] = useState(false);
const [downloadLoading, setDownloadLoading] = useState(false); const [downloadLoading, setDownloadLoading] = useState(false);
const [contentLoading, setContentLoading] = useState(false); const [contentLoading, setContentLoading] = useState(false);
@ -207,6 +209,10 @@ export default function Dashboard() {
setError(null); setError(null);
} }
/**
* Toggle editing of a file.
* @param path {string}
*/
const toggleEditing = (path) => { const toggleEditing = (path) => {
setEditing(path); setEditing(path);
}; };
@ -238,7 +244,6 @@ export default function Dashboard() {
}); });
}; };
/** /**
* Handle the state for the content being modified in the text editor. * Handle the state for the content being modified in the text editor.
*/ */
@ -282,11 +287,29 @@ export default function Dashboard() {
setShowHidden(e.target.checked); setShowHidden(e.target.checked);
}; };
/**
* Toggle the upload modal.
* This can be used in the navbar and the close button!
*/
const toggleUploading = () => {
setUploading(!uploading);
};
/**
* This will be where the magic happens, where the files are upload
* @param files {object[]}
* TODO: Actually do something here...
*/
const upload = (files) => {
console.log(files);
};
return ( return (
<div className="w-full min-h-screen h-screen pb-8"> <div className="w-full min-h-screen h-screen pb-8">
<Navbar downloadFiles={downloadFiles}/> <Navbar downloadFiles={downloadFiles} uploadFiles={toggleUploading}/>
<div className="h-full w-full flex flex-col items-center justify-center pb-8"> <div className="h-full w-full flex flex-col items-center justify-center pb-8">
{downloadLoading && <DownloadLoading/>} {downloadLoading && <DownloadLoading/>}
{uploading && <Uploader close={toggleUploading} upload={upload}/>}
{error && <Error error={error} clear={clearError}/>} {error && <Error error={error} clear={clearError}/>}
{(editing !== "" && !error) && {(editing !== "" && !error) &&