|
import { useTranslate } from '@/hooks/common-hooks'; |
|
import { CloseOutlined } from '@ant-design/icons'; |
|
import { Button, Card, Form, FormListFieldData, Input, Select } from 'antd'; |
|
import { FormInstance } from 'antd/lib'; |
|
import { humanId } from 'human-id'; |
|
import trim from 'lodash/trim'; |
|
import { |
|
ChangeEventHandler, |
|
FocusEventHandler, |
|
useCallback, |
|
useEffect, |
|
useState, |
|
} from 'react'; |
|
import { useUpdateNodeInternals } from 'reactflow'; |
|
import { Operator } from '../constant'; |
|
import { useBuildFormSelectOptions } from '../form-hooks'; |
|
|
|
interface IProps { |
|
nodeId?: string; |
|
} |
|
|
|
interface INameInputProps { |
|
value?: string; |
|
onChange?: (value: string) => void; |
|
otherNames?: string[]; |
|
validate(errors: string[]): void; |
|
} |
|
|
|
const getOtherFieldValues = ( |
|
form: FormInstance, |
|
formListName: string = 'items', |
|
field: FormListFieldData, |
|
latestField: string, |
|
) => |
|
(form.getFieldValue([formListName]) ?? []) |
|
.map((x: any) => x[latestField]) |
|
.filter( |
|
(x: string) => |
|
x !== form.getFieldValue([formListName, field.name, latestField]), |
|
); |
|
|
|
const NameInput = ({ |
|
value, |
|
onChange, |
|
otherNames, |
|
validate, |
|
}: INameInputProps) => { |
|
const [name, setName] = useState<string | undefined>(); |
|
const { t } = useTranslate('flow'); |
|
|
|
const handleNameChange: ChangeEventHandler<HTMLInputElement> = useCallback( |
|
(e) => { |
|
const val = e.target.value; |
|
|
|
if (otherNames?.some((x) => x === val)) { |
|
validate([t('nameRepeatedMsg')]); |
|
} else if (trim(val) === '') { |
|
validate([t('nameRequiredMsg')]); |
|
} else { |
|
validate([]); |
|
} |
|
setName(val); |
|
}, |
|
[otherNames, validate, t], |
|
); |
|
|
|
const handleNameBlur: FocusEventHandler<HTMLInputElement> = useCallback( |
|
(e) => { |
|
const val = e.target.value; |
|
if (otherNames?.every((x) => x !== val) && trim(val) !== '') { |
|
onChange?.(val); |
|
} |
|
}, |
|
[onChange, otherNames], |
|
); |
|
|
|
useEffect(() => { |
|
setName(value); |
|
}, [value]); |
|
|
|
return ( |
|
<Input |
|
value={name} |
|
onChange={handleNameChange} |
|
onBlur={handleNameBlur} |
|
></Input> |
|
); |
|
}; |
|
|
|
const DynamicCategorize = ({ nodeId }: IProps) => { |
|
const updateNodeInternals = useUpdateNodeInternals(); |
|
const form = Form.useFormInstance(); |
|
const buildCategorizeToOptions = useBuildFormSelectOptions( |
|
Operator.Categorize, |
|
nodeId, |
|
); |
|
const { t } = useTranslate('flow'); |
|
|
|
return ( |
|
<> |
|
<Form.List name="items"> |
|
{(fields, { add, remove }) => { |
|
const handleAdd = () => { |
|
add({ name: humanId() }); |
|
if (nodeId) updateNodeInternals(nodeId); |
|
}; |
|
return ( |
|
<div |
|
style={{ display: 'flex', rowGap: 10, flexDirection: 'column' }} |
|
> |
|
{fields.map((field) => ( |
|
<Card |
|
size="small" |
|
key={field.key} |
|
extra={ |
|
<CloseOutlined |
|
onClick={() => { |
|
remove(field.name); |
|
}} |
|
/> |
|
} |
|
> |
|
<Form.Item |
|
label={t('name')} |
|
name={[field.name, 'name']} |
|
validateTrigger={['onChange', 'onBlur']} |
|
rules={[ |
|
{ |
|
required: true, |
|
whitespace: true, |
|
message: t('nameMessage'), |
|
}, |
|
]} |
|
> |
|
<NameInput |
|
otherNames={getOtherFieldValues( |
|
form, |
|
'items', |
|
field, |
|
'name', |
|
)} |
|
validate={(errors: string[]) => |
|
form.setFields([ |
|
{ |
|
name: ['items', field.name, 'name'], |
|
errors, |
|
}, |
|
]) |
|
} |
|
></NameInput> |
|
</Form.Item> |
|
<Form.Item |
|
label={t('description')} |
|
name={[field.name, 'description']} |
|
> |
|
<Input.TextArea rows={3} /> |
|
</Form.Item> |
|
<Form.Item |
|
label={t('examples')} |
|
name={[field.name, 'examples']} |
|
> |
|
<Input.TextArea rows={3} /> |
|
</Form.Item> |
|
<Form.Item label={t('to')} name={[field.name, 'to']}> |
|
<Select |
|
allowClear |
|
options={buildCategorizeToOptions( |
|
getOtherFieldValues(form, 'items', field, 'to'), |
|
)} |
|
/> |
|
</Form.Item> |
|
</Card> |
|
))} |
|
|
|
<Button type="dashed" onClick={handleAdd} block> |
|
+ {t('addItem')} |
|
</Button> |
|
</div> |
|
); |
|
}} |
|
</Form.List> |
|
</> |
|
); |
|
}; |
|
|
|
export default DynamicCategorize; |
|
|