Files
ComfyUI_frontend/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

182 lines
5.3 KiB
Vue

<script setup lang="ts">
import { computed, provide, ref, toRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
import { SUPPORTED_EXTENSIONS_ACCEPT } from '@/extensions/core/load3d/constants'
import { useMediaAssets } from '@/platform/assets/composables/media/useMediaAssets'
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 { 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 { useWidgetSelectActions } from '@/renderer/extensions/vueNodes/widgets/composables/useWidgetSelectActions'
import { useWidgetSelectItems } from '@/renderer/extensions/vueNodes/widgets/composables/useWidgetSelectItems'
import type { ResultItemType } from '@/schemas/apiSchema'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import type { AssetKind } from '@/types/widgetTypes'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
interface Props {
widget: SimplifiedWidget<string | undefined>
nodeType?: string
assetKind?: AssetKind
allowUpload?: boolean
uploadFolder?: ResultItemType
uploadSubfolder?: string
isAssetMode?: boolean
defaultLayoutMode?: LayoutMode
}
const props = defineProps<Props>()
provide(
AssetKindKey,
computed(() => props.assetKind)
)
const modelValue = defineModel<string | undefined>({
default(props: Props) {
const values = props.widget.options?.values
return (Array.isArray(values) ? values[0] : undefined) ?? ''
}
})
const { t } = useI18n()
const outputMediaAssets = useMediaAssets('output')
const transformCompatProps = useTransformCompatOverlayProps()
const combinedProps = computed(() => ({
...filterWidgetProps(props.widget.options, PANEL_EXCLUDED_PROPS),
...transformCompatProps.value
}))
const getAssetData = () => {
const nodeType: string | undefined =
props.widget.options?.nodeType ?? props.nodeType
if (props.isAssetMode && nodeType) {
return useAssetWidgetData(toRef(nodeType))
}
return null
}
const assetData = getAssetData()
const {
dropdownItems,
displayItems,
filterSelected,
filterOptions,
ownershipSelected,
showOwnershipFilter,
ownershipOptions,
baseModelSelected,
showBaseModelFilter,
baseModelOptions,
selectedSet
} = useWidgetSelectItems({
values: () => props.widget.options?.values as unknown[] | undefined,
getOptionLabel: () => props.widget.options?.getOptionLabel,
modelValue,
assetKind: () => props.assetKind,
outputMediaAssets,
assetData,
isAssetMode: () => props.isAssetMode
})
const { updateSelectedItems, handleFilesUpdate } = useWidgetSelectActions({
modelValue,
dropdownItems,
widget: () => props.widget,
uploadFolder: () => props.uploadFolder,
uploadSubfolder: () => props.uploadSubfolder
})
const mediaPlaceholder = computed(() => {
const options = props.widget.options
if (options?.placeholder) {
return options.placeholder
}
switch (props.assetKind) {
case 'image':
return t('widgets.uploadSelect.placeholderImage')
case 'video':
return t('widgets.uploadSelect.placeholderVideo')
case 'audio':
return t('widgets.uploadSelect.placeholderAudio')
case 'mesh':
return t('widgets.uploadSelect.placeholderMesh')
case 'model':
return t('widgets.uploadSelect.placeholderModel')
case 'unknown':
return t('widgets.uploadSelect.placeholderUnknown')
}
return t('widgets.uploadSelect.placeholder')
})
const uploadable = computed(() => {
if (props.isAssetMode) return false
return props.allowUpload === true
})
const acceptTypes = computed(() => {
// Be permissive with accept types because backend uses libraries
// that can handle a wide range of formats
switch (props.assetKind) {
case 'image':
return 'image/*'
case 'video':
return 'video/*'
case 'audio':
return 'audio/*'
case 'mesh':
return SUPPORTED_EXTENSIONS_ACCEPT
default:
return undefined
}
})
const layoutMode = ref<LayoutMode>(props.defaultLayoutMode ?? 'grid')
function handleIsOpenUpdate(isOpen: boolean) {
if (isOpen && !outputMediaAssets.loading.value) {
void outputMediaAssets.refresh()
}
}
</script>
<template>
<WidgetLayoutField :widget>
<FormDropdown
v-model:filter-selected="filterSelected"
v-model:layout-mode="layoutMode"
v-model:ownership-selected="ownershipSelected"
v-model:base-model-selected="baseModelSelected"
:selected="selectedSet"
:items="dropdownItems"
:display-items="displayItems"
:placeholder="mediaPlaceholder"
:multiple="false"
:uploadable
:accept="acceptTypes"
:filter-options
:show-ownership-filter
:ownership-options
:show-base-model-filter
:base-model-options
v-bind="combinedProps"
class="w-full"
@update:selected="updateSelectedItems"
@update:files="handleFilesUpdate"
@update:is-open="handleIsOpenUpdate"
/>
</WidgetLayoutField>
</template>