shrimp/src/utils/signal.ts
2025-10-22 11:23:11 -07:00

69 lines
1.9 KiB
TypeScript

/**
* How to use a Signal:
*
* Create a signal with primitives:
* const nameSignal = new Signal<string>()
* const countSignal = new Signal<number>()
*
* Create a signal with objects:
* const chatSignal = new Signal<{ username: string, message: string }>()
*
* Create a signal with no data (void):
* const clickSignal = new Signal<void>()
* const clickSignal2 = new Signal() // Defaults to void
*
* Connect to the signal:
* const disconnect = chatSignal.connect((data) => {
* const {username, message} = data;
* console.log(`${username} said "${message}"`);
* })
*
* Emit a signal:
* nameSignal.emit("Alice")
* countSignal.emit(42)
* chatSignal.emit({ username: "Chad", message: "Hey everyone, how's it going?" });
* clickSignal.emit() // No argument for void signals
*
* Forward a signal:
* const relaySignal = new Signal<{ username: string, message: string }>()
* const disconnectRelay = chatSignal.connect(relaySignal)
* // Now, when chatSignal emits, relaySignal will also emit the same data
*
* Disconnect a single listener:
* disconnect(); // The disconnect function is returned when you connect to a signal
*
* Disconnect all listeners:
* chatSignal.disconnect()
*/
export class Signal<T = void> {
private listeners: Array<(data: T) => void> = []
connect(listenerOrSignal: Signal<T> | ((data: T) => void)) {
let listener: (data: T) => void
// If it is a signal, forward the data to the signal
if (listenerOrSignal instanceof Signal) {
listener = (data: T) => listenerOrSignal.emit(data)
} else {
listener = listenerOrSignal
}
this.listeners.push(listener)
return () => {
this.listeners = this.listeners.filter((l) => l !== listener)
}
}
emit(data: T) {
for (const listener of this.listeners) {
listener(data)
}
}
disconnect() {
this.listeners = []
}
}