mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-07 08:30:06 +00:00
[feat] add sematic AssetCard
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
:key="badge.label"
|
||||
:class="
|
||||
cn(
|
||||
'px-2 py-1 rounded text-[10px] font-medium uppercase tracking-wider text-white',
|
||||
'px-2 py-1 rounded text-xs font-medium uppercase tracking-wider text-white',
|
||||
getBadgeColor(badge.type)
|
||||
)
|
||||
"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<BaseModalLayout
|
||||
data-component-id="AssetBrowserModal"
|
||||
class="h-full w-full max-h-full max-w-full min-w-0"
|
||||
class="size-full max-h-full max-w-full min-w-0"
|
||||
:content-title="contentTitle"
|
||||
@close="handleClose"
|
||||
>
|
||||
@@ -23,7 +23,7 @@
|
||||
v-model="searchQuery"
|
||||
size="lg"
|
||||
placeholder="Search assets..."
|
||||
class="max-w-[384px]"
|
||||
class="max-w-96"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -80,14 +80,7 @@ const {
|
||||
|
||||
// Compute whether to show left panel
|
||||
const shouldShowLeftPanel = computed(() => {
|
||||
// If explicitly set to false, don't show
|
||||
if (props.showLeftPanel === false) return false
|
||||
|
||||
// If explicitly set to true, always show
|
||||
if (props.showLeftPanel === true) return true
|
||||
|
||||
// Auto-hide if only one unique asset category (excluding "All Models")
|
||||
return availableCategories.value.length >= 3
|
||||
return props.showLeftPanel ?? availableCategories.value.length >= 3
|
||||
})
|
||||
|
||||
// Handle close button - call both the prop callback and emit the event
|
||||
|
||||
@@ -46,7 +46,8 @@ type Story = StoryObj<typeof meta>
|
||||
// Default story with all data provided
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
asset: createAssetData()
|
||||
asset: createAssetData(),
|
||||
interactive: true
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
@@ -58,6 +59,22 @@ export const Default: Story = {
|
||||
}
|
||||
}
|
||||
|
||||
// Non-interactive story
|
||||
export const NonInteractive: Story = {
|
||||
args: {
|
||||
asset: createAssetData(),
|
||||
interactive: false
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
'AssetCard in non-interactive mode - renders as div without button semantics.'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Story with all edge cases in a grid layout
|
||||
export const EdgeCases: Story = {
|
||||
render: () => ({
|
||||
@@ -139,6 +156,7 @@ export const EdgeCases: Story = {
|
||||
v-for="asset in edgeCases"
|
||||
:key="asset.id"
|
||||
:asset="asset"
|
||||
:interactive="true"
|
||||
@select="(asset) => console.log('Selected:', asset)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
<template>
|
||||
<div
|
||||
<component
|
||||
:is="interactive ? 'button' : 'div'"
|
||||
data-component-id="AssetCard"
|
||||
:data-asset-id="asset.id"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-label="`Select asset ${asset.name}`"
|
||||
v-bind="elementProps"
|
||||
:class="
|
||||
cn(
|
||||
'rounded-xl overflow-hidden cursor-pointer transition-all duration-200 min-w-60 max-w-64',
|
||||
'bg-ivory-100 border border-gray-300',
|
||||
'dark-theme:bg-charcoal-400 dark-theme:border-charcoal-600',
|
||||
'hover:transform hover:-translate-y-0.5 hover:shadow-[0_8px_24px_rgba(0,0,0,0.1)] hover:border-gray-400',
|
||||
'dark-theme:hover:shadow-[0_8px_24px_rgba(0,0,0,0.3)] dark-theme:hover:border-charcoal-700',
|
||||
'focus:outline-none focus:ring-2 focus:ring-blue-500 dark-theme:focus:ring-blue-400'
|
||||
// Base layout and container styles (always applied)
|
||||
'rounded-xl overflow-hidden transition-all duration-200 min-w-60 max-w-64',
|
||||
// Button-specific styles
|
||||
interactive && [
|
||||
'appearance-none bg-transparent p-0 m-0 font-inherit text-inherit outline-none cursor-pointer text-left',
|
||||
'bg-ivory-100 border border-gray-300 dark-theme:bg-charcoal-400 dark-theme:border-charcoal-600',
|
||||
'hover:transform hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/10 hover:border-gray-400',
|
||||
'dark-theme:hover:shadow-lg dark-theme:hover:shadow-black/30 dark-theme:hover:border-charcoal-700',
|
||||
'focus:outline-none focus:ring-2 focus:ring-blue-500 dark-theme:focus:ring-blue-400'
|
||||
],
|
||||
// Div-specific styles
|
||||
!interactive && [
|
||||
'bg-ivory-100 border border-gray-300',
|
||||
'dark-theme:bg-charcoal-400 dark-theme:border-charcoal-600'
|
||||
]
|
||||
)
|
||||
"
|
||||
@click="$emit('select', asset)"
|
||||
@keydown.enter="$emit('select', asset)"
|
||||
@keydown.space.prevent="$emit('select', asset)"
|
||||
@click="interactive && $emit('select', asset)"
|
||||
@keydown.enter="interactive && $emit('select', asset)"
|
||||
@keydown.space.prevent="interactive && $emit('select', asset)"
|
||||
>
|
||||
<div class="relative w-full aspect-square overflow-hidden">
|
||||
<div
|
||||
@@ -72,19 +80,31 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import AssetBadgeGroup from './AssetBadgeGroup.vue'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
asset: AssetDisplayItem
|
||||
interactive?: boolean
|
||||
}>()
|
||||
|
||||
const elementProps = computed(() =>
|
||||
props.interactive
|
||||
? {
|
||||
type: 'button',
|
||||
'aria-label': `Select asset ${props.asset.name}`
|
||||
}
|
||||
: {}
|
||||
)
|
||||
|
||||
defineEmits<{
|
||||
select: [asset: AssetDisplayItem]
|
||||
}>()
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
v-for="(asset, index) in assets"
|
||||
:key="asset.id"
|
||||
:asset="asset"
|
||||
:interactive="true"
|
||||
role="gridcell"
|
||||
:aria-posinset="index + 1"
|
||||
@select="$emit('assetSelect', $event)"
|
||||
|
||||
Reference in New Issue
Block a user