Asset Browser Modal Component (#5607)

* [ci] ignore playwright mcp directory

* [feat] add AssetBrowserModal

And all related sub components

* [feat] reactive filter functions

* [ci] clean up storybook config

* [feat] add sematic AssetCard

* [fix] i love lucide

* [fix] AssetCard layout issues

* [fix] add AssetBadge type

* [fix] simplify useAssetBrowser

* [fix] modal layout

* [fix] simplify useAssetBrowserDialog

* [fix] add tailwind back to storybook

* [fix] better reponsive layout

* [fix] missed i18n string

* [fix] missing i18n translations

* [fix] remove erroneous prevent on keyboard.space

* [feat] add asset metadata validation utilities

* [fix] remove erroneous test code

* [fix] remove forced min and max width on AssetCard

* [fix] import statement nits
This commit is contained in:
Arjan Singh
2025-09-17 16:17:09 -07:00
committed by GitHub
parent 08220d50d9
commit dfa1cbba4f
27 changed files with 2636 additions and 61 deletions

View File

@@ -0,0 +1,178 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import AssetBrowserModal from '@/platform/assets/components/AssetBrowserModal.vue'
import {
createMockAssets,
mockAssets
} from '@/platform/assets/fixtures/ui-mock-assets'
// Story arguments interface
interface StoryArgs {
nodeType: string
inputName: string
currentValue: string
showLeftPanel?: boolean
}
const meta: Meta<StoryArgs> = {
title: 'Platform/Assets/AssetBrowserModal',
component: AssetBrowserModal,
parameters: {
layout: 'fullscreen'
},
argTypes: {
nodeType: {
control: 'select',
options: ['CheckpointLoaderSimple', 'VAELoader', 'ControlNetLoader'],
description: 'ComfyUI node type for context'
},
inputName: {
control: 'select',
options: ['ckpt_name', 'vae_name', 'control_net_name'],
description: 'Widget input name'
},
currentValue: {
control: 'text',
description: 'Current selected asset value'
},
showLeftPanel: {
control: 'boolean',
description: 'Whether to show the left panel with categories'
}
}
}
export default meta
type Story = StoryObj<typeof meta>
// Modal Layout Stories
export const Default: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: any) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
return {
...args,
onAssetSelect,
onClose,
assets: mockAssets
}
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
})
}
// Story demonstrating single asset type (auto-hides left panel)
export const SingleAssetType: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: any) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
// Create assets with only one type (checkpoints)
const singleTypeAssets = createMockAssets(15).map((asset) => ({
...asset,
type: 'checkpoint'
}))
return { ...args, onAssetSelect, onClose, assets: singleTypeAssets }
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
}),
parameters: {
docs: {
description: {
story:
'Modal with assets of only one type (checkpoint) - left panel auto-hidden.'
}
}
}
}
// Story with left panel explicitly hidden
export const NoLeftPanel: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: any) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
return { ...args, onAssetSelect, onClose, assets: mockAssets }
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
}),
parameters: {
docs: {
description: {
story:
'Modal with left panel explicitly disabled via showLeftPanel=false.'
}
}
}
}