New Name FileRise, logo rises, DragDrop Upload & Folder cards

This commit is contained in:
Ryan
2025-03-26 18:23:42 -04:00
committed by GitHub
parent c08c903810
commit 0697fcb1df
4 changed files with 755 additions and 289 deletions

355
dragAndDrop.js Normal file
View File

@@ -0,0 +1,355 @@
// dragAndDrop.js
// Moves cards into the sidebar based on the saved order in localStorage.
export function loadSidebarOrder() {
const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return;
const orderStr = localStorage.getItem('sidebarOrder');
if (orderStr) {
const order = JSON.parse(orderStr);
if (order.length > 0) {
// Ensure main wrapper is visible.
const mainWrapper = document.querySelector('.main-wrapper');
if (mainWrapper) {
mainWrapper.style.display = 'flex';
}
// For each saved ID, move the card into the sidebar.
order.forEach(id => {
const card = document.getElementById(id);
if (card && card.parentNode.id !== 'sidebarDropArea') {
sidebar.appendChild(card);
// Animate vertical slide for sidebar card
animateVerticalSlide(card);
}
});
}
}
updateSidebarVisibility();
}
// Internal helper: update sidebar visibility based on its content.
function updateSidebarVisibility() {
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
if (cards.length > 0) {
sidebar.classList.add('active');
sidebar.style.display = 'block';
} else {
sidebar.classList.remove('active');
sidebar.style.display = 'none';
}
// Save the current order in localStorage.
saveSidebarOrder();
}
}
// Internal helper: update top zone layout (center a card if one column is empty).
function updateTopZoneLayout() {
const leftCol = document.getElementById('leftCol');
const rightCol = document.getElementById('rightCol');
const leftIsEmpty = !leftCol.querySelector('#uploadCard');
const rightIsEmpty = !rightCol.querySelector('#folderManagementCard');
if (leftIsEmpty && !rightIsEmpty) {
leftCol.style.display = 'none';
rightCol.style.margin = '0 auto';
} else if (rightIsEmpty && !leftIsEmpty) {
rightCol.style.display = 'none';
leftCol.style.margin = '0 auto';
} else {
leftCol.style.display = '';
rightCol.style.display = '';
leftCol.style.margin = '';
rightCol.style.margin = '';
}
}
// When a card is being dragged, if the top drop zone is empty, set its min-height.
function addTopZoneHighlight() {
const topZone = document.getElementById('uploadFolderRow');
if (topZone) {
topZone.classList.add('highlight');
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
topZone.style.minHeight = '375px';
}
}
}
// When the drag ends, remove the extra min-height.
function removeTopZoneHighlight() {
const topZone = document.getElementById('uploadFolderRow');
if (topZone) {
topZone.classList.remove('highlight');
topZone.style.minHeight = '';
}
}
// Vertical slide/fade animation helper.
// It sets an initial vertical offset (30px down) and opacity 0, then animates to normal position and full opacity.
function animateVerticalSlide(card) {
card.style.transform = 'translateY(30px)';
card.style.opacity = '0';
// Force reflow.
card.offsetWidth;
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
}, 310);
}
// Internal helper: insert card into sidebar at a proper position based on event.clientY.
function insertCardInSidebar(card, event) {
const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return;
const existingCards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
let inserted = false;
for (const currentCard of existingCards) {
const rect = currentCard.getBoundingClientRect();
const midY = rect.top + rect.height / 2;
if (event.clientY < midY) {
sidebar.insertBefore(card, currentCard);
inserted = true;
break;
}
}
if (!inserted) {
sidebar.appendChild(card);
}
// Ensure card fills the sidebar.
card.style.width = '100%';
animateVerticalSlide(card);
}
// Internal helper: save the current sidebar card order to localStorage.
function saveSidebarOrder() {
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
const order = Array.from(cards).map(card => card.id);
localStorage.setItem('sidebarOrder', JSON.stringify(order));
}
}
// Helper: move cards from sidebar back to the top drop area when on small screens.
function moveSidebarCardsToTop() {
if (window.innerWidth < 1205) {
const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return;
const cards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
cards.forEach(card => {
const orig = document.getElementById(card.dataset.originalContainerId);
if (orig) {
orig.appendChild(card);
animateVerticalSlide(card);
}
});
updateSidebarVisibility();
updateTopZoneLayout();
}
}
// Listen for window resize to automatically move sidebar cards back to top on small screens.
window.addEventListener('resize', function () {
if (window.innerWidth < 1205) {
moveSidebarCardsToTop();
}
});
// This function ensures the top drop zone (#uploadFolderRow) has a stable width when empty.
function ensureTopZonePlaceholder() {
const topZone = document.getElementById('uploadFolderRow');
if (!topZone) return;
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
let placeholder = topZone.querySelector('.placeholder');
if (!placeholder) {
placeholder = document.createElement('div');
placeholder.className = 'placeholder';
placeholder.style.visibility = 'hidden';
placeholder.style.display = 'block';
placeholder.style.width = '100%';
placeholder.style.height = '375px';
topZone.appendChild(placeholder);
}
} else {
const placeholder = topZone.querySelector('.placeholder');
if (placeholder) placeholder.remove();
}
}
// This sets up all drag-and-drop event listeners for cards.
export function initDragAndDrop() {
function run() {
const draggableCards = document.querySelectorAll('#uploadCard, #folderManagementCard');
draggableCards.forEach(card => {
if (!card.dataset.originalContainerId) {
card.dataset.originalContainerId = card.parentNode.id;
}
const header = card.querySelector('.card-header');
if (header) {
header.classList.add('drag-header');
}
let isDragging = false;
let dragTimer = null;
let offsetX = 0, offsetY = 0;
let initialLeft, initialTop;
if (header) {
header.addEventListener('mousedown', function (e) {
e.preventDefault();
const card = this.closest('.card');
const rect = card.getBoundingClientRect();
const originX = ((e.clientX - rect.left) / rect.width) * 100;
const originY = ((e.clientY - rect.top) / rect.height) * 100;
card.style.transformOrigin = `${originX}% ${originY}%`;
dragTimer = setTimeout(() => {
isDragging = true;
card.classList.add('dragging');
addTopZoneHighlight();
const rect = card.getBoundingClientRect();
initialLeft = rect.left + window.pageXOffset;
initialTop = rect.top + window.pageYOffset;
offsetX = e.pageX - initialLeft;
offsetY = e.pageY - initialTop;
document.body.appendChild(card);
card.style.position = 'absolute';
card.style.left = initialLeft + 'px';
card.style.top = initialTop + 'px';
card.style.width = rect.width + 'px';
card.style.zIndex = '10000';
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
sidebar.classList.add('active');
sidebar.style.display = 'block';
sidebar.classList.add('highlight');
}
}, 500);
});
header.addEventListener('mouseup', function () {
clearTimeout(dragTimer);
});
}
document.addEventListener('mousemove', function (e) {
if (isDragging) {
card.style.left = (e.pageX - offsetX) + 'px';
card.style.top = (e.pageY - offsetY) + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDragging) {
isDragging = false;
card.classList.remove('dragging');
removeTopZoneHighlight();
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
sidebar.classList.remove('highlight');
}
const leftCol = document.getElementById('leftCol');
const rightCol = document.getElementById('rightCol');
let droppedInSidebar = false;
let droppedInTop = false;
const sidebarElem = document.getElementById('sidebarDropArea');
if (sidebarElem) {
const rect = sidebarElem.getBoundingClientRect();
if (
e.clientX >= rect.left &&
e.clientX <= rect.right &&
e.clientY >= rect.top &&
e.clientY <= rect.bottom
) {
insertCardInSidebar(card, e);
droppedInSidebar = true;
sidebarElem.blur();
}
}
const topRow = document.getElementById('uploadFolderRow');
if (!droppedInSidebar && topRow) {
const rect = topRow.getBoundingClientRect();
if (
e.clientX >= rect.left &&
e.clientX <= rect.right &&
e.clientY >= rect.top &&
e.clientY <= rect.bottom
) {
let container;
if (card.id === 'uploadCard') {
container = document.getElementById('leftCol');
} else if (card.id === 'folderManagementCard') {
container = document.getElementById('rightCol');
}
if (container) {
ensureTopZonePlaceholder();
container.appendChild(card);
droppedInTop = true;
container.style.position = 'relative';
card.style.position = 'absolute';
card.style.left = '0px';
// For top drop, animate vertical slide/fade.
card.style.transform = 'translateY(30px)';
card.style.opacity = '0';
card.offsetWidth; // Force reflow.
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.position = '';
container.style.position = '';
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
// Ensure the card returns to full width (via CSS: width: 100%)
card.style.width = '';
}, 310);
}
}
}
if (droppedInSidebar || droppedInTop) {
card.style.position = '';
card.style.left = '';
card.style.top = '';
card.style.zIndex = '';
} else {
const orig = document.getElementById(card.dataset.originalContainerId);
if (orig) {
orig.appendChild(card);
card.style.position = '';
card.style.left = '';
card.style.top = '';
card.style.zIndex = '';
card.style.width = '';
}
}
updateTopZoneLayout();
updateSidebarVisibility();
}
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
}

View File

@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Multi File Upload Editor</title>
<title>FileRise</title>
<link rel="icon" type="image/png" href="/assets/logo.png">
<link rel="icon" type="image/svg+xml" href="/assets/logo.svg">
<meta name="csrf-token" content="">
@@ -36,9 +36,15 @@
<stop offset="0%" style="stop-color:#2196F3;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1976D2;stop-opacity:1" />
</linearGradient>
<!-- Drop shadow filter -->
<!-- Drop shadow filter with animated attributes for a lifting effect -->
<filter id="shadowFilter" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="#000" flood-opacity="0.2" />
<feDropShadow id="dropShadow" dx="0" dy="2" stdDeviation="2" flood-color="#000" flood-opacity="0.2">
<!-- Animate the vertical offset: from 2 to 1 (as it rises), hold, then back to 2 -->
<animate attributeName="dy" values="2;1;1;2" keyTimes="0;0.2;0.8;1" dur="5s" fill="freeze" />
<!-- Animate the blur similarly: from 2 to 1.5 then back to 2 -->
<animate attributeName="stdDeviation" values="2;1.5;1.5;2" keyTimes="0;0.2;0.8;1" dur="5s"
fill="freeze" />
</feDropShadow>
</filter>
</defs>
<style type="text/css">
@@ -62,31 +68,32 @@
fill: #1565C0;
}
</style>
<!-- Cabinet Body with rounded corners, white outline, and drop shadow -->
<rect x="4" y="4" width="56" height="56" rx="6" ry="6" class="cabinet" filter="url(#shadowFilter)" />
<!-- Divider lines for drawers -->
<line x1="5" y1="22" x2="59" y2="22" class="divider" />
<line x1="5" y1="34" x2="59" y2="34" class="divider" />
<!-- Drawers with Handles -->
<rect x="8" y="24" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="27" r="1.5" class="handle" />
<rect x="8" y="36" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="39" r="1.5" class="handle" />
<rect x="8" y="48" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="51" r="1.5" class="handle" />
<!-- Additional detail: a small top handle on the cabinet door -->
<rect x="28" y="10" width="8" height="4" rx="1" ry="1" fill="#1565C0" />
<!-- Group that will animate upward and then back down once -->
<g id="cabinetGroup">
<!-- Cabinet Body with rounded corners, white outline, and drop shadow -->
<rect x="4" y="4" width="56" height="56" rx="6" ry="6" class="cabinet" filter="url(#shadowFilter)" />
<!-- Divider lines for drawers -->
<line x1="5" y1="22" x2="59" y2="22" class="divider" />
<line x1="5" y1="34" x2="59" y2="34" class="divider" />
<!-- Drawers with Handles -->
<rect x="8" y="24" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="27" r="1.5" class="handle" />
<rect x="8" y="36" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="39" r="1.5" class="handle" />
<rect x="8" y="48" width="48" height="6" rx="1" ry="1" class="drawer" />
<circle cx="54" cy="51" r="1.5" class="handle" />
<!-- Additional detail: a small top handle on the cabinet door -->
<rect x="28" y="10" width="8" height="4" rx="1" ry="1" fill="#1565C0" />
<!-- Animate transform: rises by 2 pixels over 1s, holds for 3s, then falls over 1s (total 5s) -->
<animateTransform attributeName="transform" type="translate" values="0 0; 0 -2; 0 -2; 0 0"
keyTimes="0;0.2;0.8;1" dur="5s" fill="freeze" />
</g>
</svg>
</div>
</div>
<div class="header-title">
<h1>Multi File Upload Editor</h1>
<h1>FileRise</h1>
</div>
<div class="header-right">
<div class="header-buttons">
<button id="logoutBtn" title="Logout">
@@ -95,7 +102,6 @@
<button id="changePasswordBtn" title="Change Password">
<i class="material-icons">vpn_key</i>
</button>
<!-- Restore Files Modal (Admin Only) -->
<div id="restoreFilesModal" class="modal centered-modal" style="display: none;">
<div class="modal-content">
<h4 class="custom-restore-header">
@@ -130,198 +136,193 @@
<!-- Custom Toast Container -->
<div id="customToast"></div>
<div class="container-fluid">
<!-- Login Form -->
<div class="row" id="loginForm">
<div class="col-12">
<form id="authForm" method="post">
<div class="form-group">
<label for="loginUsername">User:</label>
<input type="text" class="form-control" id="loginUsername" name="username" required />
</div>
<div class="form-group">
<label for="loginPassword">Password:</label>
<input type="password" class="form-control" id="loginPassword" name="password" required />
</div>
<button type="submit" class="btn btn-primary btn-block btn-login">Login</button>
<div class="form-group remember-me-container">
<input type="checkbox" id="rememberMeCheckbox" name="remember_me" />
<label for="rememberMeCheckbox">Remember me</label>
</div>
</form>
</div>
</div>
<!-- Main Operations: Upload and Folder Management -->
<div id="mainOperations">
<div class="container" style="max-width: 1400px; margin: 0 auto;">
<div class="row align-items-start" id="uploadFolderRow">
<!-- Upload Card: 50% width on medium, 58% on large -->
<div class="col-md-6 col-lg-7 d-flex">
<div id="uploadCard" class="card flex-fill" style="max-width: 900px; width: 100%;">
<div class="card-header">Upload Files/Folders</div>
<div class="card-body d-flex flex-column">
<form id="uploadFileForm" method="post" enctype="multipart/form-data" class="d-flex flex-column"
style="height: 100%;" novalidate>
<div class="form-group flex-grow-1" style="margin-bottom: 1rem;">
<div id="uploadDropArea"
style="border:2px dashed #ccc; padding:20px; cursor:pointer; height:100%; display:flex; flex-direction:column; justify-content:center; align-items:center; position:relative;">
<span>Drop files/folders here or click 'Choose Files'</span>
<br />
<!-- Note: Remove directory attributes so file picker only allows files -->
<input type="file" id="file" name="file[]" class="form-control-file" multiple
style="opacity:0; position:absolute; width:1px; height:1px;" />
<button type="button" id="customChooseBtn">Choose Files</button>
<!-- Main Wrapper: Hidden by default; remove "display: none;" after login -->
<div class="main-wrapper">
<!-- Sidebar Drop Zone: Hidden until you drag a card (display controlled by JS) -->
<div id="sidebarDropArea" class="drop-target-sidebar"></div>
<!-- Main Column -->
<div id="mainColumn" class="main-column">
<div class="container-fluid">
<!-- Login Form (unchanged) -->
<div class="row" id="loginForm">
<div class="col-12">
<form id="authForm" method="post">
<div class="form-group">
<label for="loginUsername">User:</label>
<input type="text" class="form-control" id="loginUsername" name="username" required />
</div>
<div class="form-group">
<label for="loginPassword">Password:</label>
<input type="password" class="form-control" id="loginPassword" name="password" required />
</div>
<button type="submit" class="btn btn-primary btn-block btn-login">Login</button>
<div class="form-group remember-me-container">
<input type="checkbox" id="rememberMeCheckbox" name="remember_me" />
<label for="rememberMeCheckbox">Remember me</label>
</div>
</form>
</div>
</div>
<!-- Main Operations: Upload and Folder Management -->
<div id="mainOperations">
<div class="container" style="max-width: 1400px; margin: 0 auto;">
<!-- Top Zone: Two columns (60% and 40%) -->
<div id="uploadFolderRow" class="row">
<!-- Left Column (60% for Upload Card) -->
<div id="leftCol" class="col-md-7" style="display: flex; justify-content: center;">
<div id="uploadCard" class="card" style="width: 100%;">
<div class="card-header">Upload Files/Folders</div>
<div class="card-body d-flex flex-column">
<form id="uploadFileForm" method="post" enctype="multipart/form-data" class="d-flex flex-column">
<div class="form-group flex-grow-1" style="margin-bottom: 1rem;">
<div id="uploadDropArea"
style="border:2px dashed #ccc; padding:20px; cursor:pointer; display:flex; flex-direction:column; justify-content:center; align-items:center; position:relative;">
<span>Drop files/folders here or click 'Choose Files'</span>
<br />
<input type="file" id="file" name="file[]" class="form-control-file" multiple
style="opacity:0; position:absolute; width:1px; height:1px;" />
<button type="button" id="customChooseBtn">Choose Files</button>
</div>
</div>
<button type="submit" id="uploadBtn" class="btn btn-primary d-block mx-auto">Upload</button>
<div id="uploadProgressContainer"></div>
</form>
</div>
</div>
</div>
<!-- Right Column (40% for Folder Management Card) -->
<div id="rightCol" class="col-md-5" style="display: flex; justify-content: center;">
<div id="folderManagementCard" class="card" style="width: 100%; position: relative;">
<div class="card-header" style="display: flex; justify-content: space-between; align-items: center;">
<span>Folder Navigation &amp; Management</span>
<button id="folderHelpBtn" class="btn btn-link" title="Folder Help"
style="padding: 0; border: none; background: none;">
<i class="material-icons folder-help-icon" style="font-size: 24px;">info</i>
</button>
</div>
<div class="card-body custom-folder-card-body">
<div class="form-group d-flex align-items-top" style="padding-top:0; margin-bottom:0;">
<div id="folderTreeContainer"></div>
</div>
<div class="folder-actions mt-3">
<button id="createFolderBtn" class="btn btn-primary">Create Folder</button>
<div id="createFolderModal" class="modal">
<div class="modal-content">
<h4>Create Folder</h4>
<input type="text" id="newFolderName" class="form-control" placeholder="Enter folder name"
style="margin-top:10px;" />
<div style="margin-top:15px; text-align:right;">
<button id="cancelCreateFolder" class="btn btn-secondary">Cancel</button>
<button id="submitCreateFolder" class="btn btn-primary">Create</button>
</div>
</div>
</div>
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
<i class="material-icons">drive_file_rename_outline</i>
</button>
<div id="renameFolderModal" class="modal">
<div class="modal-content">
<h4>Rename Folder</h4>
<input type="text" id="newRenameFolderName" class="form-control"
placeholder="Enter new folder name" style="margin-top:10px;" />
<div style="margin-top:15px; text-align:right;">
<button id="cancelRenameFolder" class="btn btn-secondary">Cancel</button>
<button id="submitRenameFolder" class="btn btn-primary">Rename</button>
</div>
</div>
</div>
<button id="deleteFolderBtn" class="btn btn-danger ml-2" title="Delete Folder">
<i class="material-icons">delete</i>
</button>
<div id="deleteFolderModal" class="modal">
<div class="modal-content">
<h4>Delete Folder</h4>
<p id="deleteFolderMessage">Are you sure you want to delete this folder?</p>
<div style="margin-top:15px; text-align:right;">
<button id="cancelDeleteFolder" class="btn btn-secondary">Cancel</button>
<button id="confirmDeleteFolder" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<div id="folderHelpTooltip" class="folder-help-tooltip"
style="display: none; position: absolute; top: 50px; right: 15px; background: #fff; border: 1px solid #ccc; padding: 10px; z-index: 1000; box-shadow: 2px 2px 6px rgba(0,0,0,0.2);">
<ul class="folder-help-list" style="margin: 0; padding-left: 20px;">
<li>Click on a folder in the tree to view its files.</li>
<li>Use [-] to collapse and [+] to expand folders.</li>
<li>Select a folder and click "Create Folder" to add a subfolder.</li>
<li>To rename or delete a folder, select it and then click the appropriate button.</li>
</ul>
</div>
</div>
<button type="submit" id="uploadBtn" class="btn btn-primary d-block mx-auto">Upload</button>
<div id="uploadProgressContainer"></div>
</form>
</div>
</div>
</div> <!-- end uploadFolderRow -->
</div> <!-- end container -->
</div> <!-- end mainOperations -->
<!-- File List Section -->
<div id="fileListContainer" style="display: none;">
<h2 id="fileListTitle">Files in (Root)</h2>
<div id="fileListActions" class="file-list-actions">
<button id="deleteSelectedBtn" class="btn action-btn" style="display: none;">Delete Files</button>
<div id="deleteFilesModal" class="modal">
<div class="modal-content">
<h4>Delete Selected Files</h4>
<p id="deleteFilesMessage">Are you sure you want to delete the selected files?</p>
<div class="modal-footer">
<button id="cancelDeleteFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmDeleteFiles" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<!-- Folder Management Card -->
<div class="col-md-6 col-lg-5 d-flex">
<div id="folderManagementCard" class="card flex-fill"
style="max-width: 900px; width: 100%; position: relative;">
<!-- Card header with folder management title and help icon -->
<div class="card-header" style="display: flex; justify-content: space-between; align-items: center;">
<span>Folder Navigation &amp; Management</span>
<button id="folderHelpBtn" class="btn btn-link" title="Folder Help"
style="padding: 0; border: none; background: none;">
<i class="material-icons folder-help-icon" style="font-size: 24px;">info</i>
</button>
<button id="copySelectedBtn" class="btn action-btn" style="display: none;" disabled>Copy Files</button>
<div id="copyFilesModal" class="modal">
<div class="modal-content">
<h4>Copy Selected Files</h4>
<p id="copyFilesMessage">Select a target folder for copying the selected files:</p>
<select id="copyTargetFolder" class="form-control modal-input"></select>
<div class="modal-footer">
<button id="cancelCopyFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmCopyFiles" class="btn btn-primary">Copy</button>
</div>
</div>
<div class="card-body custom-folder-card-body">
<div class="form-group d-flex align-items-top" style="padding-top:0; margin-bottom:0;">
<div id="folderTreeContainer"></div>
</div>
<button id="moveSelectedBtn" class="btn action-btn" style="display: none;" disabled>Move Files</button>
<div id="moveFilesModal" class="modal">
<div class="modal-content">
<h4>Move Selected Files</h4>
<p id="moveFilesMessage">Select a target folder for moving the selected files:</p>
<select id="moveTargetFolder" class="form-control modal-input"></select>
<div class="modal-footer">
<button id="cancelMoveFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmMoveFiles" class="btn btn-primary">Move</button>
</div>
<!-- Folder actions (create, rename, delete) -->
<div class="folder-actions mt-3">
<button id="createFolderBtn" class="btn btn-primary">Create Folder</button>
<!-- Create Folder Modal -->
<div id="createFolderModal" class="modal">
<div class="modal-content">
<h4>Create Folder</h4>
<input type="text" id="newFolderName" class="form-control" placeholder="Enter folder name"
style="margin-top:10px;" />
<div style="margin-top:15px; text-align:right;">
<button id="cancelCreateFolder" class="btn btn-secondary">Cancel</button>
<button id="submitCreateFolder" class="btn btn-primary">Create</button>
</div>
</div>
</div>
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
<i class="material-icons">drive_file_rename_outline</i>
</button>
<!-- Rename Folder Modal -->
<div id="renameFolderModal" class="modal">
<div class="modal-content">
<h4>Rename Folder</h4>
<input type="text" id="newRenameFolderName" class="form-control"
placeholder="Enter new folder name" style="margin-top:10px;" />
<div style="margin-top:15px; text-align:right;">
<button id="cancelRenameFolder" class="btn btn-secondary">Cancel</button>
<button id="submitRenameFolder" class="btn btn-primary">Rename</button>
</div>
</div>
</div>
<button id="deleteFolderBtn" class="btn btn-danger ml-2" title="Delete Folder">
<i class="material-icons">delete</i>
</button>
<!-- Delete Folder Modal -->
<div id="deleteFolderModal" class="modal">
<div class="modal-content">
<h4>Delete Folder</h4>
<p id="deleteFolderMessage">Are you sure you want to delete this folder?</p>
<div style="margin-top:15px; text-align:right;">
<button id="cancelDeleteFolder" class="btn btn-secondary">Cancel</button>
<button id="confirmDeleteFolder" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<!-- Help Tooltip: Initially hidden -->
<div id="folderHelpTooltip" class="folder-help-tooltip"
style="display: none; position: absolute; top: 50px; right: 15px; background: #fff; border: 1px solid #ccc; padding: 10px; z-index: 1000; box-shadow: 2px 2px 6px rgba(0,0,0,0.2);">
<ul class="folder-help-list" style="margin: 0; padding-left: 20px;">
<li>Click on a folder in the tree to view its files.</li>
<li>Use [-] to collapse and [+] to expand folders.</li>
<li>Select a folder and click "Create Folder" to add a subfolder.</li>
<li>To rename or delete a folder, select it and then click the appropriate button.</li>
</ul>
</div>
</div>
<button id="downloadZipBtn" class="btn action-btn" style="display: none;" disabled>Download ZIP</button>
<button id="extractZipBtn" class="btn btn-sm btn-info" title="Extract Zip">Extract Zip</button>
<div id="downloadZipModal" class="modal" style="display:none;">
<div class="modal-content">
<h4>Download Selected Files as Zip</h4>
<p>Enter a name for the zip file:</p>
<input type="text" id="zipFileNameInput" class="form-control" placeholder="files.zip" />
<div class="modal-footer" style="margin-top:15px; text-align:right;">
<button id="cancelDownloadZip" class="btn btn-secondary">Cancel</button>
<button id="confirmDownloadZip" class="btn btn-primary">Download</button>
</div>
</div>
</div>
</div>
<div id="fileList"></div>
</div>
</div>
</div>
<!-- File List Section -->
<div id="fileListContainer" style="display: none;">
<h2 id="fileListTitle">Files in (Root)</h2>
<div id="fileListActions" class="file-list-actions">
<button id="deleteSelectedBtn" class="btn action-btn" style="display: none;">Delete Files</button>
<!-- Delete Files Modal -->
<div id="deleteFilesModal" class="modal">
<div class="modal-content">
<h4>Delete Selected Files</h4>
<p id="deleteFilesMessage">Are you sure you want to delete the selected files?</p>
<div class="modal-footer">
<button id="cancelDeleteFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmDeleteFiles" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div> <!-- end container-fluid -->
</div> <!-- end mainColumn -->
</div> <!-- end main-wrapper -->
<button id="copySelectedBtn" class="btn action-btn" style="display: none;" disabled>Copy Files</button>
<!-- Copy Files Modal -->
<div id="copyFilesModal" class="modal">
<div class="modal-content">
<h4>Copy Selected Files</h4>
<p id="copyFilesMessage">Select a target folder for copying the selected files:</p>
<select id="copyTargetFolder" class="form-control modal-input"></select>
<div class="modal-footer">
<button id="cancelCopyFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmCopyFiles" class="btn btn-primary">Copy</button>
</div>
</div>
</div>
<button id="moveSelectedBtn" class="btn action-btn" style="display: none;" disabled>Move Files</button>
<!-- Move Files Modal -->
<div id="moveFilesModal" class="modal">
<div class="modal-content">
<h4>Move Selected Files</h4>
<p id="moveFilesMessage">Select a target folder for moving the selected files:</p>
<select id="moveTargetFolder" class="form-control modal-input"></select>
<div class="modal-footer">
<button id="cancelMoveFiles" class="btn btn-secondary">Cancel</button>
<button id="confirmMoveFiles" class="btn btn-primary">Move</button>
</div>
</div>
</div>
<button id="downloadZipBtn" class="btn action-btn" style="display: none;" disabled>Download ZIP</button>
<button id="extractZipBtn" class="btn btn-sm btn-info" title="Extract Zip">Extract Zip</button>
<!-- Download Zip Modal -->
<div id="downloadZipModal" class="modal" style="display:none;">
<div class="modal-content">
<h4>Download Selected Files as Zip</h4>
<p>Enter a name for the zip file:</p>
<input type="text" id="zipFileNameInput" class="form-control" placeholder="files.zip" />
<div class="modal-footer" style="margin-top:15px; text-align:right;">
<button id="cancelDownloadZip" class="btn btn-secondary">Cancel</button>
<button id="confirmDownloadZip" class="btn btn-primary">Download</button>
</div>
</div>
</div>
</div>
<div id="fileList"></div>
</div>
</div>
<!-- Change Password-->
<!-- Change Password, Add User, Remove User, Rename File, and Custom Confirm Modals (unchanged) -->
<div id="changePasswordModal" class="modal" style="display:none;">
<div class="modal-content" style="max-width:400px; margin:auto;">
<span id="closeChangePasswordModal" style="cursor:pointer;">&times;</span>
@@ -333,8 +334,6 @@
<button id="saveNewPasswordBtn" class="btn btn-primary" style="width:100%;">Save</button>
</div>
</div>
<!-- Add User Modal -->
<div id="addUserModal" class="modal">
<div class="modal-content">
<h3>Create New User</h3>
@@ -352,8 +351,6 @@
</div>
</div>
</div>
<!-- Remove User Modal -->
<div id="removeUserModal" class="modal">
<div class="modal-content">
<h3>Remove User</h3>
@@ -365,8 +362,6 @@
</div>
</div>
</div>
<!-- Rename File Modal -->
<div id="renameFileModal" class="modal">
<div class="modal-content">
<h4>Rename File</h4>
@@ -378,8 +373,6 @@
</div>
</div>
</div>
<!-- Custom Confirm Modal -->
<div id="customConfirmModal" class="modal" style="display:none;">
<div class="modal-content">
<p id="confirmMessage"></p>

11
main.js
View File

@@ -17,6 +17,7 @@ import { loadFolderTree } from './folderManager.js';
import { initUpload } from './upload.js';
import { initAuth, checkAuthentication } from './auth.js';
import { setupTrashRestoreDelete } from './trashRestoreDelete.js';
import { initDragAndDrop, loadSidebarOrder } from './dragAndDrop.js'
function loadCsrfToken() {
fetch('token.php', { credentials: 'include' })
@@ -64,6 +65,14 @@ document.addEventListener("DOMContentLoaded", function () {
// Call initAuth synchronously.
initAuth();
const newPasswordInput = document.getElementById("newPassword");
if (newPasswordInput) {
newPasswordInput.addEventListener("input", function() {
console.log("newPassword input event:", this.value);
});
} else {
console.error("newPassword input not found!");
}
// --- Dark Mode Persistence ---
const darkModeToggle = document.getElementById("darkModeToggle");
const storedDarkMode = localStorage.getItem("darkMode");
@@ -121,6 +130,8 @@ document.addEventListener("DOMContentLoaded", function () {
if (authenticated) {
window.currentFolder = "root";
loadFileList(window.currentFolder);
initDragAndDrop();
loadSidebarOrder();
initFileActions();
initUpload();
loadFolderTree();

View File

@@ -25,22 +25,15 @@ body {
/* CONTAINER */
.container,
.container-fluid {
margin-top: 20px;
margin-top: 10px;
padding-right: 4px !important;
padding-left: 4px !important;
}
@media (min-width: 768px) {
@media (min-width: 1300px) {
.container-fluid {
padding-left: 50px !important;
padding-right: 50px !important;
}
}
@media (min-width: 1200px) {
.container-fluid {
padding-left: 100px !important;
padding-right: 100px !important;
padding-left: 40px !important;
padding-right: 40px !important;
}
}
@@ -52,12 +45,6 @@ body {
/* FLEXBOX HEADER: LOGO, TITLE, BUTTONS FIXED */
/************************************************************/
#uploadCard,
#folderManagementCard {
min-height: 342px;
}
.btn-login {
margin-top: 10px;
}
@@ -236,17 +223,19 @@ body.dark-mode .folder-help-tooltip {
#folderHelpBtn i.material-icons.folder-help-icon {
-webkit-text-fill-color: orange !important;
color: inherit !important;
padding-right: 10px !important;
}
body.dark-mode #folderHelpBtn i.material-icons.folder-help-icon {
-webkit-text-fill-color: #ffa500 !important;
padding-right: 10px !important;
}
/************************************************************/
/* RESPONSIVE HEADER FIXES */
/************************************************************/
@media (max-width: 970px) {
@media (max-width: 790px) {
.header-container {
flex-wrap: wrap;
height: auto;
@@ -286,7 +275,7 @@ body.dark-mode #folderHelpBtn i.material-icons.folder-help-icon {
flex-direction: row;
justify-content: center;
gap: 5px;
margin-top: 10px;
margin-top: 5px;
}
}
@@ -379,21 +368,6 @@ body.dark-mode #loginForm button:hover {
CARDS & MODALS
=========================================================== */
.card {
background-color: #fff;
color: #000;
border: 1px solid #ddd;
max-width: 900px;
width: 100%;
margin: 0 auto;
}
body.dark-mode .card {
background-color: #2c2c2c;
color: #e0e0e0;
border: 1px solid #444;
}
#restoreFilesModal .modal-content {
position: fixed !important;
top: 50% !important;
@@ -1023,29 +997,6 @@ label {
display: none;
}
#uploadFolderRow {
margin-bottom: 20px;
}
@media (max-width: 768px) {
#uploadFolderRow .col-md-6 {
margin-bottom: 15px;
}
#uploadFolderRow .col-md-6:last-child {
margin-bottom: 0;
}
}
.card-header {
font-size: 1.2rem;
font-weight: bold;
}
.card-body .form-group {
margin-bottom: 5px !important;
}
#createFolderBtn {
margin-top: 0px !important;
height: 40px !important;
@@ -1124,10 +1075,6 @@ body.dark-mode .folder-option:hover {
padding: 2px 4px;
}
.custom-folder-card-body {
padding-top: 5px !important;
}
#customToast {
position: fixed;
top: 20px;
@@ -1175,8 +1122,10 @@ body.dark-mode .folder-option:hover {
#fileListContainer {
max-width: 100%;
padding: 10px 5px;
margin: 20px auto;
padding-bottom: 10px !important;
padding-left: 5px !important;
padding-right: 5px !important;
margin: 0 auto 20px;
}
@media (max-width: 750px) {
@@ -1190,11 +1139,6 @@ body.dark-mode #fileListContainer {
color: #e0e0e0;
border: 1px solid #444;
border-radius: 8px;
padding-top: 10px !important;
padding-bottom: 10px !important;
padding-left: 5px !important;
padding-right: 5px !important;
margin-top: 20px;
}
#fileListContainer>h2,
@@ -1224,6 +1168,7 @@ body.dark-mode #fileListContainer {
#fileListTitle {
font-size: 1.8em;
margin-top: 10px;
margin-bottom: 15px;
}
@@ -1288,16 +1233,13 @@ body.dark-mode #fileListContainer {
.breadcrumb-link {
cursor: pointer;
color: #007bff;
/* Blue color, for example */
text-decoration: underline;
}
/* Change color on hover */
.breadcrumb-link:hover {
color: #0056b3;
}
/* Style for the selected breadcrumb */
.breadcrumb-link.selected {
background-color: #e9ecef;
font-weight: bold;
@@ -1353,17 +1295,11 @@ body.dark-mode #fileListContainer {
.image-modal-header {
display: flex;
align-items: center;
/* Vertically center the text within a fixed height */
justify-content: center;
/* Center horizontally */
white-space: nowrap;
/* Prevent wrapping */
overflow: hidden;
/* Hide any overflowing text */
text-overflow: ellipsis;
/* Truncate with an ellipsis */
height: 25px;
/* Fixed height for a single line */
padding: 5px;
margin-bottom: 10px;
max-width: 90%;
@@ -1410,7 +1346,6 @@ body.dark-mode .image-preview-modal-content {
}
.share-btn {
/* Your custom styles here */
border: none;
color: white;
padding: 8px 12px;
@@ -1902,4 +1837,176 @@ body.dark-mode #folderContextMenu {
background-color: #2c2c2c;
border-color: #555;
color: #e0e0e0;
}
.main-wrapper {
display: flex;
flex-direction: row;
}
.drop-target-sidebar {
display: none;
width: 50px;
transition: width 0.3s ease;
background-color: #f8f9fa;
border-right: 2px dashed #1565C0;
padding: 10px;
}
@media (min-width: 769px) {
.drop-target-sidebar {
display: block;
}
}
.drop-target-sidebar.active {
width: 350px;
}
.main-column {
flex: 1;
transition: margin-left 0.3s ease;
}
#uploadFolderRow {
display: flex;
flex-wrap: nowrap;
gap: 1rem;
}
@media (max-width: 768px) {
#uploadFolderRow {
gap: 0px;
}
}
#leftCol,
#rightCol {
display: flex;
justify-content: center;
min-width: 370px;
align-self: flex-start;
}
#leftCol {
flex: 0 1 60%;
}
#rightCol {
flex: 0 1 40%;
}
@media (max-width: 768px) {
.main-wrapper {
flex-direction: column;
}
.drop-target-sidebar {
display: none !important;
}
#uploadFolderRow {
flex-wrap: wrap;
}
#leftCol, #rightCol {
flex: 0 1 100% !important;
}
#rightCol {
margin-bottom: 0;
}
}
#sidebarDropArea.highlight,
#uploadFolderRow.highlight {
border: 2px dashed #1565C0;
background-color: #eef;
}
.drag-header {
cursor: grab;
user-select: none;
position: relative;
}
.drag-header::after {
content: '⋮⋮';
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 16px;
color: #1565C0;
pointer-events: none;
}
.dragging {
transform: scale(1.05);
box-shadow: 0 20px 30px rgba(0, 0, 0, 0.3);
transition: transform 0.2s ease, box-shadow 0.2s ease;
z-index: 10000;
}
#uploadCard,
#folderManagementCard {
transition: transform 0.3s ease, opacity 0.3s ease;
width: 100%;
margin-bottom: 20px;
min-height: 353px;
}
#uploadFolderRow.highlight {
min-height: 353px;
margin-bottom: 20px;
}
#sidebarDropArea,
#uploadFolderRow {
background-color: transparent;
}
body.dark-mode #sidebarDropArea,
body.dark-mode #uploadFolderRow {
background-color: transparent;
}
body.dark-mode #sidebarDropArea.highlight,
body.dark-mode #uploadFolderRow.highlight {
background-color: #333;
border: 2px dashed #555;
color: #fff;
}
.drop-target-sidebar.highlight {
margin-top: 10px;
}
.drop-target-sidebar:not(.highlight) {
border: none !important;
}
.dragging:focus {
outline: none;
}
#sidebarDropArea > .card {
margin-bottom: 1rem;
}
.card {
background-color: #fff;
color: #000;
border: 1px solid #ddd;
max-width: 900px;
width: 100%;
margin: 0 auto;
}
body.dark-mode .card {
background-color: #2c2c2c;
color: #e0e0e0;
border: 1px solid #444;
}
.card-header {
font-size: 1.2rem;
font-weight: bold;
}
.custom-folder-card-body {
padding-top: 5px !important;
}