Skip to content

Commit 6cbdb99

Browse files
committed
feat: make node only one connect
1 parent 3ab7887 commit 6cbdb99

File tree

17 files changed

+665
-578
lines changed

17 files changed

+665
-578
lines changed

client/package-lock.json

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Edge } from './edge'
2+
3+
export const edgeTypes = {
4+
custom: Edge,
5+
}
Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { Button } from '@/components/ui'
22
import { X } from 'lucide-react'
3-
import {
4-
BaseEdge,
5-
EdgeLabelRenderer,
6-
EdgeProps,
7-
getSmoothStepPath,
8-
useReactFlow,
9-
} from 'reactflow'
3+
import { BaseEdge, EdgeProps, useReactFlow, getBezierPath } from 'reactflow'
4+
5+
const foreignObjectSize = 16
106

117
export const Edge = ({
128
id,
@@ -20,7 +16,7 @@ export const Edge = ({
2016
markerEnd,
2117
}: EdgeProps) => {
2218
const { setEdges } = useReactFlow()
23-
const [edgePath, labelX, labelY] = getSmoothStepPath({
19+
const [edgePath, labelX, labelY] = getBezierPath({
2420
sourceX,
2521
sourceY,
2622
sourcePosition,
@@ -36,21 +32,30 @@ export const Edge = ({
3632
return (
3733
<>
3834
<BaseEdge path={edgePath} markerEnd={markerEnd} style={style} />
39-
<EdgeLabelRenderer>
40-
<div
41-
style={{
42-
position: 'absolute',
43-
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
44-
fontSize: 12,
45-
pointerEvents: 'all',
46-
}}
47-
className='nodrag nopan'
35+
<path
36+
id={id}
37+
style={style}
38+
className='react-flow__edge-path edge-outline opacity-0'
39+
d={edgePath}
40+
markerEnd={markerEnd}
41+
/>
42+
<foreignObject
43+
width={foreignObjectSize}
44+
height={foreignObjectSize}
45+
x={labelX - foreignObjectSize / 2}
46+
y={labelY - foreignObjectSize / 2}
47+
className='edgebutton flex items-center justify-center '
48+
requiredExtensions='http://www.w3.org/1999/xhtml'
49+
>
50+
<Button
51+
size='icon'
52+
className='w-4 h-4 opacity-0 scale-50 invisible btn'
53+
variant='destructive'
54+
onClick={onEdgeClick}
4855
>
49-
<Button variant='outline' size='icon' className=' w-4 h-4 '>
50-
<X className='h-3 w-3 text-destructive' />
51-
</Button>
52-
</div>
53-
</EdgeLabelRenderer>
56+
<X className='w-2 h-2' />
57+
</Button>
58+
</foreignObject>
5459
</>
5560
)
5661
}

client/src/components/pages/chatbot-detail/nodes.tsx

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,58 @@
11
import { cn } from '@/lib/utils'
2-
import { Bolt } from 'lucide-react'
3-
import { Handle, HandleProps, Position } from 'reactflow'
2+
import { Bolt, MessageSquareMore } from 'lucide-react'
3+
import { useMemo } from 'react'
4+
import {
5+
Handle,
6+
HandleProps,
7+
Node,
8+
Position,
9+
getConnectedEdges,
10+
useNodeId,
11+
useStore,
12+
} from 'reactflow'
413

514
const HandleCustom = ({
615
className,
716
...props
8-
}: HandleProps & {
17+
}: Omit<HandleProps, 'isConnectable'> & {
918
className?: string
19+
isConnectable?: any
1020
}) => {
21+
const { nodeInternals, edges } = useStore((s) => ({
22+
nodeInternals: s.nodeInternals,
23+
edges: s.edges,
24+
}))
25+
26+
const nodeId = useNodeId()
27+
28+
const isHandleConnectable = useMemo(() => {
29+
if (!nodeId) return false
30+
31+
if (typeof props.isConnectable === 'function') {
32+
const node = nodeInternals.get(nodeId)
33+
const connectedEdges = getConnectedEdges([node as Node], edges)
34+
35+
const isConnectable = props.isConnectable as any
36+
37+
return isConnectable({ node, connectedEdges })
38+
}
39+
40+
if (typeof props.isConnectable === 'number') {
41+
const node = nodeInternals.get(nodeId)
42+
const connectedEdges = getConnectedEdges([node as Node], edges)
43+
44+
return connectedEdges.length < props.isConnectable
45+
}
46+
47+
return props.isConnectable
48+
}, [props, nodeInternals, nodeId, edges])
49+
1150
return (
12-
<Handle {...props} className={cn(' !bg-stone-600 !w-2 !h-2', className)} />
51+
<Handle
52+
{...props}
53+
className={cn(' !bg-stone-600 !w-2 !h-2', className)}
54+
isConnectable={isHandleConnectable}
55+
/>
1356
)
1457
}
1558

@@ -27,9 +70,9 @@ export const StartNode = () => {
2770

2871
export const NodeWrapper = ({ children }: { children: React.ReactNode }) => {
2972
return (
30-
<div className='bg-card shadow rounded-md p-4 border-card'>
73+
<div className='bg-card shadow rounded-md p-2 border-card'>
3174
{children}
32-
<HandleCustom type='target' position={Position.Left} />
75+
<HandleCustom type='target' position={Position.Left} isConnectable={1} />
3376
<HandleCustom type='source' position={Position.Right} />
3477
</div>
3578
)
@@ -39,7 +82,7 @@ export const MessageNode = () => {
3982
return (
4083
<NodeWrapper>
4184
<div className='flex items-center gap-2'>
42-
<Bolt className='w-4 h-4' />
85+
<MessageSquareMore className='w-4 h-4' />
4386
<span className='leading-none'>Message</span>
4487
</div>
4588
</NodeWrapper>

client/src/components/pages/chatbot-detail/setting.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import { Dialog } from '@radix-ui/react-dialog'
1313
import React, { useState } from 'react'
1414
import { useTranslation } from 'react-i18next'
1515
import GeneralSetting from './general-setting'
16-
import { TFlowInput } from '@/lib/schema/flow-input'
17-
import { useFlowCtx } from '.'
1816

1917
const useSettingOptions = () => {
2018
const { t } = useTranslation()

client/src/hooks/flow/use-update-flow.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ export const useUpdateFlow = () => {
1919
queryClient.invalidateQueries({
2020
queryKey: ['flows'],
2121
})
22+
queryClient.invalidateQueries({
23+
queryKey: ['channels-for-select'],
24+
})
25+
queryClient.invalidateQueries({
26+
queryKey: ['flow-detail', data.data.id],
27+
})
2228

2329
toast.success(data.message)
2430
},

client/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { SToaster } from '@/components/ui'
22
import { QueryClientProvider } from '@tanstack/react-query'
33
import { createRoot } from 'react-dom/client'
44
import i18n from './i18n'
5-
import './index.css'
5+
import '@/styles/index.css'
66
import { queryClient } from './lib/query-client'
77
import { RouterProvider } from 'react-router-dom'
88
import { router } from './app'

client/src/pages/chatbot-detail.tsx

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
FlowProvider,
44
nodeTypes,
55
} from '@/components/pages/chatbot-detail'
6-
import { Edge } from '@/components/pages/chatbot-detail/edge'
76
import { Toolbar } from '@/components/pages/chatbot-detail/toolbar'
87
import { queryFlowDetailOption } from '@/lib/query-options/flow'
98
import { EActionTypes } from '@/types/flow'
@@ -13,13 +12,14 @@ import { useParams } from 'react-router-dom'
1312
import ReactFlow, {
1413
Background,
1514
BackgroundVariant,
16-
ConnectionLineType,
1715
OnConnect,
1816
addEdge,
1917
useEdgesState,
2018
useNodesState,
2119
} from 'reactflow'
2220
import 'reactflow/dist/style.css'
21+
import '@/styles/react-flow.css'
22+
import { edgeTypes } from '@/components/pages/chatbot-detail/ede-types'
2323

2424
const ChatBotDetail = () => {
2525
const { id: flowId } = useParams()
@@ -28,10 +28,16 @@ const ChatBotDetail = () => {
2828
)
2929
const [nodes, _setNodes, onNodesChange] = useNodesState([
3030
{
31-
id: '1',
31+
id: EActionTypes.START,
3232
type: EActionTypes.START,
3333
position: { x: 100, y: 100 },
34-
data: { label: 'Start' },
34+
data: {
35+
label: 'Start',
36+
type: EActionTypes.START,
37+
id: EActionTypes.START,
38+
name: 'Start',
39+
},
40+
deletable: false,
3541
},
3642
{
3743
id: '2',
@@ -46,26 +52,34 @@ const ChatBotDetail = () => {
4652
data: { label: 'Message' },
4753
},
4854
])
49-
const [edges, setEdges, onEdgesChange] = useEdgesState([
50-
{
51-
id: 'e1-2',
52-
source: '1',
53-
target: '2',
54-
type: 'smoothstep',
55-
},
56-
])
55+
const [edges, setEdges, onEdgesChange] = useEdgesState([])
5756

5857
const onConnect: OnConnect = useCallback(
59-
(params) =>
60-
setEdges((eds) =>
61-
addEdge(
58+
(params) => {
59+
setEdges((eds) => {
60+
if (eds.length > 0) {
61+
console.log({
62+
params,
63+
eds,
64+
})
65+
66+
if (
67+
params.source === EActionTypes.START &&
68+
eds.some((edge) => edge.source === EActionTypes.START)
69+
) {
70+
return eds
71+
}
72+
}
73+
74+
return addEdge(
6275
{
6376
...params,
64-
type: 'smoothstep',
77+
type: 'custom',
6578
},
6679
eds,
67-
),
68-
),
80+
)
81+
})
82+
},
6983
[setEdges],
7084
)
7185

@@ -79,10 +93,7 @@ const ChatBotDetail = () => {
7993
onNodesChange={onNodesChange}
8094
onEdgesChange={onEdgesChange}
8195
onConnect={onConnect}
82-
edgeTypes={{
83-
smoothstep: Edge,
84-
}}
85-
connectionLineType={ConnectionLineType.SmoothStep}
96+
edgeTypes={edgeTypes}
8697
>
8798
<Background
8899
gap={24}
File renamed without changes.

client/src/styles/react-flow.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.react-flow__edge.selected .react-flow__edge-path,
2+
.react-flow__edge:focus .react-flow__edge-path,
3+
.react-flow__edge:focus-visible .react-flow__edge-path,
4+
.react-flow__edge:hover .react-flow__edge-path {
5+
@apply stroke-primary transition-all;
6+
}
7+
8+
.react-flow__edge.selected .edge-outline,
9+
.react-flow__edge:focus .edge-outline,
10+
.react-flow__edge:focus-visible .edge-outline,
11+
.react-flow__edge:hover .edge-outline {
12+
@apply stroke-[5px] transition-all stroke-primary/10 opacity-100;
13+
}
14+
15+
.react-flow__edge.selected .edgebutton .btn,
16+
.react-flow__edge:focus .edgebutton .btn,
17+
.react-flow__edge:focus-visible .edgebutton .btn,
18+
.react-flow__edge:hover .edgebutton .btn {
19+
@apply opacity-100 scale-100 visible transition-all;
20+
}

0 commit comments

Comments
 (0)