feat: support filter variable var data sync

This commit is contained in:
Joel 2024-08-30 14:14:33 +08:00
parent 0e2f78b3a6
commit 976efd93a1
5 changed files with 180 additions and 37 deletions

View File

@ -1,31 +1,106 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import ConditionOperator from '../../if-else/components/condition-list/condition-operator'
import { VarType } from '../../../types'
import type { Condition } from '../types'
import { ComparisonOperator } from '../../if-else/types'
import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils'
import SubVariablePicker from './sub-variable-picker' import SubVariablePicker from './sub-variable-picker'
import { SimpleSelect as Select } from '@/app/components/base/select'
import Input from '@/app/components/base/input' import Input from '@/app/components/base/input'
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/if-else/default'
import { SimpleSelect as Select } from '@/app/components/base/select'
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
type Props = { type Props = {
condition: Condition
onChange: (condition: Condition) => void
varType: VarType
hasSubVariable: boolean hasSubVariable: boolean
} }
const FilterCondition: FC<Props> = ({ const FilterCondition: FC<Props> = ({
condition,
varType,
onChange,
hasSubVariable, hasSubVariable,
}) => { }) => {
const [input, setInput] = React.useState('') const { t } = useTranslation()
const isSelect = [ComparisonOperator.in, ComparisonOperator.notIn, ComparisonOperator.allOf].includes(condition.comparison_operator)
const selectOptions = useMemo(() => {
if (isSelect) {
if (condition.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) {
return FILE_TYPE_OPTIONS.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`),
value: item.value,
}))
}
if (condition.key === 'transfer_method') {
return TRANSFER_METHOD.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`),
value: item.value,
}))
}
return []
}
return []
}, [condition.comparison_operator, condition.key, isSelect, t])
const handleChange = useCallback((key: string) => {
return (value: any) => {
onChange({
...condition,
[key]: value,
})
}
}, [condition, onChange])
const handleSubVariableChange = useCallback((value: string) => {
onChange({
key: value,
comparison_operator: getOperators(varType, { key: value })[0],
value: '',
})
}, [onChange, varType])
return ( return (
<div> <div>
{hasSubVariable && <SubVariablePicker className="mb-2" />} {hasSubVariable && (
<div className='flex space-x-1'> <SubVariablePicker
<Select className="mb-2"
wrapperClassName='shrink-0 h-8' value={condition.key}
className='!text-[13px]' onChange={handleSubVariableChange}
items={[
{ value: '1', name: 'include', type: '' },
]}
onSelect={() => { }}
/> />
<Input className='grow' value={input} onChange={e => setInput(e.target.value)} /> )}
<div className='flex space-x-1'>
<ConditionOperator
varType={varType}
value={condition.comparison_operator}
onSelect={handleChange('comparison_operator')}
file={hasSubVariable ? { key: condition.key } : undefined}
/>
{!comparisonOperatorNotRequireValue(condition.comparison_operator) && (
<>
{isSelect && (
<Select
items={selectOptions}
defaultValue={condition.value}
onSelect={item => handleChange('value')(item.value)}
className='!text-[13px]'
wrapperClassName='grow'
placeholder='Select value'
/>
)}
{!isSelect && (
<Input
type={((hasSubVariable && condition.key === 'size') || (!hasSubVariable && varType === VarType.number)) ? 'number' : 'text'}
className='grow'
value={condition.value}
onChange={e => handleChange('value')(e.target.value)}
/>
)}
</>
)}
</div> </div>
</div> </div>
) )

View File

@ -1,17 +1,28 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React, { useCallback } from 'react'
import { SUB_VARIABLES } from '../../if-else/default'
import type { Item } from '@/app/components/base/select'
import { SimpleSelect as Select } from '@/app/components/base/select' import { SimpleSelect as Select } from '@/app/components/base/select'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
type Props = { type Props = {
value: string
onChange: (value: string) => void
className?: string className?: string
} }
const SubVariablePicker: FC<Props> = ({ const SubVariablePicker: FC<Props> = ({
value,
onChange,
className, className,
}) => { }) => {
const subVarOptions = SUB_VARIABLES.map(item => ({
value: item,
name: item,
}))
const renderOption = ({ item }: { item: Record<string, any> }) => { const renderOption = ({ item }: { item: Record<string, any> }) => {
return ( return (
<div className='flex items-center h-6 justify-between'> <div className='flex items-center h-6 justify-between'>
@ -23,15 +34,17 @@ const SubVariablePicker: FC<Props> = ({
</div> </div>
) )
} }
const handleChange = useCallback(({ value }: Item) => {
onChange(value as string)
}, [onChange])
return ( return (
<div className={cn(className)}> <div className={cn(className)}>
<Select <Select
items={[ items={subVarOptions}
{ value: '1', name: 'name', type: 'string' }, defaultValue={value}
{ value: '2', name: 'age', type: 'number' }, onSelect={handleChange}
]}
defaultValue={'1'}
onSelect={() => { }}
className='!text-[13px]' className='!text-[13px]'
placeholder='Select sub variable key' placeholder='Select sub variable key'
optionClassName='pl-4 pr-5 py-0' optionClassName='pl-4 pr-5 py-0'

View File

@ -25,9 +25,11 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
const { const {
readOnly, readOnly,
inputs, inputs,
itemVarType,
hasSubVariable, hasSubVariable,
handleVarChanges, handleVarChanges,
filterVar, filterVar,
handleFilterChange,
handleLimitChange, handleLimitChange,
handleOrderByEnabledChange, handleOrderByEnabledChange,
handleOrderByTypeChange, handleOrderByTypeChange,
@ -53,7 +55,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
title={t(`${i18nPrefix}.filterCondition`)} title={t(`${i18nPrefix}.filterCondition`)}
isSubTitle isSubTitle
> >
<FilterCondition hasSubVariable={hasSubVariable} /> <FilterCondition
condition={inputs.filter_by[0]}
onChange={handleFilterChange}
varType={itemVarType}
hasSubVariable={hasSubVariable}
/>
</Field> </Field>
<Split /> <Split />
<Field <Field
@ -71,7 +78,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
? ( ? (
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
{hasSubVariable && ( {hasSubVariable && (
<div className='grow mr-2'><SubVariablePicker /></div> <div className='grow mr-2'>
<SubVariablePicker
value={inputs.order_by.key as string}
onChange={() => { }}
/>
</div>
)} )}
<div className={!hasSubVariable ? 'w-full grid grid-cols-2 gap-1' : 'shrink-0 flex space-x-1'}> <div className={!hasSubVariable ? 'w-full grid grid-cols-2 gap-1' : 'shrink-0 flex space-x-1'}>
<OptionCard <OptionCard

View File

@ -1,3 +1,4 @@
import type { ComparisonOperator } from '../if-else/types'
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
export enum OrderBy { export enum OrderBy {
@ -12,8 +13,8 @@ export type Limit = {
export type Condition = { export type Condition = {
key: string key: string
comparison_operator: string comparison_operator: ComparisonOperator
value: string value: string | number
} }
export type ListFilterNodeType = CommonNodeType & { export type ListFilterNodeType = CommonNodeType & {

View File

@ -3,7 +3,8 @@ import produce from 'immer'
import { useStoreApi } from 'reactflow' import { useStoreApi } from 'reactflow'
import type { ValueSelector, Var } from '../../types' import type { ValueSelector, Var } from '../../types'
import { VarType } from '../../types' import { VarType } from '../../types'
import type { Limit, ListFilterNodeType, OrderBy } from './types' import { getOperators } from '../if-else/utils'
import type { Condition, Limit, ListFilterNodeType, OrderBy } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { import {
useIsChatMode, useIsChatMode,
@ -32,27 +33,65 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload) const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload)
const { getCurrentVariableType } = useWorkflowVariables() const { getCurrentVariableType } = useWorkflowVariables()
const varType = getCurrentVariableType({ const getType = useCallback((variable?: ValueSelector) => {
parentNode: iterationNode, const varType = getCurrentVariableType({
valueSelector: inputs.variable || [], parentNode: iterationNode,
availableNodes, valueSelector: variable || inputs.variable || [],
isChatMode, availableNodes,
isConstant: false, isChatMode,
}) isConstant: false,
})
let itemVarType = VarType.string
switch (varType) {
case VarType.arrayNumber:
itemVarType = VarType.number
break
case VarType.arrayString:
itemVarType = VarType.string
break
case VarType.arrayFile:
itemVarType = VarType.file
break
case VarType.arrayObject:
itemVarType = VarType.object
break
default:
itemVarType = varType
}
return { varType, itemVarType }
}, [availableNodes, getCurrentVariableType, inputs.variable, isChatMode, iterationNode])
const hasSubVariable = [VarType.arrayFile, VarType.arrayObject].includes(varType) const { varType, itemVarType } = getType()
const hasSubVariable = [VarType.arrayFile].includes(varType)
const handleVarChanges = useCallback((variable: ValueSelector | string) => { const handleVarChanges = useCallback((variable: ValueSelector | string) => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
draft.variable = variable as ValueSelector draft.variable = variable as ValueSelector
const { varType, itemVarType } = getType(draft.variable)
const isFileArray = varType === VarType.arrayFile
draft.filter_by = [{
key: isFileArray ? 'name' : '',
comparison_operator: getOperators(itemVarType, isFileArray ? { key: 'name' } : undefined)[0],
value: '',
}]
})
setInputs(newInputs)
}, [getType, inputs, setInputs])
const filterVar = useCallback((varPayload: Var) => {
// Don't know the item struct of VarType.arrayObject, so not support it
return [VarType.arrayNumber, VarType.arrayString, VarType.arrayFile].includes(varPayload.type)
}, [])
const handleFilterChange = useCallback((condition: Condition) => {
const newInputs = produce(inputs, (draft) => {
draft.filter_by[0] = condition
}) })
setInputs(newInputs) setInputs(newInputs)
}, [inputs, setInputs]) }, [inputs, setInputs])
const filterVar = useCallback((varPayload: Var) => {
return [VarType.arrayNumber, VarType.arrayString, VarType.arrayFile, VarType.arrayObject].includes(varPayload.type)
}, [])
const handleLimitChange = useCallback((limit: Limit) => { const handleLimitChange = useCallback((limit: Limit) => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
draft.limit = limit draft.limit = limit
@ -80,8 +119,11 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
readOnly, readOnly,
inputs, inputs,
filterVar, filterVar,
varType,
itemVarType,
hasSubVariable, hasSubVariable,
handleVarChanges, handleVarChanges,
handleFilterChange,
handleLimitChange, handleLimitChange,
handleOrderByEnabledChange, handleOrderByEnabledChange,
handleOrderByTypeChange, handleOrderByTypeChange,