2025-11-03 21:22:16 +08:00
|
|
|
import { useState, RefObject } from 'react';
|
|
|
|
|
import { NodeTemplate, NodeType } from '@esengine/behavior-tree';
|
2025-11-18 14:46:51 +08:00
|
|
|
import { Position } from '../domain/value-objects/Position';
|
2025-11-03 21:22:16 +08:00
|
|
|
import { useNodeOperations } from './useNodeOperations';
|
2025-11-18 14:46:51 +08:00
|
|
|
import { createLogger } from '@esengine/ecs-framework';
|
|
|
|
|
|
|
|
|
|
const logger = createLogger('useDropHandler');
|
2025-11-03 21:22:16 +08:00
|
|
|
|
|
|
|
|
interface DraggedVariableData {
|
|
|
|
|
variableName: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface UseDropHandlerParams {
|
|
|
|
|
canvasRef: RefObject<HTMLDivElement>;
|
|
|
|
|
canvasOffset: { x: number; y: number };
|
|
|
|
|
canvasScale: number;
|
|
|
|
|
nodeOperations: ReturnType<typeof useNodeOperations>;
|
|
|
|
|
onNodeCreate?: (template: NodeTemplate, position: { x: number; y: number }) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useDropHandler(params: UseDropHandlerParams) {
|
|
|
|
|
const {
|
|
|
|
|
canvasRef,
|
|
|
|
|
canvasOffset,
|
|
|
|
|
canvasScale,
|
|
|
|
|
nodeOperations,
|
|
|
|
|
onNodeCreate
|
|
|
|
|
} = params;
|
|
|
|
|
|
|
|
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleDrop = (e: React.DragEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
setIsDragging(false);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const rect = canvasRef.current?.getBoundingClientRect();
|
|
|
|
|
if (!rect) return;
|
|
|
|
|
|
|
|
|
|
const position = {
|
|
|
|
|
x: (e.clientX - rect.left - canvasOffset.x) / canvasScale,
|
|
|
|
|
y: (e.clientY - rect.top - canvasOffset.y) / canvasScale
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const blackboardVariableData = e.dataTransfer.getData('application/blackboard-variable');
|
|
|
|
|
if (blackboardVariableData) {
|
|
|
|
|
const variableData = JSON.parse(blackboardVariableData) as DraggedVariableData;
|
|
|
|
|
|
|
|
|
|
const variableTemplate: NodeTemplate = {
|
|
|
|
|
type: NodeType.Action,
|
|
|
|
|
displayName: variableData.variableName,
|
|
|
|
|
category: 'Blackboard Variable',
|
|
|
|
|
icon: 'Database',
|
|
|
|
|
description: `Blackboard variable: ${variableData.variableName}`,
|
|
|
|
|
color: '#9c27b0',
|
|
|
|
|
defaultConfig: {
|
|
|
|
|
nodeType: 'blackboard-variable',
|
|
|
|
|
variableName: variableData.variableName
|
|
|
|
|
},
|
|
|
|
|
properties: [
|
|
|
|
|
{
|
|
|
|
|
name: 'variableName',
|
|
|
|
|
label: '变量名',
|
|
|
|
|
type: 'variable',
|
|
|
|
|
defaultValue: variableData.variableName,
|
|
|
|
|
description: '黑板变量的名称',
|
|
|
|
|
required: true
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
nodeOperations.createNode(
|
|
|
|
|
variableTemplate,
|
|
|
|
|
new Position(position.x, position.y),
|
|
|
|
|
{
|
|
|
|
|
nodeType: 'blackboard-variable',
|
|
|
|
|
variableName: variableData.variableName
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let templateData = e.dataTransfer.getData('application/behavior-tree-node');
|
|
|
|
|
if (!templateData) {
|
|
|
|
|
templateData = e.dataTransfer.getData('text/plain');
|
|
|
|
|
}
|
|
|
|
|
if (!templateData) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const template = JSON.parse(templateData) as NodeTemplate;
|
|
|
|
|
|
|
|
|
|
nodeOperations.createNode(
|
|
|
|
|
template,
|
|
|
|
|
new Position(position.x, position.y),
|
|
|
|
|
template.defaultConfig
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
onNodeCreate?.(template, position);
|
|
|
|
|
} catch (error) {
|
2025-11-18 14:46:51 +08:00
|
|
|
logger.error('Failed to create node:', error);
|
2025-11-03 21:22:16 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragOver = (e: React.DragEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
e.dataTransfer.dropEffect = 'copy';
|
|
|
|
|
if (!isDragging) {
|
|
|
|
|
setIsDragging(true);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragLeave = (e: React.DragEvent) => {
|
|
|
|
|
if (e.currentTarget === e.target) {
|
|
|
|
|
setIsDragging(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragEnter = (e: React.DragEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isDragging,
|
|
|
|
|
handleDrop,
|
|
|
|
|
handleDragOver,
|
|
|
|
|
handleDragLeave,
|
|
|
|
|
handleDragEnter
|
|
|
|
|
};
|
|
|
|
|
}
|