Skip to content

Commit 0f3d9e3

Browse files
committed
feat: zIndex Management for IO Nodes
1 parent 664772c commit 0f3d9e3

6 files changed

Lines changed: 159 additions & 16 deletions

File tree

react-compiler.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const REACT_COMPILER_ENABLED_DIRS = [
5050
"src/components/shared/ReactFlow/FlowSidebar/components/ComponentHoverPopover.tsx",
5151
"src/components/shared/ReactFlow/FlowControls/StackingControls.tsx",
5252
"src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskOverview/ZIndexEditor.tsx",
53+
"src/components/Editor/IOEditor/IOZIndexEditor.tsx",
5354

5455
// 11-20 useCallback/useMemo
5556
// "src/components/ui", // 12
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { ContentBlock } from "@/components/shared/ContextPanel/Blocks/ContentBlock";
2+
import { StackingControls } from "@/components/shared/ReactFlow/FlowControls/StackingControls";
3+
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
4+
import { ZINDEX_ANNOTATION } from "@/utils/annotations";
5+
import {
6+
type ComponentSpec,
7+
type InputSpec,
8+
type OutputSpec,
9+
} from "@/utils/componentSpec";
10+
import {
11+
inputNameToNodeId,
12+
outputNameToNodeId,
13+
} from "@/utils/nodes/nodeIdUtils";
14+
import { updateSubgraphSpec } from "@/utils/subgraphUtils";
15+
16+
interface IOZIndexEditorProps {
17+
ioSpec: InputSpec | OutputSpec;
18+
ioType: "input" | "output";
19+
}
20+
21+
export const IOZIndexEditor = ({ ioSpec, ioType }: IOZIndexEditorProps) => {
22+
const {
23+
componentSpec,
24+
currentSubgraphSpec,
25+
currentSubgraphPath,
26+
setComponentSpec,
27+
} = useComponentSpec();
28+
29+
const isInput = ioType === "input";
30+
31+
const nodeId = isInput
32+
? inputNameToNodeId(ioSpec.name)
33+
: outputNameToNodeId(ioSpec.name);
34+
35+
const handleStackingControlChange = (newZIndex: number) => {
36+
const updatedSubgraphSpec = setZIndexInAnnotations(
37+
currentSubgraphSpec,
38+
ioSpec,
39+
ioType,
40+
newZIndex,
41+
);
42+
43+
const newRootSpec = updateSubgraphSpec(
44+
componentSpec,
45+
currentSubgraphPath,
46+
updatedSubgraphSpec,
47+
);
48+
49+
setComponentSpec(newRootSpec);
50+
};
51+
52+
return (
53+
<ContentBlock className="border rounded-lg p-2 bg-background w-fit mx-auto">
54+
<StackingControls
55+
nodeId={nodeId}
56+
onChange={handleStackingControlChange}
57+
/>
58+
</ContentBlock>
59+
);
60+
};
61+
62+
function setZIndexInAnnotations(
63+
componentSpec: ComponentSpec,
64+
ioSpec: InputSpec | OutputSpec,
65+
ioType: "input" | "output",
66+
zIndex: number,
67+
): ComponentSpec {
68+
const newComponentSpec = { ...componentSpec };
69+
70+
const isInput = ioType === "input";
71+
const isOutput = ioType === "output";
72+
73+
if (isInput) {
74+
const inputs = [...(newComponentSpec.inputs || [])];
75+
const inputIndex = inputs.findIndex((input) => input.name === ioSpec.name);
76+
77+
if (inputIndex >= 0) {
78+
const annotations = inputs[inputIndex].annotations || {};
79+
80+
const updatedAnnotations = {
81+
...annotations,
82+
[ZINDEX_ANNOTATION]: `${zIndex}`,
83+
};
84+
85+
inputs[inputIndex] = {
86+
...inputs[inputIndex],
87+
annotations: updatedAnnotations,
88+
};
89+
90+
newComponentSpec.inputs = inputs;
91+
}
92+
} else if (isOutput) {
93+
const outputs = [...(newComponentSpec.outputs || [])];
94+
const outputIndex = outputs.findIndex(
95+
(output) => output.name === ioSpec.name,
96+
);
97+
98+
if (outputIndex >= 0) {
99+
const annotations = outputs[outputIndex].annotations || {};
100+
101+
const updatedAnnotations = {
102+
...annotations,
103+
[ZINDEX_ANNOTATION]: `${zIndex}`,
104+
};
105+
106+
outputs[outputIndex] = {
107+
...outputs[outputIndex],
108+
annotations: updatedAnnotations,
109+
};
110+
111+
newComponentSpec.outputs = outputs;
112+
}
113+
}
114+
115+
return newComponentSpec;
116+
}

src/components/Editor/IOEditor/InputValueEditor/InputValueEditor.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { InfoBox } from "@/components/shared/InfoBox";
66
import { removeGraphInput } from "@/components/shared/ReactFlow/FlowCanvas/utils/removeNode";
77
import { Button } from "@/components/ui/button";
88
import { Icon } from "@/components/ui/icon";
9-
import { BlockStack } from "@/components/ui/layout";
9+
import { BlockStack, InlineStack } from "@/components/ui/layout";
1010
import { Heading, Paragraph } from "@/components/ui/typography";
1111
import useConfirmationDialog from "@/hooks/useConfirmationDialog";
1212
import { useNodeSelectionTransfer } from "@/hooks/useNodeSelectionTransfer";
@@ -18,6 +18,7 @@ import { checkInputConnectionToRequiredFields } from "@/utils/inputConnectionUti
1818
import { inputNameToNodeId } from "@/utils/nodes/nodeIdUtils";
1919
import { updateSubgraphSpec } from "@/utils/subgraphUtils";
2020

21+
import { IOZIndexEditor } from "../IOZIndexEditor";
2122
import {
2223
DescriptionField,
2324
NameField,
@@ -318,11 +319,15 @@ export const InputValueEditor = ({
318319
</InfoBox>
319320
)}
320321

321-
{!disabled && (
322-
<Button onClick={deleteNode} variant="destructive" size="icon">
323-
<Icon name="Trash2" />
324-
</Button>
325-
)}
322+
<InlineStack gap="4">
323+
{!disabled && (
324+
<Button onClick={deleteNode} variant="destructive" size="icon">
325+
<Icon name="Trash2" />
326+
</Button>
327+
)}
328+
329+
<IOZIndexEditor ioSpec={input} ioType="input" />
330+
</InlineStack>
326331

327332
<ConfirmationDialog
328333
{...confirmationProps}

src/components/Editor/IOEditor/OutputNameEditor/OutputNameEditor.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ConfirmationDialog from "@/components/shared/Dialogs/ConfirmationDialog";
44
import { removeGraphOutput } from "@/components/shared/ReactFlow/FlowCanvas/utils/removeNode";
55
import { Button } from "@/components/ui/button";
66
import { Icon } from "@/components/ui/icon";
7-
import { BlockStack } from "@/components/ui/layout";
7+
import { BlockStack, InlineStack } from "@/components/ui/layout";
88
import { Heading, Paragraph } from "@/components/ui/typography";
99
import useConfirmationDialog from "@/hooks/useConfirmationDialog";
1010
import { useNodeSelectionTransfer } from "@/hooks/useNodeSelectionTransfer";
@@ -22,6 +22,7 @@ import {
2222
TypeField,
2323
} from "../InputValueEditor/FormFields";
2424
import { checkNameCollision } from "../InputValueEditor/FormFields/utils";
25+
import { IOZIndexEditor } from "../IOZIndexEditor";
2526

2627
interface OutputNameEditorProps {
2728
output: OutputSpec;
@@ -206,11 +207,15 @@ export const OutputNameEditor = ({
206207
/>
207208
</div>
208209

209-
{!disabled && (
210-
<Button onClick={deleteNode} variant="destructive" size="icon">
211-
<Icon name="Trash2" />
212-
</Button>
213-
)}
210+
<InlineStack gap="4">
211+
{!disabled && (
212+
<Button onClick={deleteNode} variant="destructive" size="icon">
213+
<Icon name="Trash2" />
214+
</Button>
215+
)}
216+
217+
<IOZIndexEditor ioSpec={output} ioType="output" />
218+
</InlineStack>
214219

215220
<ConfirmationDialog
216221
{...confirmationProps}

src/utils/nodes/createInputNode.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { type Node } from "@xyflow/react";
22

33
import type { TaskNodeData } from "@/types/taskNode";
44

5-
import { extractPositionFromAnnotations } from "../annotations";
5+
import {
6+
extractPositionFromAnnotations,
7+
extractZIndexFromAnnotations,
8+
} from "../annotations";
69
import type { InputSpec } from "../componentSpec";
710
import { inputNameToNodeId } from "./nodeIdUtils";
811

@@ -11,9 +14,13 @@ export const createInputNode = (
1114
nodeData: TaskNodeData,
1215
readOnly: boolean = false,
1316
) => {
17+
const nodeType = "input";
18+
1419
const { name, annotations, ...rest } = input;
1520

1621
const position = extractPositionFromAnnotations(annotations);
22+
const zIndex = extractZIndexFromAnnotations(input.annotations, nodeType);
23+
1724
const nodeId = inputNameToNodeId(name);
1825

1926
return {
@@ -25,6 +32,7 @@ export const createInputNode = (
2532
readOnly,
2633
},
2734
position: position,
28-
type: "input",
35+
type: nodeType,
36+
zIndex,
2937
} as Node;
3038
};

src/utils/nodes/createOutputNode.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { type Node } from "@xyflow/react";
22

33
import type { TaskNodeData } from "@/types/taskNode";
44

5-
import { extractPositionFromAnnotations } from "../annotations";
5+
import {
6+
extractPositionFromAnnotations,
7+
extractZIndexFromAnnotations,
8+
} from "../annotations";
69
import type { OutputSpec } from "../componentSpec";
710
import { outputNameToNodeId } from "./nodeIdUtils";
811

@@ -11,9 +14,13 @@ export const createOutputNode = (
1114
nodeData: TaskNodeData,
1215
readOnly: boolean = false,
1316
) => {
17+
const nodeType = "output";
18+
1419
const { name, annotations, ...rest } = output;
1520

1621
const position = extractPositionFromAnnotations(annotations);
22+
const zIndex = extractZIndexFromAnnotations(output.annotations, nodeType);
23+
1724
const nodeId = outputNameToNodeId(name);
1825

1926
return {
@@ -25,6 +32,7 @@ export const createOutputNode = (
2532
readOnly,
2633
},
2734
position: position,
28-
type: "output",
35+
type: nodeType,
36+
zIndex,
2937
} as Node;
3038
};

0 commit comments

Comments
 (0)