mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 20:51:58 +00:00
182 lines
5.3 KiB
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>
|