83 lines
2.6 KiB
TypeScript
Executable File
83 lines
2.6 KiB
TypeScript
Executable File
import { Handle, NodeProps, Position, useEdges } from '@xyflow/react';
|
|
import { MindMapNodeType } from './types';
|
|
import useMindMapStore, { RFState } from './store';
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import { useShallow } from 'zustand/shallow';
|
|
import { useClickOutside } from '../../hooks/useClickOutside';
|
|
import { useHotkeys } from 'react-hotkeys-hook';
|
|
const selector = (state: RFState) => ({
|
|
selectedNodeId: state.selectedNodeId,
|
|
editingNodeId: state.editingNodeId,
|
|
updateNodeLabel: state.updateNodeLabel,
|
|
setSelectedNodeId: state.setSelectedNodeId,
|
|
setEditingNodeId: state.setEditingNodeId
|
|
|
|
});
|
|
function MindMapNode({ id, data }: NodeProps<MindMapNodeType>) {
|
|
const nodeRef = useRef<HTMLDivElement>(null);
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
const [inputValue, setInputValue] = useState(data.label);
|
|
|
|
const {
|
|
updateNodeLabel,
|
|
selectedNodeId,
|
|
setSelectedNodeId,
|
|
setEditingNodeId,
|
|
editingNodeId
|
|
} = useMindMapStore(useShallow(selector));
|
|
useEffect(() => {
|
|
if (editingNodeId === id) {
|
|
setEditingNodeId(id);
|
|
setInputValue(data.label);
|
|
setTimeout(() => {
|
|
inputRef.current?.focus();
|
|
inputRef.current?.select();
|
|
}, 0);
|
|
} else {
|
|
inputRef.current?.blur()
|
|
}
|
|
}, [editingNodeId])
|
|
const handleDoubleClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
setEditingNodeId(id)
|
|
};
|
|
|
|
useHotkeys("space", (e) => {
|
|
if (selectedNodeId === id)
|
|
setEditingNodeId(id)
|
|
}, { preventDefault: true });
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
setSelectedNodeId(id);
|
|
};
|
|
|
|
useClickOutside(nodeRef, () => {
|
|
console.log(selectedNodeId, id)
|
|
if (selectedNodeId === id)
|
|
setSelectedNodeId(null)
|
|
if (editingNodeId === id) {
|
|
setEditingNodeId(null)
|
|
updateNodeLabel(id, inputValue)
|
|
}
|
|
});
|
|
|
|
return (
|
|
<div
|
|
ref={nodeRef}
|
|
onDoubleClick={handleDoubleClick}
|
|
onClick={handleClick}
|
|
className={`mindmap-node ${id === selectedNodeId ? 'selected' : ''}`}
|
|
>
|
|
<input
|
|
ref={inputRef}
|
|
value={inputValue}
|
|
onChange={(e) => setInputValue(e.target.value)}
|
|
className="input"
|
|
readOnly={id !== editingNodeId}
|
|
/>
|
|
<Handle type="target" position={Position.Top} />
|
|
<Handle type="source" position={Position.Top} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default MindMapNode; |