Skip to content

antarctica/universal-layer-manager

Universal Layer Manager logo

Universal Layer Manager

License: MIT TypeScript Docs

A state-machine-powered layer management library for map applications. Model your map contents as layers and layer groups, then control visibility, opacity, and ordering from any UI framework and any mapping library.

API documentation →


Packages

Package Version Description
@ulm/core npm Core state machine library — framework and map-library agnostic
@ulm/leaflet npm Leaflet adapter — syncs manager state to a Leaflet map

Features

  • XState actor model: Manager, layers, and layer groups are each XState actors.
  • Layers and layer groups: Model flat lists or nested trees with optional depth control.
  • Framework-agnostic: Pure TypeScript/XState core — works with any UI rendering layer.
  • Visibility and opacity: Per-layer enable/disable and opacity that cascades through parents.
  • Time metadata: Optional LayerTimeInfo using @internationalized/date for date ranges.
  • Typed events: Strongly typed input and output events for reactive UIs.
  • Adapter pattern: Implement LayerManagerAdapter to connect any mapping library.

Quick start

npm install @ulm/core
import { LayerManager } from '@ulm/core';

interface LayerData {
  url: string;
}

const manager = new LayerManager<LayerData>({
  onLayerAdded(info) {
    console.log('added:', info.layerId);
  },
  onVisibilityChanged(info, visible) {
    console.log(info.layerId, 'visible:', visible);
  },
});

manager.addLayer({
  layerConfig: {
    layerId: 'basemap',
    layerName: 'Basemap',
    layerType: 'layer',
    parentId: null,
    layerData: { url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' },
  },
  visible: true,
});

manager.setVisibility('basemap', false);
manager.destroy();

See @ulm/core for the full API reference.

Lower-level access

createLayerManagerMachine is exported for direct XState usage (e.g. integrating with @xstate/react):

import { createLayerManagerMachine } from '@ulm/core';
import { createActor } from 'xstate';

const actor = createActor(createLayerManagerMachine<LayerData>(), {
  input: { allowNestedGroupLayers: true },
});
actor.start();

If you're using LayerManager, the raw actor is also available via manager.actor.


Adapters

LayerManager owns state; adapters subscribe to its emitted events and perform map-library side-effects (add/remove layers, sync visibility, opacity). Implement the LayerManagerAdapter interface and attach it with manager.setAdapter(adapter).

import { LeafletLayerManagerAdapter } from '@ulm/leaflet';

manager.setAdapter(new LeafletLayerManagerAdapter(map));
// manager.setAdapter(null) detaches and calls adapter.unregister()

See @ulm/leaflet for a complete example, or implement LayerManagerAdapter yourself for other mapping libraries (OpenLayers, Mapbox, etc.).


Examples

Example Description
examples/simple Minimal vanilla TypeScript — demonstrates LayerManager with plain DOM
examples/leaflet React + Leaflet — @ulm/leaflet adapter, nested groups, layer list UI

To run an example:

npm install        # install all workspace dependencies
npm run dev        # starts all dev servers via Turbo

Repository structure

packages/
  core/       @ulm/core — state machine library
  leaflet/    @ulm/leaflet — Leaflet adapter
examples/
  simple/     vanilla TypeScript example
  leaflet/    React + Leaflet example

License

MIT

About

Universal Layer Manager for any Mapping Library or UI Framework

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors