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>
This commit is contained in:
parent
8d7f948aab
commit
e6d1b84043
10 changed files with 460 additions and 394 deletions
|
|
@ -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<HTMLDivElement>(null);
|
||||
const infoHtml = useRef<HTMLDivElement & ReactNode>(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 = (
|
||||
<div className="h-full w-full break-words">
|
||||
{info.split("\n").map((line, index) => (
|
||||
<p key={index} className="block">
|
||||
{line}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}, [info]);
|
||||
|
||||
useEffect(() => {
|
||||
if (optionalHandle && optionalHandle.length === 0) {
|
||||
optionalHandle = null;
|
||||
}
|
||||
}, [optionalHandle]);
|
||||
|
||||
const displayHandle =
|
||||
!LANGFLOW_SUPPORTED_TYPES.has(type ?? "") || optionalHandle;
|
||||
|
||||
return !showNode ? (
|
||||
displayHandle ? (
|
||||
<HandleRenderComponent
|
||||
left={true}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
proxy={proxy}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-noshownode`}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
) : (
|
||||
<div
|
||||
ref={ref}
|
||||
className={
|
||||
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
|
||||
((name === "code" && type === "code") ||
|
||||
(name.includes("code") && proxy)
|
||||
? " hidden"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
<>
|
||||
<div className="flex w-full items-center truncate text-sm">
|
||||
{proxy ? (
|
||||
<ShadTooltip content={<span>{proxy.id}</span>}>
|
||||
{<span>{title}</span>}
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<span>{<span>{title}</span>}</span>
|
||||
</div>
|
||||
)}
|
||||
<span className={(required ? "ml-2 " : "") + "text-status-red"}>
|
||||
{required ? "*" : ""}
|
||||
</span>
|
||||
<div className="">
|
||||
{info !== "" && (
|
||||
<ShadTooltip content={infoHtml.current}>
|
||||
{/* put div to avoid bug that does not display tooltip */}
|
||||
<div className="cursor-help">
|
||||
<IconComponent
|
||||
name="Info"
|
||||
className="relative bottom-px ml-1.5 h-3 w-4"
|
||||
/>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{displayHandle && (
|
||||
<HandleRenderComponent
|
||||
left={true}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
proxy={proxy}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-shownode`}
|
||||
/>
|
||||
)}
|
||||
{data.node?.template[name] !== undefined && (
|
||||
<div className="mt-2 w-full">
|
||||
<ParameterRenderComponent
|
||||
handleOnNewValue={handleOnNewValue}
|
||||
name={name}
|
||||
nodeId={data.id}
|
||||
templateData={data.node?.template[name]!}
|
||||
templateValue={data.node?.template[name].value ?? ""}
|
||||
editNode={false}
|
||||
handleNodeClass={handleNodeClass}
|
||||
nodeClass={data.node!}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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<HTMLDivElement>(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 ? (
|
||||
<HandleRenderComponent
|
||||
left={false}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-noshownode`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
ref={ref}
|
||||
className="relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
|
||||
>
|
||||
<>
|
||||
<div className="flex w-full items-center justify-end truncate text-sm">
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
disabled={disabledOutput}
|
||||
unstyled
|
||||
onClick={() => handleUpdateOutputHide()}
|
||||
data-testid={`input-inspection-${title.toLowerCase()}`}
|
||||
>
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
disabledOutput ? "text-muted-foreground" : "",
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
name={data.node?.outputs![index].hidden ? "EyeOff" : "Eye"}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<Case condition={data.node?.frozen}>
|
||||
<div className="pr-1">
|
||||
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
|
||||
</div>
|
||||
</Case>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<span className={data.node?.frozen ? "text-ice" : ""}>
|
||||
<OutputComponent
|
||||
proxy={outputProxy}
|
||||
idx={index}
|
||||
types={type?.split("|") ?? []}
|
||||
selected={
|
||||
data.node?.outputs![index].selected ??
|
||||
data.node?.outputs![index].types[0] ??
|
||||
title
|
||||
}
|
||||
nodeId={data.id}
|
||||
frozen={data.node?.frozen}
|
||||
name={title ?? type}
|
||||
/>
|
||||
</span>
|
||||
<ShadTooltip
|
||||
content={
|
||||
displayOutputPreview
|
||||
? unknownOutput
|
||||
? "Output can't be displayed"
|
||||
: "Inspect Output"
|
||||
: "Please build the component first"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
unstyled
|
||||
disabled={!displayOutputPreview || unknownOutput}
|
||||
onClick={() => setOpenOutputModal(true)}
|
||||
data-testid={`output-inspection-${title.toLowerCase()}`}
|
||||
>
|
||||
{errorOutput ? (
|
||||
<IconComponent
|
||||
className={classNames("h-5 w-5 rounded-md text-status-red")}
|
||||
name={"X"}
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
className={classNames(
|
||||
"h-5 w-5 rounded-md",
|
||||
displayOutputPreview && !unknownOutput
|
||||
? "hover:text-medium-indigo"
|
||||
: "cursor-not-allowed text-muted-foreground",
|
||||
)}
|
||||
name={"ScanEye"}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<HandleRenderComponent
|
||||
left={false}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-shownode`}
|
||||
/>
|
||||
{openOutputModal && (
|
||||
<OutputModal
|
||||
open={openOutputModal}
|
||||
nodeId={flowPoolId}
|
||||
setOpen={setOpenOutputModal}
|
||||
outputName={internalOutputName}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ export default function HandleRenderComponent({
|
|||
left: boolean;
|
||||
nodes: any;
|
||||
tooltipTitle?: string;
|
||||
proxy: any;
|
||||
proxy?: any;
|
||||
id: any;
|
||||
title: string;
|
||||
edges: any;
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"];
|
||||
|
|
@ -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<HTMLDivElement>(null);
|
||||
const infoHtml = useRef<HTMLDivElement & ReactNode>(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 = (
|
||||
<div className="h-full w-full break-words">
|
||||
{info.split("\n").map((line, index) => (
|
||||
<p key={index} className="block">
|
||||
{line}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}, [info]);
|
||||
|
||||
function renderTitle() {
|
||||
return !left ? (
|
||||
<OutputComponent
|
||||
proxy={outputProxy}
|
||||
idx={index}
|
||||
types={type?.split("|") ?? []}
|
||||
selected={
|
||||
data.node?.outputs![index].selected ??
|
||||
data.node?.outputs![index].types[0] ??
|
||||
title
|
||||
}
|
||||
nodeId={data.id}
|
||||
frozen={data.node?.frozen}
|
||||
name={title ?? type}
|
||||
/>
|
||||
) : (
|
||||
<span>{title}</span>
|
||||
);
|
||||
}
|
||||
|
||||
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 ? (
|
||||
<></>
|
||||
) : (
|
||||
<HandleRenderComponent
|
||||
left={left}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
proxy={proxy}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-noshownode`}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div
|
||||
ref={ref}
|
||||
className={
|
||||
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
|
||||
((name === "code" && type === "code") ||
|
||||
(name.includes("code") && proxy)
|
||||
? " hidden"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
"flex w-full items-center truncate text-sm" +
|
||||
(left ? "" : " justify-end")
|
||||
}
|
||||
>
|
||||
{!left && (
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
disabled={disabledOutput}
|
||||
unstyled
|
||||
onClick={() => handleUpdateOutputHide()}
|
||||
data-testid={`input-inspection-${title.toLowerCase()}`}
|
||||
>
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
disabledOutput ? "text-muted-foreground" : "",
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
name={data.node?.outputs![index].hidden ? "EyeOff" : "Eye"}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<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>}>
|
||||
{renderTitle()}
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<span className={!left && data.node?.frozen ? "text-ice" : ""}>
|
||||
{renderTitle()}
|
||||
</span>
|
||||
{!left && (
|
||||
<ShadTooltip
|
||||
content={
|
||||
displayOutputPreview
|
||||
? unknownOutput
|
||||
? "Output can't be displayed"
|
||||
: "Inspect Output"
|
||||
: "Please build the component first"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
unstyled
|
||||
disabled={!displayOutputPreview || unknownOutput}
|
||||
onClick={() => setOpenOutputModal(true)}
|
||||
data-testid={`output-inspection-${title.toLowerCase()}`}
|
||||
>
|
||||
{errorOutput ? (
|
||||
<IconComponent
|
||||
className={classNames(
|
||||
"h-5 w-5 rounded-md text-status-red",
|
||||
)}
|
||||
name={"X"}
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
className={classNames(
|
||||
"h-5 w-5 rounded-md",
|
||||
displayOutputPreview && !unknownOutput
|
||||
? "hover:text-medium-indigo"
|
||||
: "cursor-not-allowed text-muted-foreground",
|
||||
)}
|
||||
name={"ScanEye"}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<span className={(required ? "ml-2 " : "") + "text-status-red"}>
|
||||
{required ? "*" : ""}
|
||||
</span>
|
||||
<div className="">
|
||||
{info !== "" && (
|
||||
<ShadTooltip content={infoHtml.current}>
|
||||
{/* put div to avoid bug that does not display tooltip */}
|
||||
<div className="cursor-help">
|
||||
<IconComponent
|
||||
name="Info"
|
||||
className="relative bottom-px ml-1.5 h-3 w-4"
|
||||
/>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
|
||||
<></>
|
||||
) : (
|
||||
<HandleRenderComponent
|
||||
left={left}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle}
|
||||
proxy={proxy}
|
||||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-shownode`}
|
||||
/>
|
||||
)}
|
||||
{data.node?.template[name] !== undefined && (
|
||||
<div className="mt-2 w-full">
|
||||
<ParameterRenderComponent
|
||||
handleOnNewValue={handleOnNewValue}
|
||||
name={name}
|
||||
nodeId={data.id}
|
||||
templateData={data.node?.template[name]!}
|
||||
templateValue={data.node?.template[name].value ?? ""}
|
||||
editNode={false}
|
||||
handleNodeClass={handleNodeClass}
|
||||
nodeClass={data.node!}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{openOutputModal && (
|
||||
<OutputModal
|
||||
open={openOutputModal}
|
||||
nodeId={flowPoolId}
|
||||
setOpen={setOpenOutputModal}
|
||||
outputName={internalOutputName}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<ParameterComponent
|
||||
<NodeOutputField
|
||||
index={idx}
|
||||
selected={selected}
|
||||
key={
|
||||
scapedJSONStringfy({
|
||||
output_types: output.types,
|
||||
|
|
@ -324,7 +326,6 @@ export default function GenericNode({
|
|||
name: output.name,
|
||||
}}
|
||||
type={output.types.join("|")}
|
||||
left={false}
|
||||
showNode={showNode}
|
||||
outputName={output.name}
|
||||
/>
|
||||
|
|
@ -494,9 +495,7 @@ export default function GenericNode({
|
|||
(templateField: string, idx) =>
|
||||
data.node!.template[templateField]?.show &&
|
||||
!data.node!.template[templateField]?.advanced && (
|
||||
<ParameterComponent
|
||||
selected={selected}
|
||||
index={idx}
|
||||
<NodeInputField
|
||||
key={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
|
|
@ -533,7 +532,6 @@ export default function GenericNode({
|
|||
id: data.id,
|
||||
fieldName: templateField,
|
||||
}}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
|
|
@ -710,9 +708,7 @@ export default function GenericNode({
|
|||
<div key={idx}>
|
||||
{data.node!.template[templateField]?.show &&
|
||||
!data.node!.template[templateField]?.advanced ? (
|
||||
<ParameterComponent
|
||||
selected={selected}
|
||||
index={idx}
|
||||
<NodeInputField
|
||||
key={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
|
|
@ -746,7 +742,6 @@ export default function GenericNode({
|
|||
id: data.id,
|
||||
fieldName: templateField,
|
||||
}}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TEXT_FIELD_TYPES } from "@/CustomNodes/GenericNode/components/parameterComponent/constants";
|
||||
import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
|
||||
import { TEXT_FIELD_TYPES } from "@/constants/constants";
|
||||
import { APIClassType, InputFieldType } from "@/types/api";
|
||||
import { useMemo } from "react";
|
||||
import CodeAreaComponent from "../codeAreaComponent";
|
||||
|
|
|
|||
|
|
@ -879,6 +879,6 @@ export const LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS = 60 * 60 - 60 * 60 * 0.1;
|
|||
export const LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV =
|
||||
Number(process.env.ACCESS_TOKEN_EXPIRE_SECONDS) -
|
||||
Number(process.env.ACCESS_TOKEN_EXPIRE_SECONDS) * 0.1;
|
||||
|
||||
export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"];
|
||||
export const NODE_WIDTH = 384;
|
||||
export const NODE_HEIGHT = NODE_WIDTH * 3;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,36 @@ export type ParameterComponentType = {
|
|||
outputName?: string;
|
||||
outputProxy?: OutputFieldProxyType;
|
||||
};
|
||||
|
||||
export type NodeOutputFieldComponentType = {
|
||||
selected: boolean;
|
||||
data: NodeDataType;
|
||||
title: string;
|
||||
id: sourceHandleType;
|
||||
colors: string[];
|
||||
tooltipTitle: string | undefined;
|
||||
showNode: boolean;
|
||||
index: number;
|
||||
type: string | undefined;
|
||||
outputName?: string;
|
||||
outputProxy?: OutputFieldProxyType;
|
||||
};
|
||||
|
||||
export type NodeInputFieldComponentType = {
|
||||
id: targetHandleType;
|
||||
data: NodeDataType;
|
||||
tooltipTitle: string | undefined;
|
||||
title: string;
|
||||
colors: string[];
|
||||
type: string | undefined;
|
||||
name: string;
|
||||
required: boolean;
|
||||
optionalHandle: Array<String> | 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;
|
||||
|
|
|
|||
|
|
@ -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<string>) => void;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue