Files
esengine/packages/editor-app/src/components/FlexLayoutDockContainer.tsx
YHH 009f8af4e1 Feature/ecs behavior tree (#188)
* feat(behavior-tree): 完全 ECS 化的行为树系统

* feat(editor-app): 添加行为树可视化编辑器

* chore: 移除 Cocos Creator 扩展目录

* feat(editor-app): 行为树编辑器功能增强

* fix(editor-app): 修复 TypeScript 类型错误

* feat(editor-app): 使用 FlexLayout 重构面板系统并优化资产浏览器

* feat(editor-app): 改进编辑器UI样式并修复行为树执行顺序

* feat(behavior-tree,editor-app): 添加装饰器系统并优化编辑器性能

* feat(behavior-tree,editor-app): 添加属性绑定系统

* feat(editor-app,behavior-tree): 优化编辑器UI并改进行为树功能

* feat(editor-app,behavior-tree): 添加全局黑板系统并增强资产浏览器功能

* feat(behavior-tree,editor-app): 添加运行时资产导出系统

* feat(behavior-tree,editor-app): 添加SubTree系统和资产选择器

* feat(behavior-tree,editor-app): 优化系统架构并改进编辑器文件管理

* fix(behavior-tree,editor-app): 修复SubTree节点错误显示空节点警告

* fix(editor-app): 修复局部黑板类型定义文件扩展名错误
2025-10-27 09:29:11 +08:00

162 lines
4.5 KiB
TypeScript

import { useRef, useCallback, ReactNode, useMemo } from 'react';
import { Layout, Model, TabNode, IJsonModel, Actions, IJsonTabSetNode, IJsonRowNode } from 'flexlayout-react';
import 'flexlayout-react/style/light.css';
import '../styles/FlexLayoutDock.css';
export interface FlexDockPanel {
id: string;
title: string;
content: ReactNode;
closable?: boolean;
}
interface FlexLayoutDockContainerProps {
panels: FlexDockPanel[];
onPanelClose?: (panelId: string) => void;
}
export function FlexLayoutDockContainer({ panels, onPanelClose }: FlexLayoutDockContainerProps) {
const createDefaultLayout = useCallback((): IJsonModel => {
const leftPanels = panels.filter(p => p.id.includes('hierarchy'));
const rightPanels = panels.filter(p => p.id.includes('inspector'));
const bottomPanels = panels.filter(p => p.id.includes('console') || p.id.includes('asset'))
.sort((a, b) => {
// 控制台排在前面
if (a.id.includes('console')) return -1;
if (b.id.includes('console')) return 1;
return 0;
});
const centerPanels = panels.filter(p =>
!leftPanels.includes(p) && !rightPanels.includes(p) && !bottomPanels.includes(p)
);
// Build center column children
const centerColumnChildren: (IJsonTabSetNode | IJsonRowNode)[] = [];
if (centerPanels.length > 0) {
centerColumnChildren.push({
type: 'tabset',
weight: 70,
children: centerPanels.map(p => ({
type: 'tab',
name: p.title,
id: p.id,
component: p.id,
enableClose: p.closable !== false,
})),
});
}
if (bottomPanels.length > 0) {
centerColumnChildren.push({
type: 'tabset',
weight: 30,
children: bottomPanels.map(p => ({
type: 'tab',
name: p.title,
id: p.id,
component: p.id,
enableClose: p.closable !== false,
})),
});
}
// Build main row children
const mainRowChildren: (IJsonTabSetNode | IJsonRowNode)[] = [];
if (leftPanels.length > 0) {
mainRowChildren.push({
type: 'tabset',
weight: 20,
children: leftPanels.map(p => ({
type: 'tab',
name: p.title,
id: p.id,
component: p.id,
enableClose: p.closable !== false,
})),
});
}
if (centerColumnChildren.length > 0) {
if (centerColumnChildren.length === 1) {
const centerChild = centerColumnChildren[0];
if (centerChild && centerChild.type === 'tabset') {
mainRowChildren.push({
type: 'tabset',
weight: 60,
children: centerChild.children
} as IJsonTabSetNode);
} else if (centerChild) {
mainRowChildren.push({
type: 'row',
weight: 60,
children: centerChild.children
} as IJsonRowNode);
}
} else {
mainRowChildren.push({
type: 'row',
weight: 60,
children: centerColumnChildren,
});
}
}
if (rightPanels.length > 0) {
mainRowChildren.push({
type: 'tabset',
weight: 20,
children: rightPanels.map(p => ({
type: 'tab',
name: p.title,
id: p.id,
component: p.id,
enableClose: p.closable !== false,
})),
});
}
return {
global: {
tabEnableClose: true,
tabEnableRename: false,
tabSetEnableMaximize: false,
tabSetEnableDrop: true,
tabSetEnableDrag: true,
tabSetEnableDivide: true,
borderEnableDrop: true,
},
borders: [],
layout: {
type: 'row',
weight: 100,
children: mainRowChildren,
},
};
}, [panels]);
const model = useMemo(() => Model.fromJson(createDefaultLayout()), [createDefaultLayout]);
const factory = useCallback((node: TabNode) => {
const component = node.getComponent();
const panel = panels.find(p => p.id === component);
return panel?.content || <div>Panel not found</div>;
}, [panels]);
const onAction = useCallback((action: any) => {
if (action.type === Actions.DELETE_TAB) {
const tabId = action.data.node;
if (onPanelClose) {
onPanelClose(tabId);
}
}
return action;
}, [onPanelClose]);
return (
<div className="flexlayout-dock-container">
<Layout
model={model}
factory={factory}
onAction={onAction}
/>
</div>
);
}