(codeTabsComponent/index.tsx): add support for exporting code as a file

♻️ (codeTabsComponent/index.tsx): refactor code to use a switch statement instead of multiple if conditions for different template field types
🔧 (codeTabsComponent/index.tsx): fix import statements and remove unused imports
📝 (codeTabsComponent/index.tsx): add comments to improve code readability

📝 (codeTabsComponent/index.tsx): refactor code to use a switch statement instead of multiple if-else conditions for better readability and maintainability
♻️ (codeTabsComponent/index.tsx): refactor code to use a Case component for each condition in the switch statement to improve code organization and readability

 (index.tsx): remove unused import of React from "react" to improve code cleanliness
♻️ (index.tsx): refactor code to remove unused imports and variables, improve code readability and organization
📝 (index.tsx): add missing import of Case component from "../../../../shared/components/caseComponent"
🔧 (index.tsx): add missing import of TooltipRenderComponent from "../tooltipRenderComponent"
 (index.tsx): add missing hooks and functions to improve code functionality and maintainability

📝 (index.tsx): Refactor code to improve readability and maintainability

📝 (index.tsx): refactor the rendering logic in the ParameterComponent to improve readability and maintainability
🔧 (index.tsx): fix access to nested properties in the data object by using optional chaining operator (?.) to prevent potential errors when accessing undefined properties

 (tooltipRenderComponent): add a new component for rendering tooltips in the genericNode component
📝 (use-fetch-data-on-mount): add a new hook for fetching data on component mount
📝 (use-handle-new-value): add a new hook for handling new values in the component
📝 (use-handle-node-class): add a new hook for handling node class in the component
📝 (use-handle-refresh-buttons): add a new hook for handling refresh button press in the component

 (editNodeModal/index.tsx): import Case component to handle conditional rendering of different input components based on template type
📝 (editNodeModal/index.tsx): add comments to explain the purpose of the code and provide context for future developers
♻️ (editNodeModal/index.tsx): refactor code to use the Case component for conditional rendering instead of multiple if statements

📝 (file.js): update code formatting and indentation for better readability

📝 (editNodeModal/index.tsx): update code to fix a bug related to accessing nested properties in myData.node object
♻️ (editNodeModal/index.tsx): refactor code to use conditional rendering with Case component for better readability and maintainability

 (caseComponent/index.tsx): add a new component called Case that conditionally renders its children based on a given condition
📝 (components/index.ts): add an optional display_name property to the groupedObjType interface
♻️ (components/index.ts): remove unnecessary commas and fix indentation in the codeTabsPropsType interface

🐛 (reactflowUtils.ts): remove unused parameter 'edges' in isValidConnection function
♻️ (reactflowUtils.ts): refactor scapeJSONParse and scapeJSONStringfy functions to remove unnecessary exclamation marks
🐛 (reactflowUtils.ts): fix bug in updateIds function where it doesn't update the sourceHandle and targetHandle correctly
🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle empty keys correctly
🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle duplicate keys correctly
🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle dict type correctly
🐛 (reactflowUtils.ts): fix bug in validateNodes function where it doesn't handle empty nodes correctly
🐛 (reactflowUtils.ts): fix bug in updateEdges function where it doesn't update the className correctly
🐛 (reactflowUtils.ts): fix bug in handleKeyDown function where it doesn't handle control+backspace on Windows/Linux correctly
🐛 (reactflowUtils.ts): fix bug in handleOnlyIntegerInput function where it doesn't handle decimal point correctly
🐛 (reactflowUtils.ts): fix bug in getConnectedNodes function where it doesn't return the correct connected nodes
🐛 (reactflowUtils.ts): fix bug in convertObjToArray function where it doesn't handle non-dict type correctly
🐛 (reactflowUtils.ts): fix bug in generateFlow function where it doesn't filter out edges correctly
🐛 (reactflowUtils.ts): fix bug in reconnectEdges function where it doesn't update the sourceHandle correctly
🐛 (reactflowUtils.ts): fix bug in filterFlow function where it doesn't filter out nodes and edges correctly
🐛 (reactflowUtils.ts): fix bug in validateSelection function where it doesn't handle selection mode correctly

📝 (reactflowUtils.ts): remove trailing commas and unnecessary whitespace to improve code readability and consistency
♻️ (reactflowUtils.ts): refactor validateSelection function to remove duplicate code and improve readability
♻️ (reactflowUtils.ts): refactor generateNodeTemplate function to simplify code and improve readability
♻️ (reactflowUtils.ts): refactor isHandleConnected function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor generateNodeFromFlow function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor connectedInputNodesOnHandle function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor updateProxyIdsOnTemplate function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor updateEdgesIds function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor expandGroupNode function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor getGroupStatus function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor createFlowComponent function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor updateComponentNameAndType function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor removeFileNameFromComponents function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor extractFieldsFromComponenents function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor downloadFlow function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor downloadFlows function to remove unnecessary parentheses and improve readability
♻️ (reactflowUtils.ts): refactor createNewFlow function to remove unnecessary parentheses and improve readability
This commit is contained in:
cristhianzl 2024-05-16 19:01:15 -03:00
commit 8a4a346736
11 changed files with 791 additions and 541 deletions

View file

@ -26,6 +26,8 @@ import {
TabsTrigger,
} from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import ExportModal from "../../modals/exportModal";
import { Case } from "../../shared/components/caseComponent";
import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
import { codeTabsPropsType } from "../../types/components";
@ -43,7 +45,6 @@ import KeypairListComponent from "../keypairListComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Label } from "../ui/label";
import { Switch } from "../ui/switch";
import ExportModal from "../../modals/exportModal";
export default function CodeTabsComponent({
flow,
@ -87,6 +88,10 @@ export default function CodeTabsComponent({
});
};
const type = (node, templateParam) => {
return node.data.node.template[templateParam].type;
};
const downloadAsFile = () => {
const fileExtension = tabs[activeTab].language || ".txt";
const suggestedFileName = `${"generated-code."}${fileExtension}`;
@ -276,107 +281,71 @@ export default function CodeTabsComponent({
</TableCell>
<TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]">
{node.data.node.template[
templateField
].type === "str" &&
!node.data.node.template[
templateField
].options ? (
<div className="mx-auto">
{node.data.node.template[
<Case
condition={
type(node, templateField) ===
"str" &&
!node.data.node.template[
templateField
]?.list ? (
<InputListComponent
componentName={
templateField
}
editNode={true}
disabled={false}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList![
i
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks?.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
],
);
}}
/>
) : node.data.node.template[
].options
}
>
<Case
condition={
node.data.node.template[
templateField
].multiline ? (
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={
!node.data.node
.template[
]?.list
}
>
<InputListComponent
componentName={templateField}
editNode={true}
disabled={false}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList![
i
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks?.buildTweakObject!(
node["data"]["id"],
target,
node.data.node
.template[
templateField
],
);
}}
/>
</div>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList![
i
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks?.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
].password ?? false
}
],
);
}}
/>
</Case>
<Case
condition={
node.data.node.template[
templateField
].multiline
}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={
!node.data.node.template[
templateField
@ -410,11 +379,68 @@ export default function CodeTabsComponent({
);
}}
/>
)}
</div>
) : node.data.node.template[
templateField
].type === "bool" ? (
</div>
</Case>
<Case
condition={
!node.data.node.template[
templateField
].multiline &&
!node.data.node.template[
templateField
].list
}
>
<InputComponent
editNode={true}
disabled={false}
password={
node.data.node.template[
templateField
].password ?? false
}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList![
i
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks?.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
],
);
}}
/>
</Case>
</Case>
<Case
condition={
type(node, templateField) ===
"bool"
}
>
<div className="ml-auto">
{" "}
<ToggleShadComponent
@ -446,9 +472,14 @@ export default function CodeTabsComponent({
disabled={false}
/>
</div>
) : node.data.node.template[
templateField
].type === "file" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"file"
}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
@ -473,9 +504,14 @@ export default function CodeTabsComponent({
}}
></InputFileComponent>
</div>
) : node.data.node.template[
templateField
].type === "float" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"float"
}
>
<div className="mx-auto">
<FloatComponent
disabled={false}
@ -518,12 +554,17 @@ export default function CodeTabsComponent({
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "str" &&
node.data.node.template[
templateField
].options ? (
</Case>
<Case
condition={
type(node, templateField) ===
"str" &&
node.data.node.template[
templateField
].options
}
>
<div className="mx-auto">
<Dropdown
editNode={true}
@ -565,9 +606,14 @@ export default function CodeTabsComponent({
}
></Dropdown>
</div>
) : node.data.node.template[
templateField
].type === "int" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"int"
}
>
<div className="mx-auto">
<IntComponent
disabled={false}
@ -610,9 +656,14 @@ export default function CodeTabsComponent({
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "prompt" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"prompt"
}
>
<div className="mx-auto">
<PromptAreaComponent
readonly={true}
@ -651,9 +702,14 @@ export default function CodeTabsComponent({
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "code" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"code"
}
>
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
@ -692,9 +748,14 @@ export default function CodeTabsComponent({
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "dict" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"dict"
}
>
<div className="mx-auto overflow-auto custom-scroll">
<KeypairListComponent
disabled={false}
@ -712,6 +773,10 @@ export default function CodeTabsComponent({
.template[
templateField
].value,
type(
node,
templateField,
),
)
}
duplicateKey={
@ -755,9 +820,14 @@ export default function CodeTabsComponent({
}
/>
</div>
) : node.data.node.template[
templateField
].type === "NestedDict" ? (
</Case>
<Case
condition={
type(node, templateField) ===
"NestedDict"
}
>
<div className="mx-auto">
<DictComponent
disabled={false}
@ -765,7 +835,7 @@ export default function CodeTabsComponent({
value={
node.data.node!.template[
templateField
].value.toString() === "{}"
].value?.toString() === "{}"
? {
// yourkey: "value",
}
@ -795,13 +865,16 @@ export default function CodeTabsComponent({
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</Case>
<Case
condition={
type(node, templateField) ===
"Any"
}
>
<>-</>
</Case>
</div>
</TableCell>
</TableRow>

View file

@ -1,5 +1,5 @@
import { cloneDeep } from "lodash";
import React, { ReactNode, useEffect, useRef, useState } from "react";
import { ReactNode, useEffect, useRef, useState } from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
import DictComponent from "../../../../components/dictComponent";
@ -18,20 +18,15 @@ import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button";
import { RefreshButton } from "../../../../components/ui/refreshButton";
import {
INPUT_HANDLER_HOVER,
LANGFLOW_SUPPORTED_TYPES,
OUTPUT_HANDLER_HOVER,
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import { Case } from "../../../../shared/components/caseComponent";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useTypesStore } from "../../../../stores/typesStore";
import {
APIClassType,
ResponseErrorDetailAPI,
ResponseErrorTypeAPI,
} from "../../../../types/api";
import { APIClassType } from "../../../../types/api";
import { ParameterComponentType } from "../../../../types/components";
import {
debouncedHandleUpdateValues,
@ -44,12 +39,13 @@ import {
isValidConnection,
scapedJSONStringfy,
} from "../../../../utils/reactflowUtils";
import {
nodeColors,
nodeIconsLucide,
nodeNames,
} from "../../../../utils/styleUtils";
import { nodeColors } from "../../../../utils/styleUtils";
import { classNames, groupByFamily } from "../../../../utils/utils";
import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount";
import useHandleOnNewValue from "../../../hooks/use-handle-new-value";
import useHandleNodeClass from "../../../hooks/use-handle-node-class";
import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons";
import TooltipRenderComponent from "../tooltipRenderComponent";
export default function ParameterComponent({
left,
@ -75,175 +71,69 @@ export default function ParameterComponent({
const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges);
const setNode = useFlowStore((state) => state.setNode);
const myData = useTypesStore((state) => state.data);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [isLoading, setIsLoading] = useState(false);
const updateNodeInternals = useUpdateNodeInternals();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const flow = currentFlow?.data?.nodes ?? null;
const groupedEdge = useRef(null);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue(
data,
name,
takeSnapshot,
handleUpdateValues,
debouncedHandleUpdateValues,
setNode,
renderTooltips,
isLoading,
setIsLoading,
);
const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(
data,
name,
takeSnapshot,
setNode,
updateNodeInternals,
renderTooltips,
);
const { handleRefreshButtonPress: handleRefreshButtonPressHook } =
useHandleRefreshButtonPress(setIsLoading, setNode, renderTooltips);
let disabled =
edges.some(
(edge) =>
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id),
) ?? false;
const myData = useTypesStore((state) => state.data);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const handleRefreshButtonPress = async (name, data) => {
setIsLoading(true);
try {
let newTemplate = await handleUpdateValues(name, data);
if (newTemplate) {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template = newTemplate;
return newNode;
});
}
} catch (error) {
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
});
}
setIsLoading(false);
renderTooltips();
handleRefreshButtonPressHook(name, data);
};
useEffect(() => {
async function fetchData() {
if (
(data.node?.template[name]?.real_time_refresh ||
data.node?.template[name]?.refresh_button) &&
// options can be undefined but not an empty array
(data.node?.template[name]?.options?.length ?? 0) === 0
) {
setIsLoading(true);
try {
let newTemplate = await handleUpdateValues(name, data);
if (newTemplate) {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template = newTemplate;
return newNode;
});
}
} catch (error) {
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
});
}
setIsLoading(false);
renderTooltips();
}
}
fetchData();
}, []);
useFetchDataOnMount(
data,
name,
handleUpdateValues,
setNode,
renderTooltips,
setIsLoading,
);
const handleOnNewValue = async (
newValue: string | string[] | boolean | Object[],
skipSnapshot: boolean | undefined = false,
): Promise<void> => {
const nodeTemplate = data.node!.template[name];
const currentValue = nodeTemplate.value;
if (currentValue !== newValue && !skipSnapshot) {
takeSnapshot();
}
const shouldUpdate =
data.node?.template[name].real_time_refresh &&
!data.node?.template[name].refresh_button &&
currentValue !== newValue;
const typeToDebounce = nodeTemplate.type;
nodeTemplate.value = newValue;
let newTemplate;
if (shouldUpdate) {
setIsLoading(true);
try {
if (["int"].includes(typeToDebounce)) {
newTemplate = await handleUpdateValues(name, data);
} else {
newTemplate = await debouncedHandleUpdateValues(name, data);
}
} catch (error) {
let responseError = error as ResponseErrorTypeAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail.error ?? "Unknown error"],
});
}
setIsLoading(false);
}
setNode(data.id, (oldNode) => {
const newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
if (data.node?.template[name].real_time_refresh && newTemplate) {
newNode.data.node.template = newTemplate;
} else {
newNode.data.node.template[name].value = newValue;
}
return newNode;
});
renderTooltips();
handleOnNewValueHook(newValue, skipSnapshot);
};
const updateNodeInternals = useUpdateNodeInternals();
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
if (!data.node) return;
if (data.node!.template[name].value !== code) {
takeSnapshot();
}
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
node: newNodeClass,
description: newNodeClass.description ?? data.node!.description,
display_name: newNodeClass.display_name ?? data.node!.display_name,
};
newNode.data.node.template[name].value = code;
return newNode;
});
updateNodeInternals(data.id);
renderTooltips();
handleNodeClassHook(newNodeClass, code);
};
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
useEffect(() => {
// @ts-ignore
infoHtml.current = (
@ -264,88 +154,7 @@ export default function ParameterComponent({
if (groupedObj && groupedObj.length > 0) {
//@ts-ignore
refHtml.current = groupedObj.map((item, index) => {
const Icon: any =
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return (
<div
key={index}
data-testid={`available-${left ? "input" : "output"}-${
item.family
}`}
>
{index === 0 && (
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
)}
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span
className="ps-2 text-xs text-foreground"
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
>
{nodeNames[item.family] ?? "Other"}{" "}
{item?.display_name && item?.display_name?.length > 0 ? (
<span
className="text-xs"
data-testid={`tooltip-${item?.display_name}`}
>
{" "}
{item.display_name === "" ? "" : " - "}
{item.display_name.split(", ").length > 2
? item.display_name.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index ===
item.display_name.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.display_name}
</span>
) : (
<span
className="text-xs"
data-testid={`tooltip-${item?.type}`}
>
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
)}
</span>
</span>
</div>
);
return <TooltipRenderComponent index={index} item={item} left={left} />;
});
} else {
//@ts-ignore
@ -354,6 +163,7 @@ export default function ParameterComponent({
);
}
}
// If optionalHandle is an empty list, then it is not an optional handle
if (optionalHandle && optionalHandle.length === 0) {
optionalHandle = null;
@ -362,6 +172,9 @@ export default function ParameterComponent({
useEffect(() => {
renderTooltips();
}, [tooltipTitle, flow]);
console.log(left === true && type === "dict");
return !showNode ? (
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></>
@ -427,11 +240,12 @@ export default function ParameterComponent({
(left ? "" : " justify-end")
}
>
{!left && data.node?.frozen && (
<Case condition={left && data.node?.frozen}>
<div className="pr-1">
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
</div>
)}
</Case>
{proxy ? (
<ShadTooltip content={<span>{proxy.id}</span>}>
<span className={!left && data.node?.frozen ? " text-ice" : ""}>
@ -477,45 +291,37 @@ export default function ParameterComponent({
}`}
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
key={
proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
id={
proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
isValidConnection={(connection) =>
isValidConnection(connection, nodes, edges)
}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
left ? "-ml-0.5" : "-mr-0.5",
"h-3 w-3 rounded-full border-2 bg-background",
)}
style={{
borderColor: color ?? nodeColors.unknown,
}}
onClick={() => {
setFilterEdge(groupedEdge.current);
}}
></Handle>
style={{ borderColor: color ?? nodeColors.unknown }}
onClick={() => setFilterEdge(groupedEdge.current)}
/>
</ShadTooltip>
</div>
</Button>
)}
{left === true &&
type === "str" &&
!data.node?.template[name].options ? (
<Case
condition={
left === true &&
type === "str" &&
!data.node?.template[name]?.options
}
>
<div className="w-full">
{data.node?.template[name].list ? (
<Case condition={data.node?.template[name]?.list}>
<div
className={
// Commenting this out until we have a better
// way to display
// (data.node?.template[name].refresh ? "w-5/6 " : "") +
// (data.node?.template[name]?.refresh ? "w-5/6 " : "") +
"flex-grow"
}
>
@ -523,39 +329,27 @@ export default function ParameterComponent({
componentName={name}
disabled={disabled}
value={
!data.node.template[name].value ||
data.node.template[name].value === ""
!data.node!.template[name]?.value ||
data.node!.template[name]?.value === ""
? [""]
: data.node.template[name].value
: data.node!.template[name]?.value
}
onChange={handleOnNewValue}
/>
{/* {data.node?.template[name].refresh_button && (
<div className="w-1/6">
<RefreshButton
isLoading={isLoading}
disabled={disabled}
name={name}
data={data}
className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name}
/>
</div>
)} */}
</div>
) : data.node?.template[name].multiline ? (
</Case>
<Case condition={data.node?.template[name]?.multiline}>
<div className="mt-2 flex w-full flex-col ">
<div className="flex-grow">
<TextAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node!.template[name]?.value ?? ""}
onChange={handleOnNewValue}
id={"textarea-" + data.node.template[name].name}
data-testid={"textarea-" + data.node.template[name].name}
id={"textarea-" + data.node!.template[name]?.name}
data-testid={"textarea-" + data.node!.template[name]?.name}
/>
</div>
{data.node?.template[name].refresh_button && (
{data.node?.template[name]?.refresh_button && (
<div className="flex-grow">
<RefreshButton
isLoading={isLoading}
@ -563,7 +357,7 @@ export default function ParameterComponent({
name={name}
data={data}
button_text={
data.node?.template[name].refresh_button_text ??
data.node?.template[name]?.refresh_button_text ??
"Refresh"
}
className="extra-side-bar-buttons mt-1"
@ -573,12 +367,18 @@ export default function ParameterComponent({
</div>
)}
</div>
) : (
</Case>
<Case
condition={
!data.node?.template[name]?.multiline &&
!data.node?.template[name]?.list
}
>
<div className="mt-2 flex w-full items-center">
<div
className={
"flex-grow " +
(data.node?.template[name].refresh_button ? "w-5/6" : "")
(data.node?.template[name]?.refresh_button ? "w-5/6" : "")
}
>
<InputGlobalComponent
@ -598,7 +398,7 @@ export default function ParameterComponent({
data={data}
/>
</div>
{data.node?.template[name].refresh_button && (
{data.node?.template[name]?.refresh_button && (
<div className="w-1/6">
<RefreshButton
isLoading={isLoading}
@ -606,7 +406,7 @@ export default function ParameterComponent({
name={name}
data={data}
button_text={
data.node?.template[name].refresh_button_text ??
data.node?.template[name]?.refresh_button_text ??
"Refresh"
}
className="extra-side-bar-buttons ml-2 mt-1"
@ -616,52 +416,61 @@ export default function ParameterComponent({
</div>
)}
</div>
)}
</Case>
</div>
) : left === true && type === "bool" ? (
</Case>
<Case condition={left === true && type === "bool"}>
<div className="mt-2 w-full">
<ToggleShadComponent
id={"toggle-" + name}
disabled={disabled}
enabled={data.node?.template[name].value ?? false}
enabled={data.node?.template[name]?.value ?? false}
setEnabled={handleOnNewValue}
size="large"
editNode={false}
/>
</div>
) : left === true && type === "float" ? (
</Case>
<Case condition={left === true && type === "float"}>
<div className="mt-2 w-full">
<FloatComponent
disabled={disabled}
value={data.node?.template[name].value ?? ""}
value={data.node?.template[name]?.value ?? ""}
rangeSpec={data.node?.template[name]?.rangeSpec}
onChange={handleOnNewValue}
/>
</div>
) : left === true &&
type === "str" &&
(data.node?.template[name].options ||
data.node?.template[name]?.real_time_refresh) ? (
// TODO: Improve CSS
</Case>
<Case
condition={
left === true &&
type === "str" &&
(data.node?.template[name]?.options ||
data.node?.template[name]?.real_time_refresh)
}
>
<div className="mt-2 flex w-full items-center">
<div className="w-5/6 flex-grow">
<Dropdown
disabled={disabled}
isLoading={isLoading}
options={data.node.template[name].options}
options={data.node!.template[name]?.options}
onSelect={handleOnNewValue}
value={data.node.template[name].value}
value={data.node!.template[name]?.value}
id={"dropdown-" + name}
/>
</div>
{data.node?.template[name].refresh_button && (
{data.node?.template[name]?.refresh_button && (
<div className="w-1/6">
<RefreshButton
isLoading={isLoading}
disabled={disabled}
name={name}
data={data}
button_text={data.node?.template[name].refresh_button_text}
button_text={data.node?.template[name]?.refresh_button_text}
className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name}
@ -669,46 +478,54 @@ export default function ParameterComponent({
</div>
)}
</div>
) : left === true && type === "code" ? (
</Case>
<Case condition={left === true && type === "code"}>
<div className="mt-2 w-full">
<CodeAreaComponent
readonly={
data.node?.flow && data.node.template[name].dynamic
data.node?.flow && data.node.template[name]?.dynamic
? true
: false
}
dynamic={data.node?.template[name].dynamic ?? false}
dynamic={data.node?.template[name]?.dynamic ?? false}
setNodeClass={handleNodeClass}
nodeClass={data.node}
disabled={disabled}
value={data.node?.template[name].value ?? ""}
value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue}
id={"code-input-" + name}
/>
</div>
) : left === true && type === "file" ? (
</Case>
<Case condition={left === true && type === "file"}>
<div className="mt-2 w-full">
<InputFileComponent
disabled={disabled}
value={data.node?.template[name].value ?? ""}
value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue}
fileTypes={data.node?.template[name].fileTypes}
fileTypes={data.node?.template[name]?.fileTypes}
onFileChange={(filePath: string) => {
data.node!.template[name].file_path = filePath;
}}
></InputFileComponent>
</div>
) : left === true && type === "int" ? (
</Case>
<Case condition={left === true && type === "int"}>
<div className="mt-2 w-full">
<IntComponent
rangeSpec={data.node?.template[name].rangeSpec}
rangeSpec={data.node?.template[name]?.rangeSpec}
disabled={disabled}
value={data.node?.template[name].value ?? ""}
value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue}
id={"int-input-" + name}
/>
</div>
) : left === true && type === "prompt" ? (
</Case>
<Case condition={left === true && type === "prompt"}>
<div className="mt-2 w-full">
<PromptAreaComponent
readonly={data.node?.flow ? true : false}
@ -716,57 +533,59 @@ export default function ParameterComponent({
setNodeClass={handleNodeClass}
nodeClass={data.node}
disabled={disabled}
value={data.node?.template[name].value ?? ""}
value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue}
id={"prompt-input-" + name}
data-testid={"prompt-input-" + name}
/>
</div>
) : left === true && type === "NestedDict" ? (
</Case>
<Case condition={left === true && type === "NestedDict"}>
<div className="mt-2 w-full">
<DictComponent
disabled={disabled}
editNode={false}
value={
!data.node!.template[name].value ||
data.node!.template[name].value?.toString() === "{}"
!data.node!.template[name]?.value ||
data.node!.template[name]?.value?.toString() === "{}"
? {
// yourkey: "value",
}
: data.node!.template[name].value
: data.node!.template[name]?.value
}
onChange={handleOnNewValue}
id="div-dict-input"
/>
</div>
) : left === true && type === "dict" ? (
</Case>
<Case condition={left === true && type === "dict"}>
<div className="mt-2 w-full">
<KeypairListComponent
disabled={disabled}
editNode={false}
value={
data.node!.template[name].value?.length === 0 ||
!data.node!.template[name].value
data.node!.template[name]?.value?.length === 0 ||
!data.node!.template[name]?.value
? [{ "": "" }]
: convertObjToArray(data.node!.template[name].value)
: convertObjToArray(data.node!.template[name]?.value, type!)
}
duplicateKey={errorDuplicateKey}
onChange={(newValue) => {
const valueToNumbers = convertValuesToNumbers(newValue);
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
// if data.node?.template[name].list is true, then the value is an array of objects
// if data.node?.template[name]?.list is true, then the value is an array of objects
// else we need to get the first object of the array
if (data.node?.template[name].list) {
if (data.node?.template[name]?.list) {
handleOnNewValue(valueToNumbers);
} else handleOnNewValue(valueToNumbers[0]);
}}
isList={data.node?.template[name].list ?? false}
isList={data.node?.template[name]?.list ?? false}
/>
</div>
) : (
<></>
)}
</Case>
</>
</div>
);

View file

@ -0,0 +1,91 @@
import React from "react";
import {
INPUT_HANDLER_HOVER,
OUTPUT_HANDLER_HOVER,
} from "../../../../constants/constants";
import {
nodeColors,
nodeIconsLucide,
nodeNames,
} from "../../../../utils/styleUtils";
import { classNames } from "../../../../utils/utils";
const TooltipRenderComponent = ({ item, index, left }) => {
const Icon = nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return (
<div
key={index}
data-testid={`available-${left ? "input" : "output"}-${item.family}`}
>
{index === 0 && (
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
)}
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span
className="ps-2 text-xs text-foreground"
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
>
{nodeNames[item.family] ?? "Other"}{" "}
{item?.display_name && item?.display_name?.length > 0 ? (
<span
className="text-xs"
data-testid={`tooltip-${item?.display_name}`}
>
{" "}
{item.display_name === "" ? "" : " - "}
{item.display_name.split(", ").length > 2
? item.display_name.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.display_name.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.display_name}
</span>
) : (
<span className="text-xs" data-testid={`tooltip-${item?.type}`}>
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
)}
</span>
</span>
</div>
);
};
export default TooltipRenderComponent;

View file

@ -0,0 +1,54 @@
import { cloneDeep } from "lodash";
import { useEffect } from "react";
import useAlertStore from "../../stores/alertStore";
import { ResponseErrorDetailAPI } from "../../types/api";
const useFetchDataOnMount = (
data,
name,
handleUpdateValues,
setNode,
renderTooltips,
setIsLoading,
) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
useEffect(() => {
async function fetchData() {
if (
(data.node?.template[name]?.real_time_refresh ||
data.node?.template[name]?.refresh_button) &&
// options can be undefined but not an empty array
(data.node?.template[name]?.options?.length ?? 0) === 0
) {
setIsLoading(true);
try {
let newTemplate = await handleUpdateValues(name, data);
if (newTemplate) {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template = newTemplate;
return newNode;
});
}
} catch (error) {
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
});
}
setIsLoading(false);
renderTooltips();
}
}
fetchData();
}, []); // Empty dependency array ensures that this effect runs only once, on mount
};
export default useFetchDataOnMount;

View file

@ -0,0 +1,75 @@
import { cloneDeep } from "lodash";
import useAlertStore from "../../stores/alertStore";
import { ResponseErrorTypeAPI } from "../../types/api";
const useHandleOnNewValue = (
data,
name,
takeSnapshot,
handleUpdateValues,
debouncedHandleUpdateValues,
setNode,
renderTooltips,
isLoading,
setIsLoading,
) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleOnNewValue = async (newValue, skipSnapshot = false) => {
const nodeTemplate = data.node!.template[name];
const currentValue = nodeTemplate.value;
if (currentValue !== newValue && !skipSnapshot) {
takeSnapshot();
}
const shouldUpdate =
data.node?.template[name].real_time_refresh &&
!data.node?.template[name].refresh_button &&
currentValue !== newValue;
const typeToDebounce = nodeTemplate.type;
nodeTemplate.value = newValue;
let newTemplate;
if (shouldUpdate) {
setIsLoading(true);
try {
if (["int"].includes(typeToDebounce)) {
newTemplate = await handleUpdateValues(name, data);
} else {
newTemplate = await debouncedHandleUpdateValues(name, data);
}
} catch (error) {
let responseError = error as ResponseErrorTypeAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail.error ?? "Unknown error"],
});
}
setIsLoading(false);
}
setNode(data.id, (oldNode) => {
const newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
if (data.node?.template[name].real_time_refresh && newTemplate) {
newNode.data.node.template = newTemplate;
} else {
newNode.data.node.template[name].value = newValue;
}
return newNode;
});
renderTooltips();
};
return { handleOnNewValue };
};
export default useHandleOnNewValue;

View file

@ -0,0 +1,40 @@
import { cloneDeep } from "lodash";
const useHandleNodeClass = (
data,
name,
takeSnapshot,
setNode,
updateNodeInternals,
renderTooltips,
) => {
const handleNodeClass = (newNodeClass, code) => {
if (!data.node) return;
if (data.node!.template[name].value !== code) {
takeSnapshot();
}
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
node: newNodeClass,
description: newNodeClass.description ?? data.node!.description,
display_name: newNodeClass.display_name ?? data.node!.display_name,
};
newNode.data.node.template[name].value = code;
return newNode;
});
updateNodeInternals(data.id);
renderTooltips();
};
return { handleNodeClass };
};
export default useHandleNodeClass;

View file

@ -0,0 +1,39 @@
import { cloneDeep } from "lodash";
import useAlertStore from "../../stores/alertStore";
import { ResponseErrorDetailAPI } from "../../types/api";
import { handleUpdateValues } from "../../utils/parameterUtils";
const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleRefreshButtonPress = async (name, data) => {
setIsLoading(true);
try {
let newTemplate = await handleUpdateValues(name, data);
if (newTemplate) {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template = newTemplate;
return newNode;
});
}
} catch (error) {
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
});
}
setIsLoading(false);
renderTooltips();
};
return { handleRefreshButtonPress };
};
export default useHandleRefreshButtonPress;

View file

@ -28,6 +28,7 @@ import {
LANGFLOW_SUPPORTED_TYPES,
limitScrollFieldsModal,
} from "../../constants/constants";
import { Case } from "../../shared/components/caseComponent";
import useFlowStore from "../../stores/flowStore";
import { NodeDataType } from "../../types/flow";
import {
@ -52,7 +53,7 @@ const EditNodeModal = forwardRef(
open: boolean;
setOpen: (open: boolean) => void;
},
ref
ref,
) => {
const [myData, setMyData] = useState(data);
@ -84,6 +85,10 @@ const EditNodeModal = forwardRef(
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const type = (templateParam) => {
return myData.node?.template[templateParam].type;
};
return (
<BaseModal
key={data.id}
@ -116,7 +121,7 @@ const EditNodeModal = forwardRef(
"edit-node-modal-box",
nodeLength > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: ""
: "",
)}
>
{nodeLength > 0 && (
@ -138,8 +143,8 @@ const EditNodeModal = forwardRef(
templateParam.charAt(0) !== "_" &&
myData.node?.template[templateParam].show &&
LANGFLOW_SUPPORTED_TYPES.has(
myData.node.template[templateParam].type
)
myData.node!.template[templateParam].type,
),
)
.map((templateParam, index) => {
let id = {
@ -161,8 +166,8 @@ const EditNodeModal = forwardRef(
myData.node?.template[templateParam]
.proxy,
}
: id
)
: id,
),
) ?? false;
return (
<TableRow
@ -170,8 +175,7 @@ const EditNodeModal = forwardRef(
className={
"h-10 " +
((templateParam === "code" &&
myData.node?.template[templateParam].type ===
"code") ||
type(templateParam) === "code") ||
(templateParam.includes("code") &&
myData.node?.template[templateParam].proxy)
? " hidden "
@ -190,7 +194,7 @@ const EditNodeModal = forwardRef(
<span>
{myData.node?.template[templateParam]
.display_name
? myData.node.template[templateParam]
? myData.node!.template[templateParam]
.display_name
: myData.node?.template[templateParam]
.name}
@ -198,58 +202,62 @@ const EditNodeModal = forwardRef(
</ShadTooltip>
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node?.template[templateParam].type ===
"str" &&
!myData.node.template[templateParam].options ? (
<Case
condition={
type(templateParam) === "str" &&
!myData.node!.template[templateParam]
.options
}
>
<div className="mx-auto">
{myData.node.template[templateParam]
{myData.node!.template[templateParam]
?.list ? (
<InputListComponent
componentName={templateParam}
editNode={true}
disabled={disabled}
value={
!myData.node.template[templateParam]
!myData.node!.template[templateParam]
.value ||
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value === ""
? [""]
: myData.node.template[
: myData.node!.template[
templateParam
].value
}
onChange={(value: string[]) => {
handleOnNewValue(
value,
templateParam
templateParam,
);
}}
/>
) : myData.node.template[templateParam]
) : myData.node!.template[templateParam]
.multiline ? (
<TextAreaComponent
id={
"textarea-edit-" +
myData.node.template[templateParam]
myData.node!.template[templateParam]
.name
}
data-testid={
"textarea-edit-" +
myData.node.template[templateParam]
myData.node!.template[templateParam]
.name
}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(
value: string | string[]
value: string | string[],
) => {
handleOnNewValue(
value,
templateParam
templateParam,
);
}}
/>
@ -274,8 +282,13 @@ const EditNodeModal = forwardRef(
/>
)}
</div>
) : myData.node?.template[templateParam]
.type === "NestedDict" ? (
</Case>
<Case
condition={
type(templateParam) === "NestedDict"
}
>
<div className=" w-full">
<DictComponent
disabled={disabled}
@ -296,21 +309,24 @@ const EditNodeModal = forwardRef(
].value = newValue;
handleOnNewValue(
newValue,
templateParam
templateParam,
);
}}
id="editnode-div-dict-input"
/>
</div>
) : myData.node?.template[templateParam]
.type === "dict" ? (
</Case>
<Case
condition={type(templateParam) === "dict"}
>
<div
className={classNames(
"max-h-48 w-full overflow-auto custom-scroll",
myData.node!.template[templateParam].value
?.length > 1
? "my-3"
: ""
: "",
)}
>
<KeypairListComponent
@ -325,7 +341,8 @@ const EditNodeModal = forwardRef(
: convertObjToArray(
myData.node!.template[
templateParam
].value
].value,
type(templateParam)!,
)
}
duplicateKey={errorDuplicateKey}
@ -336,11 +353,11 @@ const EditNodeModal = forwardRef(
templateParam
].value = valueToNumbers;
setErrorDuplicateKey(
hasDuplicateKeys(valueToNumbers)
hasDuplicateKeys(valueToNumbers),
);
handleOnNewValue(
valueToNumbers,
templateParam
templateParam,
);
}}
isList={
@ -349,32 +366,39 @@ const EditNodeModal = forwardRef(
}
/>
</div>
) : myData.node?.template[templateParam]
.type === "bool" ? (
</Case>
<Case
condition={type(templateParam) === "bool"}
>
<div className="ml-auto">
{" "}
<ToggleShadComponent
id={
"toggle-edit-" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
disabled={disabled}
enabled={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value
}
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam
templateParam,
);
}}
size="small"
editNode={true}
/>
</div>
) : myData.node?.template[templateParam]
.type === "float" ? (
</Case>
<Case
condition={type(templateParam) === "float"}
>
<div className="mx-auto">
<FloatComponent
disabled={disabled}
@ -384,7 +408,7 @@ const EditNodeModal = forwardRef(
.rangeSpec
}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(value) => {
@ -392,32 +416,38 @@ const EditNodeModal = forwardRef(
}}
/>
</div>
) : myData.node?.template[templateParam]
.type === "str" &&
myData.node.template[templateParam]
.options ? (
</Case>
<Case
condition={
type(templateParam) === "str" &&
myData.node!.template[templateParam].options
}
>
<div className="mx-auto">
<Dropdown
editNode={true}
options={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.options
}
onSelect={(value) =>
handleOnNewValue(value, templateParam)
}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? "Choose an option"
}
id={
"dropdown-edit-" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
></Dropdown>
</div>
) : myData.node?.template[templateParam]
.type === "int" ? (
</Case>
<Case condition={type(templateParam) === "int"}>
<div className="mx-auto">
<IntComponent
rangeSpec={
@ -426,12 +456,13 @@ const EditNodeModal = forwardRef(
}
id={
"edit-int-input-" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(value) => {
@ -439,21 +470,24 @@ const EditNodeModal = forwardRef(
}}
/>
</div>
) : myData.node?.template[templateParam]
.type === "file" ? (
</Case>
<Case
condition={type(templateParam) === "file"}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
fileTypes={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.fileTypes
}
onFileChange={(filePath: string) => {
@ -463,8 +497,11 @@ const EditNodeModal = forwardRef(
}}
></InputFileComponent>
</div>
) : myData.node?.template[templateParam]
.type === "prompt" ? (
</Case>
<Case
condition={type(templateParam) === "prompt"}
>
<div className="mx-auto">
<PromptAreaComponent
readonly={
@ -478,7 +515,7 @@ const EditNodeModal = forwardRef(
myData.node = nodeClass;
}}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
@ -486,21 +523,26 @@ const EditNodeModal = forwardRef(
}}
id={
"prompt-area-edit-" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
data-testid={
"modal-prompt-input-" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
/>
</div>
) : myData.node?.template[templateParam]
.type === "code" ? (
</Case>
<Case
condition={type(templateParam) === "code"}
>
<div className="mx-auto">
<CodeAreaComponent
readonly={
myData.node?.flow &&
myData.node.template[templateParam]
myData.node!.template[templateParam]
.dynamic
? true
: false
@ -516,7 +558,7 @@ const EditNodeModal = forwardRef(
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
myData.node!.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
@ -524,16 +566,16 @@ const EditNodeModal = forwardRef(
}}
id={
"code-area-edit" +
myData.node.template[templateParam].name
myData.node!.template[templateParam]
.name
}
/>
</div>
) : myData.node?.template[templateParam]
.type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</Case>
<Case condition={type(templateParam) === "Any"}>
<>-</>
</Case>
</TableCell>
<TableCell className="p-0 text-right">
<div className="items-center text-center">
@ -588,7 +630,7 @@ const EditNodeModal = forwardRef(
</BaseModal.Footer>
</BaseModal>
);
}
},
);
export default EditNodeModal;

View file

@ -0,0 +1,15 @@
import { memo } from "react";
type BooleanLike = boolean | string | number | null | undefined;
type Props = {
condition: (() => BooleanLike) | BooleanLike;
children: React.ReactNode | any;
};
export const Case = memo(({ condition, children }: Props) => {
const conditionResult =
typeof condition === "function" ? condition() : condition;
return conditionResult ? children : null;
});

View file

@ -400,6 +400,7 @@ export type StoreApiKeyType = {
export type groupedObjType = {
family: string;
type: string;
display_name?: string;
};
export type nodeGroupedObjType = {

View file

@ -461,7 +461,8 @@ export function getConnectedNodes(
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
}
export function convertObjToArray(singleObject: object | string) {
export function convertObjToArray(singleObject: object | string, type: string) {
if (type !== "dict") return [{ "": "" }];
if (typeof singleObject === "string") {
singleObject = JSON.parse(singleObject);
}