mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-23 16:24:06 +00:00
refactor: unify DropdownItem with AssetDropdownItem
- Create AssetDropdownItem in platform/assets/types - Create toAssetDropdownItem() transform utility - Remove deprecated DropdownItem, SelectedKey aliases - Rename mediaSrc to previewUrl, remove unused metadata field - Import types directly from source, remove re-exports Amp-Thread-ID: https://ampcode.com/threads/T-019c10b4-cabd-779d-a787-1ebf5dc8a067 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
15
src/platform/assets/types/assetDropdownTypes.ts
Normal file
15
src/platform/assets/types/assetDropdownTypes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { OptionId } from './filterTypes'
|
||||
|
||||
/**
|
||||
* Lightweight display projection of AssetItem for dropdown/selection UIs.
|
||||
* Used by FormDropdown and WidgetSelectDropdown.
|
||||
*/
|
||||
export interface AssetDropdownItem {
|
||||
id: OptionId
|
||||
/** Display name (user-defined filename or asset name) */
|
||||
name: string
|
||||
/** Original filename from asset */
|
||||
label?: string
|
||||
/** Preview image/video URL */
|
||||
previewUrl: string
|
||||
}
|
||||
14
src/platform/assets/utils/assetDropdownUtils.ts
Normal file
14
src/platform/assets/utils/assetDropdownUtils.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { AssetItem } from '../schemas/assetSchema'
|
||||
import type { AssetDropdownItem } from '../types/assetDropdownTypes'
|
||||
|
||||
/**
|
||||
* Transforms an AssetItem to AssetDropdownItem for dropdown display.
|
||||
*/
|
||||
export function toAssetDropdownItem(asset: AssetItem): AssetDropdownItem {
|
||||
return {
|
||||
id: asset.id,
|
||||
name: (asset.user_metadata?.filename as string | undefined) ?? asset.name,
|
||||
label: asset.name,
|
||||
previewUrl: asset.preview_url ?? ''
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import type { AssetSortOption } from '../types/filterTypes'
|
||||
|
||||
/**
|
||||
* Minimal interface for sortable items
|
||||
* Works with both AssetItem and DropdownItem
|
||||
* Works with both AssetItem and AssetDropdownItem
|
||||
*/
|
||||
export interface SortableItem {
|
||||
name: string
|
||||
|
||||
@@ -5,15 +5,15 @@ import PrimeVue from 'primevue/config'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type { ComboInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import type { DropdownItem } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types'
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
|
||||
import WidgetSelectDropdown from '@/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue'
|
||||
|
||||
interface WidgetSelectDropdownInstance extends ComponentPublicInstance {
|
||||
inputItems: DropdownItem[]
|
||||
outputItems: DropdownItem[]
|
||||
inputItems: AssetDropdownItem[]
|
||||
outputItems: AssetDropdownItem[]
|
||||
updateSelectedItems: (selectedSet: Set<string>) => void
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('WidgetSelectDropdown custom label mapping', () => {
|
||||
|
||||
// The missing value should be accessible via dropdownItems when filter is 'all' (default)
|
||||
const dropdownItems = (
|
||||
wrapper.vm as unknown as { dropdownItems: DropdownItem[] }
|
||||
wrapper.vm as unknown as { dropdownItems: AssetDropdownItem[] }
|
||||
).dropdownItems
|
||||
expect(
|
||||
dropdownItems.some((item) => item.name === 'template_image.png')
|
||||
@@ -248,7 +248,7 @@ describe('WidgetSelectDropdown custom label mapping', () => {
|
||||
|
||||
const vmWithFilter = wrapper.vm as unknown as {
|
||||
filterSelected: string
|
||||
dropdownItems: DropdownItem[]
|
||||
dropdownItems: AssetDropdownItem[]
|
||||
}
|
||||
|
||||
vmWithFilter.filterSelected = 'inputs'
|
||||
@@ -269,8 +269,8 @@ describe('WidgetSelectDropdown custom label mapping', () => {
|
||||
|
||||
const vmWithFilter = wrapper.vm as unknown as {
|
||||
filterSelected: string
|
||||
dropdownItems: DropdownItem[]
|
||||
outputItems: DropdownItem[]
|
||||
dropdownItems: AssetDropdownItem[]
|
||||
outputItems: AssetDropdownItem[]
|
||||
}
|
||||
|
||||
vmWithFilter.filterSelected = 'outputs'
|
||||
@@ -290,7 +290,7 @@ describe('WidgetSelectDropdown custom label mapping', () => {
|
||||
const wrapper = mountComponent(widget, 'img_001.png')
|
||||
|
||||
const dropdownItems = (
|
||||
wrapper.vm as unknown as { dropdownItems: DropdownItem[] }
|
||||
wrapper.vm as unknown as { dropdownItems: AssetDropdownItem[] }
|
||||
).dropdownItems
|
||||
expect(dropdownItems).toHaveLength(2)
|
||||
expect(
|
||||
@@ -305,7 +305,7 @@ describe('WidgetSelectDropdown custom label mapping', () => {
|
||||
const wrapper = mountComponent(widget, undefined)
|
||||
|
||||
const dropdownItems = (
|
||||
wrapper.vm as unknown as { dropdownItems: DropdownItem[] }
|
||||
wrapper.vm as unknown as { dropdownItems: AssetDropdownItem[] }
|
||||
).dropdownItems
|
||||
expect(dropdownItems).toHaveLength(2)
|
||||
expect(
|
||||
|
||||
@@ -6,13 +6,13 @@ import { useTransformCompatOverlayProps } from '@/composables/useTransformCompat
|
||||
import { t } from '@/i18n'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import FormDropdown from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue'
|
||||
import { AssetKindKey } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types'
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type {
|
||||
DropdownItem,
|
||||
FilterOption,
|
||||
LayoutMode,
|
||||
SelectedKey
|
||||
} from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types'
|
||||
OptionId
|
||||
} from '@/platform/assets/types/filterTypes'
|
||||
import { AssetKindKey } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types';
|
||||
import type { LayoutMode } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types';
|
||||
import WidgetLayoutField from '@/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue'
|
||||
import { useAssetWidgetData } from '@/renderer/extensions/vueNodes/widgets/composables/useAssetWidgetData'
|
||||
import type { ResultItemType } from '@/schemas/apiSchema'
|
||||
@@ -81,7 +81,7 @@ const filterOptions = computed<FilterOption[]>(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const selectedSet = ref<Set<SelectedKey>>(new Set())
|
||||
const selectedSet = ref<Set<OptionId>>(new Set())
|
||||
|
||||
/**
|
||||
* Transforms a value using getOptionLabel if available.
|
||||
@@ -100,7 +100,7 @@ function getDisplayLabel(value: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
const inputItems = computed<DropdownItem[]>(() => {
|
||||
const inputItems = computed<AssetDropdownItem[]>(() => {
|
||||
const values = props.widget.options?.values || []
|
||||
|
||||
if (!Array.isArray(values)) {
|
||||
@@ -109,13 +109,12 @@ const inputItems = computed<DropdownItem[]>(() => {
|
||||
|
||||
return values.map((value: string, index: number) => ({
|
||||
id: `input-${index}`,
|
||||
mediaSrc: getMediaUrl(value, 'input'),
|
||||
previewUrl: getMediaUrl(value, 'input'),
|
||||
name: value,
|
||||
label: getDisplayLabel(value),
|
||||
metadata: ''
|
||||
label: getDisplayLabel(value)
|
||||
}))
|
||||
})
|
||||
const outputItems = computed<DropdownItem[]>(() => {
|
||||
const outputItems = computed<AssetDropdownItem[]>(() => {
|
||||
if (!['image', 'video'].includes(props.assetKind ?? '')) return []
|
||||
|
||||
const outputs = new Set<string>()
|
||||
@@ -140,10 +139,9 @@ const outputItems = computed<DropdownItem[]>(() => {
|
||||
|
||||
return Array.from(outputs).map((output) => ({
|
||||
id: `output-${output}`,
|
||||
mediaSrc: getMediaUrl(output.replace(' [output]', ''), 'output'),
|
||||
previewUrl: getMediaUrl(output.replace(' [output]', ''), 'output'),
|
||||
name: output,
|
||||
label: getDisplayLabel(output),
|
||||
metadata: ''
|
||||
label: getDisplayLabel(output)
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -153,7 +151,7 @@ const outputItems = computed<DropdownItem[]>(() => {
|
||||
* where the saved value may not exist in the current server environment.
|
||||
* Works for both local mode (inputItems/outputItems) and cloud mode (assetData).
|
||||
*/
|
||||
const missingValueItem = computed<DropdownItem | undefined>(() => {
|
||||
const missingValueItem = computed<AssetDropdownItem | undefined>(() => {
|
||||
const currentValue = modelValue.value
|
||||
if (!currentValue) return undefined
|
||||
|
||||
@@ -166,10 +164,9 @@ const missingValueItem = computed<DropdownItem | undefined>(() => {
|
||||
|
||||
return {
|
||||
id: `missing-${currentValue}`,
|
||||
mediaSrc: '',
|
||||
previewUrl: '',
|
||||
name: currentValue,
|
||||
label: getDisplayLabel(currentValue),
|
||||
metadata: ''
|
||||
label: getDisplayLabel(currentValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,14 +187,13 @@ const missingValueItem = computed<DropdownItem | undefined>(() => {
|
||||
|
||||
return {
|
||||
id: `missing-${currentValue}`,
|
||||
mediaSrc: getMediaUrl(strippedValue, isOutput ? 'output' : 'input'),
|
||||
previewUrl: getMediaUrl(strippedValue, isOutput ? 'output' : 'input'),
|
||||
name: currentValue,
|
||||
label: getDisplayLabel(currentValue),
|
||||
metadata: ''
|
||||
label: getDisplayLabel(currentValue)
|
||||
}
|
||||
})
|
||||
|
||||
const allItems = computed<DropdownItem[]>(() => {
|
||||
const allItems = computed<AssetDropdownItem[]>(() => {
|
||||
if (props.isAssetMode && assetData) {
|
||||
const items = assetData.dropdownItems.value
|
||||
if (missingValueItem.value) {
|
||||
@@ -212,7 +208,7 @@ const allItems = computed<DropdownItem[]>(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const dropdownItems = computed<DropdownItem[]>(() => {
|
||||
const dropdownItems = computed<AssetDropdownItem[]>(() => {
|
||||
if (props.isAssetMode) {
|
||||
return allItems.value
|
||||
}
|
||||
@@ -290,8 +286,8 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function updateSelectedItems(selectedItems: Set<SelectedKey>) {
|
||||
let id: SelectedKey | undefined = undefined
|
||||
function updateSelectedItems(selectedItems: Set<OptionId>) {
|
||||
let id: OptionId | undefined = undefined
|
||||
if (selectedItems.size > 0) {
|
||||
id = selectedItems.values().next().value!
|
||||
}
|
||||
|
||||
@@ -5,20 +5,19 @@ import { computed, ref, useTemplateRef } from 'vue'
|
||||
import { t } from '@/i18n'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type {
|
||||
FilterOption,
|
||||
OptionId
|
||||
} from '@/platform/assets/types/filterTypes'
|
||||
|
||||
import FormDropdownInput from './FormDropdownInput.vue'
|
||||
import FormDropdownMenu from './FormDropdownMenu.vue'
|
||||
import { defaultSearcher, getDefaultSortOptions } from './shared'
|
||||
import type {
|
||||
DropdownItem,
|
||||
FilterOption,
|
||||
LayoutMode,
|
||||
OptionId,
|
||||
SelectedKey,
|
||||
SortOption
|
||||
} from './types'
|
||||
import type { LayoutMode, SortOption } from './types'
|
||||
|
||||
interface Props {
|
||||
items: DropdownItem[]
|
||||
items: AssetDropdownItem[]
|
||||
placeholder?: string
|
||||
/**
|
||||
* If true, allows multiple selections. If a number is provided,
|
||||
@@ -32,15 +31,15 @@ interface Props {
|
||||
filterOptions?: FilterOption[]
|
||||
sortOptions?: SortOption[]
|
||||
isSelected?: (
|
||||
selected: Set<SelectedKey>,
|
||||
item: DropdownItem,
|
||||
selected: Set<OptionId>,
|
||||
item: AssetDropdownItem,
|
||||
index: number
|
||||
) => boolean
|
||||
searcher?: (
|
||||
query: string,
|
||||
items: DropdownItem[],
|
||||
items: AssetDropdownItem[],
|
||||
onCleanup: (cleanupFn: () => void) => void
|
||||
) => Promise<DropdownItem[]>
|
||||
) => Promise<AssetDropdownItem[]>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -54,7 +53,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
searcher: defaultSearcher
|
||||
})
|
||||
|
||||
const selected = defineModel<Set<SelectedKey>>('selected', {
|
||||
const selected = defineModel<Set<OptionId>>('selected', {
|
||||
default: new Set()
|
||||
})
|
||||
const filterSelected = defineModel<OptionId>('filterSelected', { default: '' })
|
||||
@@ -80,7 +79,7 @@ const maxSelectable = computed(() => {
|
||||
|
||||
const itemsKey = computed(() => props.items.map((item) => item.id).join('|'))
|
||||
|
||||
const filteredItems = ref<DropdownItem[]>([])
|
||||
const filteredItems = ref<AssetDropdownItem[]>([])
|
||||
|
||||
const defaultSorter = computed<SortOption['sorter']>(() => {
|
||||
const sorter = props.sortOptions.find(
|
||||
@@ -99,7 +98,7 @@ const sortedItems = computed(() => {
|
||||
return selectedSorter.value({ items: filteredItems.value }) || []
|
||||
})
|
||||
|
||||
function internalIsSelected(item: DropdownItem, index: number): boolean {
|
||||
function internalIsSelected(item: AssetDropdownItem, index: number): boolean {
|
||||
return props.isSelected?.(selected.value, item, index) ?? false
|
||||
}
|
||||
|
||||
@@ -128,7 +127,7 @@ function handleFileChange(event: Event) {
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
function handleSelection(item: DropdownItem, index: number) {
|
||||
function handleSelection(item: AssetDropdownItem, index: number) {
|
||||
if (props.disabled) return
|
||||
const sel = selected.value
|
||||
if (internalIsSelected(item, index)) {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type { OptionId } from '@/platform/assets/types/filterTypes'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import { WidgetInputBaseClass } from '../../layout'
|
||||
import type { DropdownItem, SelectedKey } from './types'
|
||||
|
||||
interface Props {
|
||||
isOpen?: boolean
|
||||
placeholder?: string
|
||||
items: DropdownItem[]
|
||||
selected: Set<SelectedKey>
|
||||
items: AssetDropdownItem[]
|
||||
selected: Set<OptionId>
|
||||
maxSelectable: number
|
||||
uploadable: boolean
|
||||
disabled: boolean
|
||||
|
||||
@@ -3,20 +3,20 @@ import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type {
|
||||
FilterOption,
|
||||
OptionId
|
||||
} from '@/platform/assets/types/filterTypes'
|
||||
|
||||
import FormDropdownMenuActions from './FormDropdownMenuActions.vue'
|
||||
import FormDropdownMenuFilter from './FormDropdownMenuFilter.vue'
|
||||
import FormDropdownMenuItem from './FormDropdownMenuItem.vue'
|
||||
import type {
|
||||
DropdownItem,
|
||||
FilterOption,
|
||||
LayoutMode,
|
||||
OptionId,
|
||||
SortOption
|
||||
} from './types'
|
||||
import type { LayoutMode, SortOption } from './types'
|
||||
|
||||
interface Props {
|
||||
items: DropdownItem[]
|
||||
isSelected: (item: DropdownItem, index: number) => boolean
|
||||
items: AssetDropdownItem[]
|
||||
isSelected: (item: AssetDropdownItem, index: number) => boolean
|
||||
filterOptions: FilterOption[]
|
||||
sortOptions: SortOption[]
|
||||
searcher?: (
|
||||
@@ -28,7 +28,7 @@ interface Props {
|
||||
|
||||
defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'item-click', item: DropdownItem, index: number): void
|
||||
(e: 'item-click', item: AssetDropdownItem, index: number): void
|
||||
}>()
|
||||
|
||||
// Define models for two-way binding
|
||||
@@ -90,10 +90,9 @@ const searchQuery = defineModel<string>('searchQuery')
|
||||
:key="item.id"
|
||||
:index="index"
|
||||
:selected="isSelected(item, index)"
|
||||
:media-src="item.mediaSrc"
|
||||
:preview-url="item.previewUrl"
|
||||
:name="item.name"
|
||||
:label="item.label"
|
||||
:metadata="item.metadata"
|
||||
:layout="layoutMode"
|
||||
@click="emit('item-click', item, index)"
|
||||
/>
|
||||
|
||||
@@ -4,10 +4,11 @@ import type { MaybeRefOrGetter } from 'vue'
|
||||
import Popover from 'primevue/popover'
|
||||
import { ref, useTemplateRef } from 'vue'
|
||||
|
||||
import type { OptionId } from '@/platform/assets/types/filterTypes'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import FormSearchInput from '../FormSearchInput.vue'
|
||||
import type { LayoutMode, OptionId, SortOption } from './types'
|
||||
import type { LayoutMode, SortOption } from './types'
|
||||
|
||||
defineProps<{
|
||||
searcher?: (
|
||||
|
||||
@@ -3,10 +3,12 @@ import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useModelUpload } from '@/platform/assets/composables/useModelUpload'
|
||||
import type {
|
||||
FilterOption,
|
||||
OptionId
|
||||
} from '@/platform/assets/types/filterTypes'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import type { FilterOption, OptionId } from './types'
|
||||
|
||||
const { filterOptions } = defineProps<{
|
||||
filterOptions: FilterOption[]
|
||||
}>()
|
||||
|
||||
@@ -10,10 +10,9 @@ import type { LayoutMode } from './types'
|
||||
interface Props {
|
||||
index: number
|
||||
selected: boolean
|
||||
mediaSrc: string
|
||||
previewUrl: string
|
||||
name: string
|
||||
label?: string
|
||||
metadata?: string
|
||||
layout?: LayoutMode
|
||||
}
|
||||
|
||||
@@ -102,16 +101,16 @@ function handleVideoLoad(event: Event) {
|
||||
/>
|
||||
</div>
|
||||
<video
|
||||
v-if="mediaSrc && isVideo"
|
||||
:src="mediaSrc"
|
||||
v-if="previewUrl && isVideo"
|
||||
:src="previewUrl"
|
||||
class="size-full object-cover"
|
||||
preload="metadata"
|
||||
muted
|
||||
@loadeddata="handleVideoLoad"
|
||||
/>
|
||||
<LazyImage
|
||||
v-else-if="mediaSrc"
|
||||
:src="mediaSrc"
|
||||
v-else-if="previewUrl"
|
||||
:src="previewUrl"
|
||||
:alt="name"
|
||||
image-class="size-full object-cover"
|
||||
@load="handleImageLoad"
|
||||
@@ -146,9 +145,9 @@ function handleVideoLoad(event: Event) {
|
||||
{{ label ?? name }}
|
||||
</span>
|
||||
<!-- Meta Data -->
|
||||
<span class="text-secondary block text-xs">{{
|
||||
metadata || actualDimensions
|
||||
}}</span>
|
||||
<span v-if="actualDimensions" class="text-secondary block text-xs">
|
||||
{{ actualDimensions }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { defaultSearcher, getDefaultSortOptions } from './shared'
|
||||
import type { DropdownItem } from './types'
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
|
||||
function createItem(name: string, label?: string): DropdownItem {
|
||||
import { defaultSearcher, getDefaultSortOptions } from './shared'
|
||||
|
||||
function createItem(name: string, label?: string): AssetDropdownItem {
|
||||
return {
|
||||
id: name,
|
||||
mediaSrc: '',
|
||||
previewUrl: '',
|
||||
name,
|
||||
label,
|
||||
metadata: ''
|
||||
label
|
||||
}
|
||||
}
|
||||
|
||||
describe('defaultSearcher', () => {
|
||||
const items: DropdownItem[] = [
|
||||
const items: AssetDropdownItem[] = [
|
||||
createItem('apple.png'),
|
||||
createItem('banana.jpg'),
|
||||
createItem('cherry.gif')
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type { AssetSortOption } from '@/platform/assets/types/filterTypes'
|
||||
import { sortAssets } from '@/platform/assets/utils/assetSortUtils'
|
||||
|
||||
import type { DropdownItem, SortOption } from './types'
|
||||
import type { SortOption } from './types'
|
||||
|
||||
export async function defaultSearcher(query: string, items: DropdownItem[]) {
|
||||
export async function defaultSearcher(
|
||||
query: string,
|
||||
items: AssetDropdownItem[]
|
||||
) {
|
||||
if (query.trim() === '') return items
|
||||
const words = query.trim().toLowerCase().split(' ')
|
||||
return items.filter((item) => {
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
import type { ComputedRef, InjectionKey } from 'vue'
|
||||
|
||||
import type {
|
||||
FilterOption,
|
||||
OptionId
|
||||
} from '@/platform/assets/types/filterTypes'
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import type { OptionId } from '@/platform/assets/types/filterTypes'
|
||||
import type { AssetKind } from '@/types/widgetTypes'
|
||||
|
||||
export type { FilterOption, OptionId }
|
||||
|
||||
export type SelectedKey = OptionId
|
||||
|
||||
export interface DropdownItem {
|
||||
id: SelectedKey
|
||||
mediaSrc: string // URL for image, video, or other media
|
||||
name: string
|
||||
label?: string
|
||||
metadata: string
|
||||
}
|
||||
|
||||
export interface SortOption<TId extends OptionId = OptionId> {
|
||||
id: TId
|
||||
name: string
|
||||
sorter: (ctx: { items: readonly DropdownItem[] }) => DropdownItem[]
|
||||
sorter: (ctx: { items: readonly AssetDropdownItem[] }) => AssetDropdownItem[]
|
||||
}
|
||||
|
||||
export type LayoutMode = 'list' | 'grid' | 'list-small'
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('useAssetWidgetData (cloud mode, isCloud=true)', () => {
|
||||
expect(item.id).toBe('asset-1')
|
||||
expect(item.name).toBe('models/beautiful_model.safetensors')
|
||||
expect(item.label).toBe('Beautiful Model')
|
||||
expect(item.mediaSrc).toBe('/api/preview/asset-1')
|
||||
expect(item.previewUrl).toBe('/api/preview/asset-1')
|
||||
})
|
||||
|
||||
it('handles API errors gracefully', async () => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { computed, toValue, watch } from 'vue'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
import type { DropdownItem } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types'
|
||||
import type { AssetDropdownItem } from '@/platform/assets/types/assetDropdownTypes'
|
||||
import { toAssetDropdownItem } from '@/platform/assets/utils/assetDropdownUtils'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useAssetsStore } from '@/stores/assetsStore'
|
||||
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
|
||||
|
||||
@@ -47,15 +48,8 @@ export function useAssetWidgetData(
|
||||
return resolvedType ? (assetsStore.getError(resolvedType) ?? null) : null
|
||||
})
|
||||
|
||||
const dropdownItems = computed<DropdownItem[]>(() => {
|
||||
return (assets.value ?? []).map((asset) => ({
|
||||
id: asset.id,
|
||||
name:
|
||||
(asset.user_metadata?.filename as string | undefined) ?? asset.name,
|
||||
label: asset.name,
|
||||
mediaSrc: asset.preview_url ?? '',
|
||||
metadata: ''
|
||||
}))
|
||||
const dropdownItems = computed<AssetDropdownItem[]>(() => {
|
||||
return (assets.value ?? []).map(toAssetDropdownItem)
|
||||
})
|
||||
|
||||
watch(
|
||||
|
||||
Reference in New Issue
Block a user