mirror of https://github.com/langgenius/dify.git
feat(trigger): dynamic options opt
This commit is contained in:
parent
7d56ca5294
commit
bf7b18d442
|
|
@ -4,7 +4,6 @@ import { FormItemValidateStatusEnum, FormTypeEnum } from '@/app/components/base/
|
|||
import Input from '@/app/components/base/input'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import RadioE from '@/app/components/base/radio/ui'
|
||||
import { PortalSelect } from '@/app/components/base/select'
|
||||
import PureSelect from '@/app/components/base/select/pure'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
|
|
@ -161,7 +160,7 @@ const BaseField = ({
|
|||
|
||||
const value = useStore(field.form.store, s => s.values[field.name])
|
||||
|
||||
const { data: dynamicOptionsData, isLoading: isDynamicOptionsLoading } = useTriggerPluginDynamicOptions(
|
||||
const { data: dynamicOptionsData, isLoading: isDynamicOptionsLoading, error: dynamicOptionsError } = useTriggerPluginDynamicOptions(
|
||||
dynamicSelectParams || {
|
||||
plugin_id: '',
|
||||
provider: '',
|
||||
|
|
@ -176,7 +175,7 @@ const BaseField = ({
|
|||
if (!dynamicOptionsData?.options)
|
||||
return []
|
||||
return dynamicOptionsData.options.map(option => ({
|
||||
name: getTranslatedContent({ content: option.label, render: renderI18nObject }),
|
||||
label: getTranslatedContent({ content: option.label, render: renderI18nObject }),
|
||||
value: option.value,
|
||||
}))
|
||||
}, [dynamicOptionsData, renderI18nObject])
|
||||
|
|
@ -250,17 +249,20 @@ const BaseField = ({
|
|||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.dynamicSelect && (
|
||||
<PortalSelect
|
||||
<PureSelect
|
||||
options={dynamicOptions}
|
||||
value={value}
|
||||
onSelect={(item: any) => field.handleChange(item.value)}
|
||||
readonly={disabled || isDynamicOptionsLoading}
|
||||
onChange={field.handleChange}
|
||||
disabled={disabled || isDynamicOptionsLoading}
|
||||
placeholder={
|
||||
isDynamicOptionsLoading
|
||||
? 'Loading options...'
|
||||
: translatedPlaceholder || 'Select an option'
|
||||
? t('common.dynamicSelect.loading')
|
||||
: translatedPlaceholder
|
||||
}
|
||||
items={dynamicOptions}
|
||||
popupClassName="z-[9999]"
|
||||
{...(dynamicOptionsError ? { popupProps: { title: t('common.dynamicSelect.error'), titleClassName: 'text-text-destructive-secondary' } }
|
||||
: (!dynamicOptions.length ? { popupProps: { title: t('common.dynamicSelect.noData') } } : {}))}
|
||||
triggerPopupSameWidth
|
||||
multiple={multiple}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -22,10 +23,8 @@ export type Option = {
|
|||
value: string
|
||||
}
|
||||
|
||||
export type PureSelectProps = {
|
||||
type SharedPureSelectProps = {
|
||||
options: Option[]
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
containerProps?: PortalToFollowElemOptions & {
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
|
|
@ -38,22 +37,39 @@ export type PureSelectProps = {
|
|||
className?: string
|
||||
itemClassName?: string
|
||||
title?: string
|
||||
titleClassName?: string
|
||||
},
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
triggerPopupSameWidth?: boolean
|
||||
}
|
||||
const PureSelect = ({
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
containerProps,
|
||||
triggerProps,
|
||||
popupProps,
|
||||
placeholder,
|
||||
disabled,
|
||||
triggerPopupSameWidth,
|
||||
}: PureSelectProps) => {
|
||||
|
||||
type SingleSelectProps = {
|
||||
multiple?: false
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
}
|
||||
|
||||
type MultiSelectProps = {
|
||||
multiple: true
|
||||
value?: string[]
|
||||
onChange?: (value: string[]) => void
|
||||
}
|
||||
|
||||
export type PureSelectProps = SharedPureSelectProps & (SingleSelectProps | MultiSelectProps)
|
||||
const PureSelect = (props: PureSelectProps) => {
|
||||
const {
|
||||
options,
|
||||
containerProps,
|
||||
triggerProps,
|
||||
popupProps,
|
||||
placeholder,
|
||||
disabled,
|
||||
triggerPopupSameWidth,
|
||||
multiple,
|
||||
value,
|
||||
onChange,
|
||||
} = props
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
open,
|
||||
|
|
@ -69,6 +85,7 @@ const PureSelect = ({
|
|||
className: popupClassName,
|
||||
itemClassName: popupItemClassName,
|
||||
title: popupTitle,
|
||||
titleClassName: popupTitleClassName,
|
||||
} = popupProps || {}
|
||||
|
||||
const [localOpen, setLocalOpen] = useState(false)
|
||||
|
|
@ -79,8 +96,13 @@ const PureSelect = ({
|
|||
setLocalOpen(openValue)
|
||||
}, [onOpenChange])
|
||||
|
||||
const selectedOption = options.find(option => option.value === value)
|
||||
const triggerText = selectedOption?.label || placeholder || t('common.placeholder.select')
|
||||
const triggerText = useMemo(() => {
|
||||
const placeholderText = placeholder || t('common.placeholder.select')
|
||||
if (multiple)
|
||||
return value?.length ? t('common.dynamicSelect.selected', { count: value.length }) : placeholderText
|
||||
|
||||
return options.find(option => option.value === value)?.label || placeholderText
|
||||
}, [multiple, value, options, placeholder])
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
|
|
@ -122,13 +144,16 @@ const PureSelect = ({
|
|||
)}>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg',
|
||||
'max-h-80 overflow-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg',
|
||||
popupClassName,
|
||||
)}
|
||||
>
|
||||
{
|
||||
popupTitle && (
|
||||
<div className='system-xs-medium-uppercase flex h-[22px] items-center px-3 text-text-tertiary'>
|
||||
<div className={cn(
|
||||
'system-xs-medium-uppercase flex h-[22px] items-center px-3 text-text-tertiary',
|
||||
popupTitleClassName,
|
||||
)}>
|
||||
{popupTitle}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -144,6 +169,14 @@ const PureSelect = ({
|
|||
title={option.label}
|
||||
onClick={() => {
|
||||
if (disabled) return
|
||||
if (multiple) {
|
||||
const currentValues = value ?? []
|
||||
const nextValues = currentValues.includes(option.value)
|
||||
? currentValues.filter(valueItem => valueItem !== option.value)
|
||||
: [...currentValues, option.value]
|
||||
onChange?.(nextValues)
|
||||
return
|
||||
}
|
||||
onChange?.(option.value)
|
||||
handleOpenChange(false)
|
||||
}}
|
||||
|
|
@ -152,7 +185,11 @@ const PureSelect = ({
|
|||
{option.label}
|
||||
</div>
|
||||
{
|
||||
value === option.value && <RiCheckLine className='h-4 w-4 shrink-0 text-text-accent' />
|
||||
(
|
||||
multiple
|
||||
? (value ?? []).includes(option.value)
|
||||
: value === option.value
|
||||
) && <RiCheckLine className='h-4 w-4 shrink-0 text-text-accent' />
|
||||
}
|
||||
</div>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -776,6 +776,12 @@ const translation = {
|
|||
supportedFormats: 'Supports PNG, JPG, JPEG, WEBP and GIF',
|
||||
},
|
||||
you: 'You',
|
||||
dynamicSelect: {
|
||||
error: 'Loading options failed',
|
||||
noData: 'No options available',
|
||||
loading: 'Loading options...',
|
||||
selected: '{{count}} selected',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -776,6 +776,12 @@ const translation = {
|
|||
title: '提供反馈',
|
||||
placeholder: '请描述发生了什么问题或我们可以如何改进...',
|
||||
},
|
||||
dynamicSelect: {
|
||||
error: '加载选项失败',
|
||||
noData: '没有可用的选项',
|
||||
loading: '加载选项...',
|
||||
selected: '已选择 {{count}} 项',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -299,8 +299,10 @@ export const useTriggerPluginDynamicOptions = (payload: {
|
|||
provider_type: 'trigger', // Add required provider_type parameter
|
||||
},
|
||||
},
|
||||
{ silent: true },
|
||||
),
|
||||
enabled: enabled && !!payload.plugin_id && !!payload.provider && !!payload.action && !!payload.parameter && !!payload.credential_id,
|
||||
retry: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue