fix: Prevent multiple asset card popovers from opening simultaneously (#6808)

## Summary

Ensures only one MediaAssetCard popover is open at a time.

## Changes

- Added `hide()` method exposure in MoreButton component
- Implemented parent-level state management for tracking open popover
- Added VueUse `whenever` to auto-close other popovers when one opens

## Test Plan

- Open multiple asset cards' more menus
- Verify only one popover remains open at a time

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6808-fix-Prevent-multiple-asset-card-popovers-from-opening-simultaneously-2b26d73d365081e1b3f0d97b869cedc5)
by [Unito](https://www.unito.io)
This commit is contained in:
Jin Yi
2025-11-22 04:01:19 +07:00
committed by GitHub
parent ff0d385db8
commit 639975b804
3 changed files with 38 additions and 4 deletions

View File

@@ -68,4 +68,8 @@ const toggle = (event: Event) => {
const hide = () => {
popover.value?.hide()
}
defineExpose({
hide
})
</script>

View File

@@ -85,10 +85,13 @@
:show-output-count="shouldShowOutputCount(item)"
:output-count="getOutputCount(item)"
:show-delete-button="shouldShowDeleteButton"
:open-popover-id="openPopoverId"
@click="handleAssetSelect(item)"
@zoom="handleZoomClick(item)"
@output-count-click="enterFolderView(item)"
@asset-deleted="refreshAssets"
@popover-opened="openPopoverId = item.id"
@popover-closed="openPopoverId = null"
/>
</template>
</VirtualGrid>
@@ -199,6 +202,9 @@ const folderPromptId = ref<string | null>(null)
const folderExecutionTime = ref<number | undefined>(undefined)
const isInFolderView = computed(() => folderPromptId.value !== null)
// Track which asset's popover is open (for single-instance popover management)
const openPopoverId = ref<string | null>(null)
// Determine if delete button should be shown
// Hide delete button when in input tab and not in cloud (OSS mode - files are from local folders)
const shouldShowDeleteButton = computed(() => {

View File

@@ -71,9 +71,10 @@
</div>
<div v-tooltip.top="$t('mediaAsset.actions.more')">
<MoreButton
ref="moreButtonRef"
size="sm"
@menu-opened="isMenuOpen = true"
@menu-closed="isMenuOpen = false"
@menu-opened="handleMenuOpened"
@menu-closed="handleMenuClosed"
@mouseenter="handleOverlayMouseEnter"
@mouseleave="handleOverlayMouseLeave"
>
@@ -139,7 +140,7 @@
</template>
<script setup lang="ts">
import { useElementHover } from '@vueuse/core'
import { useElementHover, whenever } from '@vueuse/core'
import { computed, defineAsyncComponent, provide, ref, toRef } from 'vue'
import IconButton from '@/components/button/IconButton.vue'
@@ -189,7 +190,8 @@ const {
selected,
showOutputCount,
outputCount,
showDeleteButton
showDeleteButton,
openPopoverId
} = defineProps<{
asset?: AssetItem
loading?: boolean
@@ -197,15 +199,19 @@ const {
showOutputCount?: boolean
outputCount?: number
showDeleteButton?: boolean
openPopoverId?: string | null
}>()
const emit = defineEmits<{
zoom: [asset: AssetItem]
'output-count-click': []
'asset-deleted': []
'popover-opened': []
'popover-closed': []
}>()
const cardContainerRef = ref<HTMLElement>()
const moreButtonRef = ref<InstanceType<typeof MoreButton>>()
const isVideoPlaying = ref(false)
const isMenuOpen = ref(false)
@@ -339,4 +345,22 @@ const handleOutputCountClick = () => {
const handleAssetDelete = () => {
emit('asset-deleted')
}
const handleMenuOpened = () => {
isMenuOpen.value = true
emit('popover-opened')
}
const handleMenuClosed = () => {
isMenuOpen.value = false
emit('popover-closed')
}
// Close this popover when another opens
whenever(
() => openPopoverId && openPopoverId !== asset?.id && isMenuOpen.value,
() => {
moreButtonRef.value?.hide()
}
)
</script>