From e6d1b84043ebfeb9ecaeee2533fdbc084741f5b5 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 7 Aug 2024 13:10:10 -0300 Subject: [PATCH] refactor: parameterComponent in GenericNode (#3220) * refactor: create NodeOutputField component in GenericNode * feat: update isUserChange parameter in setNode function The `isUserChange` parameter in the `setNode` function of the `FlowStoreType` interface has been updated to be optional. This change improves the flexibility and usability of the function. * feat: Add NodeInputField component to GenericNode The NodeInputField component has been added to the GenericNode component. This component is responsible for rendering input fields for nodes in the UI. It improves the functionality and user experience of the GenericNode component. * delete parameter component * [autofix.ci] apply automated fixes * feat: Refactor NodeOutputField component in GenericNode The NodeOutputField component in the GenericNode component has been refactored to improve code organization and maintainability. This refactor enhances the functionality and user experience of the GenericNode component. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../components/NodeInputField/index.tsx | 175 ++++++++ .../components/NodeOutputfield/index.tsx | 245 ++++++++++++ .../handleRenderComponent/index.tsx | 2 +- .../parameterComponent/constants.ts | 1 - .../components/parameterComponent/index.tsx | 378 ------------------ .../src/CustomNodes/GenericNode/index.tsx | 17 +- .../parameterRenderComponent/index.tsx | 2 +- src/frontend/src/constants/constants.ts | 2 +- src/frontend/src/types/components/index.ts | 30 ++ src/frontend/src/types/zustand/flow/index.ts | 2 +- 10 files changed, 460 insertions(+), 394 deletions(-) create mode 100644 src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx create mode 100644 src/frontend/src/CustomNodes/GenericNode/components/NodeOutputfield/index.tsx delete mode 100644 src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/constants.ts delete mode 100644 src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx new file mode 100644 index 000000000..061a5bcce --- /dev/null +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx @@ -0,0 +1,175 @@ +import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; +import { ParameterRenderComponent } from "@/components/parameterRenderComponent"; +import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; +import { ReactNode, useEffect, useRef, useState } from "react"; +import { default as IconComponent } from "../../../../components/genericIconComponent"; +import ShadTooltip from "../../../../components/shadTooltipComponent"; +import { LANGFLOW_SUPPORTED_TYPES } from "../../../../constants/constants"; +import useFlowStore from "../../../../stores/flowStore"; +import { useTypesStore } from "../../../../stores/typesStore"; +import { + NodeInputFieldComponentType, + ParameterComponentType, +} from "../../../../types/components"; +import { scapedJSONStringfy } from "../../../../utils/reactflowUtils"; +import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; +import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; +import HandleRenderComponent from "../handleRenderComponent"; + +export default function NodeInputField({ + id, + data, + tooltipTitle, + title, + colors, + type, + name = "", + required = false, + optionalHandle = null, + info = "", + proxy, + showNode, +}: NodeInputFieldComponentType): JSX.Element { + const ref = useRef(null); + const infoHtml = useRef(null); + const nodes = useFlowStore((state) => state.nodes); + const edges = useFlowStore((state) => state.edges); + const myData = useTypesStore((state) => state.data); + const postTemplateValue = usePostTemplateValue({ + node: data.node!, + nodeId: data.id, + parameterId: name, + }); + const setFilterEdge = useFlowStore((state) => state.setFilterEdge); + const { handleNodeClass } = useHandleNodeClass(data.id); + + let disabled = + edges.some( + (edge) => + edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), + ) ?? false; + + const { handleOnNewValue } = useHandleOnNewValue({ + node: data.node!, + nodeId: data.id, + name, + }); + + useFetchDataOnMount(data.node!, handleNodeClass, name, postTemplateValue); + + useEffect(() => { + // @ts-ignore + infoHtml.current = ( +
+ {info.split("\n").map((line, index) => ( +

+ {line} +

+ ))} +
+ ); + }, [info]); + + useEffect(() => { + if (optionalHandle && optionalHandle.length === 0) { + optionalHandle = null; + } + }, [optionalHandle]); + + const displayHandle = + !LANGFLOW_SUPPORTED_TYPES.has(type ?? "") || optionalHandle; + + return !showNode ? ( + displayHandle ? ( + + ) : ( + <> + ) + ) : ( +
+ <> +
+ {proxy ? ( + {proxy.id}}> + {{title}} + + ) : ( +
+ {{title}} +
+ )} + + {required ? "*" : ""} + +
+ {info !== "" && ( + + {/* put div to avoid bug that does not display tooltip */} +
+ +
+
+ )} +
+
+ + {displayHandle && ( + + )} + {data.node?.template[name] !== undefined && ( +
+ +
+ )} + +
+ ); +} diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeOutputfield/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeOutputfield/index.tsx new file mode 100644 index 000000000..bb12627b8 --- /dev/null +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeOutputfield/index.tsx @@ -0,0 +1,245 @@ +import { cloneDeep } from "lodash"; +import { useEffect, useRef, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; +import { useUpdateNodeInternals } from "reactflow"; +import { default as IconComponent } from "../../../../components/genericIconComponent"; +import ShadTooltip from "../../../../components/shadTooltipComponent"; +import { Button } from "../../../../components/ui/button"; +import { Case } from "../../../../shared/components/caseComponent"; +import useFlowStore from "../../../../stores/flowStore"; +import { useShortcutsStore } from "../../../../stores/shortcuts"; +import { useTypesStore } from "../../../../stores/typesStore"; +import { + NodeOutputFieldComponentType, + ParameterComponentType, +} from "../../../../types/components"; +import { + getGroupOutputNodeId, + scapedJSONStringfy, +} from "../../../../utils/reactflowUtils"; +import { + classNames, + cn, + isThereModal, + logHasMessage, + logTypeIsError, + logTypeIsUnknown, +} from "../../../../utils/utils"; +import OutputComponent from "../OutputComponent"; +import HandleRenderComponent from "../handleRenderComponent"; +import OutputModal from "../outputModal"; + +export default function NodeOutputField({ + selected, + data, + title, + id, + colors, + tooltipTitle, + showNode, + index, + type, + outputName, + outputProxy, +}: NodeOutputFieldComponentType): JSX.Element { + const ref = useRef(null); + 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 updateNodeInternals = useUpdateNodeInternals(); + const setFilterEdge = useFlowStore((state) => state.setFilterEdge); + const [openOutputModal, setOpenOutputModal] = useState(false); + const flowPool = useFlowStore((state) => state.flowPool); + + let flowPoolId = data.id; + let internalOutputName = outputName; + + if (data.node?.flow && outputProxy) { + const realOutput = getGroupOutputNodeId( + data.node.flow, + outputProxy.name, + outputProxy.id, + ); + if (realOutput) { + flowPoolId = realOutput.id; + internalOutputName = realOutput.outputName; + } + } + + const flowPoolNode = (flowPool[flowPoolId] ?? [])[ + (flowPool[flowPoolId]?.length ?? 1) - 1 + ]; + + const displayOutputPreview = + !!flowPool[flowPoolId] && + logHasMessage(flowPoolNode?.data, internalOutputName); + + const unknownOutput = logTypeIsUnknown( + flowPoolNode?.data, + internalOutputName, + ); + const errorOutput = logTypeIsError(flowPoolNode?.data, internalOutputName); + + const preventDefault = true; + + function handleOutputWShortcut() { + if (!displayOutputPreview || unknownOutput) return; + if (isThereModal() && !openOutputModal) return; + if (selected) { + setOpenOutputModal((state) => !state); + } + } + + const output = useShortcutsStore((state) => state.output); + useHotkeys(output, handleOutputWShortcut, { preventDefault }); + + let disabledOutput = + edges.some((edge) => edge.sourceHandle === scapedJSONStringfy(id)) ?? false; + + const handleUpdateOutputHide = (value?: boolean) => { + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + newNode.data = { + ...newNode.data, + node: { + ...newNode.data.node, + outputs: newNode.data.node.outputs?.map((output, i) => { + if (i === index) { + output.hidden = value ?? !output.hidden; + } + return output; + }), + }, + }; + return newNode; + }); + updateNodeInternals(data.id); + }; + + useEffect(() => { + if (disabledOutput && data.node?.outputs![index].hidden) { + handleUpdateOutputHide(false); + } + }, [disabledOutput]); + + return !showNode ? ( + + ) : ( +
+ <> +
+
+ +
+ +
+ +
+
+ +
+ + + + + + +
+
+ + {openOutputModal && ( + + )} + +
+ ); +} diff --git a/src/frontend/src/CustomNodes/GenericNode/components/handleRenderComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/handleRenderComponent/index.tsx index 51b50332e..fbf2232de 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/handleRenderComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/handleRenderComponent/index.tsx @@ -24,7 +24,7 @@ export default function HandleRenderComponent({ left: boolean; nodes: any; tooltipTitle?: string; - proxy: any; + proxy?: any; id: any; title: string; edges: any; diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/constants.ts b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/constants.ts deleted file mode 100644 index 58cb45587..000000000 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"]; diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx deleted file mode 100644 index 611fd79be..000000000 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ /dev/null @@ -1,378 +0,0 @@ -import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; -import { ParameterRenderComponent } from "@/components/parameterRenderComponent"; -import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; -import useAlertStore from "@/stores/alertStore"; -import { cloneDeep } from "lodash"; -import { ReactNode, useEffect, useRef, useState } from "react"; -import { useHotkeys } from "react-hotkeys-hook"; -import { useUpdateNodeInternals } from "reactflow"; -import { default as IconComponent } from "../../../../components/genericIconComponent"; -import ShadTooltip from "../../../../components/shadTooltipComponent"; -import { Button } from "../../../../components/ui/button"; -import { LANGFLOW_SUPPORTED_TYPES } from "../../../../constants/constants"; -import { Case } from "../../../../shared/components/caseComponent"; -import useFlowStore from "../../../../stores/flowStore"; -import { useShortcutsStore } from "../../../../stores/shortcuts"; -import { useTypesStore } from "../../../../stores/typesStore"; -import { ParameterComponentType } from "../../../../types/components"; -import { - getGroupOutputNodeId, - scapedJSONStringfy, -} from "../../../../utils/reactflowUtils"; -import { - classNames, - cn, - isThereModal, - logHasMessage, - logTypeIsError, - logTypeIsUnknown, -} from "../../../../utils/utils"; -import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; -import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; -import OutputComponent from "../OutputComponent"; -import HandleRenderComponent from "../handleRenderComponent"; -import OutputModal from "../outputModal"; - -export default function ParameterComponent({ - left, - id, - data, - tooltipTitle, - title, - colors, - type, - name = "", - required = false, - optionalHandle = null, - info = "", - proxy, - showNode, - index, - outputName, - selected, - outputProxy, -}: ParameterComponentType): JSX.Element { - const ref = useRef(null); - const infoHtml = useRef(null); - 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 postTemplateValue = usePostTemplateValue({ - node: data.node!, - nodeId: data.id, - parameterId: name, - }); - const updateNodeInternals = useUpdateNodeInternals(); - const setFilterEdge = useFlowStore((state) => state.setFilterEdge); - const [openOutputModal, setOpenOutputModal] = useState(false); - const flowPool = useFlowStore((state) => state.flowPool); - - let flowPoolId = data.id; - let internalOutputName = outputName; - - if (data.node?.flow && outputProxy) { - const realOutput = getGroupOutputNodeId( - data.node.flow, - outputProxy.name, - outputProxy.id, - ); - if (realOutput) { - flowPoolId = realOutput.id; - internalOutputName = realOutput.outputName; - } - } - - const flowPoolNode = (flowPool[flowPoolId] ?? [])[ - (flowPool[flowPoolId]?.length ?? 1) - 1 - ]; - - const displayOutputPreview = - !!flowPool[flowPoolId] && - logHasMessage(flowPoolNode?.data, internalOutputName); - - const unknownOutput = logTypeIsUnknown( - flowPoolNode?.data, - internalOutputName, - ); - const errorOutput = logTypeIsError(flowPoolNode?.data, internalOutputName); - - if (outputProxy) { - console.log(logHasMessage(flowPoolNode?.data, internalOutputName)); - } - - const preventDefault = true; - - function handleOutputWShortcut() { - if (!displayOutputPreview || unknownOutput) return; - if (isThereModal() && !openOutputModal) return; - if (selected && !left) { - setOpenOutputModal((state) => !state); - } - } - - const setErrorData = useAlertStore((state) => state.setErrorData); - - const output = useShortcutsStore((state) => state.output); - useHotkeys(output, handleOutputWShortcut, { preventDefault }); - - const { handleNodeClass } = useHandleNodeClass(data.id); - - let disabled = - edges.some( - (edge) => - edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), - ) ?? false; - - let disabledOutput = - edges.some( - (edge) => - edge.sourceHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), - ) ?? false; - - const { handleOnNewValue } = useHandleOnNewValue({ - node: data.node!, - nodeId: data.id, - name, - }); - - useFetchDataOnMount(data.node!, handleNodeClass, name, postTemplateValue); - - useEffect(() => { - // @ts-ignore - infoHtml.current = ( -
- {info.split("\n").map((line, index) => ( -

- {line} -

- ))} -
- ); - }, [info]); - - function renderTitle() { - return !left ? ( - - ) : ( - {title} - ); - } - - useEffect(() => { - if (optionalHandle && optionalHandle.length === 0) { - optionalHandle = null; - } - }, [optionalHandle]); - - const handleUpdateOutputHide = (value?: boolean) => { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - node: { - ...newNode.data.node, - outputs: newNode.data.node.outputs?.map((output, i) => { - if (i === index) { - output.hidden = value ?? !output.hidden; - } - return output; - }), - }, - }; - return newNode; - }); - updateNodeInternals(data.id); - }; - - useEffect(() => { - if (disabledOutput && data.node?.outputs![index].hidden) { - handleUpdateOutputHide(false); - } - }, [disabledOutput]); - - return !showNode ? ( - left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? ( - <> - ) : ( - - ) - ) : ( -
- <> -
- {!left && ( -
- -
- )} - -
- -
-
- - {proxy ? ( - {proxy.id}}> - {renderTitle()} - - ) : ( -
- - {renderTitle()} - - {!left && ( - - - - )} -
- )} - - {required ? "*" : ""} - -
- {info !== "" && ( - - {/* put div to avoid bug that does not display tooltip */} -
- -
-
- )} -
-
- - {left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? ( - <> - ) : ( - - )} - {data.node?.template[name] !== undefined && ( -
- -
- )} - {openOutputModal && ( - - )} - -
- ); -} diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index f4b0314a7..e0a582d44 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -43,7 +43,8 @@ import useUpdateValidationStatus from "../hooks/use-update-validation-status"; import useValidationStatusString from "../hooks/use-validation-status-string"; import getFieldTitle from "../utils/get-field-title"; import sortFields from "../utils/sort-fields"; -import ParameterComponent from "./components/parameterComponent"; +import NodeInputField from "./components/NodeInputField"; +import NodeOutputField from "./components/NodeOutputfield"; export default function GenericNode({ data, @@ -302,8 +303,9 @@ export default function GenericNode({ const renderOutputParameter = (output: OutputFieldType, idx: number) => { return ( - @@ -494,9 +495,7 @@ export default function GenericNode({ (templateField: string, idx) => data.node!.template[templateField]?.show && !data.node!.template[templateField]?.advanced && ( - {data.node!.template[templateField]?.show && !data.node!.template[templateField]?.advanced ? ( - | undefined | null; + info: string; + proxy: { field: string; id: string } | undefined; + showNode: boolean; +}; + export type InputListComponentType = { value: string[]; onChange: (value: string[], dbValue?: boolean, snapshot?: boolean) => void; diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 927638b65..59ea979fa 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -112,7 +112,7 @@ export type FlowStoreType = { setNode: ( id: string, update: Node | ((oldState: Node) => Node), - isUserChange: boolean, + isUserChange?: boolean, ) => void; getNode: (id: string) => Node | undefined; deleteNode: (nodeId: string | Array) => void;