diff --git a/.gitignore b/.gitignore index f39dd6c..37d64ab 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__ python-backend/cache # venv -venv \ No newline at end of file +venv +node_modules diff --git a/chain-forge/src/App.css b/chain-forge/src/App.css index 6071c72..47b2fab 100644 --- a/chain-forge/src/App.css +++ b/chain-forge/src/App.css @@ -45,3 +45,8 @@ path.react-flow__edge-path:hover { transform: rotate(360deg); } } + +.rich-editor { + min-width: 500px; + min-height: 500px; +} \ No newline at end of file diff --git a/chain-forge/src/App.js b/chain-forge/src/App.js index 50698a2..72c8725 100644 --- a/chain-forge/src/App.js +++ b/chain-forge/src/App.js @@ -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 = () => { + diff --git a/chain-forge/src/CsvNode.js b/chain-forge/src/CsvNode.js new file mode 100644 index 0000000..1d2c437 --- /dev/null +++ b/chain-forge/src/CsvNode.js @@ -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({e}) + html.push({e}); + if (idx < elements.length - 1) { + html.push(,); + } + }); + + setContentDiv(
+ {html} +
); + setCountText({elements.length} elements); + }, [data.text, handleDivOnClick]); + + // When isEditing changes, add input + useEffect(() => { + if (!isEditing) { + setCsvInput(null); + renderCsvDiv(); + return; + } + if (!csvInput) { + var text_val = data.text || ''; + setCsvInput( +
+