From 9651d2a5dffb6c829d57b7eb135d0f3954c53f81 Mon Sep 17 00:00:00 2001 From: Jin Yi Date: Wed, 29 Oct 2025 16:26:44 +0900 Subject: [PATCH] feat: Add multi-select support for media assets (#6256) ## Summary Implements file explorer-style multi-selection functionality for media assets in the AssetsSidebarTab component. ## Changes ### Multi-Selection Interactions - **Normal click**: Single selection (clears previous, selects new) - **Shift + click**: Range selection (from last selected to current) - **Ctrl/Cmd + click**: Toggle individual selection ### State Management - Added `assetSelectionStore` to manage selected asset IDs using Set - Created `useAssetSelection` composable for selection logic and keyboard state ### UI Enhancements - Display selection count in footer (output tab only) - Interactive selection count that shows "Deselect all" on hover - Added bulk action buttons for download/delete (UI only) ### Translation Keys Added new keys under `mediaAsset.selection`: - `selectedCount`: "{count} selected" - `deselectAll`: "Deselect all" - `downloadSelected`: "Download" - `deleteSelected`: "Delete" ## Test Plan - [x] Open Assets sidebar tab - [x] Switch to Generated tab - [x] Test single selection with normal click - [x] Test range selection with Shift + click - [x] Test toggle selection with Ctrl/Cmd + click - [x] Verify selection count updates correctly - [x] Test hover interaction on selection count - [x] Click "Deselect all" to clear selection - [x] Test bulk action buttons (UI only) ## Notes - Bulk download/delete functionality to be implemented in separate PR - Selection UI currently only shows in output (Generated) tab [screen-capture.webm](https://github.com/user-attachments/assets/740315bd-9254-4af3-a0be-10846d810d65) --- .../sidebar/tabs/AssetSidebarTemplate.vue | 3 + .../sidebar/tabs/AssetsSidebarTab.vue | 187 ++++++++++++++---- src/locales/en/main.json | 12 ++ .../assets/components/MediaAssetActions.vue | 15 +- .../assets/components/MediaAssetCard.vue | 20 +- .../assets/components/MediaAssetMoreMenu.vue | 18 +- .../assets/composables/useAssetSelection.ts | 129 ++++++++++++ .../composables/useAssetSelectionStore.ts | 81 ++++++++ .../composables/useMediaAssetActions.ts | 117 ++++++++++- 9 files changed, 510 insertions(+), 72 deletions(-) create mode 100644 src/platform/assets/composables/useAssetSelection.ts create mode 100644 src/platform/assets/composables/useAssetSelectionStore.ts diff --git a/src/components/sidebar/tabs/AssetSidebarTemplate.vue b/src/components/sidebar/tabs/AssetSidebarTemplate.vue index d38799425..f7de9022d 100644 --- a/src/components/sidebar/tabs/AssetSidebarTemplate.vue +++ b/src/components/sidebar/tabs/AssetSidebarTemplate.vue @@ -18,6 +18,9 @@ +
+ +
diff --git a/src/components/sidebar/tabs/AssetsSidebarTab.vue b/src/components/sidebar/tabs/AssetsSidebarTab.vue index cdf29b469..9b3639ff5 100644 --- a/src/components/sidebar/tabs/AssetsSidebarTab.vue +++ b/src/components/sidebar/tabs/AssetsSidebarTab.vue @@ -41,44 +41,102 @@ + @@ -91,9 +149,10 @@ diff --git a/src/locales/en/main.json b/src/locales/en/main.json index f95421968..a1f4a608a 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1779,6 +1779,8 @@ "mediaAsset": { "deleteAssetTitle": "Delete this asset?", "deleteAssetDescription": "This asset will be permanently removed.", + "deleteSelectedTitle": "Delete selected assets?", + "deleteSelectedDescription": "{count} asset(s) will be permanently removed.", "assetDeletedSuccessfully": "Asset deleted successfully", "deletingImportedFilesCloudOnly": "Deleting imported files is only supported in cloud version", "failedToDeleteAsset": "Failed to delete asset", @@ -1787,6 +1789,16 @@ "jobIdCopyFailed": "Failed to copy Job ID", "copied": "Copied", "error": "Error" + }, + "selection": { + "selectedCount": "Assets Selected: {count}", + "deselectAll": "Deselect all", + "downloadSelected": "Download", + "deleteSelected": "Delete", + "downloadStarted": "Downloading {count} files...", + "downloadsStarted": "Started downloading {count} file(s)", + "assetsDeletedSuccessfully": "{count} asset(s) deleted successfully", + "failedToDeleteAssets": "Failed to delete selected assets" } }, "actionbar": { diff --git a/src/platform/assets/components/MediaAssetActions.vue b/src/platform/assets/components/MediaAssetActions.vue index e2c6d1064..a4c9a6b55 100644 --- a/src/platform/assets/components/MediaAssetActions.vue +++ b/src/platform/assets/components/MediaAssetActions.vue @@ -1,6 +1,6 @@