Make loop node parameter a select (#906)
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
import { UpdateIcon } from "@radix-ui/react-icons";
|
import { UpdateIcon } from "@radix-ui/react-icons";
|
||||||
@@ -8,11 +14,13 @@ import {
|
|||||||
Handle,
|
Handle,
|
||||||
NodeProps,
|
NodeProps,
|
||||||
Position,
|
Position,
|
||||||
|
useEdges,
|
||||||
useNodes,
|
useNodes,
|
||||||
useReactFlow,
|
useReactFlow,
|
||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import { useState } from "react";
|
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
|
||||||
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { LoopNode } from "./types";
|
import type { LoopNode } from "./types";
|
||||||
@@ -20,14 +28,19 @@ import type { LoopNode } from "./types";
|
|||||||
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
|
const edges = useEdges();
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
id,
|
id,
|
||||||
initialValue: data.label,
|
initialValue: data.label,
|
||||||
});
|
});
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [inputs, setInputs] = useState({
|
|
||||||
loopValue: data.loopValue,
|
const [workflowParameters] = useWorkflowParametersState();
|
||||||
});
|
const parameters = workflowParameters.filter(
|
||||||
|
(parameter) => parameter.parameterType !== "credential",
|
||||||
|
);
|
||||||
|
const parameterKeys = parameters.map((parameter) => parameter.key);
|
||||||
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
|
||||||
const children = nodes.filter((node) => node.parentId === id);
|
const children = nodes.filter((node) => node.parentId === id);
|
||||||
const furthestDownChild: Node | null = children.reduce(
|
const furthestDownChild: Node | null = children.reduce(
|
||||||
@@ -93,19 +106,28 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs text-slate-300">Loop Value</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<Input
|
Loop Value Parameter
|
||||||
value={inputs.loopValue}
|
</Label>
|
||||||
onChange={(event) => {
|
<Select
|
||||||
if (!data.editable) {
|
value={data.loopValue}
|
||||||
return;
|
onValueChange={(value) => {
|
||||||
}
|
updateNodeData(id, { loopValue: value });
|
||||||
setInputs({ ...inputs, loopValue: event.target.value });
|
|
||||||
updateNodeData(id, { loopValue: event.target.value });
|
|
||||||
}}
|
}}
|
||||||
placeholder="What value are you iterating over?"
|
>
|
||||||
className="nopan"
|
<SelectTrigger>
|
||||||
/>
|
<SelectValue placeholder="Select a parameter" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{[...parameterKeys, ...outputParameterKeys].map(
|
||||||
|
(parameterKey) => (
|
||||||
|
<SelectItem key={parameterKey} value={parameterKey}>
|
||||||
|
{parameterKey}
|
||||||
|
</SelectItem>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCal
|
|||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
import { ListBulletIcon } from "@radix-ui/react-icons";
|
import { ListBulletIcon } from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Edge,
|
|
||||||
Handle,
|
Handle,
|
||||||
NodeProps,
|
NodeProps,
|
||||||
Position,
|
Position,
|
||||||
@@ -24,36 +23,13 @@ import {
|
|||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getOutputParameterKey } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch";
|
import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch";
|
||||||
import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel";
|
import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel";
|
||||||
import type { TaskNode, TaskNodeDisplayMode } from "./types";
|
import type { TaskNode, TaskNodeDisplayMode } from "./types";
|
||||||
|
|
||||||
function getPreviousNodeIds(
|
|
||||||
nodes: Array<AppNode>,
|
|
||||||
edges: Array<Edge>,
|
|
||||||
target: string,
|
|
||||||
): Array<string> {
|
|
||||||
const nodeIds: string[] = [];
|
|
||||||
const node = nodes.find((node) => node.id === target);
|
|
||||||
if (!node) {
|
|
||||||
return nodeIds;
|
|
||||||
}
|
|
||||||
let current = edges.find((edge) => edge.target === target);
|
|
||||||
if (current) {
|
|
||||||
while (current) {
|
|
||||||
nodeIds.push(current.source);
|
|
||||||
current = edges.find((edge) => edge.target === current!.source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!node.parentId) {
|
|
||||||
return nodeIds;
|
|
||||||
}
|
|
||||||
return [...nodeIds, ...getPreviousNodeIds(nodes, edges, node.parentId)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const [displayMode, setDisplayMode] = useState<TaskNodeDisplayMode>("basic");
|
const [displayMode, setDisplayMode] = useState<TaskNodeDisplayMode>("basic");
|
||||||
@@ -61,16 +37,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const previousNodeIds = getPreviousNodeIds(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
const previousNodes = nodes.filter((node) =>
|
|
||||||
previousNodeIds.includes(node.id),
|
|
||||||
);
|
|
||||||
const labels = previousNodes
|
|
||||||
.filter((node) => node.type !== "nodeAdder")
|
|
||||||
.map((node) => node.data.label);
|
|
||||||
const outputParameterKeys = labels.map((label) =>
|
|
||||||
getOutputParameterKey(label),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -677,6 +677,9 @@ function getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
|||||||
}
|
}
|
||||||
const oldLabel = labelUpdatedNode.data.label as string;
|
const oldLabel = labelUpdatedNode.data.label as string;
|
||||||
return nodes.map((node) => {
|
return nodes.map((node) => {
|
||||||
|
if (node.type === "nodeAdder") {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
if (node.type === "task") {
|
if (node.type === "task") {
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
@@ -692,6 +695,18 @@ function getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (node.type === "loop") {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
loopValue:
|
||||||
|
node.data.loopValue === getOutputParameterKey(oldLabel)
|
||||||
|
? getOutputParameterKey(newLabel)
|
||||||
|
: node.data.loopValue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
data: {
|
data: {
|
||||||
@@ -814,6 +829,48 @@ function getDefaultValueForParameterType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPreviousNodeIds(
|
||||||
|
nodes: Array<AppNode>,
|
||||||
|
edges: Array<Edge>,
|
||||||
|
target: string,
|
||||||
|
): Array<string> {
|
||||||
|
const nodeIds: string[] = [];
|
||||||
|
const node = nodes.find((node) => node.id === target);
|
||||||
|
if (!node) {
|
||||||
|
return nodeIds;
|
||||||
|
}
|
||||||
|
let current = edges.find((edge) => edge.target === target);
|
||||||
|
if (current) {
|
||||||
|
while (current) {
|
||||||
|
nodeIds.push(current.source);
|
||||||
|
current = edges.find((edge) => edge.target === current!.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!node.parentId) {
|
||||||
|
return nodeIds;
|
||||||
|
}
|
||||||
|
return [...nodeIds, ...getPreviousNodeIds(nodes, edges, node.parentId)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvailableOutputParameterKeys(
|
||||||
|
nodes: Array<AppNode>,
|
||||||
|
edges: Array<Edge>,
|
||||||
|
id: string,
|
||||||
|
): Array<string> {
|
||||||
|
const previousNodeIds = getPreviousNodeIds(nodes, edges, id);
|
||||||
|
const previousNodes = nodes.filter((node) =>
|
||||||
|
previousNodeIds.includes(node.id),
|
||||||
|
);
|
||||||
|
const labels = previousNodes
|
||||||
|
.filter((node) => node.type !== "nodeAdder")
|
||||||
|
.map((node) => node.data.label);
|
||||||
|
const outputParameterKeys = labels.map((label) =>
|
||||||
|
getOutputParameterKey(label),
|
||||||
|
);
|
||||||
|
|
||||||
|
return outputParameterKeys;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createNode,
|
createNode,
|
||||||
generateNodeData,
|
generateNodeData,
|
||||||
@@ -830,4 +887,6 @@ export {
|
|||||||
getBlockNameOfOutputParameterKey,
|
getBlockNameOfOutputParameterKey,
|
||||||
getDefaultValueForParameterType,
|
getDefaultValueForParameterType,
|
||||||
getUpdatedParametersAfterLabelUpdateForSourceParameterKey,
|
getUpdatedParametersAfterLabelUpdateForSourceParameterKey,
|
||||||
|
getPreviousNodeIds,
|
||||||
|
getAvailableOutputParameterKeys,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user