Merge pull request #32 from ianarawjo/pv

Adding CSVNode
This commit is contained in:
Priyan Vaithilingam 2023-05-10 13:15:23 -04:00 committed by GitHub
commit eced759218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 177 additions and 2 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ __pycache__
python-backend/cache
# venv
venv
venv
node_modules

View File

@ -45,3 +45,8 @@ path.react-flow__edge-path:hover {
transform: rotate(360deg);
}
}
.rich-editor {
min-width: 500px;
min-height: 500px;
}

View File

@ -15,6 +15,7 @@ import EvaluatorNode from './EvaluatorNode';
import VisNode from './VisNode';
import InspectNode from './InspectorNode';
import ScriptNode from './ScriptNode';
import CsvNode from './CsvNode';
import './text-fields-node.css';
// State management (from https://reactflow.dev/docs/guides/state-management/)
@ -40,7 +41,8 @@ const nodeTypes = {
evaluator: EvaluatorNode,
vis: VisNode,
inspect: InspectNode,
script: ScriptNode
script: ScriptNode,
csv: CsvNode,
};
const connectionLineStyle = { stroke: '#ddd' };
@ -91,6 +93,11 @@ const App = () => {
addNode({ id: 'scriptNode-'+Date.now(), type: 'script', data: {}, position: {x: x-200, y:y-100} });
};
const addCsvNode = (event) => {
const { x, y } = getViewportCenter();
addNode({ id: 'csvNode-'+Date.now(), type: 'csv', data: {}, position: {x: x-200, y:y-100} });
};
/**
* SAVING / LOADING, IMPORT / EXPORT (from JSON)
*/
@ -201,6 +208,7 @@ const App = () => {
<button onClick={addVisNode}>Add vis node</button>
<button onClick={addInspectNode}>Add inspect node</button>
<button onClick={addScriptNode}>Add script node</button>
<button onClick={addCsvNode}>Add csv node</button>
<button onClick={saveFlow} style={{marginLeft: '12px'}}>Save</button>
<button onClick={loadFlowFromCache}>Load</button>
<button onClick={exportFlow} style={{marginLeft: '12px'}}>Export</button>

129
chain-forge/src/CsvNode.js Normal file
View File

@ -0,0 +1,129 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Badge, Text } from '@mantine/core';
import useStore from './store';
import NodeLabel from './NodeLabelComponent'
import { IconCsv } from '@tabler/icons-react';
import { Handle } from 'react-flow-renderer';
const CsvNode = ({ data, id }) => {
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);
const [contentDiv, setContentDiv] = useState(null);
const [isEditing, setIsEditing] = useState(true);
const [csvInput, setCsvInput] = useState(null);
const [countText, setCountText] = useState(null);
// initializing
useEffect(() => {
if (!data.fields) {
setDataPropsForNode(id, { text: '', fields: [] });
}
}, []);
const processCsv = (csv) => {
var matches = csv.match(/(\s*"[^"]+"\s*|\s*[^,]+|,)(?=,|$)/g);
for (var n = 0; n < matches.length; ++n) {
matches[n] = matches[n].trim();
if (matches[n] == ',') matches[n] = '';
}
return matches.map(e => e.trim()).filter(e => e.length > 0);
}
// const processCsv = (csv) => {
// if (!csv) return [];
// // Split the input string by rows, and merge
// var res = csv.split('\n').join(',');
// // remove all the empty or whitespace-only elements
// return res.split(',').map(e => e.trim()).filter(e => e.length > 0);
// }
// Handle a change in a text fields' input.
const handleInputChange = useCallback((event) => {
// Update the data for this text fields' id.
let new_data = { 'text': event.target.value, 'fields': processCsv(event.target.value) };
setDataPropsForNode(id, new_data);
}, [id, setDataPropsForNode]);
const handKeyDown = useCallback((event) => {
if (event.key === 'Enter') {
setIsEditing(false);
setCsvInput(null);
}
}, []);
// handling Div Click
const handleDivOnClick = useCallback((event) => {
setIsEditing(true);
}, []);
const handleOnBlur = useCallback((event) => {
setIsEditing(false);
setCsvInput(null);
}, []);
// render csv div
const renderCsvDiv = useCallback(() => {
// Take the data.text as csv (only 1 row), and get individual elements
const elements = data.fields;
// generate a HTML code that highlights the elements
const html = [];
elements.forEach((e, idx) => {
// html.push(<Badge color="orange" size="lg" radius="sm">{e}</Badge>)
html.push(<span key={idx} className="csv-element">{e}</span>);
if (idx < elements.length - 1) {
html.push(<span key={idx + 'comma'} className="csv-comma">,</span>);
}
});
setContentDiv(<div className='csv-div nowheel' onClick={handleDivOnClick}>
{html}
</div>);
setCountText(<Text size="xs" style={{ marginTop: '5px' }} color='blue' align='right'>{elements.length} elements</Text>);
}, [data.text, handleDivOnClick]);
// When isEditing changes, add input
useEffect(() => {
if (!isEditing) {
setCsvInput(null);
renderCsvDiv();
return;
}
if (!csvInput) {
var text_val = data.text || '';
setCsvInput(
<div className="input-field" key={id}>
<textarea id={id} name={id} className="text-field-fixed nodrag csv-input" rows="2" cols="40" defaultValue={text_val} onChange={handleInputChange} placeholder='Paste your CSV text here' onKeyDown={handKeyDown} onBlur={handleOnBlur} autoFocus={true}/>
</div>
);
setContentDiv(null);
setCountText(null);
}
}, [isEditing]);
// when data.text changes, update the content div
useEffect(() => {
// When in editing mode, don't update the content div
if (isEditing) return;
if (!data.text) return;
renderCsvDiv();
}, [id, data.text]);
return (
<div className="text-fields-node cfnode">
<NodeLabel title={data.title || 'CSV Node'} nodeId={id} icon={<IconCsv size="16px" />} />
{csvInput}
{contentDiv}
{countText ? countText : <></>}
<Handle
type="source"
position="right"
id="output"
style={{ top: "50%", background: '#555' }}
/>
</div>
);
};
export default CsvNode;

View File

@ -21,4 +21,36 @@ code {
.script-node-input {
min-width: 300px;
}
.csv-element {
position: relative;
color: #8a3e07;
background-color: #FFE8CC;
font-size: inherit;
font-family: monospace;
padding: .2em .4em;
border: none;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border-radius: 10px;
}
/* set a muted text */
.csv-comma {
color: #FFC107;
}
.csv-div {
width: 350px;
max-height: 250px;
overflow-y: auto;
}
.csv-input {
width: 350px;
height: 150px;
}