Skip to content

Commit 03681a1

Browse files
authored
feat: AssetCard tweaks (#6085)
## Summary 1. fix `preview_url` logic 2. design tweaks for border radius, fallback gradient, and name line clamping (2 lines) 3. handle image error states by showing gradient 4. misc. refactors ## Screenshots <img width="1515" height="1087" alt="Screenshot 2025-10-15 at 8 13 41 PM" src="https://github.com/user-attachments/assets/85642869-d8cb-4ee4-b23d-a381e33fe802" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6085-feat-AssetCard-tweaks-28e6d73d3650818da7a2f4148be48ff7) by [Unito](https://www.unito.io)
1 parent 9bfc9b7 commit 03681a1

File tree

2 files changed

+99
-23
lines changed

2 files changed

+99
-23
lines changed

src/platform/assets/components/AssetCard.stories.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,51 @@ export const NonInteractive: Story = {
8484
}
8585
}
8686

87+
export const WithPreviewImage: Story = {
88+
args: {
89+
asset: createAssetData({
90+
preview_url: '/assets/images/comfy-logo-single.svg'
91+
}),
92+
interactive: true
93+
},
94+
decorators: [
95+
() => ({
96+
template:
97+
'<div class="p-8 bg-gray-50 dark-theme:bg-gray-900 max-w-96"><story /></div>'
98+
})
99+
],
100+
parameters: {
101+
docs: {
102+
description: {
103+
story: 'AssetCard with a preview image displayed.'
104+
}
105+
}
106+
}
107+
}
108+
109+
export const FallbackGradient: Story = {
110+
args: {
111+
asset: createAssetData({
112+
preview_url: undefined
113+
}),
114+
interactive: true
115+
},
116+
decorators: [
117+
() => ({
118+
template:
119+
'<div class="p-8 bg-gray-50 dark-theme:bg-gray-900 max-w-96"><story /></div>'
120+
})
121+
],
122+
parameters: {
123+
docs: {
124+
description: {
125+
story:
126+
'AssetCard showing fallback gradient when no preview image is available.'
127+
}
128+
}
129+
}
130+
}
131+
87132
export const EdgeCases: Story = {
88133
render: () => ({
89134
components: { AssetCard },

src/platform/assets/components/AssetCard.vue

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,29 @@
44
data-component-id="AssetCard"
55
:data-asset-id="asset.id"
66
v-bind="elementProps"
7-
:class="
8-
cn(
9-
// Base layout and container styles (always applied)
10-
'rounded-xl overflow-hidden transition-all duration-200',
11-
interactive && 'group',
12-
// Button-specific styles
13-
interactive && [
14-
'appearance-none bg-transparent p-0 m-0 font-inherit text-inherit outline-none cursor-pointer text-left',
15-
'bg-gray-100 dark-theme:bg-charcoal-800',
16-
'hover:bg-gray-200 dark-theme:hover:bg-charcoal-600',
17-
'border-none',
18-
'focus:outline-solid outline-blue-100 outline-4'
19-
],
20-
// Div-specific styles
21-
!interactive && 'bg-gray-100 dark-theme:bg-charcoal-800'
22-
)
23-
"
7+
:class="cardClasses"
248
@click="interactive && $emit('select', asset)"
259
@keydown.enter="interactive && $emit('select', asset)"
2610
>
27-
<div class="relative aspect-square w-full overflow-hidden">
11+
<div class="relative aspect-square w-full overflow-hidden rounded-xl">
12+
<img
13+
v-if="shouldShowImage"
14+
:src="asset.preview_url"
15+
class="h-full w-full object-contain"
16+
/>
2817
<div
29-
class="flex h-full w-full items-center justify-center bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-600"
18+
v-else
19+
class="flex h-full w-full items-center justify-center bg-gradient-to-br from-gray-400 via-gray-800 to-charcoal-400"
3020
></div>
3121
<AssetBadgeGroup :badges="asset.badges" />
3222
</div>
3323
<div :class="cn('p-4 h-32 flex flex-col justify-between')">
3424
<div>
3525
<h3
26+
:id="titleId"
3627
:class="
3728
cn(
38-
'mb-2 m-0 text-base font-semibold overflow-hidden text-ellipsis whitespace-nowrap',
29+
'mb-2 m-0 text-base font-semibold line-clamp-2 wrap-anywhere',
3930
'text-slate-800',
4031
'dark-theme:text-white'
4132
)
@@ -44,6 +35,7 @@
4435
{{ asset.name }}
4536
</h3>
4637
<p
38+
:id="descId"
4739
:class="
4840
cn(
4941
'm-0 text-sm leading-6 overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [display:-webkit-box]',
@@ -83,7 +75,8 @@
8375
</template>
8476

8577
<script setup lang="ts">
86-
import { computed } from 'vue'
78+
import { useImage } from '@vueuse/core'
79+
import { computed, useId } from 'vue'
8780
8881
import AssetBadgeGroup from '@/platform/assets/components/AssetBadgeGroup.vue'
8982
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
@@ -94,13 +87,51 @@ const props = defineProps<{
9487
interactive?: boolean
9588
}>()
9689
90+
const titleId = useId()
91+
const descId = useId()
92+
93+
const { error } = useImage({
94+
src: props.asset.preview_url ?? '',
95+
alt: props.asset.name
96+
})
97+
98+
const shouldShowImage = computed(() => props.asset.preview_url && !error.value)
99+
100+
const cardClasses = computed(() => {
101+
const base = [
102+
'rounded-xl',
103+
'overflow-hidden',
104+
'transition-all',
105+
'duration-200'
106+
]
107+
108+
if (!props.interactive) {
109+
return cn(...base, 'bg-gray-100 dark-theme:bg-charcoal-800')
110+
}
111+
112+
return cn(
113+
...base,
114+
'group',
115+
'appearance-none bg-transparent p-0 m-0',
116+
'font-inherit text-inherit outline-none cursor-pointer text-left',
117+
'bg-gray-100 dark-theme:bg-charcoal-800',
118+
'hover:bg-gray-200 dark-theme:hover:bg-charcoal-600',
119+
'border-none',
120+
'focus:outline-solid outline-blue-100 outline-4'
121+
)
122+
})
123+
97124
const elementProps = computed(() =>
98125
props.interactive
99126
? {
100127
type: 'button',
101-
'aria-label': `Select asset ${props.asset.name}`
128+
'aria-labelledby': titleId,
129+
'aria-describedby': descId
130+
}
131+
: {
132+
'aria-labelledby': titleId,
133+
'aria-describedby': descId
102134
}
103-
: {}
104135
)
105136
106137
defineEmits<{

0 commit comments

Comments
 (0)