Header Drop Zone Extension
This commit is contained in:
12
README.md
12
README.md
@@ -147,6 +147,10 @@ FileRise is a lightweight, secure, self-hosted web application for uploading, sy
|
|||||||
- **Top Bar Drop Zone:**
|
- **Top Bar Drop Zone:**
|
||||||
- A top drop zone is available for reordering or managing cards quickly.
|
- A top drop zone is available for reordering or managing cards quickly.
|
||||||
- Dragging a card to the top drop zone provides immediate visual feedback, ensuring a fluid and customizable workflow.
|
- Dragging a card to the top drop zone provides immediate visual feedback, ensuring a fluid and customizable workflow.
|
||||||
|
- **Header Drop Zone with State Preservation:**
|
||||||
|
- Cards can be dragged into the header drop zone, where they are represented by a compact material icon.
|
||||||
|
- **State Preservation:** Instead of removing the card from the DOM, the original card is moved into a hidden container. This ensures that dynamic features (such as the folder tree in the Folder Management card or file selection in the Upload card) remain fully initialized and retain their state on page refresh.
|
||||||
|
- **Modal Display:** When the user interacts (via hover or click) with the header icon, the card is temporarily moved into a modal overlay for full interaction. When the modal is closed, the card is returned to the hidden container, keeping its state persistent.
|
||||||
- **Seamless Interaction:**
|
- **Seamless Interaction:**
|
||||||
- Both drop zones support smooth drag-and-drop interactions with animations and pointer event adjustments, ensuring reliable card placement regardless of screen position.
|
- Both drop zones support smooth drag-and-drop interactions with animations and pointer event adjustments, ensuring reliable card placement regardless of screen position.
|
||||||
|
|
||||||
@@ -170,10 +174,10 @@ FileRise is a lightweight, secure, self-hosted web application for uploading, sy
|
|||||||
- Features an intuitive interface with Material Icons for quick recognition and access.
|
- Features an intuitive interface with Material Icons for quick recognition and access.
|
||||||
- Allows administrators to manage authentication settings, user management, and login methods in real time.
|
- Allows administrators to manage authentication settings, user management, and login methods in real time.
|
||||||
- Includes real-time validation that prevents the accidental disabling of all authentication methods simultaneously.
|
- Includes real-time validation that prevents the accidental disabling of all authentication methods simultaneously.
|
||||||
- User Permissions options
|
- **User Permissions Options:**
|
||||||
- Folder Only gives user their own root folder
|
- *Folder Only* gives user their own root folder.
|
||||||
- Read Only makes it so user can only read the files
|
- *Read Only* makes it so the user can only read the files.
|
||||||
- Disable upload
|
- *Disable Upload* prevents file uploads.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
261
dragAndDrop.js
261
dragAndDrop.js
@@ -1,4 +1,9 @@
|
|||||||
// dragAndDrop.js
|
// dragAndDrop.js
|
||||||
|
// This file handles drag-and-drop functionality for cards in the sidebar, header and top drop zones.
|
||||||
|
// It also manages the visibility of the sidebar and header drop zones based on the current state of the application.
|
||||||
|
// It includes functions to save and load the order of cards in the sidebar and header from localStorage.
|
||||||
|
// It also includes functions to handle the drag-and-drop events, including mouse movements and drop zones.
|
||||||
|
// It uses CSS classes to manage the appearance of the sidebar and header drop zones during drag-and-drop operations.
|
||||||
|
|
||||||
// Moves cards into the sidebar based on the saved order in localStorage.
|
// Moves cards into the sidebar based on the saved order in localStorage.
|
||||||
export function loadSidebarOrder() {
|
export function loadSidebarOrder() {
|
||||||
@@ -27,6 +32,25 @@ export function loadSidebarOrder() {
|
|||||||
updateSidebarVisibility();
|
updateSidebarVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: Load header order from localStorage.
|
||||||
|
export function loadHeaderOrder() {
|
||||||
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
|
if (!headerDropArea) return;
|
||||||
|
const orderStr = localStorage.getItem('headerOrder');
|
||||||
|
if (orderStr) {
|
||||||
|
const order = JSON.parse(orderStr);
|
||||||
|
if (order.length > 0) {
|
||||||
|
order.forEach(id => {
|
||||||
|
const card = document.getElementById(id);
|
||||||
|
// Only load if card is not already in header drop zone.
|
||||||
|
if (card && card.parentNode.id !== 'headerDropArea') {
|
||||||
|
insertCardInHeader(card, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal helper: update sidebar visibility based on its content.
|
// Internal helper: update sidebar visibility based on its content.
|
||||||
function updateSidebarVisibility() {
|
function updateSidebarVisibility() {
|
||||||
const sidebar = document.getElementById('sidebarDropArea');
|
const sidebar = document.getElementById('sidebarDropArea');
|
||||||
@@ -44,6 +68,17 @@ function updateSidebarVisibility() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: Save header order to localStorage.
|
||||||
|
function saveHeaderOrder() {
|
||||||
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
|
if (headerDropArea) {
|
||||||
|
const icons = Array.from(headerDropArea.children);
|
||||||
|
// Each header icon stores its associated card in the property cardElement.
|
||||||
|
const order = icons.map(icon => icon.cardElement.id);
|
||||||
|
localStorage.setItem('headerOrder', JSON.stringify(order));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal helper: update top zone layout (center a card if one column is empty).
|
// Internal helper: update top zone layout (center a card if one column is empty).
|
||||||
function updateTopZoneLayout() {
|
function updateTopZoneLayout() {
|
||||||
const leftCol = document.getElementById('leftCol');
|
const leftCol = document.getElementById('leftCol');
|
||||||
@@ -183,7 +218,166 @@ function ensureTopZonePlaceholder() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sets up all drag-and-drop event listeners for cards.
|
// --- NEW HELPER FUNCTIONS FOR HEADER DROP ZONE ---
|
||||||
|
|
||||||
|
// Show header drop zone and add a "drag-active" class so that the pseudo-element appears.
|
||||||
|
function showHeaderDropZone() {
|
||||||
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
|
if (headerDropArea) {
|
||||||
|
headerDropArea.style.display = 'inline-flex';
|
||||||
|
headerDropArea.classList.add('drag-active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide header drop zone by removing the "drag-active" class.
|
||||||
|
// If a header icon is present (i.e. a card was dropped), the drop zone remains visible without the dashed border.
|
||||||
|
function hideHeaderDropZone() {
|
||||||
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
|
if (headerDropArea) {
|
||||||
|
headerDropArea.classList.remove('drag-active');
|
||||||
|
if (headerDropArea.children.length === 0) {
|
||||||
|
headerDropArea.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === NEW FUNCTION: Insert card into header drop zone as a material icon ===
|
||||||
|
function insertCardInHeader(card, event) {
|
||||||
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
|
if (!headerDropArea) return;
|
||||||
|
|
||||||
|
// For folder management and upload cards, preserve the original by moving it to a hidden container.
|
||||||
|
if (card.id === 'folderManagementCard' || card.id === 'uploadCard') {
|
||||||
|
let hiddenContainer = document.getElementById('hiddenCardsContainer');
|
||||||
|
if (!hiddenContainer) {
|
||||||
|
hiddenContainer = document.createElement('div');
|
||||||
|
hiddenContainer.id = 'hiddenCardsContainer';
|
||||||
|
hiddenContainer.style.display = 'none';
|
||||||
|
document.body.appendChild(hiddenContainer);
|
||||||
|
}
|
||||||
|
// Move the original card to the hidden container if it's not already there.
|
||||||
|
if (card.parentNode.id !== 'hiddenCardsContainer') {
|
||||||
|
hiddenContainer.appendChild(card);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For other cards, simply remove from current container.
|
||||||
|
if (card.parentNode) {
|
||||||
|
card.parentNode.removeChild(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the header icon button.
|
||||||
|
const iconButton = document.createElement('button');
|
||||||
|
iconButton.className = 'header-card-icon';
|
||||||
|
// Remove default button styling.
|
||||||
|
iconButton.style.border = 'none';
|
||||||
|
iconButton.style.background = 'none';
|
||||||
|
iconButton.style.outline = 'none';
|
||||||
|
iconButton.style.cursor = 'pointer';
|
||||||
|
|
||||||
|
// Choose an icon based on the card type with 24px size.
|
||||||
|
if (card.id === 'uploadCard') {
|
||||||
|
iconButton.innerHTML = '<i class="material-icons" style="font-size:24px;">cloud_upload</i>';
|
||||||
|
} else if (card.id === 'folderManagementCard') {
|
||||||
|
iconButton.innerHTML = '<i class="material-icons" style="font-size:24px;">folder</i>';
|
||||||
|
} else {
|
||||||
|
iconButton.innerHTML = '<i class="material-icons" style="font-size:24px;">insert_drive_file</i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save a reference to the card in the icon button.
|
||||||
|
iconButton.cardElement = card;
|
||||||
|
// Associate this icon with the card for future removal.
|
||||||
|
card.headerIconButton = iconButton;
|
||||||
|
|
||||||
|
let modal = null;
|
||||||
|
let isLocked = false;
|
||||||
|
let hoverActive = false;
|
||||||
|
|
||||||
|
// showModal: When triggered, ensure the card is attached to the modal.
|
||||||
|
function showModal() {
|
||||||
|
if (!modal) {
|
||||||
|
modal = document.createElement('div');
|
||||||
|
modal.className = 'header-card-modal';
|
||||||
|
modal.style.position = 'fixed';
|
||||||
|
modal.style.top = '80px';
|
||||||
|
modal.style.right = '80px';
|
||||||
|
modal.style.zIndex = '11000';
|
||||||
|
// Render the modal but initially keep it hidden.
|
||||||
|
modal.style.display = 'block';
|
||||||
|
modal.style.visibility = 'hidden';
|
||||||
|
modal.style.opacity = '0';
|
||||||
|
modal.style.background = 'none';
|
||||||
|
modal.style.border = 'none';
|
||||||
|
modal.style.padding = '0';
|
||||||
|
modal.style.boxShadow = 'none';
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
// Attach modal hover events.
|
||||||
|
modal.addEventListener('mouseover', handleMouseOver);
|
||||||
|
modal.addEventListener('mouseout', handleMouseOut);
|
||||||
|
iconButton.modalInstance = modal;
|
||||||
|
}
|
||||||
|
// If the card isn't already in the modal, remove it from the hidden container and attach it.
|
||||||
|
if (!modal.contains(card)) {
|
||||||
|
const hiddenContainer = document.getElementById('hiddenCardsContainer');
|
||||||
|
if (hiddenContainer && hiddenContainer.contains(card)) {
|
||||||
|
hiddenContainer.removeChild(card);
|
||||||
|
}
|
||||||
|
modal.appendChild(card);
|
||||||
|
}
|
||||||
|
// Reveal the modal.
|
||||||
|
modal.style.visibility = 'visible';
|
||||||
|
modal.style.opacity = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
// hideModal: Hide the modal and return the card to the hidden container.
|
||||||
|
function hideModal() {
|
||||||
|
if (modal && !isLocked && !hoverActive) {
|
||||||
|
modal.style.visibility = 'hidden';
|
||||||
|
modal.style.opacity = '0';
|
||||||
|
// Return the card to the hidden container.
|
||||||
|
const hiddenContainer = document.getElementById('hiddenCardsContainer');
|
||||||
|
if (hiddenContainer && modal.contains(card)) {
|
||||||
|
hiddenContainer.appendChild(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseOver() {
|
||||||
|
hoverActive = true;
|
||||||
|
showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseOut() {
|
||||||
|
hoverActive = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!hoverActive && !isLocked) {
|
||||||
|
hideModal();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach hover events to the icon.
|
||||||
|
iconButton.addEventListener('mouseover', handleMouseOver);
|
||||||
|
iconButton.addEventListener('mouseout', handleMouseOut);
|
||||||
|
|
||||||
|
// Toggle the locked state on click so the modal stays open.
|
||||||
|
iconButton.addEventListener('click', (e) => {
|
||||||
|
isLocked = !isLocked;
|
||||||
|
if (isLocked) {
|
||||||
|
showModal();
|
||||||
|
} else {
|
||||||
|
hideModal();
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Append the header icon button into the header drop zone.
|
||||||
|
headerDropArea.appendChild(iconButton);
|
||||||
|
// Save the updated header order.
|
||||||
|
saveHeaderOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Main Drag and Drop Initialization ===
|
||||||
export function initDragAndDrop() {
|
export function initDragAndDrop() {
|
||||||
function run() {
|
function run() {
|
||||||
const draggableCards = document.querySelectorAll('#uploadCard, #folderManagementCard');
|
const draggableCards = document.querySelectorAll('#uploadCard, #folderManagementCard');
|
||||||
@@ -205,7 +399,7 @@ export function initDragAndDrop() {
|
|||||||
header.addEventListener('mousedown', function (e) {
|
header.addEventListener('mousedown', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const card = this.closest('.card');
|
const card = this.closest('.card');
|
||||||
// Capture the card's initial bounding rectangle once.
|
// Capture the card's initial bounding rectangle.
|
||||||
const initialRect = card.getBoundingClientRect();
|
const initialRect = card.getBoundingClientRect();
|
||||||
const originX = ((e.clientX - initialRect.left) / initialRect.width) * 100;
|
const originX = ((e.clientX - initialRect.left) / initialRect.width) * 100;
|
||||||
const originY = ((e.clientY - initialRect.top) / initialRect.height) * 100;
|
const originY = ((e.clientY - initialRect.top) / initialRect.height) * 100;
|
||||||
@@ -226,13 +420,28 @@ export function initDragAndDrop() {
|
|||||||
sidebar.style.height = '800px';
|
sidebar.style.height = '800px';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the stored initialRect rather than recalculating.
|
// Show header drop zone while dragging.
|
||||||
|
showHeaderDropZone();
|
||||||
|
|
||||||
|
// Use the stored initialRect.
|
||||||
initialLeft = initialRect.left + window.pageXOffset;
|
initialLeft = initialRect.left + window.pageXOffset;
|
||||||
initialTop = initialRect.top + window.pageYOffset;
|
initialTop = initialRect.top + window.pageYOffset;
|
||||||
offsetX = e.pageX - initialLeft;
|
offsetX = e.pageX - initialLeft;
|
||||||
offsetY = e.pageY - initialTop;
|
offsetY = e.pageY - initialTop;
|
||||||
|
|
||||||
// Append card to body and fix its dimensions to prevent shrinking.
|
// Remove any associated header icon if present.
|
||||||
|
if (card.headerIconButton) {
|
||||||
|
if (card.headerIconButton.parentNode) {
|
||||||
|
card.headerIconButton.parentNode.removeChild(card.headerIconButton);
|
||||||
|
}
|
||||||
|
if (card.headerIconButton.modalInstance && card.headerIconButton.modalInstance.parentNode) {
|
||||||
|
card.headerIconButton.modalInstance.parentNode.removeChild(card.headerIconButton.modalInstance);
|
||||||
|
}
|
||||||
|
card.headerIconButton = null;
|
||||||
|
saveHeaderOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append card to body and fix its dimensions.
|
||||||
document.body.appendChild(card);
|
document.body.appendChild(card);
|
||||||
card.style.position = 'absolute';
|
card.style.position = 'absolute';
|
||||||
card.style.left = initialLeft + 'px';
|
card.style.left = initialLeft + 'px';
|
||||||
@@ -269,8 +478,21 @@ export function initDragAndDrop() {
|
|||||||
sidebar.style.height = '';
|
sidebar.style.height = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any existing header icon if present.
|
||||||
|
if (card.headerIconButton) {
|
||||||
|
if (card.headerIconButton.parentNode) {
|
||||||
|
card.headerIconButton.parentNode.removeChild(card.headerIconButton);
|
||||||
|
}
|
||||||
|
if (card.headerIconButton.modalInstance && card.headerIconButton.modalInstance.parentNode) {
|
||||||
|
card.headerIconButton.modalInstance.parentNode.removeChild(card.headerIconButton.modalInstance);
|
||||||
|
}
|
||||||
|
card.headerIconButton = null;
|
||||||
|
saveHeaderOrder();
|
||||||
|
}
|
||||||
|
|
||||||
let droppedInSidebar = false;
|
let droppedInSidebar = false;
|
||||||
let droppedInTop = false;
|
let droppedInTop = false;
|
||||||
|
let droppedInHeader = false;
|
||||||
|
|
||||||
// Check if dropped in sidebar drop zone.
|
// Check if dropped in sidebar drop zone.
|
||||||
const sidebarElem = document.getElementById('sidebarDropArea');
|
const sidebarElem = document.getElementById('sidebarDropArea');
|
||||||
@@ -287,7 +509,7 @@ export function initDragAndDrop() {
|
|||||||
droppedInSidebar = true;
|
droppedInSidebar = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If not dropped in sidebar, check the top drop zone.
|
// Check the top drop zone.
|
||||||
const topRow = document.getElementById('uploadFolderRow');
|
const topRow = document.getElementById('uploadFolderRow');
|
||||||
if (!droppedInSidebar && topRow) {
|
if (!droppedInSidebar && topRow) {
|
||||||
const rect = topRow.getBoundingClientRect();
|
const rect = topRow.getBoundingClientRect();
|
||||||
@@ -308,21 +530,31 @@ export function initDragAndDrop() {
|
|||||||
updateTopZoneLayout();
|
updateTopZoneLayout();
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
droppedInTop = true;
|
droppedInTop = true;
|
||||||
// Use computed style to determine container's width.
|
// Set a fixed width during animation.
|
||||||
const containerWidth = parseFloat(window.getComputedStyle(container).width);
|
|
||||||
card.style.width = "363px";
|
card.style.width = "363px";
|
||||||
// Animate the card sliding in.
|
|
||||||
animateVerticalSlide(card);
|
animateVerticalSlide(card);
|
||||||
// After animation completes, clear the inline width.
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
card.style.removeProperty('width');
|
card.style.removeProperty('width');
|
||||||
}, 210);
|
}, 210);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check the header drop zone.
|
||||||
// If dropped in neither area, return card to its original container.
|
const headerDropArea = document.getElementById('headerDropArea');
|
||||||
if (!droppedInSidebar && !droppedInTop) {
|
if (!droppedInSidebar && !droppedInTop && headerDropArea) {
|
||||||
|
const rect = headerDropArea.getBoundingClientRect();
|
||||||
|
if (
|
||||||
|
e.clientX >= rect.left &&
|
||||||
|
e.clientX <= rect.right &&
|
||||||
|
e.clientY >= rect.top &&
|
||||||
|
e.clientY <= rect.bottom
|
||||||
|
) {
|
||||||
|
insertCardInHeader(card, e);
|
||||||
|
droppedInHeader = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If card was not dropped in any zone, return it to its original container.
|
||||||
|
if (!droppedInSidebar && !droppedInTop && !droppedInHeader) {
|
||||||
const orig = document.getElementById(card.dataset.originalContainerId);
|
const orig = document.getElementById(card.dataset.originalContainerId);
|
||||||
if (orig) {
|
if (orig) {
|
||||||
orig.appendChild(card);
|
orig.appendChild(card);
|
||||||
@@ -330,7 +562,7 @@ export function initDragAndDrop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear inline styles from dragging.
|
// Clear inline drag-related styles.
|
||||||
[
|
[
|
||||||
'position',
|
'position',
|
||||||
'left',
|
'left',
|
||||||
@@ -351,6 +583,9 @@ export function initDragAndDrop() {
|
|||||||
|
|
||||||
updateTopZoneLayout();
|
updateTopZoneLayout();
|
||||||
updateSidebarVisibility();
|
updateSidebarVisibility();
|
||||||
|
|
||||||
|
// Hide header drop zone if no icon is present.
|
||||||
|
hideHeaderDropZone();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -95,6 +95,9 @@
|
|||||||
<h1>FileRise</h1>
|
<h1>FileRise</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
|
<div class="header-buttons-wrapper" style="display: flex; align-items: center; gap: 10px;">
|
||||||
|
<!-- Your header drop zone -->
|
||||||
|
<div id="headerDropArea" class="header-drop-zone"></div>
|
||||||
<div class="header-buttons">
|
<div class="header-buttons">
|
||||||
<button id="logoutBtn" title="Logout">
|
<button id="logoutBtn" title="Logout">
|
||||||
<i class="material-icons">exit_to_app</i>
|
<i class="material-icons">exit_to_app</i>
|
||||||
@@ -132,10 +135,12 @@
|
|||||||
<button id="darkModeToggle" class="dark-mode-toggle">Dark Mode</button>
|
<button id="darkModeToggle" class="dark-mode-toggle">Dark Mode</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Custom Toast Container -->
|
<!-- Custom Toast Container -->
|
||||||
<div id="customToast"></div>
|
<div id="customToast"></div>
|
||||||
|
<div id="hiddenCardsContainer" style="display:none;"></div>
|
||||||
|
|
||||||
<!-- Main Wrapper: Hidden by default; remove "display: none;" after login -->
|
<!-- Main Wrapper: Hidden by default; remove "display: none;" after login -->
|
||||||
<div class="main-wrapper">
|
<div class="main-wrapper">
|
||||||
@@ -390,7 +395,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module" src="main.js"></script>
|
<script type="module" src="main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
4
main.js
4
main.js
@@ -17,7 +17,7 @@ import { loadFolderTree } from './folderManager.js';
|
|||||||
import { initUpload } from './upload.js';
|
import { initUpload } from './upload.js';
|
||||||
import { initAuth, checkAuthentication } from './auth.js';
|
import { initAuth, checkAuthentication } from './auth.js';
|
||||||
import { setupTrashRestoreDelete } from './trashRestoreDelete.js';
|
import { setupTrashRestoreDelete } from './trashRestoreDelete.js';
|
||||||
import { initDragAndDrop, loadSidebarOrder } from './dragAndDrop.js'
|
import { initDragAndDrop, loadSidebarOrder, loadHeaderOrder } from './dragAndDrop.js'
|
||||||
import { initTagSearch, openTagModal, filterFilesByTag } from './fileTags.js';
|
import { initTagSearch, openTagModal, filterFilesByTag } from './fileTags.js';
|
||||||
|
|
||||||
function loadCsrfToken() {
|
function loadCsrfToken() {
|
||||||
@@ -134,10 +134,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
loadFileList(window.currentFolder);
|
loadFileList(window.currentFolder);
|
||||||
initDragAndDrop();
|
initDragAndDrop();
|
||||||
loadSidebarOrder();
|
loadSidebarOrder();
|
||||||
|
loadHeaderOrder();
|
||||||
initFileActions();
|
initFileActions();
|
||||||
initUpload();
|
initUpload();
|
||||||
loadFolderTree();
|
loadFolderTree();
|
||||||
setupTrashRestoreDelete();
|
setupTrashRestoreDelete();
|
||||||
|
|
||||||
const helpBtn = document.getElementById("folderHelpBtn");
|
const helpBtn = document.getElementById("folderHelpBtn");
|
||||||
const helpTooltip = document.getElementById("folderHelpTooltip");
|
const helpTooltip = document.getElementById("folderHelpTooltip");
|
||||||
helpBtn.addEventListener("click", function () {
|
helpBtn.addEventListener("click", function () {
|
||||||
|
|||||||
77
styles.css
77
styles.css
@@ -2023,20 +2023,17 @@ body.dark-mode .card {
|
|||||||
z-index: 6000 !important;
|
z-index: 6000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default (light mode) for admin panel content */
|
|
||||||
.admin-panel-content {
|
.admin-panel-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode overrides for admin panel content */
|
|
||||||
body.dark-mode .admin-panel-content {
|
body.dark-mode .admin-panel-content {
|
||||||
background: #2c2c2c; /* dark background */
|
background: #2c2c2c;
|
||||||
color: #e0e0e0; /* light text */
|
color: #e0e0e0;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optionally, adjust input, label, etc. for dark mode */
|
|
||||||
body.dark-mode .admin-panel-content input,
|
body.dark-mode .admin-panel-content input,
|
||||||
body.dark-mode .admin-panel-content select,
|
body.dark-mode .admin-panel-content select,
|
||||||
body.dark-mode .admin-panel-content textarea {
|
body.dark-mode .admin-panel-content textarea {
|
||||||
@@ -2067,3 +2064,73 @@ body.dark-mode .admin-panel-content label {
|
|||||||
.spinning {
|
.spinning {
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rise-effect {
|
||||||
|
transform: translateY(-20px);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-modal-btn,
|
||||||
|
.collapse-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #616161;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-modal-btn:hover,
|
||||||
|
.collapse-btn:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-modal-btn:focus,
|
||||||
|
.collapse-btn:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-drop-zone {
|
||||||
|
width: 66px;
|
||||||
|
height: 36px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-drop-zone.drag-active {
|
||||||
|
border: 2px dashed #1565C0;
|
||||||
|
background-color: #eef;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body.dark-mode .header-drop-zone.drag-active {
|
||||||
|
background-color: #333;
|
||||||
|
border: 2px dashed #555;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-drop-zone.drag-active:empty::before {
|
||||||
|
content: "Drop";
|
||||||
|
font-size: 10px;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user