Files
ComfyUI_frontend/src/platform/assets/components/AssetCard.vue
Arjan Singh dc8a0e04a5 fix(AssetCard): use tooltip instead of title for overflow text (#6556)
## Summary

The default `title` attribute was too small and too slow to show for
users. We were getting complaints about not being able to see the full
name of the model.

Using [Prime Vue's tooltip](https://primevue.org/tooltip) instead.

## Screenshots (if applicable)

Before:

<img width="480" height="246" alt="image"
src="https://github.com/user-attachments/assets/db4d1cc7-9e9a-40b3-bf16-57f2def93726"
/>


After:

<img width="518" height="720" alt="image"
src="https://github.com/user-attachments/assets/500389e5-c781-4d3b-b62e-1e72d2b7e2c6"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6556-fix-AssetCard-use-tooltip-instead-of-title-for-overflow-text-2a06d73d365081939dc0d863b05f1e90)
by [Unito](https://www.unito.io)
2025-11-03 19:59:42 +00:00

144 lines
3.9 KiB
Vue

<template>
<component
:is="interactive ? 'button' : 'div'"
data-component-id="AssetCard"
:data-asset-id="asset.id"
v-bind="elementProps"
:class="cardClasses"
@click="interactive && $emit('select', asset)"
@keydown.enter="interactive && $emit('select', asset)"
>
<div class="relative aspect-square w-full overflow-hidden rounded-xl">
<img
v-if="shouldShowImage"
:src="asset.preview_url"
class="h-full w-full object-contain"
/>
<div
v-else
class="flex h-full w-full items-center justify-center bg-gradient-to-br from-smoke-400 via-smoke-800 to-charcoal-400"
></div>
<AssetBadgeGroup :badges="asset.badges" />
</div>
<div :class="cn('p-4 h-32 flex flex-col justify-between')">
<div>
<h3
:id="titleId"
v-tooltip.top="{ value: asset.name, showDelay: tooltipDelay }"
:class="
cn(
'mb-2 m-0 text-base font-semibold line-clamp-2 wrap-anywhere',
'text-base-foreground'
)
"
>
{{ asset.name }}
</h3>
<p
:id="descId"
v-tooltip.top="{ value: asset.description, showDelay: tooltipDelay }"
:class="
cn(
'm-0 text-sm leading-6 overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [display:-webkit-box]',
'text-ash-500',
'dark-theme:text-slate-100'
)
"
>
{{ asset.description }}
</p>
</div>
<div
:class="
cn('flex gap-4 text-xs', 'text-stone-400', 'dark-theme:text-ash-300')
"
>
<span v-if="asset.stats.stars" class="flex items-center gap-1">
<i class="icon-[lucide--star] size-3" />
{{ asset.stats.stars }}
</span>
<span v-if="asset.stats.downloadCount" class="flex items-center gap-1">
<i class="icon-[lucide--download] size-3" />
{{ asset.stats.downloadCount }}
</span>
<span v-if="asset.stats.formattedDate" class="flex items-center gap-1">
<i class="icon-[lucide--clock] size-3" />
{{ asset.stats.formattedDate }}
</span>
</div>
</div>
</component>
</template>
<script setup lang="ts">
import { useImage } from '@vueuse/core'
import { computed, useId } from 'vue'
import AssetBadgeGroup from '@/platform/assets/components/AssetBadgeGroup.vue'
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
import { useSettingStore } from '@/platform/settings/settingStore'
import { cn } from '@/utils/tailwindUtil'
const props = defineProps<{
asset: AssetDisplayItem
interactive?: boolean
}>()
const settingStore = useSettingStore()
const titleId = useId()
const descId = useId()
const tooltipDelay = computed<number>(() =>
settingStore.get('LiteGraph.Node.TooltipDelay')
)
const { error } = useImage({
src: props.asset.preview_url ?? '',
alt: props.asset.name
})
const shouldShowImage = computed(() => props.asset.preview_url && !error.value)
const cardClasses = computed(() => {
const base = [
'rounded-xl',
'overflow-hidden',
'transition-all',
'duration-200'
]
if (!props.interactive) {
return cn(...base, 'bg-smoke-100 dark-theme:bg-charcoal-800')
}
return cn(
...base,
'group',
'appearance-none bg-transparent p-0 m-0',
'font-inherit text-inherit outline-none cursor-pointer text-left',
'bg-smoke-100 dark-theme:bg-charcoal-800',
'hover:bg-secondary-background',
'border-none',
'focus:outline-solid outline-azure-600 outline-4'
)
})
const elementProps = computed(() =>
props.interactive
? {
type: 'button',
'aria-labelledby': titleId,
'aria-describedby': descId
}
: {
'aria-labelledby': titleId,
'aria-describedby': descId
}
)
defineEmits<{
select: [asset: AssetDisplayItem]
}>()
</script>