Spaces:
Sleeping
Sleeping
| import { memo, useEffect } from 'react'; | |
| import { useUpdateNodeInternals } from '@xyflow/react'; | |
| import NodeShell from '../components/NodeShell.jsx'; | |
| import { NodeDraftInput } from '../components/NodeDraftField.jsx'; | |
| import { useWorkflow } from '../context/WorkflowContext.jsx'; | |
| import { createBrowserId } from '../lib/ids.js'; | |
| import { getNodeAccent } from '../lib/nodeRegistry.js'; | |
| function JsonParserFlowNode({ id, data, selected, type }) { | |
| const { getNodeHandles, replaceNodeData, removeHandleConnections } = useWorkflow(); | |
| const updateNodeInternals = useUpdateNodeInternals(); | |
| const handles = getNodeHandles(type, data); | |
| const runtime = data.runtime || {}; | |
| const values = runtime.values || {}; | |
| useEffect(() => { | |
| updateNodeInternals(id); | |
| }, [id, data.extracts.length, updateNodeInternals]); | |
| const addExtract = () => { | |
| replaceNodeData(id, (current) => ({ | |
| ...current, | |
| extracts: [ | |
| ...current.extracts, | |
| { | |
| id: createBrowserId('json-extract'), | |
| label: `result ${current.extracts.length}`, | |
| path: '', | |
| fields: '', | |
| }, | |
| ], | |
| })); | |
| }; | |
| const updateExtract = (extractId, patch) => { | |
| replaceNodeData(id, (current) => ({ | |
| ...current, | |
| extracts: current.extracts.map((extract) => | |
| extract.id === extractId ? { ...extract, ...patch } : extract, | |
| ), | |
| })); | |
| }; | |
| const removeExtract = (extractId) => { | |
| if (data.extracts.length <= 1) { | |
| return; | |
| } | |
| removeHandleConnections(id, extractId, 'source'); | |
| replaceNodeData(id, (current) => ({ | |
| ...current, | |
| extracts: current.extracts.filter((extract) => extract.id !== extractId), | |
| })); | |
| }; | |
| const filledValuesCount = Object.values(values).filter(Boolean).length; | |
| return ( | |
| <NodeShell | |
| nodeId={id} | |
| title={data.title} | |
| accent={getNodeAccent(type)} | |
| selected={selected} | |
| status={runtime.status} | |
| inputs={handles.inputs} | |
| outputs={handles.outputs} | |
| > | |
| <div className="field-stack"> | |
| <div className="json-parser-rule-list"> | |
| {data.extracts.map((extract, index) => ( | |
| <div key={extract.id} className="json-parser-rule"> | |
| <div className="field-row"> | |
| <label className="field-stack"> | |
| <span className="field-label">Выход</span> | |
| <NodeDraftInput | |
| className="nodrag node-input" | |
| value={extract.label} | |
| placeholder={`результат ${index + 1}`} | |
| onCommit={(value) => updateExtract(extract.id, { label: value })} | |
| /> | |
| </label> | |
| <button | |
| type="button" | |
| className="nodrag chip__remove" | |
| onClick={() => removeExtract(extract.id)} | |
| disabled={data.extracts.length <= 1} | |
| > | |
| x | |
| </button> | |
| </div> | |
| <label className="field-stack"> | |
| <span className="field-label">Путь</span> | |
| <NodeDraftInput | |
| className="nodrag node-input" | |
| value={extract.path} | |
| placeholder="data.filter_group.items" | |
| onCommit={(value) => updateExtract(extract.id, { path: value })} | |
| /> | |
| </label> | |
| <label className="field-stack"> | |
| <span className="field-label">Поля</span> | |
| <NodeDraftInput | |
| className="nodrag node-input" | |
| value={extract.fields} | |
| placeholder="key, name" | |
| onCommit={(value) => updateExtract(extract.id, { fields: value })} | |
| /> | |
| </label> | |
| </div> | |
| ))} | |
| </div> | |
| <button type="button" className="nodrag node-button node-button--ghost" onClick={addExtract}> | |
| + Добавить извлечение | |
| </button> | |
| <div className="node-note"> | |
| {filledValuesCount > 0 | |
| ? `Извлечено выходов: ${filledValuesCount}.` | |
| : 'Путь использует dot notation. Поля опциональны и задаются через запятую.'} | |
| </div> | |
| {runtime.error ? <div className="node-error">{runtime.error}</div> : null} | |
| </div> | |
| </NodeShell> | |
| ); | |
| } | |
| export default memo(JsonParserFlowNode); | |