feat: enhance connection validation and node creation logic, improve type handling in various components

This commit is contained in:
2026-03-09 21:45:05 +01:00
parent 649d866bef
commit eb1413322f
14 changed files with 77 additions and 81 deletions

View File

@@ -217,9 +217,12 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
)
const isValidConnection = useCallback(
(connection: Connection) => {
const sourceNode = nodes.find((n) => n.id === connection.source)
const targetNode = nodes.find((n) => n.id === connection.target)
(connection: Connection | AppEdge) => {
const src = 'source' in connection ? connection.source : undefined
const tgt = 'target' in connection ? connection.target : undefined
if (typeof src !== 'string' || typeof tgt !== 'string') return false
const sourceNode = nodes.find((n) => n.id === src)
const targetNode = nodes.find((n) => n.id === tgt)
const sourceType = sourceNode?.type
const targetType = targetNode?.type
if (!sourceType || !targetType) return false
@@ -227,13 +230,16 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
const targetData = targetNode?.data as { configType?: string } | undefined
if (targetData?.configType !== 'markdown') return false
}
return isConnectionAllowed(sourceType, targetType, connection.source, connection.target)
return isConnectionAllowed(sourceType, targetType, src, tgt)
},
[nodes]
)
const onConnectStart = useCallback(
(_: React.MouseEvent | React.TouchEvent, params: { nodeId?: string | null; handleId?: string | null; handleType?: string | null }) => {
(
_: React.MouseEvent<Element> | React.TouchEvent<Element> | MouseEvent | TouchEvent,
params: { nodeId?: string | null; handleId?: string | null; handleType?: string | null }
) => {
if (params.handleType !== 'source' || !params.nodeId) {
setConnectionFrom(null)
return
@@ -371,14 +377,14 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
const createNode = useCallback(
(type: string) => {
const position = getMenuPosition()
if (position == null) return
const nodeType = type as Node['type']
const newId = getNextNodeId(nodeType, nodesRef.current.map((n) => n.id))
const dataMap = getDefaultDataForType(nodeType, newId)
const style = getDefaultStyle(nodeType)
if (position == null || typeof type !== 'string') return
const existingIds = nodesRef.current.map((n) => n.id).filter((id): id is string => id != null)
const newId = getNextNodeId(type, existingIds)
const dataMap = getDefaultDataForType(type, newId)
const style = getDefaultStyle(type)
const newNode: Node = {
id: newId,
type: nodeType,
type: type as Node['type'],
position: { x: position.x, y: position.y },
data: dataMap,
style,
@@ -416,14 +422,16 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
const raw = JSON.parse(text) as { id?: string; type?: string; data?: Record<string, unknown>; position?: { x: number; y: number }; style?: unknown }
const validIds = getRegisteredNodeTypeIds()
if (!raw || typeof raw.type !== 'string' || !validIds.includes(raw.type)) return
const nodeType = raw.type
setNodes((nds) => {
const newId = getNextNodeId(raw.type as Node['type'], nds.map((n) => n.id))
const existingIds = nds.map((n) => n.id).filter((id): id is string => id != null)
const newId = getNextNodeId(nodeType, existingIds)
const data = raw.data != null && typeof raw.data === 'object' ? { ...raw.data } : {}
if (raw.type === 'config' && data && 'title' in data) data.title = `config-${newId}`
const style = getDefaultStyle(raw.type)
if (nodeType === 'config' && data && 'title' in data) data.title = `config-${newId}`
const style = getDefaultStyle(nodeType)
const newNode: Node = {
id: newId,
type: raw.type as Node['type'],
type: nodeType as Node['type'],
position: { x: position.x, y: position.y },
data,
style,
@@ -560,7 +568,8 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
nodesConnectable
elementsSelectable
>
<Background variant="dots" gap={20} />
{/* BackgroundVariant from @xyflow/system expects enum; 'dots' is valid at runtime */}
<Background variant={'dots' as React.ComponentProps<typeof Background>['variant']} gap={20} />
<div role="group" aria-label="Canvas controls: zoom and fit view">
<Controls />
</div>