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)
This commit is contained in:
Jin Yi
2025-10-29 16:26:44 +09:00
committed by GitHub
parent 22f307b468
commit 9651d2a5df
9 changed files with 510 additions and 72 deletions

View File

@@ -75,10 +75,10 @@
</template>
</IconTextButton>
<MediaAssetButtonDivider v-if="showCopyJobId && showDeleteButton" />
<MediaAssetButtonDivider v-if="showCopyJobId && shouldShowDeleteButton" />
<IconTextButton
v-if="showDeleteButton"
v-if="shouldShowDeleteButton"
type="transparent"
class="dark-theme:text-white"
label="Delete"
@@ -101,8 +101,9 @@ import { useMediaAssetActions } from '../composables/useMediaAssetActions'
import { MediaAssetKey } from '../schemas/mediaAssetSchema'
import MediaAssetButtonDivider from './MediaAssetButtonDivider.vue'
const { close } = defineProps<{
const { close, showDeleteButton } = defineProps<{
close: () => void
showDeleteButton?: boolean
}>()
const emit = defineEmits<{
@@ -124,13 +125,12 @@ const showCopyJobId = computed(() => {
return assetType.value !== 'input'
})
// Delete button should be shown for:
// - All output files (can be deleted via history)
// - Input files only in cloud environment
const showDeleteButton = computed(() => {
return (
const shouldShowDeleteButton = computed(() => {
const propAllows = showDeleteButton ?? true
const typeAllows =
assetType.value === 'output' || (assetType.value === 'input' && isCloud)
)
return propAllows && typeAllows
})
const handleInspect = () => {