Refactored data state management on parameter components and editnode modal, bug on saving(not storing changes on ReactFlowInstance)

This commit is contained in:
Lucas Oliveira 2023-07-16 00:55:22 -03:00
commit d5ba08f776
10 changed files with 121 additions and 92 deletions

View file

@ -1,3 +1,4 @@
import { cloneDeep } from "lodash";
import { Info } from "lucide-react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
@ -31,6 +32,7 @@ export default function ParameterComponent({
left,
id,
data,
setData,
tooltipTitle,
title,
color,
@ -62,10 +64,13 @@ export default function ParameterComponent({
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
const [myData, setMyData] = useState(useContext(typesContext).data);
const { data: myData } = useContext(typesContext);
const handleOnNewValue = (newValue: any) => {
data.node.template[name].value = newValue;
let newData = cloneDeep(data);
newData.node.template[name].value = newValue;
setData(newData);
// Set state to pending
setTabsState((prev) => {
return {

View file

@ -17,15 +17,16 @@ import {
import ParameterComponent from "./components/parameterComponent";
export default function GenericNode({
data,
data: olddata,
selected,
}: {
data: NodeDataType;
selected: boolean;
}) {
const [data, setData] = useState(olddata);
const { setErrorData } = useContext(alertContext);
const showError = useRef(true);
const { types, deleteNode } = useContext(typesContext);
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
// any to avoid type conflict
const Icon: any =
nodeIconsLucide[data.type] || nodeIconsLucide[types[data.type]];
@ -33,6 +34,10 @@ export default function GenericNode({
// State for outline color
const { sseData, isBuilding } = useSSE();
const refHtml = useRef(null);
useEffect(() => {
console.log("atualizou", data);
olddata = data;
}, [data, reactFlowInstance]);
// useEffect(() => {
// if (reactFlowInstance) {
@ -63,12 +68,12 @@ export default function GenericNode({
deleteNode(data.id);
return;
}
useEffect(() => {}, [data.node.template]);
return (
<>
<NodeToolbar>
<NodeToolbarComponent
data={data}
setData={setData}
deleteNode={deleteNode}
></NodeToolbarComponent>
</NodeToolbar>
@ -184,6 +189,7 @@ export default function GenericNode({
!data.node.template[t].advanced ? (
<ParameterComponent
data={data}
setData={setData}
color={
nodeColors[types[data.node.template[t].type]] ??
nodeColors[data.node.template[t].type] ??
@ -233,6 +239,7 @@ export default function GenericNode({
</div> */}
<ParameterComponent
data={data}
setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={
data.node.output_types && data.node.output_types.length > 0

View file

@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect } from "react";
import { TabsContext } from "../../contexts/tabsContext";
import { FloatComponentType } from "../../types/components";
@ -9,7 +9,6 @@ export default function FloatComponent({
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
const step = 0.1;
@ -18,7 +17,6 @@ export default function FloatComponent({
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
@ -44,7 +42,7 @@ export default function FloatComponent({
}
}}
max={max}
value={myValue}
value={value ?? ""}
className={
editNode
? "input-edit-node"
@ -54,7 +52,6 @@ export default function FloatComponent({
editNode ? "Number 0 to 1" : "Type a number from zero to one"
}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -11,13 +11,11 @@ export default function InputComponent({
password,
editNode = false,
}: InputComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const [pwdVisible, setPwdVisible] = useState(false);
const { setDisableCopyPaste } = useContext(TabsContext);
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
@ -25,7 +23,7 @@ export default function InputComponent({
return (
<div className={disabled ? "input-component-div" : "relative"}>
<input
value={myValue}
value={value}
onFocus={() => {
if (disableCopyPaste) setDisableCopyPaste(true);
}}
@ -34,16 +32,13 @@ export default function InputComponent({
}}
className={classNames(
disabled ? " input-disable " : "",
password && !pwdVisible && myValue !== ""
? " text-clip password "
: "",
password && !pwdVisible && value !== "" ? " text-clip password " : "",
editNode ? " input-edit-node " : " input-primary ",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : ""
)}
placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -13,6 +13,12 @@ export default function InputListComponent({
}: InputListComponentType) {
const [inputList, setInputList] = useState(value ?? [""]);
useEffect(() => {
if (value) {
setInputList(value);
}
}, [value]);
useEffect(() => {
if (disabled) {
setInputList([""]);

View file

@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect } from "react";
import { TabsContext } from "../../contexts/tabsContext";
import { FloatComponentType } from "../../types/components";
@ -9,13 +9,11 @@ export default function IntComponent({
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
const min = 0;
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
@ -60,7 +58,7 @@ export default function IntComponent({
e.target.value = min.toString();
}
}}
value={myValue}
value={value ?? ""}
className={
editNode
? " input-edit-node "
@ -68,7 +66,6 @@ export default function IntComponent({
}
placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect } from "react";
import GenericModal from "../../modals/genericModal";
import { TextAreaComponentType } from "../../types/components";
@ -11,12 +11,10 @@ export default function TextAreaComponent({
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { setDisableCopyPaste } = useContext(TabsContext);
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
@ -25,7 +23,7 @@ export default function TextAreaComponent({
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center">
<input
value={myValue}
value={value}
onFocus={() => {
setDisableCopyPaste(true);
}}
@ -40,7 +38,6 @@ export default function TextAreaComponent({
}
placeholder={"Type something..."}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>
@ -49,9 +46,8 @@ export default function TextAreaComponent({
type={"text"}
buttonText="Finishing Editing"
modalTitle="Edit Text"
value={myValue}
value={value}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
>

View file

@ -1,5 +1,6 @@
import { cloneDeep } from "lodash";
import { Variable } from "lucide-react";
import { ReactNode, forwardRef, useContext, useState } from "react";
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
@ -30,17 +31,19 @@ const EditNodeModal = forwardRef(
(
{
data,
setData,
nodeLength,
children,
}: {
data: NodeDataType;
setData: (data: NodeDataType) => void;
nodeLength: number;
children: ReactNode;
},
ref
) => {
const [modalOpen, setModalOpen] = useState(false);
const [nodeValue, setNodeValue] = useState(null);
const [myData, setMyData] = useState(data);
const { setTabsState, tabId } = useContext(TabsContext);
const { reactFlowInstance } = useContext(typesContext);
@ -49,30 +52,31 @@ const EditNodeModal = forwardRef(
false;
function changeAdvanced(n) {
data.node.template[n].advanced = !data.node.template[n].advanced;
setNodeValue(!nodeValue);
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[n].advanced = !newData.node.template[n].advanced;
return newData;
});
}
const handleOnNewValue = (newValue: any, name) => {
data.node.template[name].value = newValue;
// Set state to pending
setTabsState((prev) => {
return {
...prev,
[tabId]: {
...prev[tabId],
isPending: true,
},
};
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[name].value = newValue;
return newData;
});
};
useEffect(() => {
setMyData(data); // reset data to what it is on node when opening modal
}, [modalOpen]);
return (
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={data.node?.description}>
<span className="pr-2">{data.type}</span>
<Badge variant="secondary">ID: {data.id}</Badge>
<BaseModal.Header description={myData.node?.description}>
<span className="pr-2">{myData.type}</span>
<Badge variant="secondary">ID: {myData.id}</Badge>
</BaseModal.Header>
<BaseModal.Content>
<div className="flex pb-2">
@ -102,49 +106,51 @@ const EditNodeModal = forwardRef(
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(data.node.template)
{Object.keys(myData.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "int")
myData.node.template[t].show &&
(myData.node.template[t].type === "str" ||
myData.node.template[t].type === "bool" ||
myData.node.template[t].type === "float" ||
myData.node.template[t].type === "code" ||
myData.node.template[t].type === "prompt" ||
myData.node.template[t].type === "file" ||
myData.node.template[t].type === "int")
)
.map((n, i) => (
<TableRow key={i} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{data.node.template[n].name
? data.node.template[n].name
: data.node.template[n].display_name}
{myData.node.template[n].name
? myData.node.template[n].name
: myData.node.template[n].display_name}
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{data.node.template[n].type === "str" &&
!data.node.template[n].options ? (
{myData.node.template[n].type === "str" &&
!myData.node.template[n].options ? (
<div className="mx-auto">
{data.node.template[n].list ? (
{myData.node.template[n].list ? (
<InputListComponent
editNode={true}
disabled={disabled}
value={
!data.node.template[n].value ||
data.node.template[n].value === ""
!myData.node.template[n].value ||
myData.node.template[n].value === ""
? [""]
: data.node.template[n].value
: myData.node.template[n].value
}
onChange={(t: string[]) => {
handleOnNewValue(t, n);
}}
/>
) : data.node.template[n].multiline ? (
) : myData.node.template[n].multiline ? (
<TextAreaComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={
myData.node.template[n].value ?? ""
}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
@ -154,107 +160,112 @@ const EditNodeModal = forwardRef(
editNode={true}
disabled={disabled}
password={
data.node.template[n].password ?? false
myData.node.template[n].password ??
false
}
value={
myData.node.template[n].value ?? ""
}
value={data.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
}}
/>
)}
</div>
) : data.node.template[n].type === "bool" ? (
) : myData.node.template[n].type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
disabled={disabled}
enabled={data.node.template[n].value}
enabled={myData.node.template[n].value}
setEnabled={(t) => {
handleOnNewValue(t, n);
}}
size="small"
/>
</div>
) : data.node.template[n].type === "float" ? (
) : myData.node.template[n].type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "str" &&
data.node.template[n].options ? (
) : myData.node.template[n].type === "str" &&
myData.node.template[n].options ? (
<div className="mx-auto">
<Dropdown
numberOfOptions={nodeLength}
editNode={true}
options={data.node.template[n].options}
options={myData.node.template[n].options}
onSelect={(t) => handleOnNewValue(t, n)}
value={
data.node.template[n].value ??
myData.node.template[n].value ??
"Choose an option"
}
></Dropdown>
</div>
) : data.node.template[n].type === "int" ? (
) : myData.node.template[n].type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "file" ? (
) : myData.node.template[n].type === "file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
fileTypes={data.node.template[n].fileTypes}
suffixes={data.node.template[n].suffixes}
fileTypes={
myData.node.template[n].fileTypes
}
suffixes={myData.node.template[n].suffixes}
onFileChange={(t: string) => {
handleOnNewValue(t, n);
}}
></InputFileComponent>
</div>
) : data.node.template[n].type === "prompt" ? (
) : myData.node.template[n].type === "prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
field_name={n}
editNode={true}
disabled={disabled}
nodeClass={data.node}
nodeClass={myData.node}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
myData.node = nodeClass;
}}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "code" ? (
) : myData.node.template[n].type === "code" ? (
<div className="mx-auto">
<CodeAreaComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "Any" ? (
) : myData.node.template[n].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
@ -263,7 +274,7 @@ const EditNodeModal = forwardRef(
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
enabled={!data.node.template[n].advanced}
enabled={!myData.node.template[n].advanced}
setEnabled={(e) => changeAdvanced(n)}
disabled={disabled}
size="small"
@ -284,6 +295,16 @@ const EditNodeModal = forwardRef(
<Button
className="mt-3"
onClick={() => {
setData(cloneDeep(myData)); //saves data with actual state of modal
setTabsState((prev) => {
return {
...prev,
[tabId]: {
...prev[tabId],
isPending: true,
},
};
});
setModalOpen(false);
}}
type="submit"

View file

@ -6,7 +6,7 @@ import { TabsContext } from "../../../../contexts/tabsContext";
import EditNodeModal from "../../../../modals/EditNodeModal";
import { classNames } from "../../../../utils";
export default function NodeToolbarComponent({ data, deleteNode }) {
export default function NodeToolbarComponent({ data, setData, deleteNode }) {
const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter(
(t) =>
@ -93,7 +93,11 @@ export default function NodeToolbarComponent({ data, deleteNode }) {
</ShadTooltip>
<ShadTooltip content="Edit" side="top">
<EditNodeModal data={data} nodeLength={nodeLength}>
<EditNodeModal
data={data}
setData={setData}
nodeLength={nodeLength}
>
<div
className={classNames(
"relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +

View file

@ -28,6 +28,7 @@ export type DropDownComponentType = {
};
export type ParameterComponentType = {
data: NodeDataType;
setData: (value: NodeDataType) => void;
title: string;
id: string;
color: string;