diff --git a/chain-forge/src/TemplateHooksComponent.js b/chain-forge/src/TemplateHooksComponent.js
index 3d3458a..8589f47 100644
--- a/chain-forge/src/TemplateHooksComponent.js
+++ b/chain-forge/src/TemplateHooksComponent.js
@@ -1,10 +1,10 @@
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { Handle } from 'react-flow-renderer';
import useStore from './store'
export default function TemplateHooks({ vars, nodeId, startY }) {
- const genTemplateHooks = (temp_var_names, names_to_blink) => {
+ const genTemplateHooks = useCallback((temp_var_names, names_to_blink) => {
return temp_var_names.map((name, idx) => {
const className = (names_to_blink.includes(name)) ? 'text-blink' : '';
const pos = (idx * 35) + startY + 'px';
@@ -14,7 +14,7 @@ export default function TemplateHooks({ vars, nodeId, startY }) {
)
});
- };
+ }, [startY]);
const [templateHooks, setTemplateHooks] = useState(genTemplateHooks(vars || [], []));
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);
@@ -28,8 +28,8 @@ export default function TemplateHooks({ vars, nodeId, startY }) {
useEffect(() => {
setTemplateHooks(genTemplateHooks(vars, []));
- setDataPropsForNode(nodeId, {vars: vars});
- }, [vars]);
+ // setDataPropsForNode(nodeId, {vars: vars});
+ }, [vars, startY, genTemplateHooks, nodeId, setDataPropsForNode]);
return (
diff --git a/chain-forge/src/TextFieldsNode.js b/chain-forge/src/TextFieldsNode.js
index 47c4f61..474ad98 100644
--- a/chain-forge/src/TextFieldsNode.js
+++ b/chain-forge/src/TextFieldsNode.js
@@ -1,56 +1,98 @@
-import React, { useState } from 'react';
+import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Handle } from 'react-flow-renderer';
import useStore from './store';
import NodeLabel from './NodeLabelComponent'
+import TemplateHooks from './TemplateHooksComponent';
+
+const union = (setA, setB) => {
+ const _union = new Set(setA);
+ for (const elem of setB) {
+ _union.add(elem);
+ }
+ return _union;
+}
const TextFieldsNode = ({ data, id }) => {
+ const [templateVars, setTemplateVars] = useState(data.vars || []);
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);
- const handleInputChange = (event) => {
- // Update the data for this text fields' id.
- if (!data.fields)
- data.fields = {}
- data['fields'][event.target.id] = event.target.value;
- let new_data = { fields: {...data.fields} };
+ // Handle a change in a text fields' input.
+ const handleInputChange = useCallback((event) => {
+ // Update the data for this text fields' id.
+ let new_data = { 'fields': {...data.fields} };
+ new_data.fields[event.target.id] = event.target.value;
setDataPropsForNode(id, new_data);
- }
- const createInitFields = () => {
+ // TODO: Optimize this check.
+ let all_found_vars = new Set();
+ const braces_regex = /(? {
+ let found_vars = new_data['fields'][fieldId].match(braces_regex);
+ if (found_vars && found_vars.length > 0) {
+ found_vars = found_vars.map(name => name.substring(1, name.length-1)); // remove brackets {}
+ all_found_vars = union(all_found_vars, new Set(found_vars));
+ }
+ });
+
+ // Update template var fields + handles, if there's a change in sets
+ const past_vars = new Set(templateVars);
+ if (all_found_vars !== past_vars) {
+ setTemplateVars(Array.from(all_found_vars));
+ }
+ }, [data, id, setDataPropsForNode, templateVars]);
+
+ // Initialize fields (run once at init)
+ const [fields, setFields] = useState([]);
+ useEffect(() => {
+ if (!data.fields)
+ setDataPropsForNode(id, { fields: {f0: ''}} );
+ }, []);
+
+ // Whenever 'data' changes, update the input fields to reflect the current state.
+ useEffect(() => {
const f = data.fields ? Object.keys(data.fields) : ['f0'];
- return f.map((i, idx) => {
+ setFields(f.map((i) => {
const val = data.fields ? data.fields[i] : '';
return (
- )});
- };
+ )}));
+ }, [data.fields, handleInputChange]);
- const [fields, setFields] = useState(createInitFields());
+ // Add a field
+ const handleAddField = useCallback(() => {
+ // Update the data for this text fields' id.
+ const num_fields = data.fields ? Object.keys(data.fields).length : 0;
+ let new_data = { 'fields': {...data.fields} };
+ new_data.fields['f'+num_fields.toString()] = "";
+ setDataPropsForNode(id, new_data);
+ }, [data, id, setDataPropsForNode]);
- const handleAddField = (event) => {
- const i = fields.length;
- const f = fields.concat((
-
-
-
- ));
- setFields(f);
- }
+ // Dynamically update the y-position of the template hook
s
+ const ref = useRef(null);
+ const [hooksY, setHooksY] = useState(120);
+ useEffect(() => {
+ const node_height = ref.current.clientHeight;
+ setHooksY(node_height + 70);
+ }, [fields]);
return (
- {fields}
+
+ {fields}
+
+
diff --git a/chain-forge/src/store.js b/chain-forge/src/store.js
index cd20ac5..0354426 100644
--- a/chain-forge/src/store.js
+++ b/chain-forge/src/store.js
@@ -74,7 +74,7 @@ const useStore = create((set, get) => ({
if (n.id === id) {
for (const key of Object.keys(data_props))
n.data[key] = data_props[key];
- n.data = {...n.data};
+ n.data = JSON.parse(JSON.stringify(n.data)); // deep copy json
}
return n;
})