// Draggable node with avatar function Node({ node, selected, onSelect, onDrag, onDragEnd, onStartConnect, onFinishConnect, onDelete, dragging, worldZoom }){ const ref = React.useRef(null); const moveState = React.useRef(null); const onPointerDown = (e) => { if (e.target.closest('.connect-handle')) return; if (e.target.closest('.delete-btn')) return; e.stopPropagation(); onSelect(node.id); const startX = e.clientX, startY = e.clientY; const ox = node.x, oy = node.y; moveState.current = { startX, startY, ox, oy, moved:false }; e.currentTarget.setPointerCapture(e.pointerId); }; const onPointerMove = (e) => { if (!moveState.current) return; const { startX, startY, ox, oy } = moveState.current; const dx = (e.clientX - startX) / worldZoom; const dy = (e.clientY - startY) / worldZoom; if (Math.abs(dx)+Math.abs(dy) > 2) moveState.current.moved = true; onDrag(node.id, ox+dx, oy+dy); }; const onPointerUp = (e) => { if (!moveState.current) return; const moved = moveState.current.moved; moveState.current = null; onDragEnd(node.id, moved); try { e.currentTarget.releasePointerCapture(e.pointerId); } catch(_){} }; const handleConnectDown = (e) => { e.stopPropagation(); e.preventDefault(); onStartConnect(node.id, e); }; const handleFinishUp = (e) => { // If currently connecting, finish here. Parent handles routing on pointerup too. onFinishConnect(node.id); }; const shapeClass = node.shape || 'circle'; const avatarStyle = { background: node.color || '#fff' }; let inner; if (node.image){ inner = ; } else if (node.emoji){ inner =
{node.emoji}
; } else { const ini = (node.name||'?').trim().charAt(0).toUpperCase(); inner =
{ini}
; } return (
{inner}
{(node.name || node.subtitle) ? (
{node.name ?
{node.name}
: null} {node.subtitle ?
{node.subtitle}
: null}
) : null}
{e.stopPropagation(); onDelete(node.id);}} title="削除" >×
); } function contrastInk(bg){ if (!bg) return '#fff'; const m = bg.match(/^#([0-9a-f]{6})$/i); if (!m) return '#fff'; const r = parseInt(m[1].slice(0,2),16); const g = parseInt(m[1].slice(2,4),16); const b = parseInt(m[1].slice(4,6),16); const L = (0.299*r+0.587*g+0.114*b)/255; return L>0.6 ? '#3a2c3a' : '#fff'; } window.Node = Node; window.contrastInk = contrastInk;