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 (