diff --git a/README.md b/README.md
index 318f86f..10ec74e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ https://github.com/user-attachments/assets/179e6940-5798-4482-9a69-696f806c37de
changelogs available here:
-FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web application for uploading, syntax highlight editing, drag & drop and managing files. Built with an Apache/PHP backend and a modern JavaScript (ES6 modules) frontend, it offers a responsive, dynamic file management interface. It serves as an alternative to solutions like FileGator TinyFileManager or ProjectSend, providing an easy-to-setup experience ideal for document management, image galleries, firmware file hosting, and more.
+FileRise is a lightweight, secure, self-hosted web application for uploading, syntax-highlight editing, drag & drop file management, and more. Built with an Apache/PHP backend and a modern JavaScript (ES6 modules) frontend, it offers a responsive and dynamic interface designed to simplify file handling. As an alternative to solutions like FileGator, TinyFileManager, or ProjectSend, FileRise provides an easy-to-set-up experience ideal for document management, image galleries, firmware hosting, and other file-intensive applications.
---
@@ -49,7 +49,7 @@ FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web ap
- **Move Files:** Move selected files to a different folder, automatically generating a unique filename if needed to avoid data loss.
- **Download Files as ZIP:** Download selected files as a ZIP archive. Users can specify a custom name for the ZIP file via a modal dialog.
- **Extract Zip:** When one or more ZIP files are selected, users can extract the archive(s) directly into the current folder.
- - **Drag & Drop:** Easily move files by selecting them from the file list and simply dragging them onto your desired folder in the folder tree or breadcrumb. When you drop the files onto a folder, the system automatically moves them, updating your file organization in one seamless action.
+ - **Drag & Drop (File Movement):** Easily move files by selecting them from the file list and dragging them onto your desired folder in the folder tree or breadcrumb. When you drop the files onto a folder, the system automatically moves them, updating your file organization in one seamless action.
- **Enhanced Context Menu & Keyboard Shortcuts:**
- **Right-Click Context Menu:**
- A custom context menu appears on right-clicking within the file list.
@@ -126,6 +126,16 @@ FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web ap
- The trash modal displays details such as file name, uploader/deleter, and the trashed date/time.
- Material icons with tooltips visually represent the restore and delete actions.
+- **Drag & Drop Cards with Dedicated Drop Zones:**
+ - **Sidebar Drop Zone:**
+ - Cards (such as the upload card or folder management card) can be dragged into a dedicated sidebar drop zone for quick access to frequently used operations.
+ - The sidebar drop zone expands dynamically to accept drops anywhere within its visual area.
+ - **Top Bar Drop Zone:**
+ - 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.
+ - **Seamless Interaction:**
+ - Both drop zones support smooth drag and drop interactions with animations and pointer event adjustments to prevent interference, ensuring that cards can be dropped reliably regardless of screen position.
+
---
## Screenshots
@@ -214,6 +224,12 @@ For users who prefer containerization, a Docker image is available
docker pull error311/filerise-docker:latest
```
+ macos M series:
+
+ ```bash
+ docker pull --platform linux/x86_64 error311/filerise-docker:latest
+ ```
+
2. **Run the Container:**
```bash
diff --git a/dragAndDrop.js b/dragAndDrop.js
index a4574b5..2d5c11d 100644
--- a/dragAndDrop.js
+++ b/dragAndDrop.js
@@ -2,354 +2,363 @@
// 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);
- }
- });
+ 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';
}
- }
- 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);
+ // 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();
- 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);
- }
+ 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 {
- const placeholder = topZone.querySelector('.placeholder');
- if (placeholder) placeholder.remove();
+ 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';
}
}
-
- // 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();
+}
+
+// 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.
+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');
+ // Disable pointer events on the card so it doesn't block drop detection.
+ card.style.pointerEvents = 'none';
+ addTopZoneHighlight();
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
- sidebar.classList.remove('highlight');
+ sidebar.classList.add('active');
+ sidebar.style.display = 'block';
+ sidebar.classList.add('highlight');
+ // Force the sidebar to have a tall drop zone while dragging.
+ sidebar.style.height = '800px';
}
-
- 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 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';
+ }, 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;
+ // Re-enable pointer events on the card.
+ card.style.pointerEvents = '';
+ card.classList.remove('dragging');
+ removeTopZoneHighlight();
+ const sidebar = document.getElementById('sidebarDropArea');
+ if (sidebar) {
+ sidebar.classList.remove('highlight');
+ // Remove the forced height once the drag ends.
+ sidebar.style.height = '';
+ }
+
+ const leftCol = document.getElementById('leftCol');
+ const rightCol = document.getElementById('rightCol');
+ let droppedInSidebar = false;
+ let droppedInTop = false;
+
+ const sidebarElem = document.getElementById('sidebarDropArea');
+ if (sidebarElem) {
+ // Instead of using elementsFromPoint, use a virtual drop zone.
+ const rect = sidebarElem.getBoundingClientRect();
+ // Define a drop zone from the top of the sidebar to 1000px below its top.
+ const dropZoneBottom = rect.top + 800;
+ if (
+ e.clientX >= rect.left &&
+ e.clientX <= rect.right &&
+ e.clientY >= rect.top &&
+ e.clientY <= dropZoneBottom
+ ) {
+ 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';
+
+ // 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 = '';
+ card.style.width = '';
+ }, 310);
}
}
-
- 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) {
+ }
+
+ 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 = '';
- } 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 = '';
- }
+ card.style.width = '';
}
- updateTopZoneLayout();
- updateSidebarVisibility();
}
- });
+ updateTopZoneLayout();
+ updateSidebarVisibility();
+ }
});
- }
-
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', run);
- } else {
- run();
- }
- }
\ No newline at end of file
+ });
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', run);
+ } else {
+ run();
+ }
+}
\ No newline at end of file
diff --git a/folderManager.js b/folderManager.js
index 45ad395..f357c56 100644
--- a/folderManager.js
+++ b/folderManager.js
@@ -63,8 +63,6 @@ function getParentFolder(folder) {
// Breadcrumb Functions
// ----------------------
// Render breadcrumb for a normalized folder path.
-// For example, if window.currentFolder is "Folder1/Folder1SubFolder2",
-// this will return: Root / Folder1 / Folder1SubFolder2.
function renderBreadcrumb(normalizedFolder) {
if (normalizedFolder === "root") {
return `Root`;
@@ -307,16 +305,10 @@ export async function loadFolderTree(selectedFolder) {
}
let html = `
- [-]
- (Root)
-
`;
- if (folders.length === 0) {
- html += ``;
- } else {
+ [-]
+ (Root)
+ `;
+ if (folders.length > 0) {
const tree = buildFolderTree(folders);
html += renderFolderTree(tree, "", "block");
}
@@ -730,14 +722,14 @@ document.addEventListener("click", function () {
hideFolderManagerContextMenu();
});
-document.addEventListener("DOMContentLoaded", function() {
- document.addEventListener("keydown", function(e) {
+document.addEventListener("DOMContentLoaded", function () {
+ document.addEventListener("keydown", function (e) {
// Skip if the user is typing in an input, textarea, or contentEditable element.
const tag = e.target.tagName.toLowerCase();
if (tag === "input" || tag === "textarea" || e.target.isContentEditable) {
return;
}
-
+
// On macOS, "Delete" is typically reported as "Backspace" (keyCode 8)
if (e.key === "Delete" || e.key === "Backspace" || e.keyCode === 46 || e.keyCode === 8) {
// Ensure a folder is selected and it isn't the root folder.