feat(question-classifier): add drag-and-drop sorting for topics list (#22066)

Co-authored-by: crazywoola <427733928@qq.com>
pull/22128/head
Minamiyama 10 months ago committed by GitHub
parent 881a151d30
commit a9cc19f530
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -11,6 +11,8 @@ import { uniqueId } from 'lodash-es'
const i18nPrefix = 'workflow.nodes.questionClassifiers' const i18nPrefix = 'workflow.nodes.questionClassifiers'
type Props = { type Props = {
className?: string
headerClassName?: string
nodeId: string nodeId: string
payload: Topic payload: Topic
onChange: (payload: Topic) => void onChange: (payload: Topic) => void
@ -21,6 +23,8 @@ type Props = {
} }
const ClassItem: FC<Props> = ({ const ClassItem: FC<Props> = ({
className,
headerClassName,
nodeId, nodeId,
payload, payload,
onChange, onChange,
@ -49,6 +53,8 @@ const ClassItem: FC<Props> = ({
return ( return (
<Editor <Editor
className={className}
headerClassName={headerClassName}
title={`${t(`${i18nPrefix}.class`)} ${index}`} title={`${t(`${i18nPrefix}.class`)} ${index}`}
placeholder={t(`${i18nPrefix}.topicPlaceholder`)!} placeholder={t(`${i18nPrefix}.topicPlaceholder`)!}
value={payload.name} value={payload.name}

@ -8,6 +8,9 @@ import AddButton from '../../_base/components/add-button'
import Item from './class-item' import Item from './class-item'
import type { Topic } from '@/app/components/workflow/nodes/question-classifier/types' import type { Topic } from '@/app/components/workflow/nodes/question-classifier/types'
import type { ValueSelector, Var } from '@/app/components/workflow/types' import type { ValueSelector, Var } from '@/app/components/workflow/types'
import { ReactSortable } from 'react-sortablejs'
import { noop } from 'lodash-es'
import cn from '@/utils/classnames'
const i18nPrefix = 'workflow.nodes.questionClassifiers' const i18nPrefix = 'workflow.nodes.questionClassifiers'
@ -17,6 +20,7 @@ type Props = {
onChange: (list: Topic[]) => void onChange: (list: Topic[]) => void
readonly?: boolean readonly?: boolean
filterVar: (payload: Var, valueSelector: ValueSelector) => boolean filterVar: (payload: Var, valueSelector: ValueSelector) => boolean
handleSortTopic?: (newTopics: (Topic & { id: string })[]) => void
} }
const ClassList: FC<Props> = ({ const ClassList: FC<Props> = ({
@ -25,6 +29,7 @@ const ClassList: FC<Props> = ({
onChange, onChange,
readonly, readonly,
filterVar, filterVar,
handleSortTopic = noop,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { handleEdgeDeleteByDeleteBranch } = useEdgesInteractions() const { handleEdgeDeleteByDeleteBranch } = useEdgesInteractions()
@ -55,22 +60,48 @@ const ClassList: FC<Props> = ({
} }
}, [list, onChange, handleEdgeDeleteByDeleteBranch, nodeId]) }, [list, onChange, handleEdgeDeleteByDeleteBranch, nodeId])
const topicCount = list.length
const handleSideWidth = 3
// Todo Remove; edit topic name // Todo Remove; edit topic name
return ( return (
<div className='space-y-2'> <ReactSortable
list={list.map(item => ({ ...item }))}
setList={handleSortTopic}
handle='.handle'
ghostClass='bg-components-panel-bg'
animation={150}
disabled={readonly}
className='space-y-2'
>
{ {
list.map((item, index) => { list.map((item, index) => {
const canDrag = (() => {
if (readonly)
return false
return topicCount >= 2
})()
return ( return (
<Item <div key={item.id}
nodeId={nodeId} className={cn(
key={list[index].id} 'group relative rounded-[10px] bg-components-panel-bg',
payload={item} `-ml-${handleSideWidth} min-h-[40px] px-0 py-0`,
onChange={handleClassChange(index)} )}>
onRemove={handleRemoveClass(index)} <div >
index={index + 1} <Item
readonly={readonly} className={cn(canDrag && 'handle')}
filterVar={filterVar} headerClassName={cn(canDrag && 'cursor-grab')}
/> nodeId={nodeId}
key={list[index].id}
payload={item}
onChange={handleClassChange(index)}
onRemove={handleRemoveClass(index)}
index={index + 1}
readonly={readonly}
filterVar={filterVar}
/>
</div>
</div>
) )
}) })
} }
@ -81,7 +112,7 @@ const ClassList: FC<Props> = ({
/> />
)} )}
</div> </ReactSortable>
) )
} }
export default React.memo(ClassList) export default React.memo(ClassList)

@ -40,6 +40,7 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
handleVisionResolutionChange, handleVisionResolutionChange,
handleVisionResolutionEnabledChange, handleVisionResolutionEnabledChange,
filterVar, filterVar,
handleSortTopic,
} = useConfig(id, data) } = useConfig(id, data)
const model = inputs.model const model = inputs.model
@ -99,6 +100,7 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
onChange={handleTopicsChange} onChange={handleTopicsChange}
readonly={readOnly} readonly={readOnly}
filterVar={filterVar} filterVar={filterVar}
handleSortTopic={handleSortTopic}
/> />
</Field> </Field>
<Split /> <Split />

@ -9,13 +9,15 @@ import {
import { useStore } from '../../store' import { useStore } from '../../store'
import useAvailableVarList from '../_base/hooks/use-available-var-list' import useAvailableVarList from '../_base/hooks/use-available-var-list'
import useConfigVision from '../../hooks/use-config-vision' import useConfigVision from '../../hooks/use-config-vision'
import type { QuestionClassifierNodeType } from './types' import type { QuestionClassifierNodeType, Topic } 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 { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants'
import { useUpdateNodeInternals } from 'reactflow'
const useConfig = (id: string, payload: QuestionClassifierNodeType) => { const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
const updateNodeInternals = useUpdateNodeInternals()
const { nodesReadOnly: readOnly } = useNodesReadOnly() const { nodesReadOnly: readOnly } = useNodesReadOnly()
const isChatMode = useIsChatMode() const isChatMode = useIsChatMode()
const defaultConfig = useStore(s => s.nodesDefaultConfigs)[payload.type] const defaultConfig = useStore(s => s.nodesDefaultConfigs)[payload.type]
@ -166,6 +168,17 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
return varPayload.type === VarType.string return varPayload.type === VarType.string
}, []) }, [])
const handleSortTopic = useCallback((newTopics: (Topic & { id: string })[]) => {
const newInputs = produce(inputs, (draft) => {
draft.classes = newTopics.filter(Boolean).map(item => ({
id: item.id,
name: item.name,
}))
})
setInputs(newInputs)
updateNodeInternals(id)
}, [id, inputs, setInputs, updateNodeInternals])
return { return {
readOnly, readOnly,
inputs, inputs,
@ -185,6 +198,7 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
isVisionModel, isVisionModel,
handleVisionResolutionEnabledChange, handleVisionResolutionEnabledChange,
handleVisionResolutionChange, handleVisionResolutionChange,
handleSortTopic,
} }
} }

Loading…
Cancel
Save