diff --git a/static/css/components/breadcrumb.css b/static/css/components/breadcrumb.css index 2db03859..2d487d18 100755 --- a/static/css/components/breadcrumb.css +++ b/static/css/components/breadcrumb.css @@ -12,6 +12,7 @@ .breadcrumb-item { padding: 2px 4px; border-radius: 4px; + border: 2px solid transparent; transition: background 0.15s, color 0.15s; } @@ -20,6 +21,11 @@ color: #5a6f8a; } +.breadcrumb-link.drop-target { + background-color: rgba(255, 193, 7, 0.1); + border: 2px dashed #ffc107; +} + .breadcrumb-link:hover { text-decoration: underline; color: #ff5e3a; diff --git a/static/js/app/ui.js b/static/js/app/ui.js index e4557a0f..b3f284bc 100755 --- a/static/js/app/ui.js +++ b/static/js/app/ui.js @@ -516,34 +516,15 @@ const ui = { } breadcrumb.appendChild(homeIcon); - // -- Root/Home folder name (if available) -- + // -- Root/Home folder name (if available) is always the first element of the breadcrumb -- + // TODO clarify the difference between homeIcon & this first element if (window.app.userHomeFolderName) { - const separator1 = document.createElement('span'); - separator1.className = 'breadcrumb-separator'; - separator1.textContent = '>'; - breadcrumb.appendChild(separator1); - - const rootFolderItem = document.createElement('span'); - rootFolderItem.className = 'breadcrumb-item'; - rootFolderItem.textContent = window.app.userHomeFolderName; - - // If we're at the home folder level (no deeper navigation), show as current - if (path.length === 0) { - rootFolderItem.classList.add('breadcrumb-current'); - } else { - // Otherwise clickable to go back to home - rootFolderItem.classList.add('breadcrumb-link'); - rootFolderItem.addEventListener('click', () => { - window.app.breadcrumbPath = []; - window.app.currentPath = window.app.userHomeFolderId; - self.updateBreadcrumb(); - window.loadFiles(); - }); + if (path.length === 0 || path[0].id !== window.app.userHomeFolderId) { + path.unshift({ name: window.app.userHomeFolderName, id: window.app.userHomeFolderId}); } - breadcrumb.appendChild(rootFolderItem); } - // -- Intermediate + current segments -- + // -- Root/Home + Intermediate + current segments -- path.forEach((segment, index) => { const isLast = index === path.length - 1; @@ -557,6 +538,7 @@ const ui = { const item = document.createElement('span'); item.className = 'breadcrumb-item'; item.textContent = segment.name; + item.dataset.folderId = segment.id; if (!isLast) { // Intermediate segment: clickable – truncate path to this level @@ -567,6 +549,40 @@ const ui = { self.updateBreadcrumb(); window.loadFiles(); }); + + // can drag files on this folder + // dragover – only folders are valid drop targets + item.addEventListener('dragover', (e) => { + const card = e.target.closest("span"); + if (!card || !card.dataset.folderId) return; + e.preventDefault(); + card.classList.add('drop-target'); + }); + + // dragleave + item.addEventListener('dragleave', (e) => { + console.log( "dragleave ",e ); + const card = e.target.closest("span"); + if (!card || !card.dataset.folderId) return; + card.classList.remove('drop-target'); + }); + + // drop – only folders accept drops + item.addEventListener('drop', async (e) => { + const card = e.target.closest("span"); + if (!card) return; + const targetFolderId = card.dataset.folderId; + if (!targetFolderId) return; + + e.preventDefault(); + card.classList.remove('drop-target'); + + const id = e.dataTransfer.getData('text/plain'); + const isFolder = + e.dataTransfer.getData('application/oxicloud-folder') === 'true'; + + await self.move( id, isFolder, targetFolderId); + }); } else { // Last segment: current location, not clickable item.classList.add('breadcrumb-current'); @@ -575,6 +591,32 @@ const ui = { }); }, + + // TODO: support multiple elements to move ? (API does) + /** + * proceed to the drag & drop + * @param {string} sourceId to move (can be a uniq object or) + * @param {boolean} sourceIsAFolder + * @param {string} targetFolderId + */ + async move(sourceId, sourceIsAFolder, targetFolderId) { + if (!sourceId) return; + + console.log(`request move ${ sourceIsAFolder ? "folder": "file"} ${sourceId} to folder ${targetFolderId}`); + + if (sourceIsAFolder) { + if (sourceId === targetFolderId) { + alert("You cannot move a folder to itself"); + return; + } + // TODO: handle errors... + await fileOps.moveFolder(sourceId, targetFolderId); + } else { + // TODO: handle errors... + await fileOps.moveFile(sourceId, targetFolderId); + } + }, + /** * Check if a file can be previewed in the viewer * @param {Object} file - File object with mime_type property @@ -951,17 +993,7 @@ const ui = { const isFolder = e.dataTransfer.getData('application/oxicloud-folder') === 'true'; - if (id) { - if (isFolder) { - if (id === targetFolderId) { - alert("You cannot move a folder to itself"); - return; - } - await fileOps.moveFolder(id, targetFolderId); - } else { - await fileOps.moveFile(id, targetFolderId); - } - } + await self.move( id, isFolder, targetFolderId); }); } },