mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-12 08:30:08 +00:00
[QPOv2] Add list view to assets sidepanel (#7737)
This adds the list view to the media assets sidepanel, while also adding the active jobs to be displayed right now. The design for this is actually changing, which is why it is in draft right now. There are technical limitations of the virtual grid that doesn't make it easy for both the active jobs and generated assets to exist on the same container. Currently WIP right now. Part of the QPO v2 iteration, figma design can be found [here](https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3330-37286&m=dev). This will be implemented in a series of stacked PRs that can be reviewed and merged individually. main <-- #7737, #7743, #7745 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7737-QPOv2-Add-list-view-to-assets-sidepanel-2d26d73d365081858e22c48902bd56e2) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
99
src/platform/assets/components/AssetsListItem.stories.ts
Normal file
99
src/platform/assets/components/AssetsListItem.stories.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import AssetsListItem from '@/platform/assets/components/AssetsListItem.vue'
|
||||
|
||||
const meta: Meta<typeof AssetsListItem> = {
|
||||
title: 'Platform/Assets/AssetsListItem',
|
||||
component: AssetsListItem,
|
||||
parameters: {
|
||||
layout: 'centered'
|
||||
},
|
||||
decorators: [
|
||||
() => ({
|
||||
template: '<div class="p-8 bg-base-background"><story /></div>'
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
type AssetsListItemProps = InstanceType<typeof AssetsListItem>['$props']
|
||||
|
||||
function renderActiveJob(args: AssetsListItemProps) {
|
||||
return {
|
||||
components: { Button, AssetsListItem },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<AssetsListItem v-bind="args">
|
||||
<template #primary>
|
||||
<div class="flex items-center gap-1 text-text-primary">
|
||||
<span>Total:</span>
|
||||
<span class="font-medium">30%</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #secondary>
|
||||
<div class="flex items-center gap-1 text-text-secondary">
|
||||
<span>CLIP Text Encode:</span>
|
||||
<span>70%</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<Button variant="destructive" size="icon" aria-label="Cancel">
|
||||
<i class="icon-[lucide--x] size-4" />
|
||||
</Button>
|
||||
</template>
|
||||
</AssetsListItem>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
function renderGeneratedAsset(args: AssetsListItemProps) {
|
||||
return {
|
||||
components: { AssetsListItem },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<AssetsListItem v-bind="args">
|
||||
<template #secondary>
|
||||
<div class="flex items-center gap-2 text-text-secondary">
|
||||
<span>1m 56s</span>
|
||||
<span>512x512</span>
|
||||
</div>
|
||||
</template>
|
||||
</AssetsListItem>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export const ActiveJob: Story = {
|
||||
args: {
|
||||
previewUrl: '/assets/images/comfy-logo-single.svg',
|
||||
previewAlt: 'Job preview',
|
||||
progressTotalPercent: 30,
|
||||
progressCurrentPercent: 70
|
||||
},
|
||||
render: renderActiveJob
|
||||
}
|
||||
|
||||
export const FailedJob: Story = {
|
||||
args: {
|
||||
iconName: 'icon-[lucide--circle-alert]',
|
||||
iconClass: 'text-destructive-background',
|
||||
iconWrapperClass: 'bg-modal-card-placeholder-background',
|
||||
primaryText: 'Failed',
|
||||
secondaryText: '8:59:30pm'
|
||||
}
|
||||
}
|
||||
|
||||
export const GeneratedAsset: Story = {
|
||||
args: {
|
||||
previewUrl: '/assets/images/comfy-logo-single.svg',
|
||||
previewAlt: 'image03.png',
|
||||
primaryText: 'image03.png'
|
||||
},
|
||||
render: renderGeneratedAsset
|
||||
}
|
||||
116
src/platform/assets/components/AssetsListItem.vue
Normal file
116
src/platform/assets/components/AssetsListItem.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative flex items-center gap-2 overflow-hidden rounded-lg p-2 select-none"
|
||||
>
|
||||
<div
|
||||
v-if="hasAnyProgressPercent(progressTotalPercent, progressCurrentPercent)"
|
||||
:class="progressBarContainerClass"
|
||||
>
|
||||
<div
|
||||
v-if="hasProgressPercent(progressTotalPercent)"
|
||||
:class="progressBarPrimaryClass"
|
||||
:style="progressPercentStyle(progressTotalPercent)"
|
||||
/>
|
||||
<div
|
||||
v-if="hasProgressPercent(progressCurrentPercent)"
|
||||
:class="progressBarSecondaryClass"
|
||||
:style="progressPercentStyle(progressCurrentPercent)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'relative z-1 flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-sm bg-secondary-background',
|
||||
iconWrapperClass
|
||||
)
|
||||
"
|
||||
:aria-label="iconAriaLabel || undefined"
|
||||
>
|
||||
<slot
|
||||
name="icon"
|
||||
:preview-url="previewUrl"
|
||||
:preview-alt="previewAlt"
|
||||
:icon-name="iconName"
|
||||
:icon-class="iconClass"
|
||||
:icon-aria-label="iconAriaLabel"
|
||||
>
|
||||
<img
|
||||
v-if="previewUrl"
|
||||
:src="previewUrl"
|
||||
:alt="previewAlt"
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
<div v-else class="flex size-full items-center justify-center">
|
||||
<i
|
||||
aria-hidden="true"
|
||||
:class="
|
||||
cn(
|
||||
iconName ?? 'icon-[lucide--image]',
|
||||
'size-4 text-text-secondary',
|
||||
iconClass
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="relative z-1 flex min-w-0 flex-1 flex-col gap-1">
|
||||
<div
|
||||
v-if="$slots.primary || primaryText"
|
||||
class="text-xs leading-none text-text-primary"
|
||||
>
|
||||
<slot name="primary">{{ primaryText }}</slot>
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots.secondary || secondaryText"
|
||||
class="text-xs leading-none text-text-secondary"
|
||||
>
|
||||
<slot name="secondary">{{ secondaryText }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="$slots.actions" class="relative z-1 flex items-center gap-2">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useProgressBarBackground } from '@/composables/useProgressBarBackground'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const {
|
||||
previewUrl,
|
||||
previewAlt = '',
|
||||
iconName,
|
||||
iconAriaLabel,
|
||||
iconClass,
|
||||
iconWrapperClass,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
progressTotalPercent,
|
||||
progressCurrentPercent
|
||||
} = defineProps<{
|
||||
previewUrl?: string
|
||||
previewAlt?: string
|
||||
iconName?: string
|
||||
iconAriaLabel?: string
|
||||
iconClass?: string
|
||||
iconWrapperClass?: string
|
||||
primaryText?: string
|
||||
secondaryText?: string
|
||||
progressTotalPercent?: number
|
||||
progressCurrentPercent?: number
|
||||
}>()
|
||||
|
||||
const {
|
||||
progressBarContainerClass,
|
||||
progressBarPrimaryClass,
|
||||
progressBarSecondaryClass,
|
||||
hasProgressPercent,
|
||||
hasAnyProgressPercent,
|
||||
progressPercentStyle
|
||||
} = useProgressBarBackground()
|
||||
</script>
|
||||
14
src/platform/assets/utils/mediaIconUtil.ts
Normal file
14
src/platform/assets/utils/mediaIconUtil.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { MediaKind } from '@/platform/assets/schemas/mediaAssetSchema'
|
||||
|
||||
export function iconForMediaType(mediaType: MediaKind): string {
|
||||
switch (mediaType) {
|
||||
case 'video':
|
||||
return 'icon-[lucide--video]'
|
||||
case 'audio':
|
||||
return 'icon-[lucide--music]'
|
||||
case '3D':
|
||||
return 'icon-[lucide--box]'
|
||||
default:
|
||||
return 'icon-[lucide--image]'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user