feat(blueprint, node-editor): 重构蓝图装饰器系统,添加 Shadow DOM 支持 (#430)
**blueprint** - 移除 Reflect.getMetadata 依赖,装饰器要求显式指定类型 - 新增 ECS 节点:Entity、Component、Flow 控制节点 - 新增组件自动注册系统 (BlueprintExpose, BlueprintProperty, BlueprintMethod) - 删除未实现的事件节点占位文件 **node-editor** - 新增 injectNodeEditorStyles() 函数支持 Shadow DOM 样式注入 - 导出 nodeEditorCssText 用于手动样式注入
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useCallback, useState, useMemo } from 'react';
|
||||
import React, { useRef, useCallback, useState, useMemo, useEffect } from 'react';
|
||||
import { Graph } from '../../domain/models/Graph';
|
||||
import { GraphNode, NodeTemplate } from '../../domain/models/GraphNode';
|
||||
import { Connection } from '../../domain/models/Connection';
|
||||
@@ -127,6 +127,18 @@ export const NodeEditor: React.FC<NodeEditorProps> = ({
|
||||
const [connectionDrag, setConnectionDrag] = useState<ConnectionDragState | null>(null);
|
||||
const [hoveredPin, setHoveredPin] = useState<Pin | null>(null);
|
||||
|
||||
// Force re-render after mount to ensure connections are drawn correctly
|
||||
// 挂载后强制重渲染以确保连接线正确绘制
|
||||
const [, forceUpdate] = useState(0);
|
||||
useEffect(() => {
|
||||
// Use requestAnimationFrame to wait for DOM to be fully rendered
|
||||
// 使用 requestAnimationFrame 等待 DOM 完全渲染
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
forceUpdate(n => n + 1);
|
||||
});
|
||||
return () => cancelAnimationFrame(rafId);
|
||||
}, [graph.id]);
|
||||
|
||||
/**
|
||||
* Converts screen coordinates to canvas coordinates
|
||||
* 将屏幕坐标转换为画布坐标
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// Import styles (导入样式)
|
||||
import './styles/index.css';
|
||||
|
||||
// CSS utilities for Shadow DOM (Shadow DOM 的 CSS 工具)
|
||||
export { nodeEditorCssText, injectNodeEditorStyles } from './styles/cssText';
|
||||
|
||||
// Domain models (领域模型)
|
||||
export {
|
||||
// Models
|
||||
|
||||
55
packages/devtools/node-editor/src/styles/cssText.ts
Normal file
55
packages/devtools/node-editor/src/styles/cssText.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @zh 节点编辑器 CSS 样式文本
|
||||
* @en Node Editor CSS style text
|
||||
*
|
||||
* @zh 此文件在构建时由 vite 插件自动生成
|
||||
* @en This file is auto-generated by vite plugin during build
|
||||
*/
|
||||
|
||||
// Placeholder - will be replaced by vite plugin during build
|
||||
export const nodeEditorCssText = '__NODE_EDITOR_CSS_PLACEHOLDER__';
|
||||
|
||||
/**
|
||||
* @zh 将 CSS 注入到指定的根节点(支持 Shadow DOM)
|
||||
* @en Inject CSS into specified root node (supports Shadow DOM)
|
||||
*
|
||||
* @param root - @zh 目标根节点(Document 或 ShadowRoot)@en Target root node (Document or ShadowRoot)
|
||||
* @param styleId - @zh 样式标签的 ID @en ID for the style tag
|
||||
* @returns @zh 创建的 style 元素 @en The created style element
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Inject into Shadow DOM
|
||||
* const shadowRoot = element.attachShadow({ mode: 'open' });
|
||||
* injectNodeEditorStyles(shadowRoot);
|
||||
*
|
||||
* // Inject into document (with custom ID)
|
||||
* injectNodeEditorStyles(document, 'my-editor-styles');
|
||||
* ```
|
||||
*/
|
||||
export function injectNodeEditorStyles(
|
||||
root: Document | ShadowRoot | DocumentFragment,
|
||||
styleId: string = 'esengine-node-editor-styles'
|
||||
): HTMLStyleElement | null {
|
||||
// Check if already injected
|
||||
const existingStyle = (root as any).getElementById?.(styleId) ||
|
||||
(root as any).querySelector?.(`#${styleId}`);
|
||||
if (existingStyle) {
|
||||
return existingStyle as HTMLStyleElement;
|
||||
}
|
||||
|
||||
// Create and inject style element
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
style.textContent = nodeEditorCssText;
|
||||
|
||||
if ('head' in root) {
|
||||
// Document
|
||||
(root as Document).head.appendChild(style);
|
||||
} else {
|
||||
// ShadowRoot or DocumentFragment
|
||||
root.appendChild(style);
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
Reference in New Issue
Block a user