import React, { useState, useEffect } from 'react'; import { SimulationMap, MapNode, MagnetLine, FloorMark } from '../types'; import { Save, Wand2 } from 'lucide-react'; interface PropertyPanelProps { selectedItemIds: Set; mapData: SimulationMap; onUpdate: (type: 'NODE' | 'MAGNET' | 'MARK', id: string, data: any) => void; } const PropertyPanel: React.FC = ({ selectedItemIds, mapData, onUpdate }) => { const [formData, setFormData] = useState(null); const [position, setPosition] = useState({ x: 1100, y: 100 }); const [isDragging, setIsDragging] = useState(false); const dragOffset = React.useRef({ x: 0, y: 0 }); // When selection changes, update form data useEffect(() => { if (selectedItemIds.size === 1) { const id = Array.from(selectedItemIds)[0]; const node = mapData.nodes.find(n => n.id === id); const magnet = mapData.magnets.find(m => m.id === id); const mark = mapData.marks.find(m => m.id === id); if (node) { setFormData({ ...node, formType: 'NODE' }); } else if (magnet) { setFormData({ ...magnet, formType: 'MAGNET' }); } else if (mark) { setFormData({ ...mark, formType: 'MARK' }); } else { setFormData(null); } } else { setFormData(null); } }, [selectedItemIds, mapData.nodes, mapData.magnets, mapData.marks]); const handleChange = (field: string, value: string | number | boolean) => { if (!formData) return; setFormData((prev: any) => ({ ...prev, [field]: value })); }; const handleSave = () => { if (!formData) return; // Validate RFID Duplication if (formData.formType === 'NODE' && formData.rfidId && formData.rfidId !== '0000') { const isDuplicate = mapData.nodes.some(n => n.rfidId === formData.rfidId && n.id !== formData.id ); if (isDuplicate) { alert(`Error: RFID "${formData.rfidId}" is already used by another node.`); return; } } onUpdate(formData.formType, formData.id, formData); }; // Auto Generate RFID const handleAutoRfid = () => { const usedRfids = new Set(mapData.nodes.map(n => n.rfidId)); let nextRfid = 1; // Start from 1, assuming 0000 is default/null while (nextRfid < 10000) { const candidate = nextRfid.toString().padStart(4, '0'); if (!usedRfids.has(candidate)) { handleChange('rfidId', candidate); return; } nextRfid++; } alert('No available RFID found between 0001 and 9999'); }; // Drag Logic const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true); dragOffset.current = { x: e.clientX - position.x, y: e.clientY - position.y }; }; const handleMouseMove = React.useCallback((e: MouseEvent) => { if (isDragging) { setPosition({ x: e.clientX - dragOffset.current.x, y: e.clientY - dragOffset.current.y }); } }, [isDragging]); const handleMouseUp = React.useCallback(() => { setIsDragging(false); }, []); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } else { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging, handleMouseMove, handleMouseUp]); if (!formData || selectedItemIds.size !== 1) return null; return (
{/* Header / Drag Handle */}
Properties
{/* Common Fields */}
{formData.formType === 'NODE' && ( <>
handleChange('name', e.target.value)} className="bg-gray-700 border border-gray-600 text-gray-200 text-sm p-1 rounded focus:border-blue-500 outline-none" />
handleChange('rfidId', e.target.value)} className="flex-1 bg-gray-700 border border-gray-600 text-gray-200 text-sm p-1 rounded focus:border-blue-500 outline-none font-mono" />
handleChange('x', parseFloat(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-sm p-1 rounded focus:border-blue-500 outline-none" />
handleChange('y', parseFloat(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-sm p-1 rounded focus:border-blue-500 outline-none" />
handleChange('stationType', parseInt(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-xs p-1 rounded" />
handleChange('speedLimit', parseInt(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-xs p-1 rounded" />
handleChange('dockDirection', parseInt(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-xs p-1 rounded" />
handleChange('nodeTextFontSize', parseInt(e.target.value))} className="bg-gray-700 border border-gray-600 text-gray-200 text-xs p-1 rounded" />
handleChange('nodeTextForeColor', e.target.value)} className="bg-transparent w-6 h-6 p-0 border-0" /> handleChange('nodeTextForeColor', e.target.value)} className="flex-1 bg-gray-700 border border-gray-600 text-gray-200 text-xs p-1 rounded font-mono" />
{[ { k: 'canDocking', l: 'Can Docking' }, { k: 'canTurnLeft', l: 'Can Turn Left' }, { k: 'canTurnRight', l: 'Can Turn Right' }, { k: 'disableCross', l: 'Disable Cross' }, { k: 'isActive', l: 'Is Active' }, ].map((item) => ( ))}
)}
); }; export default PropertyPanel;