From b04f22ea22a023ac9e59c33f99b7a4f628d054ae Mon Sep 17 00:00:00 2001 From: matthieu Date: Wed, 29 Oct 2025 16:28:40 +0100 Subject: [PATCH] [ISSUE-1] Add src/components/nodes/VariablesNode.tsx - Modular NPC system --- src/components/nodes/VariablesNode.tsx | 211 +++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/components/nodes/VariablesNode.tsx diff --git a/src/components/nodes/VariablesNode.tsx b/src/components/nodes/VariablesNode.tsx new file mode 100644 index 0000000..a13f2c0 --- /dev/null +++ b/src/components/nodes/VariablesNode.tsx @@ -0,0 +1,211 @@ +import { memo, useState } from 'react'; +import { Handle, Position } from '@xyflow/react'; +import type { NodeProps } from '@xyflow/react'; +import { Code, Plus, Trash2, ChevronDown, ChevronUp } from 'lucide-react'; +import type { VariablesNodeData } from '../../types/nodes'; +import type { MoLangConfigVariable } from '../../types/npc'; + +export const VariablesNode = memo(({ data }: NodeProps) => { + const [isExpanded, setIsExpanded] = useState(true); + const nodeData = data as VariablesNodeData; + + const handleChange = (variables: MoLangConfigVariable[]) => { + Object.assign(nodeData, { configVariables: variables }); + }; + + const addVariable = () => { + const newVariable: MoLangConfigVariable = { + variableName: `var_${Date.now()}`, + displayName: 'New Variable', + description: 'Description', + type: 'NUMBER', + defaultValue: 0, + }; + handleChange([...(nodeData.configVariables || []), newVariable]); + }; + + const removeVariable = (index: number) => { + const newVariables = [...(nodeData.configVariables || [])]; + newVariables.splice(index, 1); + handleChange(newVariables); + }; + + const updateVariable = ( + index: number, + field: keyof MoLangConfigVariable, + value: string | number | boolean + ) => { + const newVariables = [...(nodeData.configVariables || [])]; + newVariables[index] = { ...newVariables[index], [field]: value }; + handleChange(newVariables); + }; + + return ( +
+ {/* Node Header */} +
+
+ + Config Variables +
+ +
+ + {/* Input Handle */} + + + {/* Node Content */} + {isExpanded && ( +
+
+ + Variables ({nodeData.configVariables?.length || 0}) + + +
+ +
+ {(!nodeData.configVariables || nodeData.configVariables.length === 0) && ( +
+ No variables defined +
+ )} + + {nodeData.configVariables?.map((variable, index) => ( +
+
+ + Variable {index + 1} + + +
+ +
+ + + updateVariable(index, 'variableName', e.target.value) + } + className="w-full px-2 py-1 text-xs border border-gray-300 rounded focus:ring-2 focus:ring-yellow-500 focus:border-yellow-500" + placeholder="my_variable" + /> +
+ +
+ + + updateVariable(index, 'displayName', e.target.value) + } + className="w-full px-2 py-1 text-xs border border-gray-300 rounded focus:ring-2 focus:ring-yellow-500 focus:border-yellow-500" + placeholder="My Variable" + /> +
+ +
+ + +
+ +
+ + {variable.type === 'BOOLEAN' ? ( + + ) : ( + + updateVariable( + index, + 'defaultValue', + variable.type === 'NUMBER' + ? parseFloat(e.target.value) || 0 + : e.target.value + ) + } + className="w-full px-2 py-1 text-xs border border-gray-300 rounded focus:ring-2 focus:ring-yellow-500 focus:border-yellow-500" + /> + )} +
+
+ ))} +
+
+ )} + + {/* Output Handle */} + +
+ ); +}); + +VariablesNode.displayName = 'VariablesNode';