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'
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 { SimpleSelect as Select } from '@/app/components/base/select'
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 = {
condition: Condition
onChange: (condition: Condition) => void
varType: VarType
hasSubVariable: boolean
}
const FilterCondition: FC<Props> = ({
condition,
varType,
onChange,
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 (
<div>
{hasSubVariable && <SubVariablePicker className="mb-2" />}
<div className='flex space-x-1'>
<Select
wrapperClassName='shrink-0 h-8'
className='!text-[13px]'
items={[
{ value: '1', name: 'include', type: '' },
]}
onSelect={() => { }}
{hasSubVariable && (
<SubVariablePicker
className="mb-2"
value={condition.key}
onChange={handleSubVariableChange}
/>
<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>
)

View File

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

View File

@ -25,9 +25,11 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
const {
readOnly,
inputs,
itemVarType,
hasSubVariable,
handleVarChanges,
filterVar,
handleFilterChange,
handleLimitChange,
handleOrderByEnabledChange,
handleOrderByTypeChange,
@ -53,7 +55,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
title={t(`${i18nPrefix}.filterCondition`)}
isSubTitle
>
<FilterCondition hasSubVariable={hasSubVariable} />
<FilterCondition
condition={inputs.filter_by[0]}
onChange={handleFilterChange}
varType={itemVarType}
hasSubVariable={hasSubVariable}
/>
</Field>
<Split />
<Field
@ -71,7 +78,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
? (
<div className='flex items-center justify-between'>
{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'}>
<OptionCard

View File

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

View File

@ -3,7 +3,8 @@ import produce from 'immer'
import { useStoreApi } from 'reactflow'
import type { ValueSelector, Var } 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 {
useIsChatMode,
@ -32,27 +33,65 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload)
const { getCurrentVariableType } = useWorkflowVariables()
const varType = getCurrentVariableType({
parentNode: iterationNode,
valueSelector: inputs.variable || [],
availableNodes,
isChatMode,
isConstant: false,
})
const getType = useCallback((variable?: ValueSelector) => {
const varType = getCurrentVariableType({
parentNode: iterationNode,
valueSelector: variable || inputs.variable || [],
availableNodes,
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 newInputs = produce(inputs, (draft) => {
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)
}, [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 newInputs = produce(inputs, (draft) => {
draft.limit = limit
@ -80,8 +119,11 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
readOnly,
inputs,
filterVar,
varType,
itemVarType,
hasSubVariable,
handleVarChanges,
handleFilterChange,
handleLimitChange,
handleOrderByEnabledChange,
handleOrderByTypeChange,