### What problem does this PR solve? feat: Display input parameters on operator nodes #3240 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.14.0
| @@ -17,6 +17,7 @@ | |||
| "@radix-ui/react-dropdown-menu": "^2.1.2", | |||
| "@radix-ui/react-icons": "^1.3.1", | |||
| "@radix-ui/react-label": "^2.1.0", | |||
| "@radix-ui/react-popover": "^1.1.2", | |||
| "@radix-ui/react-select": "^2.1.2", | |||
| "@radix-ui/react-separator": "^1.1.0", | |||
| "@radix-ui/react-slot": "^1.1.0", | |||
| @@ -4370,6 +4371,42 @@ | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-popover": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", | |||
| "integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", | |||
| "dependencies": { | |||
| "@radix-ui/primitive": "1.1.0", | |||
| "@radix-ui/react-compose-refs": "1.1.0", | |||
| "@radix-ui/react-context": "1.1.1", | |||
| "@radix-ui/react-dismissable-layer": "1.1.1", | |||
| "@radix-ui/react-focus-guards": "1.1.1", | |||
| "@radix-ui/react-focus-scope": "1.1.0", | |||
| "@radix-ui/react-id": "1.1.0", | |||
| "@radix-ui/react-popper": "1.2.0", | |||
| "@radix-ui/react-portal": "1.1.2", | |||
| "@radix-ui/react-presence": "1.1.1", | |||
| "@radix-ui/react-primitive": "2.0.0", | |||
| "@radix-ui/react-slot": "1.1.0", | |||
| "@radix-ui/react-use-controllable-state": "1.1.0", | |||
| "aria-hidden": "^1.1.1", | |||
| "react-remove-scroll": "2.6.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-popper": { | |||
| "version": "1.2.0", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", | |||
| @@ -28,6 +28,7 @@ | |||
| "@radix-ui/react-dropdown-menu": "^2.1.2", | |||
| "@radix-ui/react-icons": "^1.3.1", | |||
| "@radix-ui/react-label": "^2.1.0", | |||
| "@radix-ui/react-popover": "^1.1.2", | |||
| "@radix-ui/react-select": "^2.1.2", | |||
| "@radix-ui/react-separator": "^1.1.0", | |||
| "@radix-ui/react-slot": "^1.1.0", | |||
| @@ -0,0 +1,31 @@ | |||
| 'use client'; | |||
| import * as PopoverPrimitive from '@radix-ui/react-popover'; | |||
| import * as React from 'react'; | |||
| import { cn } from '@/lib/utils'; | |||
| const Popover = PopoverPrimitive.Root; | |||
| const PopoverTrigger = PopoverPrimitive.Trigger; | |||
| const PopoverContent = React.forwardRef< | |||
| React.ElementRef<typeof PopoverPrimitive.Content>, | |||
| React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> | |||
| >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( | |||
| <PopoverPrimitive.Portal> | |||
| <PopoverPrimitive.Content | |||
| ref={ref} | |||
| align={align} | |||
| sideOffset={sideOffset} | |||
| className={cn( | |||
| 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', | |||
| className, | |||
| )} | |||
| {...props} | |||
| /> | |||
| </PopoverPrimitive.Portal> | |||
| )); | |||
| PopoverContent.displayName = PopoverPrimitive.Content.displayName; | |||
| export { Popover, PopoverContent, PopoverTrigger }; | |||
| @@ -0,0 +1,117 @@ | |||
| import * as React from 'react'; | |||
| import { cn } from '@/lib/utils'; | |||
| const Table = React.forwardRef< | |||
| HTMLTableElement, | |||
| React.HTMLAttributes<HTMLTableElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <div className="relative w-full overflow-auto"> | |||
| <table | |||
| ref={ref} | |||
| className={cn('w-full caption-bottom text-sm', className)} | |||
| {...props} | |||
| /> | |||
| </div> | |||
| )); | |||
| Table.displayName = 'Table'; | |||
| const TableHeader = React.forwardRef< | |||
| HTMLTableSectionElement, | |||
| React.HTMLAttributes<HTMLTableSectionElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} /> | |||
| )); | |||
| TableHeader.displayName = 'TableHeader'; | |||
| const TableBody = React.forwardRef< | |||
| HTMLTableSectionElement, | |||
| React.HTMLAttributes<HTMLTableSectionElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <tbody | |||
| ref={ref} | |||
| className={cn('[&_tr:last-child]:border-0', className)} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableBody.displayName = 'TableBody'; | |||
| const TableFooter = React.forwardRef< | |||
| HTMLTableSectionElement, | |||
| React.HTMLAttributes<HTMLTableSectionElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <tfoot | |||
| ref={ref} | |||
| className={cn( | |||
| 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', | |||
| className, | |||
| )} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableFooter.displayName = 'TableFooter'; | |||
| const TableRow = React.forwardRef< | |||
| HTMLTableRowElement, | |||
| React.HTMLAttributes<HTMLTableRowElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <tr | |||
| ref={ref} | |||
| className={cn( | |||
| 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', | |||
| className, | |||
| )} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableRow.displayName = 'TableRow'; | |||
| const TableHead = React.forwardRef< | |||
| HTMLTableCellElement, | |||
| React.ThHTMLAttributes<HTMLTableCellElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <th | |||
| ref={ref} | |||
| className={cn( | |||
| 'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0', | |||
| className, | |||
| )} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableHead.displayName = 'TableHead'; | |||
| const TableCell = React.forwardRef< | |||
| HTMLTableCellElement, | |||
| React.TdHTMLAttributes<HTMLTableCellElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <td | |||
| ref={ref} | |||
| className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableCell.displayName = 'TableCell'; | |||
| const TableCaption = React.forwardRef< | |||
| HTMLTableCaptionElement, | |||
| React.HTMLAttributes<HTMLTableCaptionElement> | |||
| >(({ className, ...props }, ref) => ( | |||
| <caption | |||
| ref={ref} | |||
| className={cn('mt-4 text-sm text-muted-foreground', className)} | |||
| {...props} | |||
| /> | |||
| )); | |||
| TableCaption.displayName = 'TableCaption'; | |||
| export { | |||
| Table, | |||
| TableBody, | |||
| TableCaption, | |||
| TableCell, | |||
| TableFooter, | |||
| TableHead, | |||
| TableHeader, | |||
| TableRow, | |||
| }; | |||
| @@ -11,7 +11,8 @@ export interface BaseState { | |||
| export interface IModalProps<T> { | |||
| showModal?(): void; | |||
| hideModal(): void; | |||
| hideModal?(): void; | |||
| switchVisible?(visible: boolean): void; | |||
| visible?: boolean; | |||
| loading?: boolean; | |||
| onOk?(payload?: T): Promise<any> | void; | |||
| @@ -1031,8 +1031,11 @@ The above is the content you need to summarize.`, | |||
| 'If the response is HTML formatted and only the primary content wanted, please toggle it on.', | |||
| reference: 'Reference', | |||
| input: 'Input', | |||
| output: 'Output', | |||
| parameter: 'Parameter', | |||
| howUseId: 'How to use agent ID?', | |||
| content: 'Content', | |||
| operationResults: 'Operation Results', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -979,8 +979,11 @@ export default { | |||
| cleanHtmlTip: '如果回應是 HTML 格式並且只需要主要內容,請將其開啟。', | |||
| reference: '引用', | |||
| input: '輸入', | |||
| output: '輸出', | |||
| parameter: '參數', | |||
| howUseId: '如何使用Agent ID?', | |||
| content: '內容', | |||
| operationResults: '運行結果', | |||
| }, | |||
| footer: { | |||
| profile: '“保留所有權利 @ react”', | |||
| @@ -999,8 +999,11 @@ export default { | |||
| cleanHtmlTip: '如果响应是 HTML 格式且只需要主要内容,请将其打开。', | |||
| reference: '引用', | |||
| input: '输入', | |||
| output: '输出', | |||
| parameter: '参数', | |||
| howUseId: '如何使用Agent ID?', | |||
| content: '内容', | |||
| operationResults: '运行结果', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -0,0 +1,57 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Card, | |||
| CardContent, | |||
| CardDescription, | |||
| CardFooter, | |||
| CardHeader, | |||
| CardTitle, | |||
| } from '@/components/ui/card'; | |||
| import { Input } from '@/components/ui/input'; | |||
| import { Label } from '@/components/ui/label'; | |||
| import { | |||
| Select, | |||
| SelectContent, | |||
| SelectItem, | |||
| SelectTrigger, | |||
| SelectValue, | |||
| } from '@/components/ui/select'; | |||
| export function CardWithForm() { | |||
| return ( | |||
| <Card className="w-[350px]"> | |||
| <CardHeader> | |||
| <CardTitle>Create project</CardTitle> | |||
| <CardDescription>Deploy your new project in one-click.</CardDescription> | |||
| </CardHeader> | |||
| <CardContent> | |||
| <form> | |||
| <div className="grid w-full items-center gap-4"> | |||
| <div className="flex flex-col space-y-1.5"> | |||
| <Label htmlFor="name">Name</Label> | |||
| <Input id="name" placeholder="Name of your project" /> | |||
| </div> | |||
| <div className="flex flex-col space-y-1.5"> | |||
| <Label htmlFor="framework">Framework</Label> | |||
| <Select> | |||
| <SelectTrigger id="framework"> | |||
| <SelectValue placeholder="Select" /> | |||
| </SelectTrigger> | |||
| <SelectContent position="popper"> | |||
| <SelectItem value="next">Next.js</SelectItem> | |||
| <SelectItem value="sveltekit">SvelteKit</SelectItem> | |||
| <SelectItem value="astro">Astro</SelectItem> | |||
| <SelectItem value="nuxt">Nuxt.js</SelectItem> | |||
| </SelectContent> | |||
| </Select> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </CardContent> | |||
| <CardFooter className="flex justify-between"> | |||
| <Button variant="outline">Cancel</Button> | |||
| <Button>Deploy</Button> | |||
| </CardFooter> | |||
| </Card> | |||
| ); | |||
| } | |||
| @@ -8,55 +8,52 @@ import { RightHandleStyle } from './handle-icon'; | |||
| import { useBuildCategorizeHandlePositions } from './hooks'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) { | |||
| const { positions } = useBuildCategorizeHandlePositions({ data, id }); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical gap={8}> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| {positions.map((position, idx) => { | |||
| return ( | |||
| <div key={idx}> | |||
| <div className={styles.nodeText}>{position.text}</div> | |||
| <Handle | |||
| key={position.text} | |||
| id={position.text} | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| style={{ ...RightHandleStyle, top: position.top }} | |||
| ></Handle> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| <Flex vertical gap={8}> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| {positions.map((position, idx) => { | |||
| return ( | |||
| <div key={idx}> | |||
| <div className={styles.nodeText}>{position.text}</div> | |||
| <Handle | |||
| key={position.text} | |||
| id={position.text} | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| style={{ ...RightHandleStyle, top: position.top }} | |||
| ></Handle> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -8,7 +8,6 @@ import { IGenerateParameter, NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function GenerateNode({ | |||
| id, | |||
| @@ -20,55 +19,53 @@ export function GenerateNode({ | |||
| const getLabel = useGetComponentLabelByValue(id); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| <Flex gap={8} vertical className={styles.generateParameters}> | |||
| {parameters.map((x) => ( | |||
| <Flex | |||
| key={x.id} | |||
| align="center" | |||
| gap={6} | |||
| className={styles.conditionBlock} | |||
| > | |||
| <label htmlFor="">{x.key}</label> | |||
| <span className={styles.parameterValue}> | |||
| {getLabel(x.component_id)} | |||
| </span> | |||
| </Flex> | |||
| ))} | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| <Flex gap={8} vertical className={styles.generateParameters}> | |||
| {parameters.map((x) => ( | |||
| <Flex | |||
| key={x.id} | |||
| align="center" | |||
| gap={6} | |||
| className={styles.conditionBlock} | |||
| > | |||
| <label htmlFor="">{x.key}</label> | |||
| <span className={styles.parameterValue}> | |||
| {getLabel(x.component_id)} | |||
| </span> | |||
| </Flex> | |||
| ))} | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -4,7 +4,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function RagNode({ | |||
| id, | |||
| @@ -13,30 +12,28 @@ export function RagNode({ | |||
| selected, | |||
| }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| id="b" | |||
| style={RightHandleStyle} | |||
| ></Handle> | |||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||
| </section> | |||
| </NodePopover> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| id="b" | |||
| style={RightHandleStyle} | |||
| ></Handle> | |||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -7,7 +7,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function InvokeNode({ | |||
| id, | |||
| @@ -18,39 +17,37 @@ export function InvokeNode({ | |||
| const { t } = useTranslation(); | |||
| const url = get(data, 'form.url'); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| id="b" | |||
| style={RightHandleStyle} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical> | |||
| <div>{t('flow.url')}</div> | |||
| <div className={styles.nodeText}>{url}</div> | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| id="b" | |||
| style={RightHandleStyle} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical> | |||
| <div>{t('flow.url')}</div> | |||
| <div className={styles.nodeText}>{url}</div> | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -6,7 +6,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function KeywordNode({ | |||
| id, | |||
| @@ -15,40 +14,38 @@ export function KeywordNode({ | |||
| selected, | |||
| }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| </section> | |||
| </NodePopover> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -4,7 +4,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function LogicNode({ | |||
| id, | |||
| @@ -13,30 +12,28 @@ export function LogicNode({ | |||
| selected, | |||
| }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||
| </section> | |||
| </NodePopover> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -6,7 +6,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function MessageNode({ | |||
| id, | |||
| @@ -17,47 +16,45 @@ export function MessageNode({ | |||
| const messages: string[] = get(data, 'form.messages', []); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={classNames({ | |||
| [styles.nodeHeader]: messages.length > 0, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={classNames({ | |||
| [styles.nodeHeader]: messages.length > 0, | |||
| })} | |||
| ></NodeHeader> | |||
| ></NodeHeader> | |||
| <Flex vertical gap={8} className={styles.messageNodeContainer}> | |||
| {messages.map((message, idx) => { | |||
| return ( | |||
| <div className={styles.nodeText} key={idx}> | |||
| {message} | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| <Flex vertical gap={8} className={styles.messageNodeContainer}> | |||
| {messages.map((message, idx) => { | |||
| return ( | |||
| <div className={styles.nodeText} key={idx}> | |||
| {message} | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -1,34 +1,52 @@ | |||
| import { Flex } from 'antd'; | |||
| import { Operator, operatorMap } from '../../constant'; | |||
| import OperatorIcon from '../../operator-icon'; | |||
| import NodeDropdown from './dropdown'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import styles from './index.less'; | |||
| import { NextNodePopover } from './popover'; | |||
| interface IProps { | |||
| id: string; | |||
| label: string; | |||
| name: string; | |||
| label?: string; | |||
| name?: string; | |||
| gap?: number; | |||
| className?: string; | |||
| } | |||
| export function RunStatus({ id, name }: IProps) { | |||
| const { t } = useTranslate('flow'); | |||
| return ( | |||
| <section className="flex justify-end items-center pb-1 "> | |||
| <NextNodePopover nodeId={id} name={name}> | |||
| <span className="text-blue-600 cursor-pointer text-[10px]"> | |||
| {t('operationResults')} | |||
| </span> | |||
| </NextNodePopover> | |||
| </section> | |||
| ); | |||
| } | |||
| const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => { | |||
| return ( | |||
| <Flex | |||
| flex={1} | |||
| align="center" | |||
| justify={'space-between'} | |||
| gap={gap} | |||
| className={className} | |||
| > | |||
| <OperatorIcon | |||
| name={label as Operator} | |||
| color={operatorMap[label as Operator].color} | |||
| ></OperatorIcon> | |||
| <span className={styles.nodeTitle}>{name}</span> | |||
| <NodeDropdown id={id}></NodeDropdown> | |||
| </Flex> | |||
| <section className="haha"> | |||
| {label !== Operator.Answer && <RunStatus id={id} name={name}></RunStatus>} | |||
| <Flex | |||
| flex={1} | |||
| align="center" | |||
| justify={'space-between'} | |||
| gap={gap} | |||
| className={className} | |||
| > | |||
| <OperatorIcon | |||
| name={label as Operator} | |||
| color={operatorMap[label as Operator].color} | |||
| ></OperatorIcon> | |||
| <span className={styles.nodeTitle}>{name}</span> | |||
| <NodeDropdown id={id}></NodeDropdown> | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| }; | |||
| @@ -1,49 +1,98 @@ | |||
| import { useFetchFlow } from '@/hooks/flow-hooks'; | |||
| import { Popover } from 'antd'; | |||
| import get from 'lodash/get'; | |||
| import React, { useMemo } from 'react'; | |||
| import React, { MouseEventHandler, useCallback, useMemo } from 'react'; | |||
| import JsonView from 'react18-json-view'; | |||
| import 'react18-json-view/src/style.css'; | |||
| import { Operator } from '../../constant'; | |||
| import { useReplaceIdWithText } from '../../hooks'; | |||
| import styles from './index.less'; | |||
| import { | |||
| Popover, | |||
| PopoverContent, | |||
| PopoverTrigger, | |||
| } from '@/components/ui/popover'; | |||
| import { | |||
| Table, | |||
| TableBody, | |||
| TableCell, | |||
| TableHead, | |||
| TableHeader, | |||
| TableRow, | |||
| } from '@/components/ui/table'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| interface IProps extends React.PropsWithChildren { | |||
| nodeId: string; | |||
| name?: string; | |||
| } | |||
| const NodePopover = ({ children, nodeId }: IProps) => { | |||
| export function NextNodePopover({ children, nodeId, name }: IProps) { | |||
| const { t } = useTranslate('flow'); | |||
| const { data } = useFetchFlow(); | |||
| const component = useMemo(() => { | |||
| return get(data, ['dsl', 'components', nodeId], {}); | |||
| }, [nodeId, data]); | |||
| const inputs: Array<{ component_id: string; content: string }> = get( | |||
| component, | |||
| ['obj', 'params', 'inputs'], | |||
| [], | |||
| ); | |||
| const output = get(component, ['obj', 'params', 'output'], {}); | |||
| const componentName = get(component, ['obj', 'component_name'], ''); | |||
| const replacedOutput = useReplaceIdWithText(output); | |||
| const content = | |||
| componentName !== Operator.Answer ? ( | |||
| <div | |||
| onClick={(e) => { | |||
| e.preventDefault(); | |||
| e.stopPropagation(); | |||
| }} | |||
| > | |||
| <JsonView | |||
| src={replacedOutput} | |||
| displaySize={30} | |||
| className={styles.jsonView} | |||
| /> | |||
| </div> | |||
| ) : undefined; | |||
| const { replacedOutput, getNameById } = useReplaceIdWithText(output); | |||
| const stopPropagation: MouseEventHandler = useCallback((e) => { | |||
| e.stopPropagation(); | |||
| }, []); | |||
| return ( | |||
| <Popover content={content} placement="right" destroyTooltipOnHide> | |||
| {children} | |||
| <Popover> | |||
| <PopoverTrigger onClick={stopPropagation} asChild> | |||
| {children} | |||
| </PopoverTrigger> | |||
| <PopoverContent | |||
| align={'start'} | |||
| side={'right'} | |||
| sideOffset={20} | |||
| onClick={stopPropagation} | |||
| className="w-[400px]" | |||
| > | |||
| <div className="mb-3 font-semibold text-[16px]"> | |||
| {name} {t('operationResults')} | |||
| </div> | |||
| <div className="flex w-full gap-4 flex-col"> | |||
| <div className="flex flex-col space-y-1.5"> | |||
| <span className="font-semibold text-[14px]">{t('input')}</span> | |||
| <div className="bg-gray-100 p-1 rounded"> | |||
| <Table> | |||
| <TableHeader> | |||
| <TableRow> | |||
| <TableHead>{t('componentId')}</TableHead> | |||
| <TableHead className="w-[60px]">{t('content')}</TableHead> | |||
| </TableRow> | |||
| </TableHeader> | |||
| <TableBody> | |||
| {inputs.map((x, idx) => ( | |||
| <TableRow key={idx}> | |||
| <TableCell>{getNameById(x.component_id)}</TableCell> | |||
| <TableCell className="truncate">{x.content}</TableCell> | |||
| </TableRow> | |||
| ))} | |||
| </TableBody> | |||
| </Table> | |||
| </div> | |||
| </div> | |||
| <div className="flex flex-col space-y-1.5"> | |||
| <span className="font-semibold text-[14px]">{t('output')}</span> | |||
| <div className="bg-gray-100 p-1 rounded"> | |||
| <JsonView | |||
| src={replacedOutput} | |||
| displaySize={30} | |||
| className="w-full max-h-[300px] break-words overflow-auto" | |||
| /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </PopoverContent> | |||
| </Popover> | |||
| ); | |||
| }; | |||
| export default NodePopover; | |||
| } | |||
| @@ -3,7 +3,6 @@ import classNames from 'classnames'; | |||
| import { Handle, NodeProps, Position } from 'reactflow'; | |||
| import { NodeData } from '../../interface'; | |||
| import { RightHandleStyle } from './handle-icon'; | |||
| import NodePopover from './popover'; | |||
| import { get } from 'lodash'; | |||
| import styles from './index.less'; | |||
| @@ -13,53 +12,51 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) { | |||
| const yes = get(data, 'form.yes'); | |||
| const no = get(data, 'form.no'); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'yes'} | |||
| style={{ ...RightHandleStyle, top: 59 }} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'no'} | |||
| style={{ ...RightHandleStyle, top: 112 }} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'yes'} | |||
| style={{ ...RightHandleStyle, top: 59 }} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'no'} | |||
| style={{ ...RightHandleStyle, top: 112 }} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical gap={10}> | |||
| <Flex vertical> | |||
| <div className={styles.relevantLabel}>Yes</div> | |||
| <div className={styles.nodeText}>{yes}</div> | |||
| </Flex> | |||
| <Flex vertical> | |||
| <div className={styles.relevantLabel}>No</div> | |||
| <div className={styles.nodeText}>{no}</div> | |||
| </Flex> | |||
| <Flex vertical gap={10}> | |||
| <Flex vertical> | |||
| <div className={styles.relevantLabel}>Yes</div> | |||
| <div className={styles.nodeText}>{yes}</div> | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| <Flex vertical> | |||
| <div className={styles.relevantLabel}>No</div> | |||
| <div className={styles.nodeText}>{no}</div> | |||
| </Flex> | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -9,7 +9,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function RetrievalNode({ | |||
| id, | |||
| @@ -31,55 +30,53 @@ export function RetrievalNode({ | |||
| }, [knowledgeList, knowledgeBaseIds]); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={classNames({ | |||
| [styles.nodeHeader]: knowledgeBaseIds.length > 0, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={classNames({ | |||
| [styles.nodeHeader]: knowledgeBaseIds.length > 0, | |||
| })} | |||
| ></NodeHeader> | |||
| <Flex vertical gap={8}> | |||
| {knowledgeBases.map((knowledge) => { | |||
| return ( | |||
| <div className={styles.nodeText} key={knowledge.id}> | |||
| <Flex align={'center'} gap={6}> | |||
| <Avatar | |||
| size={26} | |||
| icon={<UserOutlined />} | |||
| src={knowledge.avatar} | |||
| /> | |||
| <Flex className={styles.knowledgeNodeName} flex={1}> | |||
| {knowledge.name} | |||
| </Flex> | |||
| ></NodeHeader> | |||
| <Flex vertical gap={8}> | |||
| {knowledgeBases.map((knowledge) => { | |||
| return ( | |||
| <div className={styles.nodeText} key={knowledge.id}> | |||
| <Flex align={'center'} gap={6}> | |||
| <Avatar | |||
| size={26} | |||
| icon={<UserOutlined />} | |||
| src={knowledge.avatar} | |||
| /> | |||
| <Flex className={styles.knowledgeNodeName} flex={1}> | |||
| {knowledge.name} | |||
| </Flex> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| </Flex> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -6,7 +6,6 @@ import { NodeData } from '../../interface'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| export function RewriteNode({ | |||
| id, | |||
| @@ -15,40 +14,38 @@ export function RewriteNode({ | |||
| selected, | |||
| }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={LeftHandleStyle} | |||
| ></Handle> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| style={RightHandleStyle} | |||
| id="b" | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| </section> | |||
| </NodePopover> | |||
| <div className={styles.nodeText}> | |||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -7,7 +7,6 @@ import { RightHandleStyle } from './handle-icon'; | |||
| import { useBuildSwitchHandlePositions } from './hooks'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import NodePopover from './popover'; | |||
| const getConditionKey = (idx: number, length: number) => { | |||
| if (idx === 0 && length !== 1) { | |||
| @@ -58,55 +57,53 @@ export function SwitchNode({ id, data, selected }: NodeProps<NodeData>) { | |||
| const { positions } = useBuildSwitchHandlePositions({ data, id }); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical gap={10}> | |||
| {positions.map((position, idx) => { | |||
| return ( | |||
| <div key={idx}> | |||
| <Flex vertical> | |||
| <Flex justify={'space-between'}> | |||
| <span>{idx < positions.length - 1 && position.text}</span> | |||
| <span>{getConditionKey(idx, positions.length)}</span> | |||
| </Flex> | |||
| {position.condition && ( | |||
| <ConditionBlock | |||
| nodeId={id} | |||
| condition={position.condition} | |||
| ></ConditionBlock> | |||
| )} | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| > | |||
| <Handle | |||
| type="target" | |||
| position={Position.Left} | |||
| isConnectable | |||
| className={styles.handle} | |||
| id={'a'} | |||
| ></Handle> | |||
| <NodeHeader | |||
| id={id} | |||
| name={data.name} | |||
| label={data.label} | |||
| className={styles.nodeHeader} | |||
| ></NodeHeader> | |||
| <Flex vertical gap={10}> | |||
| {positions.map((position, idx) => { | |||
| return ( | |||
| <div key={idx}> | |||
| <Flex vertical> | |||
| <Flex justify={'space-between'}> | |||
| <span>{idx < positions.length - 1 && position.text}</span> | |||
| <span>{getConditionKey(idx, positions.length)}</span> | |||
| </Flex> | |||
| <Handle | |||
| key={position.text} | |||
| id={position.text} | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| style={{ ...RightHandleStyle, top: position.top }} | |||
| ></Handle> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| </NodePopover> | |||
| {position.condition && ( | |||
| <ConditionBlock | |||
| nodeId={id} | |||
| condition={position.condition} | |||
| ></ConditionBlock> | |||
| )} | |||
| </Flex> | |||
| <Handle | |||
| key={position.text} | |||
| id={position.text} | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable | |||
| className={styles.handle} | |||
| style={{ ...RightHandleStyle, top: position.top }} | |||
| ></Handle> | |||
| </div> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -524,7 +524,10 @@ export const useReplaceIdWithText = (output: unknown) => { | |||
| return getNode(id)?.data.name; | |||
| }; | |||
| return replaceIdWithText(output, getNameById); | |||
| return { | |||
| replacedOutput: replaceIdWithText(output, getNameById), | |||
| getNameById, | |||
| }; | |||
| }; | |||
| /** | |||