refactor(core): 移除@Inject参数装饰器,统一使用@InjectProperty (#229)
* refactor(core): 移除@Inject参数装饰器,统一使用@InjectProperty * refactor(core): 移除@Inject参数装饰器,统一使用@InjectProperty
This commit is contained in:
@@ -213,6 +213,7 @@ export class Core {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this._debugManager = this._serviceContainer.resolve(DebugManager);
|
this._debugManager = this._serviceContainer.resolve(DebugManager);
|
||||||
|
this._debugManager.onInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
@@ -488,6 +489,7 @@ export class Core {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this._instance._debugManager = this._instance._serviceContainer.resolve(DebugManager);
|
this._instance._debugManager = this._instance._serviceContainer.resolve(DebugManager);
|
||||||
|
this._instance._debugManager.onInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新Core配置
|
// 更新Core配置
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 依赖注入装饰器
|
* 依赖注入装饰器
|
||||||
*
|
*
|
||||||
* 提供 @Injectable、@Inject 和 @Updatable 装饰器,用于标记可注入的类和依赖注入点
|
* 提供 @Injectable、@InjectProperty 和 @Updatable 装饰器,用于标记可注入的类和依赖注入点
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ServiceContainer } from '../ServiceContainer';
|
import type { ServiceContainer } from '../ServiceContainer';
|
||||||
@@ -13,7 +13,6 @@ import type { IService, ServiceType } from '../ServiceContainer';
|
|||||||
type Constructor = abstract new (...args: unknown[]) => unknown;
|
type Constructor = abstract new (...args: unknown[]) => unknown;
|
||||||
|
|
||||||
const injectableMetadata = new WeakMap<Constructor, InjectableMetadata>();
|
const injectableMetadata = new WeakMap<Constructor, InjectableMetadata>();
|
||||||
const injectMetadata = new WeakMap<Constructor, Map<number, ServiceType<IService> | string | symbol>>();
|
|
||||||
const updatableMetadata = new WeakMap<Constructor, UpdatableMetadata>();
|
const updatableMetadata = new WeakMap<Constructor, UpdatableMetadata>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,10 +66,11 @@ export interface UpdatableMetadata {
|
|||||||
*
|
*
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* class PhysicsSystem extends EntitySystem {
|
* class PhysicsSystem extends EntitySystem {
|
||||||
* constructor(
|
* @InjectProperty(TimeService)
|
||||||
* @Inject(TimeService) private timeService: TimeService
|
* private timeService!: TimeService;
|
||||||
* ) {
|
*
|
||||||
* super();
|
* constructor() {
|
||||||
|
* super(Matcher.empty());
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
@@ -135,27 +135,6 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
|||||||
} as ClassDecorator;
|
} as ClassDecorator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Inject() 装饰器
|
|
||||||
*
|
|
||||||
* 标记构造函数参数需要注入的服务类型
|
|
||||||
*
|
|
||||||
* @param serviceType 服务类型标识符
|
|
||||||
*/
|
|
||||||
export function Inject(serviceType: ServiceType<IService> | string | symbol): ParameterDecorator {
|
|
||||||
return function (target: object, _propertyKey: string | symbol | undefined, parameterIndex: number) {
|
|
||||||
// 获取或创建注入元数据
|
|
||||||
let params = injectMetadata.get(target as Constructor);
|
|
||||||
if (!params) {
|
|
||||||
params = new Map();
|
|
||||||
injectMetadata.set(target as Constructor, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录参数索引和服务类型的映射
|
|
||||||
params.set(parameterIndex, serviceType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @InjectProperty() 装饰器
|
* @InjectProperty() 装饰器
|
||||||
*
|
*
|
||||||
@@ -164,6 +143,27 @@ export function Inject(serviceType: ServiceType<IService> | string | symbol): Pa
|
|||||||
* 注入时机:在构造函数执行后、onInitialize() 调用前完成
|
* 注入时机:在构造函数执行后、onInitialize() 调用前完成
|
||||||
*
|
*
|
||||||
* @param serviceType 服务类型
|
* @param serviceType 服务类型
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class PhysicsSystem extends EntitySystem {
|
||||||
|
* @InjectProperty(TimeService)
|
||||||
|
* private timeService!: TimeService;
|
||||||
|
*
|
||||||
|
* @InjectProperty(CollisionService)
|
||||||
|
* private collision!: CollisionService;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* super(Matcher.empty());
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public onInitialize(): void {
|
||||||
|
* // 此时属性已注入完成,可以安全使用
|
||||||
|
* console.log(this.timeService.getDeltaTime());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
export function InjectProperty(serviceType: ServiceType<IService>): PropertyDecorator {
|
export function InjectProperty(serviceType: ServiceType<IService>): PropertyDecorator {
|
||||||
return function (target: object, propertyKey: string | symbol) {
|
return function (target: object, propertyKey: string | symbol) {
|
||||||
@@ -207,13 +207,14 @@ export function getInjectableMetadata(target: Constructor): InjectableMetadata |
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取构造函数参数的注入元数据
|
* 获取属性注入元数据
|
||||||
*
|
*
|
||||||
* @param target 目标类
|
* @param target 目标类
|
||||||
* @returns 参数索引到服务类型的映射
|
* @returns 属性名到服务类型的映射
|
||||||
*/
|
*/
|
||||||
export function getInjectMetadata(target: Constructor): Map<number, ServiceType<IService> | string | symbol> {
|
export function getPropertyInjectMetadata(target: Constructor): Map<string | symbol, ServiceType<IService>> {
|
||||||
return injectMetadata.get(target) || new Map();
|
const metadata = injectableMetadata.get(target);
|
||||||
|
return metadata?.properties || new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,38 +233,13 @@ export function createInstance<T>(
|
|||||||
constructor: new (...args: any[]) => T,
|
constructor: new (...args: any[]) => T,
|
||||||
container: ServiceContainer
|
container: ServiceContainer
|
||||||
): T {
|
): T {
|
||||||
// 获取参数注入元数据
|
// 创建实例(无参数注入)
|
||||||
const injectParams = getInjectMetadata(constructor as Constructor);
|
const instance = new constructor();
|
||||||
|
|
||||||
// 解析依赖
|
// 注入属性依赖
|
||||||
const dependencies: unknown[] = [];
|
injectProperties(instance as object, container);
|
||||||
|
|
||||||
// 获取构造函数参数数量
|
return instance;
|
||||||
const paramCount = constructor.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < paramCount; i++) {
|
|
||||||
const serviceType = injectParams.get(i);
|
|
||||||
|
|
||||||
if (serviceType) {
|
|
||||||
// 如果有显式的@Inject标记,使用标记的类型
|
|
||||||
if (typeof serviceType === 'string' || typeof serviceType === 'symbol') {
|
|
||||||
// 字符串或Symbol类型的服务标识
|
|
||||||
throw new Error(
|
|
||||||
'String and Symbol service identifiers are not yet supported in constructor injection. ' +
|
|
||||||
`Please use class types for ${constructor.name} parameter ${i}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 类类型
|
|
||||||
dependencies.push(container.resolve(serviceType as ServiceType<IService>));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 没有@Inject标记,传入undefined
|
|
||||||
dependencies.push(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建实例
|
|
||||||
return new constructor(...dependencies);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,12 +6,11 @@
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Injectable,
|
Injectable,
|
||||||
Inject,
|
|
||||||
InjectProperty,
|
InjectProperty,
|
||||||
Updatable,
|
Updatable,
|
||||||
isInjectable,
|
isInjectable,
|
||||||
getInjectableMetadata,
|
getInjectableMetadata,
|
||||||
getInjectMetadata,
|
getPropertyInjectMetadata,
|
||||||
isUpdatable,
|
isUpdatable,
|
||||||
getUpdatableMetadata,
|
getUpdatableMetadata,
|
||||||
createInstance,
|
createInstance,
|
||||||
|
|||||||
@@ -70,11 +70,14 @@ export interface SystemMetadata {
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // 配置更新顺序
|
* // 配置更新顺序和依赖注入
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* @ECSSystem('Physics', { updateOrder: 10 })
|
* @ECSSystem('Physics', { updateOrder: 10 })
|
||||||
* class PhysicsSystem extends EntitySystem {
|
* class PhysicsSystem extends EntitySystem {
|
||||||
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
* @InjectProperty(CollisionSystem)
|
||||||
|
* private collision!: CollisionSystem;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
* super(Matcher.empty().all(Transform, RigidBody));
|
* super(Matcher.empty().all(Transform, RigidBody));
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
|
|||||||
@@ -612,7 +612,7 @@ export class Scene implements IScene {
|
|||||||
* 在场景中添加一个EntitySystem处理器
|
* 在场景中添加一个EntitySystem处理器
|
||||||
*
|
*
|
||||||
* 支持两种使用方式:
|
* 支持两种使用方式:
|
||||||
* 1. 传入类型(推荐):自动使用DI创建实例,支持@Injectable和@Inject装饰器
|
* 1. 传入类型(推荐):自动使用DI创建实例,支持@Injectable和@InjectProperty装饰器
|
||||||
* 2. 传入实例:直接使用提供的实例
|
* 2. 传入实例:直接使用提供的实例
|
||||||
*
|
*
|
||||||
* @param systemTypeOrInstance 系统类型或系统实例
|
* @param systemTypeOrInstance 系统类型或系统实例
|
||||||
@@ -623,7 +623,10 @@ export class Scene implements IScene {
|
|||||||
* // 方式1:传入类型,自动DI(推荐)
|
* // 方式1:传入类型,自动DI(推荐)
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* class PhysicsSystem extends EntitySystem {
|
* class PhysicsSystem extends EntitySystem {
|
||||||
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
* @InjectProperty(CollisionSystem)
|
||||||
|
* private collision!: CollisionSystem;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
* super(Matcher.empty().all(Transform));
|
* super(Matcher.empty().all(Transform));
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@@ -718,7 +721,10 @@ export class Scene implements IScene {
|
|||||||
* @Injectable()
|
* @Injectable()
|
||||||
* @ECSSystem('Physics', { updateOrder: 10 })
|
* @ECSSystem('Physics', { updateOrder: 10 })
|
||||||
* class PhysicsSystem extends EntitySystem implements IService {
|
* class PhysicsSystem extends EntitySystem implements IService {
|
||||||
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
* @InjectProperty(CollisionSystem)
|
||||||
|
* private collision!: CollisionSystem;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
* super(Matcher.empty().all(Transform, RigidBody));
|
* super(Matcher.empty().all(Transform, RigidBody));
|
||||||
* }
|
* }
|
||||||
* dispose() {}
|
* dispose() {}
|
||||||
@@ -779,7 +785,7 @@ export class Scene implements IScene {
|
|||||||
/**
|
/**
|
||||||
* 获取指定类型的EntitySystem处理器
|
* 获取指定类型的EntitySystem处理器
|
||||||
*
|
*
|
||||||
* @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或在System构造函数中使用 `@Inject(SystemType)` 装饰器。
|
* @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或使用 `@InjectProperty(SystemType)` 装饰器。
|
||||||
*
|
*
|
||||||
* @param type 处理器类型
|
* @param type 处理器类型
|
||||||
* @returns 处理器实例,如果未找到则返回null
|
* @returns 处理器实例,如果未找到则返回null
|
||||||
@@ -788,8 +794,11 @@ export class Scene implements IScene {
|
|||||||
* ```typescript
|
* ```typescript
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* class MySystem extends EntitySystem {
|
* class MySystem extends EntitySystem {
|
||||||
* constructor(@Inject(PhysicsSystem) private physics: PhysicsSystem) {
|
* @InjectProperty(PhysicsSystem)
|
||||||
* super();
|
* private physics!: PhysicsSystem;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* super(Matcher.empty());
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type { IService } from '../../Core/ServiceContainer';
|
|||||||
import type { IUpdatable } from '../../Types/IUpdatable';
|
import type { IUpdatable } from '../../Types/IUpdatable';
|
||||||
import { SceneManager } from '../../ECS/SceneManager';
|
import { SceneManager } from '../../ECS/SceneManager';
|
||||||
import { PerformanceMonitor } from '../PerformanceMonitor';
|
import { PerformanceMonitor } from '../PerformanceMonitor';
|
||||||
import { Injectable, Inject, Updatable } from '../../Core/DI/Decorators';
|
import { Injectable, InjectProperty, Updatable } from '../../Core/DI/Decorators';
|
||||||
import { DebugConfigService } from './DebugConfigService';
|
import { DebugConfigService } from './DebugConfigService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,19 +24,26 @@ import { DebugConfigService } from './DebugConfigService';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@Updatable()
|
@Updatable()
|
||||||
export class DebugManager implements IService, IUpdatable {
|
export class DebugManager implements IService, IUpdatable {
|
||||||
private config: IECSDebugConfig;
|
private config!: IECSDebugConfig;
|
||||||
private webSocketManager: WebSocketManager;
|
private webSocketManager!: WebSocketManager;
|
||||||
private entityCollector: EntityDataCollector;
|
private entityCollector!: EntityDataCollector;
|
||||||
private systemCollector: SystemDataCollector;
|
private systemCollector!: SystemDataCollector;
|
||||||
private performanceCollector: PerformanceDataCollector;
|
private performanceCollector!: PerformanceDataCollector;
|
||||||
private componentCollector: ComponentDataCollector;
|
private componentCollector!: ComponentDataCollector;
|
||||||
private sceneCollector: SceneDataCollector;
|
private sceneCollector!: SceneDataCollector;
|
||||||
private sceneManager: SceneManager;
|
|
||||||
private performanceMonitor: PerformanceMonitor;
|
@InjectProperty(SceneManager)
|
||||||
|
private sceneManager!: SceneManager;
|
||||||
|
|
||||||
|
@InjectProperty(PerformanceMonitor)
|
||||||
|
private performanceMonitor!: PerformanceMonitor;
|
||||||
|
|
||||||
|
@InjectProperty(DebugConfigService)
|
||||||
|
private configService!: DebugConfigService;
|
||||||
|
|
||||||
private frameCounter: number = 0;
|
private frameCounter: number = 0;
|
||||||
private lastSendTime: number = 0;
|
private lastSendTime: number = 0;
|
||||||
private sendInterval: number;
|
private sendInterval: number = 0;
|
||||||
private isRunning: boolean = false;
|
private isRunning: boolean = false;
|
||||||
private originalConsole = {
|
private originalConsole = {
|
||||||
log: console.log.bind(console),
|
log: console.log.bind(console),
|
||||||
@@ -46,14 +53,8 @@ export class DebugManager implements IService, IUpdatable {
|
|||||||
error: console.error.bind(console)
|
error: console.error.bind(console)
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
public onInitialize(): void {
|
||||||
@Inject(SceneManager) sceneManager: SceneManager,
|
this.config = this.configService.getConfig();
|
||||||
@Inject(PerformanceMonitor) performanceMonitor: PerformanceMonitor,
|
|
||||||
@Inject(DebugConfigService) configService: DebugConfigService
|
|
||||||
) {
|
|
||||||
this.config = configService.getConfig();
|
|
||||||
this.sceneManager = sceneManager;
|
|
||||||
this.performanceMonitor = performanceMonitor;
|
|
||||||
|
|
||||||
// 初始化数据收集器
|
// 初始化数据收集器
|
||||||
this.entityCollector = new EntityDataCollector();
|
this.entityCollector = new EntityDataCollector();
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ export * from './Plugins';
|
|||||||
// 依赖注入
|
// 依赖注入
|
||||||
export {
|
export {
|
||||||
Injectable,
|
Injectable,
|
||||||
Inject,
|
InjectProperty,
|
||||||
Updatable,
|
Updatable,
|
||||||
registerInjectable,
|
registerInjectable,
|
||||||
createInstance,
|
createInstance,
|
||||||
|
injectProperties,
|
||||||
isUpdatable,
|
isUpdatable,
|
||||||
getUpdatableMetadata
|
getUpdatableMetadata,
|
||||||
|
getPropertyInjectMetadata
|
||||||
} from './Core/DI';
|
} from './Core/DI';
|
||||||
export type { InjectableMetadata, UpdatableMetadata } from './Core/DI';
|
export type { InjectableMetadata, UpdatableMetadata } from './Core/DI';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, Inject, isInjectable, getInjectMetadata, createInstance, registerInjectable } from '../../src/Core/DI';
|
import { Injectable, InjectProperty, isInjectable, getPropertyInjectMetadata, createInstance, registerInjectable } from '../../src/Core/DI';
|
||||||
import { ServiceContainer } from '../../src/Core/ServiceContainer';
|
import { ServiceContainer } from '../../src/Core/ServiceContainer';
|
||||||
import type { IService } from '../../src/Core/ServiceContainer';
|
import type { IService } from '../../src/Core/ServiceContainer';
|
||||||
|
|
||||||
@@ -14,9 +14,8 @@ class SimpleService implements IService {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class DependentService implements IService {
|
class DependentService implements IService {
|
||||||
constructor(
|
@InjectProperty(SimpleService)
|
||||||
@Inject(SimpleService) public simpleService: SimpleService
|
public simpleService!: SimpleService;
|
||||||
) {}
|
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
// 清理资源
|
// 清理资源
|
||||||
@@ -25,10 +24,11 @@ class DependentService implements IService {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class MultiDependencyService implements IService {
|
class MultiDependencyService implements IService {
|
||||||
constructor(
|
@InjectProperty(SimpleService)
|
||||||
@Inject(SimpleService) public service1: SimpleService,
|
public service1!: SimpleService;
|
||||||
@Inject(DependentService) public service2: DependentService
|
|
||||||
) {}
|
@InjectProperty(DependentService)
|
||||||
|
public service2!: DependentService;
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
// 清理资源
|
// 清理资源
|
||||||
@@ -58,18 +58,18 @@ describe('DI - 依赖注入装饰器测试', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('@Inject 装饰器', () => {
|
describe('@InjectProperty 装饰器', () => {
|
||||||
test('应该记录参数注入元数据', () => {
|
test('应该记录属性注入元数据', () => {
|
||||||
const metadata = getInjectMetadata(DependentService as any);
|
const metadata = getPropertyInjectMetadata(DependentService as any);
|
||||||
expect(metadata.size).toBe(1);
|
expect(metadata.size).toBe(1);
|
||||||
expect(metadata.get(0)).toBe(SimpleService);
|
expect(metadata.get('simpleService')).toBe(SimpleService);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该记录多个参数的注入元数据', () => {
|
test('应该记录多个属性的注入元数据', () => {
|
||||||
const metadata = getInjectMetadata(MultiDependencyService as any);
|
const metadata = getPropertyInjectMetadata(MultiDependencyService as any);
|
||||||
expect(metadata.size).toBe(2);
|
expect(metadata.size).toBe(2);
|
||||||
expect(metadata.get(0)).toBe(SimpleService);
|
expect(metadata.get('service1')).toBe(SimpleService);
|
||||||
expect(metadata.get(1)).toBe(DependentService);
|
expect(metadata.get('service2')).toBe(DependentService);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
|
|||||||
import { Entity } from '../../src/ECS/Entity';
|
import { Entity } from '../../src/ECS/Entity';
|
||||||
import { Component } from '../../src/ECS/Component';
|
import { Component } from '../../src/ECS/Component';
|
||||||
import { Matcher } from '../../src/ECS/Utils/Matcher';
|
import { Matcher } from '../../src/ECS/Utils/Matcher';
|
||||||
import { Injectable, Inject, InjectProperty } from '../../src/Core/DI';
|
import { Injectable, InjectProperty } from '../../src/Core/DI';
|
||||||
import { Core } from '../../src/Core';
|
import { Core } from '../../src/Core';
|
||||||
import type { IService } from '../../src/Core/ServiceContainer';
|
import type { IService } from '../../src/Core/ServiceContainer';
|
||||||
import { ECSSystem } from '../../src/ECS/Decorators';
|
import { ECSSystem } from '../../src/ECS/Decorators';
|
||||||
@@ -82,9 +82,10 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('Physics')
|
@ECSSystem('Physics')
|
||||||
class PhysicsSystem extends EntitySystem implements IService {
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
constructor(
|
@InjectProperty(CollisionSystem)
|
||||||
@Inject(CollisionSystem) public collision: CollisionSystem
|
public collision!: CollisionSystem;
|
||||||
) {
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty().all(Transform, Velocity));
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +125,10 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('B')
|
@ECSSystem('B')
|
||||||
class SystemB extends EntitySystem implements IService {
|
class SystemB extends EntitySystem implements IService {
|
||||||
constructor(@Inject(SystemA) public systemA: SystemA) {
|
@InjectProperty(SystemA)
|
||||||
|
public systemA!: SystemA;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty());
|
super(Matcher.empty());
|
||||||
}
|
}
|
||||||
override dispose() {}
|
override dispose() {}
|
||||||
@@ -133,10 +137,13 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('C')
|
@ECSSystem('C')
|
||||||
class SystemC extends EntitySystem implements IService {
|
class SystemC extends EntitySystem implements IService {
|
||||||
constructor(
|
@InjectProperty(SystemA)
|
||||||
@Inject(SystemA) public systemA: SystemA,
|
public systemA!: SystemA;
|
||||||
@Inject(SystemB) public systemB: SystemB
|
|
||||||
) {
|
@InjectProperty(SystemB)
|
||||||
|
public systemB!: SystemB;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty());
|
super(Matcher.empty());
|
||||||
}
|
}
|
||||||
override dispose() {}
|
override dispose() {}
|
||||||
@@ -166,7 +173,10 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('Physics', { updateOrder: 10 })
|
@ECSSystem('Physics', { updateOrder: 10 })
|
||||||
class PhysicsSystem extends EntitySystem implements IService {
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
constructor(@Inject(CollisionSystem) public collision: CollisionSystem) {
|
@InjectProperty(CollisionSystem)
|
||||||
|
public collision!: CollisionSystem;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty().all(Transform, Velocity));
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
}
|
}
|
||||||
override dispose() {}
|
override dispose() {}
|
||||||
@@ -175,7 +185,10 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('Render', { updateOrder: 20 })
|
@ECSSystem('Render', { updateOrder: 20 })
|
||||||
class RenderSystem extends EntitySystem implements IService {
|
class RenderSystem extends EntitySystem implements IService {
|
||||||
constructor(@Inject(PhysicsSystem) public physics: PhysicsSystem) {
|
@InjectProperty(PhysicsSystem)
|
||||||
|
public physics!: PhysicsSystem;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty().all(Transform));
|
super(Matcher.empty().all(Transform));
|
||||||
}
|
}
|
||||||
override dispose() {}
|
override dispose() {}
|
||||||
@@ -346,7 +359,7 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Issue #76 场景验证', () => {
|
describe('Issue #76 场景验证', () => {
|
||||||
test('应该消除硬编码依赖,使用构造函数注入', () => {
|
test('应该消除硬编码依赖,使用属性注入', () => {
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('TimeService')
|
@ECSSystem('TimeService')
|
||||||
class TimeService extends EntitySystem implements IService {
|
class TimeService extends EntitySystem implements IService {
|
||||||
@@ -378,10 +391,13 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
@ECSSystem('Physics')
|
@ECSSystem('Physics')
|
||||||
class PhysicsSystem extends EntitySystem implements IService {
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
constructor(
|
@InjectProperty(TimeService)
|
||||||
@Inject(TimeService) private time: TimeService,
|
private time!: TimeService;
|
||||||
@Inject(CollisionService) private collision: CollisionService
|
|
||||||
) {
|
@InjectProperty(CollisionService)
|
||||||
|
private collision!: CollisionService;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super(Matcher.empty().all(Transform, Velocity));
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,49 +560,5 @@ describe('EntitySystem - 依赖注入测试', () => {
|
|||||||
|
|
||||||
expect(consumer.getInitializeValue()).toBe(42);
|
expect(consumer.getInitializeValue()).toBe(42);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('属性注入可以与构造函数注入混合使用', () => {
|
|
||||||
@Injectable()
|
|
||||||
@ECSSystem('A')
|
|
||||||
class ServiceA extends EntitySystem implements IService {
|
|
||||||
public valueA = 'A';
|
|
||||||
constructor() {
|
|
||||||
super(Matcher.empty());
|
|
||||||
}
|
|
||||||
override dispose() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
@ECSSystem('B')
|
|
||||||
class ServiceB extends EntitySystem implements IService {
|
|
||||||
public valueB = 'B';
|
|
||||||
constructor() {
|
|
||||||
super(Matcher.empty());
|
|
||||||
}
|
|
||||||
override dispose() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
@ECSSystem('Mixed')
|
|
||||||
class MixedSystem extends EntitySystem implements IService {
|
|
||||||
@InjectProperty(ServiceB)
|
|
||||||
serviceB!: ServiceB;
|
|
||||||
|
|
||||||
constructor(@Inject(ServiceA) public serviceA: ServiceA) {
|
|
||||||
super(Matcher.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override onInitialize(): void {
|
|
||||||
expect(this.serviceA).toBeInstanceOf(ServiceA);
|
|
||||||
expect(this.serviceB).toBeInstanceOf(ServiceB);
|
|
||||||
expect(this.serviceA.valueA).toBe('A');
|
|
||||||
expect(this.serviceB.valueB).toBe('B');
|
|
||||||
}
|
|
||||||
|
|
||||||
override dispose() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.registerSystems([ServiceA, ServiceB, MixedSystem]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
import { ChevronDown, ChevronRight, Settings } from 'lucide-react';
|
||||||
import { PropertyContext, PropertyRendererRegistry } from '@esengine/editor-core';
|
import { PropertyContext, PropertyRendererRegistry } from '@esengine/editor-core';
|
||||||
import { Core } from '@esengine/ecs-framework';
|
import { Core } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ export function ComponentItem({ component, decimalPlaces = 4 }: ComponentItemPro
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
{isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
||||||
|
<Settings size={14} style={{ marginLeft: "4px", color: "#888" }} />
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
marginLeft: '6px',
|
marginLeft: '6px',
|
||||||
|
|||||||
@@ -10,24 +10,17 @@ const VectorInput: React.FC<{
|
|||||||
value: number;
|
value: number;
|
||||||
onChange: (value: number) => void;
|
onChange: (value: number) => void;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
}> = ({ label, value, onChange, readonly }) => (
|
axis: 'x' | 'y' | 'z' | 'w';
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
}> = ({ label, value, onChange, readonly, axis }) => (
|
||||||
<span style={{ color: '#888', fontSize: '10px', minWidth: '12px' }}>{label}:</span>
|
<div className="property-vector-axis-compact">
|
||||||
|
<span className={`property-vector-axis-label property-vector-axis-${axis}`}>{label}</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(parseFloat(e.target.value) || 0)}
|
onChange={(e) => onChange(parseFloat(e.target.value) || 0)}
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{
|
className="property-input property-input-number property-input-number-compact"
|
||||||
width: '60px',
|
|
||||||
padding: '2px 4px',
|
|
||||||
backgroundColor: '#2a2a2a',
|
|
||||||
border: '1px solid #444',
|
|
||||||
borderRadius: '3px',
|
|
||||||
color: '#e0e0e0',
|
|
||||||
fontSize: '11px'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -47,18 +40,20 @@ export class Vector2FieldEditor implements IFieldEditor<Vector2> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{label}</label>
|
<label className="property-label">{label}</label>
|
||||||
<div style={{ display: 'flex', gap: '8px' }}>
|
<div className="property-vector-compact">
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="X"
|
label="X"
|
||||||
value={v.x}
|
value={v.x}
|
||||||
onChange={(x) => onChange({ ...v, x })}
|
onChange={(x) => onChange({ ...v, x })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="x"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="Y"
|
label="Y"
|
||||||
value={v.y}
|
value={v.y}
|
||||||
onChange={(y) => onChange({ ...v, y })}
|
onChange={(y) => onChange({ ...v, y })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="y"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,24 +76,27 @@ export class Vector3FieldEditor implements IFieldEditor<Vector3> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{label}</label>
|
<label className="property-label">{label}</label>
|
||||||
<div style={{ display: 'flex', gap: '8px' }}>
|
<div className="property-vector-compact">
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="X"
|
label="X"
|
||||||
value={v.x}
|
value={v.x}
|
||||||
onChange={(x) => onChange({ ...v, x })}
|
onChange={(x) => onChange({ ...v, x })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="x"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="Y"
|
label="Y"
|
||||||
value={v.y}
|
value={v.y}
|
||||||
onChange={(y) => onChange({ ...v, y })}
|
onChange={(y) => onChange({ ...v, y })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="y"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="Z"
|
label="Z"
|
||||||
value={v.z}
|
value={v.z}
|
||||||
onChange={(z) => onChange({ ...v, z })}
|
onChange={(z) => onChange({ ...v, z })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="z"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,30 +119,34 @@ export class Vector4FieldEditor implements IFieldEditor<Vector4> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{label}</label>
|
<label className="property-label">{label}</label>
|
||||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
<div className="property-vector-compact">
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="X"
|
label="X"
|
||||||
value={v.x}
|
value={v.x}
|
||||||
onChange={(x) => onChange({ ...v, x })}
|
onChange={(x) => onChange({ ...v, x })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="x"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="Y"
|
label="Y"
|
||||||
value={v.y}
|
value={v.y}
|
||||||
onChange={(y) => onChange({ ...v, y })}
|
onChange={(y) => onChange({ ...v, y })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="y"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="Z"
|
label="Z"
|
||||||
value={v.z}
|
value={v.z}
|
||||||
onChange={(z) => onChange({ ...v, z })}
|
onChange={(z) => onChange({ ...v, z })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="z"
|
||||||
/>
|
/>
|
||||||
<VectorInput
|
<VectorInput
|
||||||
label="W"
|
label="W"
|
||||||
value={v.w}
|
value={v.w}
|
||||||
onChange={(w) => onChange({ ...v, w })}
|
onChange={(w) => onChange({ ...v, w })}
|
||||||
readonly={context.readonly}
|
readonly={context.readonly}
|
||||||
|
axis="w"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
import { ChevronDown, ChevronRight, Settings } from 'lucide-react';
|
||||||
import { IPropertyRenderer, PropertyContext, PropertyRendererRegistry } from '@esengine/editor-core';
|
import { IPropertyRenderer, PropertyContext, PropertyRendererRegistry } from '@esengine/editor-core';
|
||||||
import { Core } from '@esengine/ecs-framework';
|
import { Core } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
@@ -43,6 +43,7 @@ export class ComponentRenderer implements IPropertyRenderer<ComponentData> {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
{isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
||||||
|
<Settings size={14} style={{ marginLeft: "4px", color: "#888" }} />
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
marginLeft: '6px',
|
marginLeft: '6px',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
|
||||||
import { IPropertyRenderer, PropertyContext } from '@esengine/editor-core';
|
import { IPropertyRenderer, PropertyContext } from '@esengine/editor-core';
|
||||||
import { formatNumber } from '../../components/inspectors/utils';
|
import { formatNumber } from '../../components/inspectors/utils';
|
||||||
|
|
||||||
@@ -23,6 +22,20 @@ interface Color {
|
|||||||
a: number;
|
a: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VectorValue: React.FC<{
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
axis: 'x' | 'y' | 'z' | 'w';
|
||||||
|
decimals: number;
|
||||||
|
}> = ({ label, value, axis, decimals }) => (
|
||||||
|
<div className="property-vector-axis-compact">
|
||||||
|
<span className={`property-vector-axis-label property-vector-axis-${axis}`}>{label}</span>
|
||||||
|
<span className="property-input property-input-number property-input-number-compact" style={{ cursor: 'default' }}>
|
||||||
|
{formatNumber(value, decimals)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export class Vector2Renderer implements IPropertyRenderer<Vector2> {
|
export class Vector2Renderer implements IPropertyRenderer<Vector2> {
|
||||||
readonly id = 'app.vector2';
|
readonly id = 'app.vector2';
|
||||||
readonly name = 'Vector2 Renderer';
|
readonly name = 'Vector2 Renderer';
|
||||||
@@ -44,9 +57,10 @@ export class Vector2Renderer implements IPropertyRenderer<Vector2> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{context.name}</label>
|
<label className="property-label">{context.name}</label>
|
||||||
<span className="property-value-text" style={{ color: '#9cdcfe', fontFamily: 'monospace' }}>
|
<div className="property-vector-compact">
|
||||||
({formatNumber(value.x, decimals)}, {formatNumber(value.y, decimals)})
|
<VectorValue label="X" value={value.x} axis="x" decimals={decimals} />
|
||||||
</span>
|
<VectorValue label="Y" value={value.y} axis="y" decimals={decimals} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -74,9 +88,11 @@ export class Vector3Renderer implements IPropertyRenderer<Vector3> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{context.name}</label>
|
<label className="property-label">{context.name}</label>
|
||||||
<span className="property-value-text" style={{ color: '#9cdcfe', fontFamily: 'monospace' }}>
|
<div className="property-vector-compact">
|
||||||
({formatNumber(value.x, decimals)}, {formatNumber(value.y, decimals)}, {formatNumber(value.z, decimals)})
|
<VectorValue label="X" value={value.x} axis="x" decimals={decimals} />
|
||||||
</span>
|
<VectorValue label="Y" value={value.y} axis="y" decimals={decimals} />
|
||||||
|
<VectorValue label="Z" value={value.z} axis="z" decimals={decimals} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -108,18 +124,13 @@ export class ColorRenderer implements IPropertyRenderer<Color> {
|
|||||||
return (
|
return (
|
||||||
<div className="property-field">
|
<div className="property-field">
|
||||||
<label className="property-label">{context.name}</label>
|
<label className="property-label">{context.name}</label>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
<div className="property-color-wrapper">
|
||||||
<div
|
<div
|
||||||
style={{
|
className="property-color-preview"
|
||||||
width: '20px',
|
style={{ backgroundColor: colorHex }}
|
||||||
height: '20px',
|
|
||||||
backgroundColor: colorHex,
|
|
||||||
border: '1px solid #444',
|
|
||||||
borderRadius: '2px'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<span className="property-value-text" style={{ fontFamily: 'monospace' }}>
|
<span className="property-input property-input-color-text" style={{ cursor: 'default' }}>
|
||||||
rgba({r}, {g}, {b}, {value.a.toFixed(2)})
|
{colorHex.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -100,12 +100,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flexlayout__tab_button_content {
|
.flexlayout__tab_button_content {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 120px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.3px;
|
letter-spacing: 0.3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flexlayout__tab_button--selected .flexlayout__tab_button_content {
|
.flexlayout__tab_button--selected .flexlayout__tab_button_content {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 120px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,9 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 6px center;
|
background-position: right 6px center;
|
||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
appearance: none;
|
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.property-input-select:hover {
|
.property-input-select:hover {
|
||||||
@@ -286,10 +286,13 @@
|
|||||||
|
|
||||||
.property-input-number-compact {
|
.property-input-number-compact {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 32px;
|
min-width: 24px;
|
||||||
text-align: center;
|
max-width: 40px;
|
||||||
padding: 2px 4px;
|
text-align: right;
|
||||||
font-size: 10px;
|
padding: 1px 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.property-vector-expanded {
|
.property-vector-expanded {
|
||||||
@@ -338,6 +341,12 @@
|
|||||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.property-vector-axis-w {
|
||||||
|
background: rgba(168, 85, 247, 0.2);
|
||||||
|
color: #c084fc;
|
||||||
|
border: 1px solid rgba(168, 85, 247, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
.property-field:focus-within {
|
.property-field:focus-within {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
@@ -357,6 +366,7 @@
|
|||||||
|
|
||||||
input[type="number"].property-input {
|
input[type="number"].property-input {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"].property-input::-webkit-outer-spin-button,
|
input[type="number"].property-input::-webkit-outer-spin-button,
|
||||||
|
|||||||
@@ -29,6 +29,11 @@
|
|||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-name-container {
|
.scene-name-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user