feat: implement createNodeWithEdgeToTarget mutation for enhanced node connections

- Added a new mutation to create nodes connected to existing nodes, allowing for more dynamic interactions on the canvas.
- Updated the CanvasPlacementContext to include the new mutation, improving the workflow for node creation and edge management.
- Enhanced optimistic updates for immediate UI feedback during node and edge creation processes.
- Refactored related components to support the new connection method, streamlining user interactions.
This commit is contained in:
Matthias
2026-03-28 17:50:45 +01:00
parent b3a1ed54db
commit 9694c50195
6 changed files with 552 additions and 85 deletions

View File

@@ -81,6 +81,29 @@ type CreateNodeWithEdgeFromSourceMutation = ReactMutation<
>
>;
type CreateNodeWithEdgeToTargetMutation = ReactMutation<
FunctionReference<
"mutation",
"public",
{
canvasId: Id<"canvases">;
type: string;
positionX: number;
positionY: number;
width: number;
height: number;
data: unknown;
parentId?: Id<"nodes">;
zIndex?: number;
clientRequestId?: string;
targetNodeId: Id<"nodes">;
sourceHandle?: string;
targetHandle?: string;
},
Id<"nodes">
>
>;
type FlowPoint = { x: number; y: number };
type CreateNodeWithIntersectionInput = {
@@ -105,6 +128,12 @@ export type CreateNodeConnectedFromSourceInput = CreateNodeWithIntersectionInput
targetHandle?: string;
};
export type CreateNodeConnectedToTargetInput = CreateNodeWithIntersectionInput & {
targetNodeId: Id<"nodes">;
sourceHandle?: string;
targetHandle?: string;
};
type CanvasPlacementContextValue = {
createNodeWithIntersection: (
input: CreateNodeWithIntersectionInput,
@@ -112,6 +141,9 @@ type CanvasPlacementContextValue = {
createNodeConnectedFromSource: (
input: CreateNodeConnectedFromSourceInput,
) => Promise<Id<"nodes">>;
createNodeConnectedToTarget: (
input: CreateNodeConnectedToTargetInput,
) => Promise<Id<"nodes">>;
};
const CanvasPlacementContext = createContext<CanvasPlacementContextValue | null>(
@@ -172,6 +204,7 @@ interface CanvasPlacementProviderProps {
createNode: CreateNodeMutation;
createNodeWithEdgeSplit: CreateNodeWithEdgeSplitMutation;
createNodeWithEdgeFromSource: CreateNodeWithEdgeFromSourceMutation;
createNodeWithEdgeToTarget: CreateNodeWithEdgeToTargetMutation;
onCreateNodeSettled?: (payload: {
clientRequestId?: string;
realId: Id<"nodes">;
@@ -184,6 +217,7 @@ export function CanvasPlacementProvider({
createNode,
createNodeWithEdgeSplit,
createNodeWithEdgeFromSource,
createNodeWithEdgeToTarget,
onCreateNodeSettled,
children,
}: CanvasPlacementProviderProps) {
@@ -327,9 +361,65 @@ export function CanvasPlacementProvider({
[canvasId, createNodeWithEdgeFromSource, onCreateNodeSettled],
);
const createNodeConnectedToTarget = useCallback(
async ({
type,
position,
width,
height,
data,
zIndex,
clientRequestId,
targetNodeId,
sourceHandle,
targetHandle,
}: CreateNodeConnectedToTargetInput) => {
const defaults = NODE_DEFAULTS[type] ?? {
width: 200,
height: 100,
data: {},
};
const effectiveWidth = width ?? defaults.width;
const effectiveHeight = height ?? defaults.height;
const payload = {
canvasId,
type,
positionX: position.x,
positionY: position.y,
width: effectiveWidth,
height: effectiveHeight,
data: {
...defaults.data,
...(data ?? {}),
canvasId,
},
...(zIndex !== undefined ? { zIndex } : {}),
...(clientRequestId !== undefined ? { clientRequestId } : {}),
targetNodeId,
sourceHandle,
targetHandle,
};
const realId = await createNodeWithEdgeToTarget(payload);
onCreateNodeSettled?.({ clientRequestId, realId });
return realId;
},
[canvasId, createNodeWithEdgeToTarget, onCreateNodeSettled],
);
const value = useMemo(
() => ({ createNodeWithIntersection, createNodeConnectedFromSource }),
[createNodeConnectedFromSource, createNodeWithIntersection],
() => ({
createNodeWithIntersection,
createNodeConnectedFromSource,
createNodeConnectedToTarget,
}),
[
createNodeConnectedFromSource,
createNodeConnectedToTarget,
createNodeWithIntersection,
],
);
return (