Merge pull request #309 from EdouardVanbelle/refactor/keep-badge-order
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
padding: 6px;
|
||||
min-width: 200px;
|
||||
z-index: 2000;
|
||||
display: none;
|
||||
display: block;
|
||||
animation: contextMenuIn 0.15s ease-out;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay-light);
|
||||
display: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 3000;
|
||||
@@ -106,7 +106,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay-light);
|
||||
display: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 3000;
|
||||
@@ -205,7 +205,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay-light);
|
||||
display: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 3000;
|
||||
@@ -213,10 +213,6 @@
|
||||
animation: modalFadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
.shared-dialog.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.shared-dialog-content {
|
||||
background-color: var(--color-bg-surface);
|
||||
border-radius: 16px;
|
||||
@@ -512,7 +508,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay-light);
|
||||
display: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 4000;
|
||||
@@ -593,16 +589,12 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay);
|
||||
display: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.dialog.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
background-color: var(--color-bg-surface);
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* About Modal */
|
||||
.about-modal-overlay {
|
||||
display: none;
|
||||
display: flex;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--color-overlay);
|
||||
@@ -10,10 +10,6 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.about-modal-overlay.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.about-modal {
|
||||
background: var(--color-bg-surface);
|
||||
border-radius: 20px;
|
||||
@@ -130,7 +126,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--color-overlay);
|
||||
display: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 3000;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
color: var(--color-text-medium);
|
||||
display: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropzone-icon {
|
||||
@@ -53,7 +53,7 @@
|
||||
.upload-progress {
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
display: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.upload-dropdown-menu {
|
||||
display: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
left: 0;
|
||||
@@ -25,10 +25,6 @@
|
||||
animation: dropdownFadeIn 0.15s ease-out;
|
||||
}
|
||||
|
||||
.upload-dropdown-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes dropdownFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
||||
+3
-3
@@ -216,12 +216,12 @@
|
||||
<span class="breadcrumb-item" data-i18n="breadcrumb.home">Home</span>
|
||||
</div>
|
||||
|
||||
<div class="dropzone" id="dropzone">
|
||||
<div class="dropzone hidden" id="dropzone">
|
||||
<i class="fas fa-cloud-upload-alt dropzone-icon"></i>
|
||||
<p data-i18n="dropzone.drag_files">Drag files here or click to select</p>
|
||||
<input type="file" id="file-input" class="hidden" multiple>
|
||||
<input type="file" id="folder-input" class="hidden" webkitdirectory directory multiple>
|
||||
<div class="upload-progress">
|
||||
<div class="upload-progress hidden">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill"></div>
|
||||
</div>
|
||||
@@ -266,7 +266,7 @@
|
||||
</div>
|
||||
|
||||
<!-- About Modal -->
|
||||
<div class="about-modal-overlay" id="about-modal-overlay">
|
||||
<div class="about-modal-overlay hidden" id="about-modal-overlay">
|
||||
<div class="about-modal">
|
||||
<div class="about-modal-logo">
|
||||
<i class="fas fa-cloud"></i>
|
||||
|
||||
+12
-13
@@ -89,7 +89,7 @@ const ACTIONS_BAR_TEMPLATES = {
|
||||
<span data-i18n="actions.upload">Upload</span>
|
||||
<i class="fas fa-caret-down icon-ml"></i>
|
||||
</button>
|
||||
<div class="upload-dropdown-menu" id="upload-dropdown-menu">
|
||||
<div class="upload-dropdown-menu hidden" id="upload-dropdown-menu">
|
||||
<button class="upload-dropdown-item" id="upload-files-btn">
|
||||
<i class="fas fa-file"></i>
|
||||
<span data-i18n="actions.upload_files">Upload files</span>
|
||||
@@ -187,14 +187,14 @@ function setupActionsBarDelegation() {
|
||||
case 'upload-files-btn': {
|
||||
e.stopPropagation();
|
||||
const menu = document.getElementById('upload-dropdown-menu');
|
||||
if (menu) menu.classList.remove('show');
|
||||
if (menu) menu.classList.add('hidden');
|
||||
if (elements.fileInput) elements.fileInput.click();
|
||||
break;
|
||||
}
|
||||
case 'upload-folder-btn': {
|
||||
e.stopPropagation();
|
||||
const menu = document.getElementById('upload-dropdown-menu');
|
||||
if (menu) menu.classList.remove('show');
|
||||
if (menu) menu.classList.add('hidden');
|
||||
const folderInput = document.getElementById('folder-input');
|
||||
if (folderInput) folderInput.click();
|
||||
break;
|
||||
@@ -456,13 +456,13 @@ function setupUploadDropdown() {
|
||||
'click',
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
const isOpen = menu.classList.contains('show');
|
||||
const isOpen = !menu.classList.contains('hidden');
|
||||
// Close any other open dropdowns
|
||||
document.querySelectorAll('.upload-dropdown-menu.show').forEach((m) => {
|
||||
m.classList.remove('show');
|
||||
document.querySelectorAll('.upload-dropdown-menu').forEach((m) => {
|
||||
m.classList.add('hidden');
|
||||
});
|
||||
if (!isOpen) {
|
||||
menu.classList.add('show');
|
||||
menu.classList.remove('hidden');
|
||||
}
|
||||
},
|
||||
{ signal }
|
||||
@@ -475,8 +475,8 @@ function setupUploadDropdown() {
|
||||
}
|
||||
uploadDropdownDocumentClickHandler = (e) => {
|
||||
if (e.target.closest('#upload-dropdown')) return;
|
||||
document.querySelectorAll('.upload-dropdown-menu.show').forEach((m) => {
|
||||
m.classList.remove('show');
|
||||
document.querySelectorAll('.upload-dropdown-menu').forEach((m) => {
|
||||
m.classList.add('hidden');
|
||||
});
|
||||
};
|
||||
document.addEventListener('click', uploadDropdownDocumentClickHandler);
|
||||
@@ -663,13 +663,12 @@ function setupEventListeners() {
|
||||
// Global events to close context menus and deselect cards
|
||||
document.addEventListener('click', (e) => {
|
||||
const folderMenu = document.getElementById('folder-context-menu');
|
||||
const fileMenu = document.getElementById('file-context-menu');
|
||||
|
||||
if (folderMenu && folderMenu.style.display === 'block' && !folderMenu.contains(e.target)) {
|
||||
if (folderMenu && !folderMenu.classList.contains('hidden') && !folderMenu.contains(e.target)) {
|
||||
ui.closeContextMenu();
|
||||
}
|
||||
|
||||
if (fileMenu && fileMenu.style.display === 'block' && !fileMenu.contains(e.target)) {
|
||||
const fileMenu = document.getElementById('file-context-menu');
|
||||
if (fileMenu && !fileMenu.classList.contains('hidden') && !fileMenu.contains(e.target)) {
|
||||
ui.closeFileContextMenu();
|
||||
}
|
||||
});
|
||||
|
||||
+93
-52
@@ -7,7 +7,7 @@
|
||||
|
||||
import { escapeHtml, formatDateTime, formatFileSize } from '../core/formatters.js';
|
||||
import { i18n } from '../core/i18n.js';
|
||||
import { OxiIcons, replaceIconsInElement } from '../core/icons.js';
|
||||
import { OxiIcons } from '../core/icons.js';
|
||||
import { contextMenus } from '../features/files/contextMenus.js';
|
||||
import { fileOps } from '../features/files/fileOperations.js';
|
||||
import { inlineViewer } from '../features/files/inlineViewer.js';
|
||||
@@ -38,7 +38,7 @@ const ui = {
|
||||
// Folder context menu
|
||||
if (!document.getElementById('folder-context-menu')) {
|
||||
const folderMenu = document.createElement('div');
|
||||
folderMenu.className = 'context-menu';
|
||||
folderMenu.classList.add('context-menu', 'hidden');
|
||||
folderMenu.id = 'folder-context-menu';
|
||||
folderMenu.innerHTML = `
|
||||
<div class="context-menu-item" id="download-folder-option">
|
||||
@@ -68,7 +68,7 @@ const ui = {
|
||||
// File context menu
|
||||
if (!document.getElementById('file-context-menu')) {
|
||||
const fileMenu = document.createElement('div');
|
||||
fileMenu.className = 'context-menu';
|
||||
fileMenu.classList.add('context-menu', 'hidden');
|
||||
fileMenu.id = 'file-context-menu';
|
||||
fileMenu.innerHTML = `
|
||||
<div class="context-menu-item" id="view-file-option">
|
||||
@@ -111,7 +111,7 @@ const ui = {
|
||||
// Rename dialog — modern
|
||||
if (!document.getElementById('rename-dialog')) {
|
||||
const renameDialog = document.createElement('div');
|
||||
renameDialog.className = 'rename-dialog';
|
||||
renameDialog.classList.add('rename-dialog', 'hidden');
|
||||
renameDialog.id = 'rename-dialog';
|
||||
renameDialog.innerHTML = `
|
||||
<div class="rename-dialog-content">
|
||||
@@ -134,7 +134,7 @@ const ui = {
|
||||
// Move dialog — modern with navigation
|
||||
if (!document.getElementById('move-file-dialog')) {
|
||||
const moveDialog = document.createElement('div');
|
||||
moveDialog.className = 'rename-dialog';
|
||||
moveDialog.classList.add('rename-dialog', 'hidden');
|
||||
moveDialog.id = 'move-file-dialog';
|
||||
moveDialog.innerHTML = `
|
||||
<div class="rename-dialog-content">
|
||||
@@ -161,7 +161,7 @@ const ui = {
|
||||
// Share dialog
|
||||
if (!document.getElementById('share-dialog')) {
|
||||
const shareDialog = document.createElement('div');
|
||||
shareDialog.className = 'share-dialog';
|
||||
shareDialog.classList.add('share-dialog', 'hidden');
|
||||
shareDialog.id = 'share-dialog';
|
||||
shareDialog.innerHTML = `
|
||||
<div class="share-dialog-content">
|
||||
@@ -208,6 +208,7 @@ const ui = {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-small" id="share-confirm-btn" data-i18n="actions.share">Share</button>
|
||||
</div>
|
||||
|
||||
<div id="new-share-section" class="share-section hidden">
|
||||
@@ -226,15 +227,15 @@ const ui = {
|
||||
</div>
|
||||
|
||||
<div class="share-dialog-buttons">
|
||||
<button class="btn btn-secondary" id="share-cancel-btn" data-i18n="actions.cancel">Cancel</button>
|
||||
<button class="btn btn-primary" id="share-confirm-btn" data-i18n="actions.share">Share</button>
|
||||
<button class="btn btn-secondary" id="share-close-btn" data-i18n="actions.close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
i18n.translateElement(shareDialog);
|
||||
document.body.appendChild(shareDialog);
|
||||
|
||||
// Add event listeners for share dialog
|
||||
document.getElementById('share-cancel-btn').addEventListener('click', () => {
|
||||
document.getElementById('share-close-btn').addEventListener('click', () => {
|
||||
contextMenus.closeShareDialog();
|
||||
});
|
||||
|
||||
@@ -251,12 +252,26 @@ const ui = {
|
||||
const shareUrl = document.getElementById('generated-share-url').value;
|
||||
contextMenus.showEmailNotificationDialog(shareUrl);
|
||||
});
|
||||
|
||||
// FIXME make generic function (close all dialog / etc)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const dialog = document.getElementById('share-dialog');
|
||||
if (e.key === 'Escape' && !dialog?.classList.contains('hidden')) {
|
||||
contextMenus.closeShareDialog();
|
||||
}
|
||||
});
|
||||
|
||||
shareDialog.addEventListener('click', (e) => {
|
||||
if (e.target === shareDialog) {
|
||||
contextMenus.closeShareDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Notification dialog
|
||||
if (!document.getElementById('notification-dialog')) {
|
||||
const notificationDialog = document.createElement('div');
|
||||
notificationDialog.className = 'share-dialog';
|
||||
notificationDialog.classList.add('share-dialog', 'hidden');
|
||||
notificationDialog.id = 'notification-dialog';
|
||||
notificationDialog.innerHTML = `
|
||||
<div class="share-dialog-content">
|
||||
@@ -298,7 +313,7 @@ const ui = {
|
||||
// Playlist selection dialog
|
||||
if (!document.getElementById('playlist-dialog')) {
|
||||
const playlistDialog = document.createElement('div');
|
||||
playlistDialog.className = 'share-dialog';
|
||||
playlistDialog.classList.add('share-dialog', 'hidden');
|
||||
playlistDialog.id = 'playlist-dialog';
|
||||
playlistDialog.innerHTML = `
|
||||
<div class="share-dialog-content">
|
||||
@@ -417,7 +432,7 @@ const ui = {
|
||||
fileOps.uploadFiles(droppedEntries.map((x) => x.file));
|
||||
}
|
||||
setTimeout(() => {
|
||||
dropzone.style.display = 'none';
|
||||
dropzone?.classList.add('hidden');
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
@@ -431,7 +446,7 @@ const ui = {
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
dropzone.style.display = 'none';
|
||||
dropzone?.classList.add('hidden');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
@@ -439,8 +454,8 @@ const ui = {
|
||||
document.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer.types.includes('Files')) {
|
||||
dropzone.style.display = 'block';
|
||||
dropzone.classList.add('active');
|
||||
dropzone?.classList.remove('hidden');
|
||||
dropzone?.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -449,7 +464,7 @@ const ui = {
|
||||
dropzone.classList.remove('active');
|
||||
setTimeout(() => {
|
||||
if (!dropzone.classList.contains('active')) {
|
||||
dropzone.style.display = 'none';
|
||||
dropzone?.classList.add('hidden');
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
@@ -473,7 +488,7 @@ const ui = {
|
||||
fileOps.uploadFiles(droppedEntries.map((x) => x.file));
|
||||
}
|
||||
setTimeout(() => {
|
||||
dropzone.style.display = 'none';
|
||||
dropzone?.classList.add('hidden');
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
@@ -488,7 +503,7 @@ const ui = {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
dropzone.style.display = 'none';
|
||||
dropzone?.classList.add('hidden');
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
@@ -667,7 +682,7 @@ const ui = {
|
||||
closeContextMenu() {
|
||||
const menu = document.getElementById('folder-context-menu');
|
||||
if (menu) {
|
||||
menu.style.display = 'none';
|
||||
menu.classList.add('hidden');
|
||||
app.contextMenuTargetFolder = null;
|
||||
}
|
||||
},
|
||||
@@ -678,7 +693,7 @@ const ui = {
|
||||
closeFileContextMenu() {
|
||||
const menu = document.getElementById('file-context-menu');
|
||||
if (menu) {
|
||||
menu.style.display = 'none';
|
||||
menu.classList.add('hidden');
|
||||
app.contextMenuTargetFile = null;
|
||||
}
|
||||
},
|
||||
@@ -994,7 +1009,7 @@ const ui = {
|
||||
}
|
||||
menu.style.left = `${e.pageX}px`;
|
||||
menu.style.top = `${e.pageY}px`;
|
||||
menu.style.display = 'block';
|
||||
menu?.classList.remove('hidden');
|
||||
});
|
||||
|
||||
// dragstart
|
||||
@@ -1119,18 +1134,19 @@ const ui = {
|
||||
* ================================================================ */
|
||||
_bindStarClick(el) {
|
||||
const star = el.querySelector('.favorite-star');
|
||||
if (!star) return;
|
||||
|
||||
star.addEventListener('click', (e) => {
|
||||
star?.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!favorites) return;
|
||||
|
||||
const itemId = star.dataset.itemId;
|
||||
const itemType = star.dataset.itemType;
|
||||
const itemName = star.dataset.itemName;
|
||||
// FIXME: make a function
|
||||
const itemElement = shared?.closest('.file-item');
|
||||
|
||||
const itemId = itemElement.dataset.fileId ? itemElement.dataset.fileId : itemElement.dataset.folderId;
|
||||
const itemType = itemElement.dataset.fileId ? 'file' : 'folder';
|
||||
const itemName = itemElement.dataset.fileId ? itemElement.dataset.fileName : itemElement.dataset.folderName;
|
||||
|
||||
const isActive = star.classList.contains('active');
|
||||
|
||||
@@ -1147,6 +1163,30 @@ const ui = {
|
||||
contextMenus.syncFavoriteOptionLabels();
|
||||
}
|
||||
});
|
||||
|
||||
const shared = el.querySelector('.file-badge-shared');
|
||||
shared?.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// FIXME: make a function
|
||||
const itemElement = shared?.closest('.file-item');
|
||||
|
||||
const itemId = itemElement.dataset.fileId ? itemElement.dataset.fileId : itemElement.dataset.folderId;
|
||||
const itemType = itemElement.dataset.fileId ? 'file' : 'folder';
|
||||
const itemName = itemElement.dataset.fileId ? itemElement.dataset.fileName : itemElement.dataset.folderName;
|
||||
|
||||
// TODO corrently dirty
|
||||
const item = {
|
||||
id: itemId,
|
||||
item_id: itemId,
|
||||
item_type: itemType,
|
||||
item_name: itemName
|
||||
};
|
||||
|
||||
contextMenus.showShareDialog(item, itemType);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1155,9 +1195,10 @@ const ui = {
|
||||
setFavoriteVisualState(itemId, itemType, isFavorite) {
|
||||
const selector = itemType === 'folder' ? `#files-list .file-item[data-folder-id="${itemId}"]` : `#files-list .file-item[data-file-id="${itemId}"]`;
|
||||
|
||||
const card = document.querySelector(selector);
|
||||
const starBtn = card ? card.querySelector('.favorite-star') : null;
|
||||
const item = document.querySelector(selector);
|
||||
const starBtn = item?.querySelector('.favorite-star');
|
||||
|
||||
// chzn
|
||||
if (starBtn) {
|
||||
starBtn.classList.toggle('active', !!isFavorite);
|
||||
|
||||
@@ -1180,20 +1221,21 @@ const ui = {
|
||||
}
|
||||
}
|
||||
|
||||
const listItem = document.querySelector(selector);
|
||||
if (listItem) {
|
||||
const nameCell = listItem.querySelector('.name-cell');
|
||||
if (nameCell) {
|
||||
let inlineStar = nameCell.querySelector('.favorite-star-inline');
|
||||
if (isFavorite && !inlineStar) {
|
||||
inlineStar = document.createElement('i');
|
||||
inlineStar.className = 'fas fa-star favorite-star-inline';
|
||||
nameCell.appendChild(inlineStar);
|
||||
replaceIconsInElement(nameCell);
|
||||
} else if (!isFavorite && inlineStar) {
|
||||
inlineStar.remove();
|
||||
}
|
||||
}
|
||||
// toggle favorite's badge
|
||||
if (item) {
|
||||
const badgeFavorite = item.querySelector('.file-badge-favorite');
|
||||
badgeFavorite?.classList.toggle('hidden', !isFavorite);
|
||||
}
|
||||
},
|
||||
|
||||
setSharedVisualState(itemId, itemType, isShared) {
|
||||
console.log(`setSharedVisual call for ${itemId} ${itemType} to ${isShared}`);
|
||||
const selector = itemType === 'folder' ? `#files-list .file-item[data-folder-id="${itemId}"]` : `#files-list .file-item[data-file-id="${itemId}"]`;
|
||||
// toggle favorite's badge
|
||||
const item = document.querySelector(selector);
|
||||
if (item) {
|
||||
const badgeShared = item.querySelector('.file-badge-shared');
|
||||
badgeShared?.classList.toggle('hidden', !isShared);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1220,14 +1262,14 @@ const ui = {
|
||||
<i class="fas fa-folder"></i>
|
||||
</div>
|
||||
<span>${escapeHtml(folder.name)}</span>
|
||||
${isFav ? '<i class="fas fa-star favorite-star-inline"></i>' : ''}
|
||||
${isShared ? '<div class="file-badge-shared"><i class="fas fa-share-alt"></i></div>' : ''}
|
||||
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-share-alt"></i></div>
|
||||
</div>
|
||||
<div class="type-cell">${i18n ? i18n.t('files.file_types.folder') : 'Folder'}</div>
|
||||
<div class="size-cell">--</div>
|
||||
<div class="date-cell">${formattedDate}</div>
|
||||
<div class="action-cell">
|
||||
<button class="favorite-star${isFav ? ' active' : ''}" data-item-id="${folder.id}" data-item-type="folder" data-item-name="${escapeHtml(folder.name)}">
|
||||
<button class="favorite-star${isFav ? ' active' : ''}">
|
||||
<i class="${isFav ? 'fas' : 'far'} fa-star"></i>
|
||||
</button>
|
||||
<button class="file-actions"><i class="fas fa-ellipsis-v"></i></button>
|
||||
@@ -1268,15 +1310,14 @@ const ui = {
|
||||
<i class="${iconClass}"></i>
|
||||
</div>
|
||||
<span>${escapeHtml(file.name)}</span>
|
||||
${isFav ? '<i class="fas fa-star favorite-star-inline"></i>' : ''}
|
||||
${isShared ? '<div class="file-badge-shared"><i class="fas fa-share-alt"></i></div>' : ''}
|
||||
|
||||
<div class="file-badge file-badge-favorite ${isFav ? '' : 'hidden'}"><i class="fas fa-star favorite-star-inline"></i></div>
|
||||
<div class="file-badge file-badge-shared ${isShared ? '' : 'hidden'}"><i class="fas fa-share-alt"></i></div>
|
||||
</div>
|
||||
<div class="type-cell">${typeLabel}</div>
|
||||
<div class="size-cell">${fileSize}</div>
|
||||
<div class="date-cell">${formattedDate}</div>
|
||||
<div class="action-cell">
|
||||
<button class="favorite-star${isFav ? ' active' : ''}" data-item-id="${file.id}" data-item-type="file" data-item-name="${escapeHtml(file.name)}">
|
||||
<button class="favorite-star${isFav ? ' active' : ''}">
|
||||
<i class="${isFav ? 'fas' : 'far'} fa-star"></i>
|
||||
</button>
|
||||
<button class="file-actions"><i class="fas fa-ellipsis-v"></i></button>
|
||||
@@ -1434,7 +1475,7 @@ function toggleCardSelection(card, event) {
|
||||
function showContextMenuAtElement(triggerElement, menuId) {
|
||||
// Hide any open menus first
|
||||
document.querySelectorAll('.context-menu').forEach((m) => {
|
||||
m.style.display = 'none';
|
||||
m.classList.add('hidden');
|
||||
});
|
||||
|
||||
const menu = document.getElementById(menuId);
|
||||
@@ -1465,7 +1506,7 @@ function showContextMenuAtElement(triggerElement, menuId) {
|
||||
|
||||
menu.style.left = `${left}px`;
|
||||
menu.style.top = `${top}px`;
|
||||
menu.style.display = 'block';
|
||||
menu.classList.remove('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,56 +7,34 @@ import { notifications } from '../core/notifications.js';
|
||||
|
||||
const uiNotifications = {
|
||||
show(title, message) {
|
||||
if (notifications && typeof notifications.addNotification === 'function') {
|
||||
const normalizedTitle = String(title || '').toLowerCase();
|
||||
let icon = 'fa-info-circle';
|
||||
let iconClass = 'upload';
|
||||
const normalizedTitle = String(title || '').toLowerCase();
|
||||
let icon = 'fa-info-circle';
|
||||
let iconClass = 'upload';
|
||||
|
||||
if (normalizedTitle.includes('error') || normalizedTitle.includes('failed') || normalizedTitle.includes('fail')) {
|
||||
icon = 'fa-exclamation-circle';
|
||||
iconClass = 'error';
|
||||
} else if (normalizedTitle.includes('favorite') || normalizedTitle.includes('favorit') || normalizedTitle.includes('fav')) {
|
||||
icon = 'fa-star';
|
||||
iconClass = 'success';
|
||||
} else if (
|
||||
normalizedTitle.includes('delete') ||
|
||||
normalizedTitle.includes('removed') ||
|
||||
normalizedTitle.includes('trash') ||
|
||||
normalizedTitle.includes('rename') ||
|
||||
normalizedTitle.includes('complete')
|
||||
) {
|
||||
icon = 'fa-check-circle';
|
||||
iconClass = 'success';
|
||||
}
|
||||
|
||||
notifications.addNotification({
|
||||
icon,
|
||||
iconClass,
|
||||
title: title || '',
|
||||
text: message || ''
|
||||
});
|
||||
return;
|
||||
if (normalizedTitle.includes('error') || normalizedTitle.includes('failed') || normalizedTitle.includes('fail')) {
|
||||
icon = 'fa-exclamation-circle';
|
||||
iconClass = 'error';
|
||||
} else if (normalizedTitle.includes('favorite') || normalizedTitle.includes('favorit') || normalizedTitle.includes('fav')) {
|
||||
icon = 'fa-star';
|
||||
iconClass = 'success';
|
||||
} else if (
|
||||
normalizedTitle.includes('delete') ||
|
||||
normalizedTitle.includes('removed') ||
|
||||
normalizedTitle.includes('trash') ||
|
||||
normalizedTitle.includes('rename') ||
|
||||
normalizedTitle.includes('complete')
|
||||
) {
|
||||
icon = 'fa-check-circle';
|
||||
iconClass = 'success';
|
||||
}
|
||||
|
||||
let notification = document.querySelector('.notification');
|
||||
if (!notification) {
|
||||
notification = document.createElement('div');
|
||||
notification.className = 'notification';
|
||||
notification.innerHTML = `
|
||||
<div class="notification-title">${title}</div>
|
||||
<div class="notification-message">${message}</div>
|
||||
`;
|
||||
document.body.appendChild(notification);
|
||||
} else {
|
||||
notification.querySelector('.notification-title').textContent = title;
|
||||
notification.querySelector('.notification-message').textContent = message;
|
||||
}
|
||||
|
||||
notification.style.display = 'block';
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.display = 'none';
|
||||
}, 5000);
|
||||
notifications.addNotification({
|
||||
icon,
|
||||
iconClass,
|
||||
title: title || '',
|
||||
text: message || ''
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ function setupUserMenu() {
|
||||
aboutBtn.addEventListener('click', () => {
|
||||
wrapper.classList.remove('open');
|
||||
const overlay = document.getElementById('about-modal-overlay');
|
||||
if (overlay) overlay.classList.add('show');
|
||||
if (overlay) overlay.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -137,18 +137,18 @@ function setupUserMenu() {
|
||||
const aboutOverlay = document.getElementById('about-modal-overlay');
|
||||
if (aboutCloseBtn) {
|
||||
aboutCloseBtn.addEventListener('click', () => {
|
||||
aboutOverlay.classList.remove('show');
|
||||
aboutOverlay.classList.add('hidden');
|
||||
});
|
||||
}
|
||||
if (aboutOverlay) {
|
||||
aboutOverlay.addEventListener('click', (e) => {
|
||||
if (e.target === aboutOverlay) {
|
||||
aboutOverlay.classList.remove('show');
|
||||
aboutOverlay.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && aboutOverlay.classList.contains('show')) {
|
||||
aboutOverlay.classList.remove('show');
|
||||
if (e.key === 'Escape' && !aboutOverlay.classList.contains('hidden')) {
|
||||
aboutOverlay.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -218,7 +218,7 @@ function showUserProfileModal() {
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'profile-modal-overlay';
|
||||
overlay.className = 'about-modal-overlay';
|
||||
overlay.classList.add('about-modal-overlay', 'hidden');
|
||||
overlay.innerHTML = `
|
||||
<div class="about-modal about-modal-body">
|
||||
<div class="about-modal-header">
|
||||
|
||||
+11
-2
@@ -8,14 +8,23 @@ import { replaceIconsInElement } from './icons.js';
|
||||
|
||||
const Modal = {
|
||||
// Modal element references
|
||||
/** @private @type {HTMLElement | null} */
|
||||
overlay: null,
|
||||
// FIXME: unused ?
|
||||
container: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
icon: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
title: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
label: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
input: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
cancelBtn: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
confirmBtn: null,
|
||||
/** @private @type {HTMLElement | null} */
|
||||
closeBtn: null,
|
||||
|
||||
// Current callback
|
||||
@@ -169,7 +178,7 @@ const Modal = {
|
||||
if (!this.overlay) return;
|
||||
|
||||
// Show overlay
|
||||
this.overlay.style.display = 'flex';
|
||||
this.overlay.classList.remove('hidden');
|
||||
|
||||
// Trigger animation
|
||||
requestAnimationFrame(() => {
|
||||
@@ -206,7 +215,7 @@ const Modal = {
|
||||
this.overlay.classList.remove('active');
|
||||
|
||||
setTimeout(() => {
|
||||
this.overlay.style.display = 'none';
|
||||
this.overlay.classList.add('hidden');
|
||||
|
||||
if (!confirmed && this.onCancel) {
|
||||
this.onCancel();
|
||||
|
||||
@@ -294,7 +294,7 @@ const contextMenus = {
|
||||
// Each handler checks its own state, so multiple dialogs can be closed with multiple Escape presses
|
||||
if (!_moveDialogEscapeHandler) {
|
||||
_moveDialogEscapeHandler = (e) => {
|
||||
if (e.key === 'Escape' && moveFileDialog.style.display === 'flex') {
|
||||
if (e.key === 'Escape' && !moveFileDialog?.classList.contains('hidden')) {
|
||||
this.closeMoveDialog();
|
||||
}
|
||||
};
|
||||
@@ -383,7 +383,7 @@ const contextMenus = {
|
||||
// Update header text
|
||||
const headerSpan = renameDialog.querySelector('.rename-dialog-header span');
|
||||
if (headerSpan) headerSpan.textContent = i18n ? i18n.t('dialogs.rename_folder') : 'Rename folder';
|
||||
renameDialog.style.display = 'flex';
|
||||
renameDialog?.classList.remove('hidden');
|
||||
renameInput.focus();
|
||||
renameInput.select();
|
||||
},
|
||||
@@ -403,7 +403,7 @@ const contextMenus = {
|
||||
// Update header text
|
||||
const headerSpan = renameDialog.querySelector('.rename-dialog-header span');
|
||||
if (headerSpan) headerSpan.textContent = i18n ? i18n.t('dialogs.rename_file') : 'Rename file';
|
||||
renameDialog.style.display = 'flex';
|
||||
renameDialog?.classList.remove('hidden');
|
||||
renameInput.focus();
|
||||
renameInput.select();
|
||||
},
|
||||
@@ -412,7 +412,7 @@ const contextMenus = {
|
||||
* Close rename dialog
|
||||
*/
|
||||
closeRenameDialog() {
|
||||
document.getElementById('rename-dialog').style.display = 'none';
|
||||
document.getElementById('rename-dialog')?.classList.add('hidden');
|
||||
app.contextMenuTargetFolder = null;
|
||||
app.renameTarget = null;
|
||||
},
|
||||
@@ -478,14 +478,14 @@ const contextMenus = {
|
||||
await this.loadMoveDialogFolders(startFolderId);
|
||||
|
||||
// Show dialog
|
||||
document.getElementById('move-file-dialog').style.display = 'flex';
|
||||
document.getElementById('move-file-dialog')?.classList.remove('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Close move dialog
|
||||
*/
|
||||
closeMoveDialog() {
|
||||
document.getElementById('move-file-dialog').style.display = 'none';
|
||||
document.getElementById('move-file-dialog')?.classList.add('hidden');
|
||||
app.contextMenuTargetFile = null;
|
||||
app.contextMenuTargetFolder = null;
|
||||
},
|
||||
@@ -909,6 +909,7 @@ const contextMenus = {
|
||||
btn.closest('.existing-share-item').remove();
|
||||
if (existingSharesContainer.children.length === 0) {
|
||||
document.getElementById('existing-shares-section').classList.add('hidden');
|
||||
ui.setSharedVisualState(item.id, item.type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -923,7 +924,7 @@ const contextMenus = {
|
||||
if (newShareSection) newShareSection.classList.add('hidden');
|
||||
|
||||
// Show dialog
|
||||
shareDialog.style.display = 'flex';
|
||||
shareDialog.classList.remove('hidden');
|
||||
console.log('Share dialog opened for', itemType, item.name);
|
||||
} catch (error) {
|
||||
console.error('Error opening share dialog:', error);
|
||||
@@ -992,6 +993,9 @@ const contextMenus = {
|
||||
shareUrl.select();
|
||||
}
|
||||
|
||||
// Update Item's shared badge
|
||||
ui.setSharedVisualState(item.id, item.type, true);
|
||||
|
||||
// Show success message
|
||||
ui.showNotification(
|
||||
i18n ? i18n.t('notifications.link_created') : 'Link created',
|
||||
@@ -1017,7 +1021,7 @@ const contextMenus = {
|
||||
app.notificationShareUrl = shareUrl;
|
||||
|
||||
// Show dialog
|
||||
document.getElementById('notification-dialog').style.display = 'flex';
|
||||
document.getElementById('notification-dialog')?.classList.remove('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1042,7 +1046,7 @@ const contextMenus = {
|
||||
|
||||
try {
|
||||
fileSharing.sendShareNotification(shareUrl, email, message);
|
||||
document.getElementById('notification-dialog').style.display = 'none';
|
||||
document.getElementById('notification-dialog')?.classList.add('hidden');
|
||||
} catch (error) {
|
||||
console.error('Error sending notification:', error);
|
||||
ui.showNotification('Error', 'Could not send notification');
|
||||
@@ -1054,7 +1058,7 @@ const contextMenus = {
|
||||
*/
|
||||
closeShareDialog() {
|
||||
const dialog = document.getElementById('share-dialog');
|
||||
if (dialog) dialog.style.display = 'none';
|
||||
if (dialog) dialog.classList.add('hidden');
|
||||
app.shareDialogItem = null;
|
||||
app.shareDialogItemType = null;
|
||||
},
|
||||
@@ -1063,7 +1067,7 @@ const contextMenus = {
|
||||
* Close notification dialog
|
||||
*/
|
||||
closeNotificationDialog() {
|
||||
document.getElementById('notification-dialog').style.display = 'none';
|
||||
document.getElementById('notification-dialog')?.classList.add('hidden');
|
||||
app.notificationShareUrl = null;
|
||||
},
|
||||
|
||||
@@ -1096,7 +1100,7 @@ const contextMenus = {
|
||||
if (addBtn) addBtn.disabled = true;
|
||||
|
||||
// Show dialog
|
||||
dialog.style.display = 'flex';
|
||||
dialog.classList.remove('hidden');
|
||||
requestAnimationFrame(() => dialog.classList.add('active'));
|
||||
|
||||
// Load playlists
|
||||
@@ -1195,7 +1199,7 @@ const contextMenus = {
|
||||
if (dialog) {
|
||||
dialog.classList.remove('active');
|
||||
setTimeout(() => {
|
||||
dialog.style.display = 'none';
|
||||
dialog.classList.add('hidden');
|
||||
}, 200);
|
||||
}
|
||||
app.playlistDialogFiles = null;
|
||||
|
||||
@@ -289,7 +289,7 @@ const fileOps = {
|
||||
const progressBar = document.querySelector('.progress-fill');
|
||||
const uploadProgressDiv = document.querySelector('.upload-progress');
|
||||
if (uploadProgressDiv) {
|
||||
uploadProgressDiv.style.display = 'block';
|
||||
uploadProgressDiv.classList.remove('hidden');
|
||||
}
|
||||
if (progressBar) {
|
||||
progressBar.style.width = '0%';
|
||||
@@ -322,7 +322,7 @@ const fileOps = {
|
||||
}
|
||||
|
||||
if (totalFiles === 0) {
|
||||
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
|
||||
if (uploadProgressDiv) uploadProgressDiv.classList.add('hidden');
|
||||
this._isUploading = false;
|
||||
return;
|
||||
}
|
||||
@@ -412,8 +412,8 @@ const fileOps = {
|
||||
}
|
||||
|
||||
const dropzone = document.getElementById('dropzone');
|
||||
if (dropzone) dropzone.style.display = 'none';
|
||||
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
|
||||
if (dropzone) dropzone.classList.add('hidden');
|
||||
if (uploadProgressDiv) uploadProgressDiv.classList.add('hidden');
|
||||
} finally {
|
||||
this._isUploading = false;
|
||||
}
|
||||
@@ -450,7 +450,7 @@ const fileOps = {
|
||||
const progressBar = document.querySelector('.progress-fill');
|
||||
const uploadProgressDiv = document.querySelector('.upload-progress');
|
||||
if (uploadProgressDiv) {
|
||||
uploadProgressDiv.style.display = 'block';
|
||||
uploadProgressDiv.classList.remove('hidden');
|
||||
}
|
||||
if (progressBar) {
|
||||
progressBar.style.width = '0%';
|
||||
@@ -468,7 +468,7 @@ const fileOps = {
|
||||
|
||||
const totalFiles = validEntries.length;
|
||||
if (totalFiles === 0) {
|
||||
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
|
||||
if (uploadProgressDiv) uploadProgressDiv.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -678,8 +678,8 @@ const fileOps = {
|
||||
}
|
||||
|
||||
const dropzone = document.getElementById('dropzone');
|
||||
if (dropzone) dropzone.style.display = 'none';
|
||||
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
|
||||
if (dropzone) dropzone.classList.add('hidden');
|
||||
if (uploadProgressDiv) uploadProgressDiv.classList.add('hidden');
|
||||
} finally {
|
||||
this._isUploading = false;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ const sharedView = {
|
||||
</div>
|
||||
|
||||
<!-- Share Edit Dialog (sharedView-specific) -->
|
||||
<div id="shared-view-edit-dialog" class="shared-dialog">
|
||||
<div id="shared-view-edit-dialog" class="shared-dialog hidden">
|
||||
<div class="shared-dialog-content">
|
||||
<div class="shared-dialog-header">
|
||||
<span id="sv-dialog-icon">📄</span>
|
||||
@@ -196,7 +196,7 @@ const sharedView = {
|
||||
</div>
|
||||
|
||||
<!-- Notification Dialog (sharedView-specific) -->
|
||||
<div id="sv-notification-dialog" class="shared-dialog">
|
||||
<div id="sv-notification-dialog" class="shared-dialog hidden">
|
||||
<div class="shared-dialog-content">
|
||||
<div class="shared-dialog-header">
|
||||
<span id="sv-notify-dialog-icon">📧</span>
|
||||
@@ -493,12 +493,12 @@ const sharedView = {
|
||||
}
|
||||
}
|
||||
|
||||
shareDialog.classList.add('active');
|
||||
shareDialog.classList.remove('hidden');
|
||||
},
|
||||
|
||||
closeShareDialog() {
|
||||
const d = document.getElementById('shared-view-edit-dialog');
|
||||
if (d) d.classList.remove('active');
|
||||
if (d) d.classList.add('hidden');
|
||||
this.currentItem = null;
|
||||
},
|
||||
|
||||
@@ -516,12 +516,12 @@ const sharedView = {
|
||||
if (nameEl) nameEl.textContent = dn;
|
||||
if (emailEl) emailEl.value = '';
|
||||
if (msgEl) msgEl.value = '';
|
||||
d.classList.add('active');
|
||||
d.classList.remove('hidden');
|
||||
},
|
||||
|
||||
closeNotificationDialog() {
|
||||
const d = document.getElementById('sv-notification-dialog');
|
||||
if (d) d.classList.remove('active');
|
||||
if (d) d.classList.add('hidden');
|
||||
this.currentItem = null;
|
||||
},
|
||||
|
||||
@@ -575,6 +575,7 @@ const sharedView = {
|
||||
};
|
||||
|
||||
try {
|
||||
// FIXME: redundance with fileSharing
|
||||
const res = await fetch(`/api/shares/${this.currentItem.id}`, {
|
||||
method: 'PUT',
|
||||
headers: this._headers(true),
|
||||
@@ -589,7 +590,8 @@ const sharedView = {
|
||||
console.error('Error updating share:', err);
|
||||
this.showNotification(err.message || 'Error updating share', 'error');
|
||||
}
|
||||
|
||||
// update UI
|
||||
ui.setSharedVisualState(this.currentItem.item_id, this.currentItem.item_type, true);
|
||||
this.closeShareDialog();
|
||||
await this.loadItems(true);
|
||||
this.filterAndSortItems();
|
||||
@@ -600,6 +602,7 @@ const sharedView = {
|
||||
if (!this.currentItem) return;
|
||||
|
||||
try {
|
||||
// FIXME: redundance with fileSharing
|
||||
const res = await fetch(`/api/shares/${this.currentItem.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: this._headers()
|
||||
@@ -614,6 +617,8 @@ const sharedView = {
|
||||
this.closeShareDialog();
|
||||
await this.loadItems(true);
|
||||
this.filterAndSortItems();
|
||||
// update UI
|
||||
ui.setSharedVisualState(this.currentItem.item_id, this.currentItem.item_type, this.isShared(this.currentItem.item_id, this.currentItem.item_type));
|
||||
},
|
||||
|
||||
// Send notification (stub)
|
||||
|
||||
Reference in New Issue
Block a user