signal.ts 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. /**
  2. * Tiny listener-set primitive for pure event signals (no stored state).
  3. *
  4. * Collapses the ~8-line `const listeners = new Set(); function subscribe(){…};
  5. * function notify(){for(const l of listeners) l()}` boilerplate that was
  6. * duplicated ~15× across the codebase into a one-liner.
  7. *
  8. * Distinct from a store (AppState, createStore) — there is no snapshot, no
  9. * getState. Use this when subscribers only need to know "something happened",
  10. * optionally with event args, not "what is the current value".
  11. *
  12. * Usage:
  13. * const changed = createSignal<[SettingSource]>()
  14. * export const subscribe = changed.subscribe
  15. * // later: changed.emit('userSettings')
  16. */
  17. export type Signal<Args extends unknown[] = []> = {
  18. /** Subscribe a listener. Returns an unsubscribe function. */
  19. subscribe: (listener: (...args: Args) => void) => () => void
  20. /** Call all subscribed listeners with the given arguments. */
  21. emit: (...args: Args) => void
  22. /** Remove all listeners. Useful in dispose/reset paths. */
  23. clear: () => void
  24. }
  25. export function createSignal<Args extends unknown[] = []>(): Signal<Args> {
  26. const listeners = new Set<(...args: Args) => void>()
  27. return {
  28. subscribe(listener) {
  29. listeners.add(listener)
  30. return () => {
  31. listeners.delete(listener)
  32. }
  33. },
  34. emit(...args) {
  35. for (const listener of listeners) listener(...args)
  36. },
  37. clear() {
  38. listeners.clear()
  39. },
  40. }
  41. }