Fixes to textfield hooks

This commit is contained in:
Ian Arawjo 2023-05-01 21:11:50 -04:00
parent 0584600334
commit 2d519c2de3
3 changed files with 71 additions and 29 deletions

View File

@ -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 }) {
<Handle type="target" position="left" id={name} style={style} />
</div>)
});
};
}, [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 (
<div className="template-hooks">

View File

@ -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 = /(?<!\\){(.*?)(?<!\\)}/g; // gets all strs within braces {} that aren't escaped; e.g., ignores \{this\} but captures {this}
Object.keys(new_data['fields']).forEach((fieldId) => {
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 (
<div className="input-field" key={i}>
<textarea id={i} name={i} className="text-field-fixed nodrag" rows="3" cols="40" defaultValue={val} onChange={handleInputChange} />
</div>
)});
};
)}));
}, [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((
<div className="input-field" key={i}>
<textarea id={"f"+i} name={"f"+i} className="text-field-fixed nodrag" rows="3" cols="40" defaultValue={''} onChange={handleInputChange} />
</div>
));
setFields(f);
}
// Dynamically update the y-position of the template hook <Handle>s
const ref = useRef(null);
const [hooksY, setHooksY] = useState(120);
useEffect(() => {
const node_height = ref.current.clientHeight;
setHooksY(node_height + 70);
}, [fields]);
return (
<div className="text-fields-node">
<div className="node-header">
<NodeLabel title={data.title || 'TextFields Node'} nodeId={id} />
</div>
{fields}
<div ref={ref}>
{fields}
</div>
<Handle
type="source"
position="right"
id="output"
style={{ top: "50%", background: '#555' }}
/>
<TemplateHooks vars={templateVars} nodeId={id} startY={hooksY} />
<div className="add-text-field-btn">
<button onClick={handleAddField}>+</button>
</div>

View File

@ -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;
})