mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
feat: add picker shower
This commit is contained in:
@ -16,7 +16,7 @@ import {
|
|||||||
} from '@floating-ui/react'
|
} from '@floating-ui/react'
|
||||||
|
|
||||||
import type { OffsetOptions, Placement } from '@floating-ui/react'
|
import type { OffsetOptions, Placement } from '@floating-ui/react'
|
||||||
|
import cn from 'classnames'
|
||||||
export type PortalToFollowElemOptions = {
|
export type PortalToFollowElemOptions = {
|
||||||
/*
|
/*
|
||||||
* top, bottom, left, right
|
* top, bottom, left, right
|
||||||
@ -129,7 +129,7 @@ React.HTMLProps<HTMLElement> & { asChild?: boolean }
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className='inline-block'
|
className={cn('inline-block', props.className)}
|
||||||
// The user can style the trigger based on the state
|
// The user can style the trigger based on the state
|
||||||
data-state={context.open ? 'open' : 'closed'}
|
data-state={context.open ? 'open' : 'closed'}
|
||||||
{...context.getReferenceProps(props)}
|
{...context.getReferenceProps(props)}
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { mockNodesData } from '../../../mock'
|
import { mockNodeOutputVars, mockNodesData } from '../../../mock'
|
||||||
|
import VarReferencePopup from './var-reference-popup'
|
||||||
import type { ValueSelector } from '@/app/components/workflow/types'
|
import type { ValueSelector } from '@/app/components/workflow/types'
|
||||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
isShowNodeName: boolean
|
isShowNodeName: boolean
|
||||||
@ -15,6 +22,14 @@ type Props = {
|
|||||||
onChange: (value: ValueSelector) => void
|
onChange: (value: ValueSelector) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const toShowVarType = (type: string) => {
|
||||||
|
// if (['text-input', 'paragraph', 'select', 'url'].includes(type))
|
||||||
|
// return 'String'
|
||||||
|
|
||||||
|
// return type.charAt(0).toUpperCase() + type.substring(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: get data from context
|
||||||
const getNodeInfoById = (id: string) => {
|
const getNodeInfoById = (id: string) => {
|
||||||
return mockNodesData[id]
|
return mockNodesData[id]
|
||||||
}
|
}
|
||||||
@ -25,6 +40,7 @@ const VarReferencePicker: FC<Props> = ({
|
|||||||
isShowNodeName,
|
isShowNodeName,
|
||||||
value,
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
const hasValue = value.length > 0
|
const hasValue = value.length > 0
|
||||||
const node = hasValue ? getNodeInfoById(value[0]) : null
|
const node = hasValue ? getNodeInfoById(value[0]) : null
|
||||||
const varName = hasValue ? value[value.length - 1] : ''
|
const varName = hasValue ? value[value.length - 1] : ''
|
||||||
@ -32,33 +48,48 @@ const VarReferencePicker: FC<Props> = ({
|
|||||||
const getVarType = () => {
|
const getVarType = () => {
|
||||||
return 'string'
|
return 'string'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, !readonly && 'cursor-pointer')}>
|
<div className={cn(className, !readonly && 'cursor-pointer')}>
|
||||||
<div className={cn('w-full h-8 p-1 rounded-lg bg-gray-100')}>
|
<PortalToFollowElem
|
||||||
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
|
open={open}
|
||||||
{hasValue && (
|
onOpenChange={setOpen}
|
||||||
<>
|
placement='bottom-start'
|
||||||
{isShowNodeName && (
|
>
|
||||||
<div className='flex items-center'>
|
<PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='!flex'>
|
||||||
<div className='p-[1px]'>
|
<div className={cn('w-full h-8 p-1 rounded-lg bg-gray-100')}>
|
||||||
<VarBlockIcon
|
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
|
||||||
className='!text-gray-900'
|
{hasValue && (
|
||||||
type={node?.type}
|
<>
|
||||||
/>
|
{isShowNodeName && (
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<div className='p-[1px]'>
|
||||||
|
<VarBlockIcon
|
||||||
|
className='!text-gray-900'
|
||||||
|
type={node?.type}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mx-0.5 text-xs font-medium text-gray-700'>{node?.title}</div>
|
||||||
|
<Line3 className='mr-0.5'></Line3>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className='flex items-center text-primary-600'>
|
||||||
|
<Variable02 className='w-3.5 h-3.5' />
|
||||||
|
<div className='ml-0.5 text-xs font-medium'>{varName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mx-0.5 text-xs font-medium text-gray-700'>{node?.title}</div>
|
<div className='ml-0.5 text-xs font-normal text-gray-500'>{getVarType()}</div>
|
||||||
<Line3 className='mr-0.5'></Line3>
|
</>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div className='flex items-center text-primary-600'>
|
</div>
|
||||||
<Variable02 className='w-3.5 h-3.5' />
|
</div>
|
||||||
<div className='ml-0.5 text-xs font-medium'>{varName}</div>
|
</PortalToFollowElemTrigger>
|
||||||
</div>
|
<PortalToFollowElemContent style={{
|
||||||
<div className='ml-0.5 text-xs font-normal text-gray-500'>{getVarType()}</div>
|
zIndex: 100,
|
||||||
</>
|
width: 227,
|
||||||
)}
|
}}>
|
||||||
</div>
|
<VarReferencePopup vars={mockNodeOutputVars} />
|
||||||
</div>
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||||
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
vars: NodeOutPutVar[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const VarReferencePopup: FC<Props> = ({
|
||||||
|
vars,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='p-1 bg-white rounded-lg border border-gray-200 shadow-lg space-y-1'>
|
||||||
|
{vars.map((item, i) => (
|
||||||
|
<div key={i}>
|
||||||
|
<div className='flex items-center h-[22px] px-3 text-xs font-medium text-gray-500 uppercase'>{item.title}</div>
|
||||||
|
{item.vars.map((v, j) => (
|
||||||
|
<div key={j} className='flex items-center h-6 pl-3 pr-[18px] rounded-md cursor-pointer hover:bg-gray-50'>
|
||||||
|
<div className='flex items-center w-0 grow'>
|
||||||
|
<Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />
|
||||||
|
<div className='ml-1 w-0 grow text-ellipsis text-[13px] font-normal text-gray-900'>{v.variable}</div>
|
||||||
|
</div>
|
||||||
|
<div className='ml-1 shrink-0 text-xs font-normal text-gray-500'>{v.type}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(VarReferencePopup)
|
||||||
@ -2,7 +2,7 @@ import type { FC } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import BasePanel from '../_base/panel'
|
import BasePanel from '../_base/panel'
|
||||||
import VarList from '../_base/components/variable/var-list'
|
import VarList from '../_base/components/variable/var-list'
|
||||||
import useInput from './use-input'
|
import useConfig from './use-config'
|
||||||
import { mockLLMNodeData } from './mock'
|
import { mockLLMNodeData } from './mock'
|
||||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||||
import AddButton from '@/app/components/base/button/add-button'
|
import AddButton from '@/app/components/base/button/add-button'
|
||||||
@ -23,10 +23,10 @@ const Panel: FC = () => {
|
|||||||
handleVarListChange,
|
handleVarListChange,
|
||||||
handleAddVariable,
|
handleAddVariable,
|
||||||
toggleContextEnabled,
|
toggleContextEnabled,
|
||||||
} = useInput(mockLLMNodeData)
|
} = useConfig(mockLLMNodeData)
|
||||||
const model = inputs.model
|
const model = inputs.model
|
||||||
const modelMode = inputs.model?.mode
|
// const modelMode = inputs.model?.mode
|
||||||
const isChatMode = modelMode === 'chat'
|
// const isChatMode = modelMode === 'chat'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePanel
|
<BasePanel
|
||||||
|
|||||||
@ -1,13 +1,43 @@
|
|||||||
|
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
export const mockNodesData: Record<string, any> = {
|
export const mockNodesData: Record<string, any> = {
|
||||||
aaa: {
|
aaa: {
|
||||||
title: 'Start',
|
title: 'Start',
|
||||||
type: BlockEnum.Start,
|
type: BlockEnum.Start,
|
||||||
|
|
||||||
},
|
},
|
||||||
bbb: {
|
bbb: {
|
||||||
title: 'Knowledge',
|
title: 'Knowledge',
|
||||||
type: BlockEnum.KnowledgeRetrieval,
|
type: BlockEnum.KnowledgeRetrieval,
|
||||||
},
|
},
|
||||||
|
ccc: {
|
||||||
|
title: 'Code',
|
||||||
|
type: BlockEnum.Code,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mockNodeOutputVars: NodeOutPutVar[] = [
|
||||||
|
{
|
||||||
|
title: 'Start',
|
||||||
|
vars: [
|
||||||
|
{
|
||||||
|
variable: 'query',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variable: 'age',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'LLM',
|
||||||
|
vars: [
|
||||||
|
{
|
||||||
|
variable: 'usage',
|
||||||
|
type: 'object',
|
||||||
|
struct: ['token', 'value'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|||||||
@ -83,6 +83,15 @@ export type LLMNodeData = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NodeOutPutVar = {
|
||||||
|
title: string
|
||||||
|
vars: {
|
||||||
|
variable: string
|
||||||
|
type: string
|
||||||
|
struct?: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
export type Block = {
|
export type Block = {
|
||||||
classification?: string
|
classification?: string
|
||||||
type: BlockEnum
|
type: BlockEnum
|
||||||
|
|||||||
Reference in New Issue
Block a user