{/* Toolbar */}
setSearchQuery(e.target.value)}
/>
{!isShowingRemote && (
<>
>
)}
{isRemoteConnected && !isProfilerMode && (
)}
{showRemoteIndicator && (
)}
{/* Column Headers */}
handleSortClick('name')}
>
Item Label
{sortColumn === 'name' && (
{sortDirection === 'asc' ? '▲' : '▼'}
)}
handleSortClick('type')}
>
Type
{sortColumn === 'type' && (
{sortDirection === 'asc' ? '▲' : '▼'}
)}
{/* Entity List */}
!isShowingRemote && handleContextMenu(e, null)}>
{isShowingRemote ? (
// Remote entities view (flat list)
remoteEntities.length === 0 ? (
{locale === 'zh' ? '远程游戏中没有实体' : 'No entities in remote game'}
) : (
{filterRemoteEntities(remoteEntities).map((entity) => (
handleRemoteEntityClick(entity)}
>
{entityTypeIcons[entity.componentTypes?.[0] || 'Entity'] || }
{entity.name}
{entity.componentTypes?.[0] || 'Entity'}
))}
)
) : (
// Local entities view (hierarchical tree)
entities.length === 0 ? (
{locale === 'zh' ? '创建实体开始使用' : 'Create an entity to get started'}
) : (
{/* World/Scene Root */}
toggleExpand(-1)}
>
{ e.stopPropagation(); toggleExpand(-1); }}
>
{expandedIds.has(-1) ? : }
{displaySceneName} (Editor)
World
{/* Hierarchical Entity Items */}
{flattenedEntities.map((node) => {
const { entity, depth, hasChildren, bIsFolder } = node;
const bIsExpanded = expandedIds.has(entity.id);
const bIsSelected = selectedIds.has(entity.id);
const bIsDragging = draggedEntityId === entity.id;
const currentDropTarget = dropTarget?.entityId === entity.id ? dropTarget : null;
// 计算缩进 (每层 16px,加上基础 8px)
const indent = 8 + depth * 16;
// 构建 drop indicator 类名
let dropIndicatorClass = '';
if (currentDropTarget) {
dropIndicatorClass = `drop-${currentDropTarget.indicator}`;
}
return (
handleEntityClick(entity, e)}
onDragStart={(e) => handleDragStart(e, entity.id)}
onDragOver={(e) => handleDragOver(e, node)}
onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e, node)}
onDragEnd={handleDragEnd}
onContextMenu={(e) => {
e.stopPropagation();
handleEntityClick(entity, e);
handleContextMenu(e, entity.id);
}}
>
{/* 展开/折叠按钮 */}
{hasChildren || bIsFolder ? (
{ e.stopPropagation(); toggleExpand(entity.id); }}
>
{bIsExpanded ? : }
) : (
)}
{getEntityIcon(entity)}
{entity.name || `Entity ${entity.id}`}
{getEntityType(entity)}
);
})}
)
)}
{/* Status Bar */}
{totalCount} {locale === 'zh' ? '个对象' : 'actors'}
{selectedCount > 0 && (
({selectedCount} {locale === 'zh' ? '个已选中' : 'selected'})
)}
{contextMenu && !isShowingRemote && (
{ handleCreateEntity(); closeContextMenu(); }}
onCreateFolder={() => { handleCreateFolder(); closeContextMenu(); }}
onCreateFromTemplate={async (template) => {
await template.create(contextMenu.entityId ?? undefined);
closeContextMenu();
}}
onDelete={() => { handleDeleteEntity(); closeContextMenu(); }}
onClose={closeContextMenu}
/>
)}
);
}
interface ContextMenuWithSubmenuProps {
x: number;
y: number;
locale: string;
entityId: number | null;
pluginTemplates: EntityCreationTemplate[];
onCreateEmpty: () => void;
onCreateFolder: () => void;
onCreateFromTemplate: (template: EntityCreationTemplate) => void;
onDelete: () => void;
onClose: () => void;
}
function ContextMenuWithSubmenu({
x, y, locale, entityId, pluginTemplates,
onCreateEmpty, onCreateFolder, onCreateFromTemplate, onDelete
}: ContextMenuWithSubmenuProps) {
const [activeSubmenu, setActiveSubmenu] = useState