refactor(js): move multiSelect into batchToolbar + move residual method into relevant components

This commit is contained in:
Edouard Vanbelle
2026-05-26 22:59:43 +02:00
parent 1cd934d594
commit 6ac4e6177c
13 changed files with 215 additions and 81 deletions
+1 -1
View File
@@ -25,7 +25,7 @@
@import url("./components/notifications.css"); @import url("./components/notifications.css");
@import url("./components/userMenu.css"); @import url("./components/userMenu.css");
@import url("./components/languageSelector.css"); @import url("./components/languageSelector.css");
@import url("./components/multiSelect.css"); @import url("./components/batchToolbar.css");
@import url("./components/spinner.css"); @import url("./components/spinner.css");
@import url("./components/search.css"); @import url("./components/search.css");
@import url("./components/icons.css"); @import url("./components/icons.css");
+4 -4
View File
@@ -2,7 +2,7 @@
import { i18n } from '../core/i18n.js'; import { i18n } from '../core/i18n.js';
import { inlineViewer } from '../features/files/inlineViewer.js'; import { inlineViewer } from '../features/files/inlineViewer.js';
import { multiSelect } from '../features/files/multiSelect.js'; import { batchToolbar } from '../features/files/batchToolbar.js';
import { resolveHomeFolder } from './authSession.js'; import { resolveHomeFolder } from './authSession.js';
import { updateHistory } from './main.js'; import { updateHistory } from './main.js';
import { app } from './state.js'; import { app } from './state.js';
@@ -217,9 +217,9 @@ async function loadFiles(options = { insertHistory: true }) {
ui._items.clear(); ui._items.clear();
ui.resetFilesList(); ui.resetFilesList();
if (multiSelect) { if (batchToolbar) {
multiSelect.clear(); batchToolbar.clear();
multiSelect.init(); // this will wire buttons & select-all-checkbox batchToolbar.init(); // this will wire buttons & select-all-checkbox
} }
/** @type {FolderItem[]} */ /** @type {FolderItem[]} */
+7 -7
View File
@@ -12,7 +12,7 @@ import { formatFileSize, formatQuotaSize } from '../core/formatters.js';
import { i18n } from '../core/i18n.js'; import { i18n } from '../core/i18n.js';
import { oxiIconsInit } from '../core/icons.js'; import { oxiIconsInit } from '../core/icons.js';
import { fileOps } from '../features/files/fileOperations.js'; import { fileOps } from '../features/files/fileOperations.js';
import { multiSelect } from '../features/files/multiSelect.js'; import { batchToolbar } from '../features/files/batchToolbar.js';
import { favorites } from '../features/library/favorites.js'; import { favorites } from '../features/library/favorites.js';
import { recent } from '../features/library/recent.js'; import { recent } from '../features/library/recent.js';
import { fileSharing } from '../features/sharing/fileSharing.js'; import { fileSharing } from '../features/sharing/fileSharing.js';
@@ -50,7 +50,7 @@ let uploadDropdownDocumentClickHandler = null;
let uploadDropdownBindingsController = null; let uploadDropdownBindingsController = null;
let actionsBarDelegationBound = false; let actionsBarDelegationBound = false;
const _multiSelectButons = ` const _batchToolbarButons = `
<div class="action-buttons batch-selection-bar hidden" id="multi-select-buttons"> <div class="action-buttons batch-selection-bar hidden" id="multi-select-buttons">
<div class="list-header-checkbox"> <div class="list-header-checkbox">
<button class="batch-bar-close" id="batch-selection-close" title="Cancel selection"> <button class="batch-bar-close" id="batch-selection-close" title="Cancel selection">
@@ -117,7 +117,7 @@ const ACTIONS_BAR_TEMPLATES = {
<span data-i18n="actions.new_folder">New folder</span> <span data-i18n="actions.new_folder">New folder</span>
</button> </button>
</div> </div>
${_multiSelectButons} ${_batchToolbarButons}
${_toggleButtons} ${_toggleButtons}
`, `,
trash: ` trash: `
@@ -131,7 +131,7 @@ const ACTIONS_BAR_TEMPLATES = {
`, `,
favorites: ` favorites: `
<div class="action-buttons" id="default-buttons"></div> <div class="action-buttons" id="default-buttons"></div>
${_multiSelectButons} ${_batchToolbarButons}
${_toggleButtons} ${_toggleButtons}
`, `,
recent: ` recent: `
@@ -141,7 +141,7 @@ const ACTIONS_BAR_TEMPLATES = {
<span data-i18n="actions.clear_recent">Clear recent</span> <span data-i18n="actions.clear_recent">Clear recent</span>
</button> </button>
</div> </div>
${_multiSelectButons} ${_batchToolbarButons}
${_toggleButtons} ${_toggleButtons}
`, `,
sharedwithme: ` sharedwithme: `
@@ -386,9 +386,9 @@ function initApp() {
} }
// Initialize multi-select / batch actions // Initialize multi-select / batch actions
if (multiSelect?.init) { if (batchToolbar?.init) {
console.log('Initializing multi-select module'); console.log('Initializing multi-select module');
multiSelect.init(); batchToolbar.init();
} }
window.addEventListener('authenticationDone', async () => { window.addEventListener('authenticationDone', async () => {
+10 -10
View File
@@ -4,7 +4,7 @@
*/ */
import { i18n } from '../core/i18n.js'; import { i18n } from '../core/i18n.js';
import { multiSelect } from '../features/files/multiSelect.js'; import { batchToolbar } from '../features/files/batchToolbar.js';
import { favorites } from '../features/library/favorites.js'; import { favorites } from '../features/library/favorites.js';
import { musicView } from '../features/library/music.js'; import { musicView } from '../features/library/music.js';
import { photosView } from '../features/library/photos.js'; import { photosView } from '../features/library/photos.js';
@@ -201,7 +201,7 @@ function switchToSharedSection() {
sharedView.show(); sharedView.show();
}); });
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
function switchToSharedWithMeSection() { function switchToSharedWithMeSection() {
@@ -221,7 +221,7 @@ function switchToSharedWithMeSection() {
toggleFileContainer(true); toggleFileContainer(true);
syncViewContainers(); syncViewContainers();
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
// Load and render items into the files container // Load and render items into the files container
sharedWithMeView.init(); sharedWithMeView.init();
@@ -253,7 +253,7 @@ function switchToFilesSection() {
app.currentPath = app.userHomeFolderId || ''; app.currentPath = app.userHomeFolderId || '';
app.breadcrumbPath = []; app.breadcrumbPath = [];
ui.updateBreadcrumb(); ui.updateBreadcrumb();
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
// temp solution // temp solution
sharedView.loadItems().then(() => { sharedView.loadItems().then(() => {
@@ -296,7 +296,7 @@ function switchToFavoritesSection() {
`); `);
} }
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
function switchToRecentFilesSection() { function switchToRecentFilesSection() {
@@ -329,7 +329,7 @@ function switchToRecentFilesSection() {
<p>Error loading the recent module</p> <p>Error loading the recent module</p>
`); `);
} }
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
function switchToPhotosSection() { function switchToPhotosSection() {
@@ -352,7 +352,7 @@ function switchToPhotosSection() {
if (photosView) { if (photosView) {
photosView.show(); photosView.show();
} }
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
function switchToTrashSection() { function switchToTrashSection() {
@@ -377,7 +377,7 @@ function switchToTrashSection() {
// Load trash items // Load trash items
loadTrashItems(); loadTrashItems();
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
function switchToMusicSection() { function switchToMusicSection() {
@@ -404,7 +404,7 @@ function switchToMusicSection() {
if (musicView) { if (musicView) {
musicView.show(); musicView.show();
} }
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
/** /**
@@ -423,7 +423,7 @@ function activateFilesUI() {
breadcrumb?.classList.remove('hidden'); breadcrumb?.classList.remove('hidden');
toggleFileContainer(true); toggleFileContainer(true);
syncViewContainers(); syncViewContainers();
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
} }
export { export {
+2 -2
View File
@@ -5,7 +5,7 @@
import { escapeHtml, formatDateTime } from '../core/formatters.js'; import { escapeHtml, formatDateTime } from '../core/formatters.js';
import { i18n } from '../core/i18n.js'; import { i18n } from '../core/i18n.js';
import { fileOps } from '../features/files/fileOperations.js'; import { fileOps } from '../features/files/fileOperations.js';
import { multiSelect } from '../features/files/multiSelect.js'; import { batchToolbar } from '../features/files/batchToolbar.js';
import * as pathTooltip from '../features/pathTooltip.js'; import * as pathTooltip from '../features/pathTooltip.js';
import { appElements } from './state.js'; import { appElements } from './state.js';
import { ui } from './ui.js'; import { ui } from './ui.js';
@@ -22,7 +22,7 @@ async function loadTrashItems() {
const elements = appElements; const elements = appElements;
try { try {
if (multiSelect) multiSelect.clear(); if (batchToolbar) batchToolbar.clear();
pathTooltip.destroy(elements.filesList); pathTooltip.destroy(elements.filesList);
ui.resetFilesList(); // ensure also list visible & error hidden ui.resetFilesList(); // ensure also list visible & error hidden
elements.filesList.innerHTML = ` elements.filesList.innerHTML = `
+18 -18
View File
@@ -13,7 +13,7 @@ import { OxiIcons } from '../core/icons.js';
import { contextMenus } from '../features/files/contextMenus.js'; import { contextMenus } from '../features/files/contextMenus.js';
import { fileOps } from '../features/files/fileOperations.js'; import { fileOps } from '../features/files/fileOperations.js';
import { inlineViewer } from '../features/files/inlineViewer.js'; import { inlineViewer } from '../features/files/inlineViewer.js';
import { multiSelect } from '../features/files/multiSelect.js'; import { batchToolbar } from '../features/files/batchToolbar.js';
import { wopiEditor } from '../features/files/wopiEditor.js'; import { wopiEditor } from '../features/files/wopiEditor.js';
import { favorites } from '../features/library/favorites.js'; import { favorites } from '../features/library/favorites.js';
import { recent } from '../features/library/recent.js'; import { recent } from '../features/library/recent.js';
@@ -727,9 +727,9 @@ const ui = {
* @param {any} dataTransfer fallback if nothing is selected * @param {any} dataTransfer fallback if nothing is selected
*/ */
async _dropToFolder(action, targetFolderId, dataTransfer) { async _dropToFolder(action, targetFolderId, dataTransfer) {
const selection = multiSelect.getSelection(targetFolderId); const selection = batchToolbar.getSelection(targetFolderId);
multiSelect.clear(); batchToolbar.clear();
if (selection.fileIds.length === 0 && selection.folderIds.length === 0) { if (selection.fileIds.length === 0 && selection.folderIds.length === 0) {
// try to use dataTransfer (direct move without selection) // try to use dataTransfer (direct move without selection)
@@ -774,7 +774,7 @@ const ui = {
console.error(`drag and drop: action ${action} unknown`); console.error(`drag and drop: action ${action} unknown`);
return; return;
} }
multiSelect.showBatchResult(action, result); batchToolbar.showBatchResult(action, result);
console.log(result); console.log(result);
}, },
@@ -941,8 +941,8 @@ const ui = {
} }
// shiftkey is used to complete selection // shiftkey is used to complete selection
if (e.shiftKey && multiSelect) { if (e.shiftKey && batchToolbar) {
multiSelect.handleToggleItem(card, e); batchToolbar.handleToggleItem(card, e);
return; return;
} }
@@ -1512,13 +1512,13 @@ const ui = {
/** /**
* Toggle selection state of a file/folder card. * Toggle selection state of a file/folder card.
* Routes through the multiSelect module so batch actions know about selected items. * Routes through the batchToolbar module so batch actions know about selected items.
* @param {HTMLDivElement} card * @param {HTMLDivElement} card
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
function toggleCardSelection(card, event) { function toggleCardSelection(card, event) {
if (multiSelect) { if (batchToolbar) {
multiSelect.handleToggleItem(card, event); batchToolbar.handleToggleItem(card, event);
} else { } else {
card.classList.toggle('selected'); card.classList.toggle('selected');
} }
@@ -1641,17 +1641,17 @@ function initRubberBandSelection() {
if (intersects) { if (intersects) {
card.classList.add('selected'); card.classList.add('selected');
// Sync with multiSelect module // Sync with batchToolbar module
if (multiSelect) { if (batchToolbar) {
const info = multiSelect._extractInfo(/** @type {HTMLDivElement} */ (card)); const info = batchToolbar._extractInfo(/** @type {HTMLDivElement} */ (card));
if (info) multiSelect.select(info.id, info.name, info.type, info.parentId); if (info) batchToolbar.select(info.id, info.name, info.type, info.parentId);
} }
} else { } else {
card.classList.remove('selected'); card.classList.remove('selected');
// Deselect from multiSelect module // Deselect from batchToolbar module
if (multiSelect) { if (batchToolbar) {
const info = multiSelect._extractInfo(/** @type {HTMLDivElement} */ (card)); const info = batchToolbar._extractInfo(/** @type {HTMLDivElement} */ (card));
if (info) multiSelect.deselect(info.id); if (info) batchToolbar.deselect(info.id);
} }
} }
}); });
@@ -1663,7 +1663,7 @@ function initRubberBandSelection() {
const hadSelection = selRect.style.display === 'block'; const hadSelection = selRect.style.display === 'block';
selRect.style.display = 'none'; selRect.style.display = 'none';
// Update the batch bar after rubber band selection completes // Update the batch bar after rubber band selection completes
if (multiSelect) multiSelect._syncUI(); if (batchToolbar) batchToolbar._syncUI();
// Suppress the click event that follows mouseup so the global // Suppress the click event that follows mouseup so the global
// deselect handler doesn't immediately clear the selection. // deselect handler doesn't immediately clear the selection.
if (hadSelection) { if (hadSelection) {
+145 -13
View File
@@ -86,6 +86,9 @@ export class ResourceListComponent {
/** @type {Set<string>} */ /** @type {Set<string>} */
this._selected = new Set(); this._selected = new Set();
/** Index of the last clicked item — used for shift-click range selection. */
this._lastClickedIndex = -1;
this._ownerVisible = this._cfg.showOwner; this._ownerVisible = this._cfg.showOwner;
this._initDelegation(); this._initDelegation();
@@ -110,8 +113,10 @@ export class ResourceListComponent {
this._selected.clear(); this._selected.clear();
this._items.clear(); this._items.clear();
this._lastClickedIndex = -1;
this._appendItems(folders, files, groupFn); this._appendItems(folders, files, groupFn);
this._wireSelectAll();
} }
/** /**
@@ -132,6 +137,40 @@ export class ResourceListComponent {
if (header) this._container.appendChild(header); if (header) this._container.appendChild(header);
this._selected.clear(); this._selected.clear();
this._items.clear(); this._items.clear();
this._lastClickedIndex = -1;
}
/**
* Deselect all items without removing them from the DOM.
* Used by the batch toolbar after an operation completes.
*/
clearSelection() {
this._selected.clear();
this._lastClickedIndex = -1;
this._container.querySelectorAll('.file-item.selected').forEach((card) => {
card.classList.remove('selected');
const cb = /** @type {HTMLInputElement | null} */ (card.querySelector('.item-checkbox'));
if (cb) cb.checked = false;
});
this._syncSelectAllCheckbox();
this._cfg.onSelectionChange?.([]);
}
/**
* Select all visible items in the container.
*/
selectAll() {
this._container.querySelectorAll('.file-item').forEach((card) => {
const el = /** @type {HTMLElement} */ (card);
const id = el.dataset.fileId || el.dataset.folderId || '';
if (!id) return;
el.classList.add('selected');
const cb = /** @type {HTMLInputElement | null} */ (el.querySelector('.item-checkbox'));
if (cb) cb.checked = true;
this._selected.add(id);
});
this._syncSelectAllCheckbox();
this._notifySelectionChange();
} }
/** /**
@@ -406,9 +445,13 @@ export class ResourceListComponent {
return; return;
} }
// Checkbox cell → selection // Checkbox cell → selection (shift extends range)
if (cfg.selectable && target.closest('.checkbox-cell')) { if (cfg.selectable && target.closest('.checkbox-cell')) {
this._toggleSelection(card, /** @type {MouseEvent} */ (e)); if (e.shiftKey) {
this._handleShiftSelect(card);
} else {
this._toggleSelection(card);
}
return; return;
} }
@@ -417,7 +460,13 @@ export class ResourceListComponent {
// Modifier-key click → selection toggle // Modifier-key click → selection toggle
if (e.metaKey || e.altKey || e.ctrlKey) { if (e.metaKey || e.altKey || e.ctrlKey) {
if (cfg.selectable) this._toggleSelection(card, /** @type {MouseEvent} */ (e)); if (cfg.selectable) this._toggleSelection(card);
return;
}
// Shift-click anywhere on the card → extend selection range
if (e.shiftKey && cfg.selectable) {
this._handleShiftSelect(card);
return; return;
} }
@@ -454,11 +503,11 @@ export class ResourceListComponent {
} }
/** /**
* Toggle selection state on a card and notify via `onSelectionChange`. * Toggle selection on a single card and notify.
* Tracks `_lastClickedIndex` for subsequent shift-clicks.
* @param {HTMLElement} card * @param {HTMLElement} card
* @param {MouseEvent} _e - Reserved for future shift-click range selection.
*/ */
_toggleSelection(card, _e) { _toggleSelection(card) {
const id = card.dataset.fileId || card.dataset.folderId || ''; const id = card.dataset.fileId || card.dataset.folderId || '';
if (!id) return; if (!id) return;
@@ -474,13 +523,96 @@ export class ResourceListComponent {
this._selected.delete(id); this._selected.delete(id);
} }
if (this._cfg.onSelectionChange) { // Record position for the next shift-click
/** @type {Array<FileItem|FolderItem>} */ const items = [...this._container.querySelectorAll('.file-item')];
const selectedItems = [...this._selected].flatMap((sid) => { this._lastClickedIndex = items.indexOf(card);
const item = this._items.get(sid);
return item ? [item] : []; this._syncSelectAllCheckbox();
}); this._notifySelectionChange();
this._cfg.onSelectionChange(selectedItems); }
/**
* Extend the selection from `_lastClickedIndex` to `card` (inclusive).
* If no previous click exists, falls back to a plain toggle.
* @param {HTMLElement} card
*/
_handleShiftSelect(card) {
const items = /** @type {HTMLElement[]} */ ([...this._container.querySelectorAll('.file-item')]);
const index = items.indexOf(card);
if (this._lastClickedIndex >= 0 && index >= 0) {
const start = Math.min(this._lastClickedIndex, index);
const end = Math.max(this._lastClickedIndex, index);
for (let i = start; i <= end; i++) {
const el = items[i];
const id = el.dataset.fileId || el.dataset.folderId || '';
if (!id) continue;
el.classList.add('selected');
const cb = /** @type {HTMLInputElement | null} */ (el.querySelector('.item-checkbox'));
if (cb) cb.checked = true;
this._selected.add(id);
}
} else {
this._toggleSelection(card);
return;
}
this._lastClickedIndex = index;
this._syncSelectAllCheckbox();
this._notifySelectionChange();
}
/**
* Find the select-all checkbox in the container header and wire its
* `change` event. Called after every `render()`.
*/
_wireSelectAll() {
if (!this._cfg.selectable) return;
const cb = /** @type {HTMLInputElement | null} */ (this._container.querySelector('#select-all-checkbox'));
if (!cb) return;
// Replace with a fresh listener to avoid duplicates across re-renders
const fresh = /** @type {HTMLInputElement} */ (cb.cloneNode(true));
cb.parentNode?.replaceChild(fresh, cb);
fresh.addEventListener('change', () => {
if (fresh.checked) {
this.selectAll();
} else {
this.clearSelection();
}
});
}
/**
* Sync the three-state select-all checkbox in the list header.
* Checked = all selected, indeterminate = some selected, unchecked = none.
*/
_syncSelectAllCheckbox() {
const cb = /** @type {HTMLInputElement | null} */ (this._container.querySelector('#select-all-checkbox'));
if (!cb) return;
const total = this._container.querySelectorAll('.file-item').length;
if (total === 0) {
cb.checked = false;
cb.indeterminate = false;
} else if (this._selected.size >= total) {
cb.checked = true;
cb.indeterminate = false;
} else if (this._selected.size > 0) {
cb.checked = false;
cb.indeterminate = true;
} else {
cb.checked = false;
cb.indeterminate = false;
} }
} }
/** Build the selected-items array and fire `onSelectionChange`. */
_notifySelectionChange() {
if (!this._cfg.onSelectionChange) return;
/** @type {Array<FileItem|FolderItem>} */
const selectedItems = [...this._selected].flatMap((id) => {
const item = this._items.get(id);
return item ? [item] : [];
});
this._cfg.onSelectionChange(selectedItems);
}
} }
@@ -1,14 +1,16 @@
/** /**
* OxiCloud - Multi-Select & Batch Actions Module * OxiCloud — Batch Toolbar Module
* *
* Adds checkboxes to grid and list views, replaces the list-view header * Manages the floating selection bar that appears when items are selected,
* with a NextCloud-style selection bar when items are selected, and * and executes batch operations (delete, move, download, favorites).
* provides batch delete / move / download / favorites operations. *
* Selection state (_selected, handleToggleItem, selectAll, …) is kept here
* while the main file manager still uses its own delegation (ui.js).
* Once ui.js is migrated to ResourceListComponent (plan step B5), all
* selection mechanics will live in the component and this module will
* shrink to only the toolbar UI and batch-operation API calls.
*/ */
// TODO: rename into selection-bar ?
// TODO: merge with photo part
import { loadFiles } from '../../app/filesView.js'; import { loadFiles } from '../../app/filesView.js';
import { app } from '../../app/state.js'; import { app } from '../../app/state.js';
import { showConfirmDialog, ui } from '../../app/ui.js'; import { showConfirmDialog, ui } from '../../app/ui.js';
@@ -22,7 +24,7 @@ import { getAuthHeaders } from './fileOperations.js';
* @import {BatchResult} from './fileOperations.js' * @import {BatchResult} from './fileOperations.js'
*/ */
const multiSelect = { const batchToolbar = {
/** @type {Map<String, LightItem>} items: Map<id, { id, name, type, parentId }> */ /** @type {Map<String, LightItem>} items: Map<id, { id, name, type, parentId }> */
_selected: new Map(), _selected: new Map(),
@@ -536,4 +538,4 @@ const multiSelect = {
} }
}; };
export { multiSelect }; export { batchToolbar };
+9 -9
View File
@@ -18,7 +18,7 @@ import { musicView } from '../library/music.js';
import { fileSharing } from '../sharing/fileSharing.js'; import { fileSharing } from '../sharing/fileSharing.js';
import { fileOps } from './fileOperations.js'; import { fileOps } from './fileOperations.js';
import { inlineViewer } from './inlineViewer.js'; import { inlineViewer } from './inlineViewer.js';
import { multiSelect } from './multiSelect.js'; import { batchToolbar } from './batchToolbar.js';
import { wopiEditor } from './wopiEditor.js'; import { wopiEditor } from './wopiEditor.js';
/** /**
@@ -330,8 +330,8 @@ const contextMenus = {
// Copy button handler // Copy button handler
copyConfirmBtn.addEventListener('click', async () => { copyConfirmBtn.addEventListener('click', async () => {
// Batch copy mode (from multiSelect) // Batch copy mode (from batchToolbar)
if (app.moveDialogMode === 'batch' && multiSelect) { if (app.moveDialogMode === 'batch' && batchToolbar) {
const targetId = app.selectedTargetFolderId; const targetId = app.selectedTargetFolderId;
const items = app.batchMoveItems || []; const items = app.batchMoveItems || [];
@@ -341,10 +341,10 @@ const contextMenus = {
const result = await fileOps.batchCopy(fileIds, folderIds, targetId); const result = await fileOps.batchCopy(fileIds, folderIds, targetId);
this.closeMoveDialog(); this.closeMoveDialog();
multiSelect.clear(); batchToolbar.clear();
loadFiles(); loadFiles();
multiSelect.showBatchResult('copy', result); batchToolbar.showBatchResult('copy', result);
return; return;
} }
@@ -363,8 +363,8 @@ const contextMenus = {
}); });
moveConfirmBtn.addEventListener('click', async () => { moveConfirmBtn.addEventListener('click', async () => {
// Batch move mode (from multiSelect) // Batch move mode (from batchToolbar)
if (app.moveDialogMode === 'batch' && multiSelect) { if (app.moveDialogMode === 'batch' && batchToolbar) {
const targetId = app.selectedTargetFolderId; const targetId = app.selectedTargetFolderId;
const items = app.batchMoveItems || []; const items = app.batchMoveItems || [];
@@ -374,9 +374,9 @@ const contextMenus = {
const result = await fileOps.batchMove(fileIds, folderIds, targetId); const result = await fileOps.batchMove(fileIds, folderIds, targetId);
this.closeMoveDialog(); this.closeMoveDialog();
multiSelect.clear(); batchToolbar.clear();
loadFiles(); loadFiles();
multiSelect.showBatchResult('move', result); batchToolbar.showBatchResult('move', result);
return; return;
} }
+2 -2
View File
@@ -9,7 +9,7 @@
import { ui } from '../../app/ui.js'; import { ui } from '../../app/ui.js';
import { getCsrfHeaders } from '../../core/csrf.js'; import { getCsrfHeaders } from '../../core/csrf.js';
import { i18n } from '../../core/i18n.js'; import { i18n } from '../../core/i18n.js';
import { multiSelect } from '../files/multiSelect.js'; import { batchToolbar } from '../files/batchToolbar.js';
import * as pathTooltip from '../pathTooltip.js'; import * as pathTooltip from '../pathTooltip.js';
/** @import {FavoriteItem, FileItem, FolderItem} from '../../core/types.js' */ /** @import {FavoriteItem, FileItem, FolderItem} from '../../core/types.js' */
@@ -181,7 +181,7 @@ const favorites = {
ui.resetFilesList(); // ensure also list visible & error hidden ui.resetFilesList(); // ensure also list visible & error hidden
// wire buttons & select-all-checkbox as list header has changed in ui.resetFilesList() // wire buttons & select-all-checkbox as list header has changed in ui.resetFilesList()
// FIXME: this case is not easy to understand, should apply better implementation // FIXME: this case is not easy to understand, should apply better implementation
multiSelect.init(); batchToolbar.init();
ui.updateBreadcrumb(); ui.updateBreadcrumb();
+4 -4
View File
@@ -9,7 +9,7 @@
import { ui } from '../../app/ui.js'; import { ui } from '../../app/ui.js';
import { getCsrfHeaders } from '../../core/csrf.js'; import { getCsrfHeaders } from '../../core/csrf.js';
import { i18n } from '../../core/i18n.js'; import { i18n } from '../../core/i18n.js';
import { multiSelect } from '../files/multiSelect.js'; import { batchToolbar } from '../files/batchToolbar.js';
import * as pathTooltip from '../pathTooltip.js'; import * as pathTooltip from '../pathTooltip.js';
/** @import {FileItem, FolderItem, ItemTypeEnum, RecentItem} from '../../core/types.js' */ /** @import {FileItem, FolderItem, ItemTypeEnum, RecentItem} from '../../core/types.js' */
@@ -111,9 +111,9 @@ const recent = {
</div> </div>
`; `;
if (multiSelect) { if (batchToolbar) {
multiSelect.clear(); batchToolbar.clear();
multiSelect.init(); // this will wire buttons & select-all-checkbox batchToolbar.init(); // this will wire buttons & select-all-checkbox
} }
ui.updateBreadcrumb(); ui.updateBreadcrumb();
@@ -16,7 +16,7 @@
import { ui } from '../../app/ui.js'; import { ui } from '../../app/ui.js';
import { i18n } from '../../core/i18n.js'; import { i18n } from '../../core/i18n.js';
import { multiSelect } from '../../features/files/multiSelect.js'; import { batchToolbar } from '../../features/files/batchToolbar.js';
import { ownerTooltip } from '../../features/ownerTooltip.js'; import { ownerTooltip } from '../../features/ownerTooltip.js';
import { grants } from '../../model/grants.js'; import { grants } from '../../model/grants.js';
import { systemUsers } from '../../model/systemUsers.js'; import { systemUsers } from '../../model/systemUsers.js';
@@ -52,7 +52,7 @@ const sharedWithMeView = {
// Standard files-view setup: clear list, show container, init multiselect // Standard files-view setup: clear list, show container, init multiselect
ui.resetFilesList(); ui.resetFilesList();
multiSelect.init(); batchToolbar.init();
ui.updateBreadcrumb(); ui.updateBreadcrumb();
await this._loadPage(); await this._loadPage();