From d12e9b81e3d8c4a6e3faba8b3369981f98dac022 Mon Sep 17 00:00:00 2001 From: twwu Date: Sat, 26 Apr 2025 21:50:21 +0800 Subject: [PATCH] feat: Introduce new form field components and enhance existing ones with label options --- .../form/components/field/custom-select.tsx | 26 ++---- .../base/form/components/field/file-types.tsx | 83 +++++++++++++++++++ .../field/input-type-select/hooks.tsx | 51 ++++++++++++ .../field/input-type-select/index.tsx | 64 ++++++++++++++ .../field/input-type-select/option.tsx | 21 +++++ .../field/input-type-select/trigger.tsx | 42 ++++++++++ .../field/input-type-select/types.tsx | 19 +++++ .../form/components/field/number-input.tsx | 16 +--- .../form/components/field/number-slider.tsx | 47 +++++++++++ .../base/form/components/field/options.tsx | 7 +- .../base/form/components/field/select.tsx | 24 ++---- .../base/form/components/field/text.tsx | 16 +--- .../form/components/field/upload-method.tsx | 58 +++++++++++++ .../base/form/form-scenarios/base/field.tsx | 29 +++++-- .../base/form/form-scenarios/base/index.tsx | 4 +- .../base/form/form-scenarios/base/types.ts | 1 + web/app/components/base/select/custom.tsx | 4 +- .../components/input-number-with-slider.tsx | 4 +- 18 files changed, 435 insertions(+), 81 deletions(-) create mode 100644 web/app/components/base/form/components/field/file-types.tsx create mode 100644 web/app/components/base/form/components/field/input-type-select/hooks.tsx create mode 100644 web/app/components/base/form/components/field/input-type-select/index.tsx create mode 100644 web/app/components/base/form/components/field/input-type-select/option.tsx create mode 100644 web/app/components/base/form/components/field/input-type-select/trigger.tsx create mode 100644 web/app/components/base/form/components/field/input-type-select/types.tsx create mode 100644 web/app/components/base/form/components/field/number-slider.tsx create mode 100644 web/app/components/base/form/components/field/upload-method.tsx diff --git a/web/app/components/base/form/components/field/custom-select.tsx b/web/app/components/base/form/components/field/custom-select.tsx index 82c531a800..0e605184dc 100644 --- a/web/app/components/base/form/components/field/custom-select.tsx +++ b/web/app/components/base/form/components/field/custom-select.tsx @@ -2,52 +2,36 @@ import cn from '@/utils/classnames' import { useFieldContext } from '../..' import type { CustomSelectProps, Option } from '../../../select/custom' import CustomSelect from '../../../select/custom' +import type { LabelProps } from '../label' import Label from '../label' -import { useCallback } from 'react' type CustomSelectFieldProps = { label: string + labelOptions?: Omit options: T[] - onChange?: (value: string) => void - isRequired?: boolean - showOptional?: boolean - tooltip?: string className?: string - labelClassName?: string } & Omit, 'options' | 'value' | 'onChange'> const CustomSelectField = ({ label, + labelOptions, options, - onChange, - isRequired, - showOptional, - tooltip, className, - labelClassName, ...selectProps }: CustomSelectFieldProps) => { const field = useFieldContext() - const handleChange = useCallback((value: string) => { - field.handleChange(value) - onChange?.(value) - }, [field, onChange]) - return (
diff --git a/web/app/components/base/form/components/field/file-types.tsx b/web/app/components/base/form/components/field/file-types.tsx new file mode 100644 index 0000000000..44c77dc894 --- /dev/null +++ b/web/app/components/base/form/components/field/file-types.tsx @@ -0,0 +1,83 @@ +import cn from '@/utils/classnames' +import type { LabelProps } from '../label' +import { useFieldContext } from '../..' +import Label from '../label' +import { SupportUploadFileTypes } from '@/app/components/workflow/types' +import FileTypeItem from '@/app/components/workflow/nodes/_base/components/file-type-item' +import { useCallback } from 'react' + +type FieldValue = { + allowedFileTypes: string[], + allowedFileExtensions: string[] +} + +type FileTypesFieldProps = { + label: string + labelOptions?: Omit + className?: string +} + +const FileTypesField = ({ + label, + labelOptions, + className, +}: FileTypesFieldProps) => { + const field = useFieldContext() + + const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => { + let newAllowFileTypes = [...field.state.value.allowedFileTypes] + if (type === SupportUploadFileTypes.custom) { + if (!newAllowFileTypes.includes(SupportUploadFileTypes.custom)) + newAllowFileTypes = [SupportUploadFileTypes.custom] + else + newAllowFileTypes = newAllowFileTypes.filter(v => v !== type) + } + else { + newAllowFileTypes = newAllowFileTypes.filter(v => v !== SupportUploadFileTypes.custom) + if (newAllowFileTypes.includes(type)) + newAllowFileTypes = newAllowFileTypes.filter(v => v !== type) + else + newAllowFileTypes.push(type) + } + field.handleChange({ + ...field.state.value, + allowedFileTypes: newAllowFileTypes, + }) + }, [field]) + + const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => { + field.handleChange({ + ...field.state.value, + allowedFileExtensions: customFileTypes, + }) + }, [field]) + + return ( +
+
+ ) +} + +export default FileTypesField diff --git a/web/app/components/base/form/components/field/input-type-select/hooks.tsx b/web/app/components/base/form/components/field/input-type-select/hooks.tsx new file mode 100644 index 0000000000..44a3b2a67b --- /dev/null +++ b/web/app/components/base/form/components/field/input-type-select/hooks.tsx @@ -0,0 +1,51 @@ +import { InputVarType } from '@/app/components/workflow/types' +import { InputType } from './types' +import { useTranslation } from 'react-i18next' +import { + RiAlignLeft, + RiCheckboxLine, + RiFileCopy2Line, + RiFileTextLine, + RiHashtag, + RiListCheck3, + RiTextSnippet, +} from '@remixicon/react' + +const i18nFileTypeMap: Record = { + 'file': 'single-file', + 'file-list': 'multi-files', +} + +const INPUT_TYPE_ICON = { + [InputVarType.textInput]: RiTextSnippet, + [InputVarType.paragraph]: RiAlignLeft, + [InputVarType.number]: RiHashtag, + [InputVarType.select]: RiListCheck3, + [InputVarType.checkbox]: RiCheckboxLine, + [InputVarType.singleFile]: RiFileTextLine, + [InputVarType.multiFiles]: RiFileCopy2Line, +} + +const DATA_TYPE = { + [InputVarType.textInput]: 'string', + [InputVarType.paragraph]: 'string', + [InputVarType.number]: 'number', + [InputVarType.select]: 'string', + [InputVarType.checkbox]: 'boolean', + [InputVarType.singleFile]: 'file', + [InputVarType.multiFiles]: 'array[file]', +} + +export const useInputTypeOptions = (supportFile: boolean) => { + const { t } = useTranslation() + const options = supportFile ? InputType.options : InputType.exclude(['file', 'file-list']).options + + return options.map((value) => { + return { + value, + label: t(`appDebug.variableConfig.${i18nFileTypeMap[value] || value}`), + Icon: INPUT_TYPE_ICON[value], + type: DATA_TYPE[value], + } + }) +} diff --git a/web/app/components/base/form/components/field/input-type-select/index.tsx b/web/app/components/base/form/components/field/input-type-select/index.tsx new file mode 100644 index 0000000000..983b7f1883 --- /dev/null +++ b/web/app/components/base/form/components/field/input-type-select/index.tsx @@ -0,0 +1,64 @@ +import cn from '@/utils/classnames' +import { useFieldContext } from '../../..' +import type { CustomSelectProps } from '../../../../select/custom' +import CustomSelect from '../../../../select/custom' +import type { LabelProps } from '../../label' +import Label from '../../label' +import { useCallback } from 'react' +import Trigger from './trigger' +import type { FileTypeSelectOption } from './types' +import { useInputTypeOptions } from './hooks' +import Option from './option' + +type InputTypeSelectFieldProps = { + label: string + labeOptions?: Omit + supportFile: boolean + className?: string +} & Omit, 'options' | 'value' | 'onChange' | 'CustomTrigger' | 'CustomOption'> + +const InputTypeSelectField = ({ + label, + labeOptions, + supportFile, + className, + ...customSelectProps +}: InputTypeSelectFieldProps) => { + const field = useFieldContext() + const inputTypeOptions = useInputTypeOptions(supportFile) + + const renderTrigger = useCallback((option: FileTypeSelectOption | undefined, open: boolean) => { + return + }, []) + const renderOption = useCallback((option: FileTypeSelectOption) => { + return