@web-engine-dev/signals
Godot-style reactive signal system for web-engine.
Features
- Type-Safe Signals: Generic typed signals
- Connect/Disconnect: Subscribe to signals
- One-Shot: Auto-disconnect after first emit
- Deferred Emit: Queue signals for later
- Signal Groups: Batch signal operations
Installation
bash
npm install @web-engine-dev/signals
# or
pnpm add @web-engine-dev/signalsQuick Start
typescript
import { Signal, signal } from '@web-engine-dev/signals';
// Create signal
const onDamage = signal<{ amount: number; source: Entity }>();
// Connect listener
const disconnect = onDamage.connect((data) => {
console.log(`Took ${data.amount} damage from ${data.source}`);
});
// Emit signal
onDamage.emit({ amount: 10, source: enemy });
// Disconnect
disconnect();API Overview
Signal Creation
typescript
// Using factory
const onDeath = signal<void>();
const onScoreChanged = signal<number>();
const onCollision = signal<{ a: Entity; b: Entity }>();
// Using class
const health = new Signal<number>();Connecting
typescript
// Basic connect
const disconnect = mySignal.connect(handler);
// One-shot (auto-disconnects)
mySignal.connectOnce(handler);
// With priority (lower = earlier)
mySignal.connect(handler, { priority: 0 });
// Bound to object
mySignal.connect(this.onEvent, { bind: this });Emitting
typescript
// Immediate emit
mySignal.emit(value);
// Deferred emit (next frame)
mySignal.emitDeferred(value);
// Check if has connections
if (mySignal.hasConnections()) {
mySignal.emit(value);
}Component Integration
typescript
class Enemy {
readonly onDeath = signal<void>();
readonly onDamaged = signal<number>();
takeDamage(amount: number) {
this.health -= amount;
this.onDamaged.emit(amount);
if (this.health <= 0) {
this.onDeath.emit();
}
}
}
// Usage
enemy.onDeath.connect(() => {
spawnLoot(enemy.position);
addScore(100);
});Signal Groups
typescript
const group = new SignalGroup();
group.add(health.onChanged);
group.add(mana.onChanged);
group.add(stamina.onChanged);
// Disconnect all at once
group.disconnectAll();