ef.js

Declarative DOM helper experiment

API

Interface types for Signals

export interface Disposable {
  // Stop reactivity on the object
  dispose(): void;

  // ...internal members
}

export interface Computation {
  // ...internal members
}

export interface ReactiveValue<T> {
  // Get the containing value and associate a surrounding or specified Computation
  // to the ReactiveValue.
  get(computation?: Computation): T;

  // Alias for `get()`
  get value(): T;

  // Get the containing value without associating to any Computations.
  once(): T;

  // ...internal members
}

Signal class

export class Signal<T> implements Disposable, ReactiveValue<T> {
  constructor(initialValue: T);

  get(computation?: Computation): T;

  // Set a new value and triggers updates.
  set(newValue: T): void;

  // Update a containing value based on the previous value.
  update(fn: (prevValue: T) => T): void;

  // Get or Set a containing value.
  value: T;

  once(): T;

  dispose(): void;
}

// Alias for `new Signal(initialValue)`
export function signal<T>(initialValue: T): Signal<T>;

Derived class

export class Derived<T> implements Disposable, Computation, ReactiveValue<T> {
  constructor(compute: DerivedValueComputeFunction<T>);

  get(computation?: Computation): T;

  readonly value: T;

  once(): T;

  dispose(): void;
}

// Alias for `new Derived(compute)`
export function derived<T>(compute: DerivedValueComputeFunction<T>): Derived<T>;

Supplimental types

type DerivedValueComputeFunction<T> = (computation: Computation) => T;

Effect class

export class Effect implements Disposable, Computation {
  constructor(fn: EffectFunction);

  dispose(): void;
}

// Alias for `new Effect(fn)`
export function effect(fn: EffectFunction): Effect;

Supplimental types

type EffectCleanupCallback = () => void;

type EffectFunction = (
  computation: Computation,
) => void | EffectCleanupCallback;

Signals helpers

// This helper is especially useful for writing reusable UIs (or components).
export type ReactiveOrStatic<T> = T | ReactiveValue<T>;

// Returns true if `x` is ReactiveValue, false otherwise.
export function isReactive<T>(x: ReactiveOrStatic<T>): x is ReactiveValue<T>;

// Creates a ReactiveValue that tracks Promise state changes.
export function asyncDerived<T, E = unknown>(
  fn: (ctx: Computation, signal: AbortSignal) => Promise<T>,
): ReactiveValue<PromiseSnapshot<T, E>>;

Supplimental types

// The promise is not settled.
export interface Pending {
  isSettled: false;
  isRejected: false;
  isResolved: false;
}

// The promise is resolved.
export interface Resolved<T> {
  isSettled: true;
  isRejected: false;
  isResolved: true;
  data: T;
}

// The promise is rejected.
export interface Rejected<E> {
  isSettled: true;
  isRejected: true;
  isResolved: false;
  error: E;
}

// State of a Promise at a particular point.
export type PromiseSnapshot<T, E> = Pending | Resolved<T> | Rejected<E>;

DOM node creation

export type NodeChild = Node | string | null | undefined;

export type ElementSetup<T extends Element> = (el: T) => void;

// Creates an HTMLElement
export function el(
  tagName: string,
  setups?: ElementSetup<HTMLElement>[],
  children?: ReactiveOrStatic<NodeChild>[],
): HTMLElement;

// Creates a SVGElement
export function svg(
  tagName: string,
  setups?: ElementSetup<SVGElement>[],
  children?: ReactiveOrStatic<NodeChild>[],
): SVGElement;

// Creates a MathMLElement
export function mathml(
  tagName: string,
  setups?: ElementSetup<MathMLElement>[],
  children?: ReactiveOrStatic<NodeChild>[],
): MathMLElement;

// Creates DocumentFragment
export function fragment(
  children: ReactiveOrStatic<NodeChild>[],
): DocumentFragment;

Element setups

// Set or Remove attribute from an element.
export function attr(
  name: string,
  value: ReactiveOrStatic<string | boolean>,
): ElementSetup<Element>;

// Set value to a property of an element.
export function prop<T extends Element, K extends keyof T = keyof T>(
  name: T,
  value: ReactiveOrStatic<T[K]>,
): ElementSetup<T>;

// Add an event listener to an element.
export function on(
  eventName: string,
  listener: ReactiveOrStatic<(event: Event) => void>,
  options?: AddEventListenerOptions,
): ElementSetup<Element>;

// Set a list of class to an element.
export function classList(
  ...classes: ReactiveOrStatic<string | null>[]
): ElementSetup<Element>;

// Set value to a stylesheet property of an element.
export function style(
  name: string,
  value: ReactiveOrStatic<string | null>,
): ElementSetup<Element>;