← All @molecule/* packages · App templates
@molecule/app-mapsFeature · maps · App (browser) · v1.0.1 · Apache-2.0
Map provider contract (markers, overlays, viewport, events) with a built-in placeholder provider — bring your own map SDK provider
npm install @molecule/app-maps@molecule/app-maps is a ready-made maps feature for the app (browser) side. It composes the core interfaces it needs, so it works with whichever providers your app has bonded.
import { createSimpleMapProvider, setProvider, getProvider } from '@molecule/app-maps'
// Development placeholder (renders a grey panel, NOT a real map).
// For production, implement MapProvider against your map SDK and wire that
// provider here instead.
setProvider(createSimpleMapProvider())
const container = document.getElementById('map')
if (container) {
const map = await getProvider().createMap({
container, // must have an explicit CSS height
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
})
map.addMarker({ id: 'hq', position: { lat: 37.7749, lng: -122.4194 }, title: 'HQ' })
map.on('click', (e) => console.log('clicked', e))
}Providers (1): @molecule/app-maps-leaflet
Works with: @molecule/app-bond, @molecule/app-i18n
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Map provider contract for molecule.dev — a unified, framework-agnostic API (create map, viewport control, markers, polylines/polygons/circles, popups, events, geocoding hooks) designed so different map SDKs can sit behind one interface.
Exports the MapProvider / MapInstance / config types, the bond-wiring
helpers (provider, setProvider, getProvider, hasProvider,
createMap), geometry utilities (calculateBounds, calculateCenter,
calculateDistance), and createSimpleMapProvider().
IMPORTANT: for a REAL map, bond @molecule/app-maps-leaflet (Leaflet +
OpenStreetMap tiles, no API key): import 'leaflet/dist/leaflet.css' once,
then import { provider } from '@molecule/app-maps-leaflet' +
setProvider(provider) (equivalent to bond('maps', provider)) at startup.
The built-in createSimpleMapProvider() is only a PLACEHOLDER — it renders a
static grey panel ("Map Placeholder", tagged data-mol-map-placeholder)
instead of a map and console.warns once when it engages (markers tracked in
memory but never drawn, overlays/events no-ops). Use the placeholder only in
tests / before the real provider is wired. (To use a different SDK — MapLibre,
Mapbox GL, Google Maps — implement MapProvider against it; but the Leaflet
bond covers the common "show a map with pins" case with zero config.)
import { createSimpleMapProvider, setProvider, getProvider } from '@molecule/app-maps'
// Development placeholder (renders a grey panel, NOT a real map).
// For production, implement MapProvider against your map SDK and wire that
// provider here instead.
setProvider(createSimpleMapProvider())
const container = document.getElementById('map')
if (container) {
const map = await getProvider().createMap({
container, // must have an explicit CSS height
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
})
map.addMarker({ id: 'hq', position: { lat: 37.7749, lng: -122.4194 }, title: 'HQ' })
map.on('click', (e) => console.log('clicked', e))
}
feature
npm install @molecule/app-maps @molecule/app-bond @molecule/app-i18n
BoundsGeographic bounds (bounding box).
interface Bounds {
/**
* Northeast corner.
*/
ne: Coordinates
/**
* Southwest corner.
*/
sw: Coordinates
}
CircleConfigMap circle overlay (center, radius in meters, fill/stroke colors).
interface CircleConfig {
/**
* Unique ID.
*/
id: string
/**
* Center position.
*/
center: Coordinates
/**
* Radius in meters.
*/
radius: number
/**
* Fill color.
*/
fillColor?: string
/**
* Fill opacity (0-1).
*/
fillOpacity?: number
/**
* Stroke color.
*/
strokeColor?: string
/**
* Stroke width.
*/
strokeWidth?: number
/**
* Additional metadata.
*/
meta?: Record<string, unknown>
}
CoordinatesLatitude/longitude pair with optional altitude.
interface Coordinates {
/**
* Latitude.
*/
lat: number
/**
* Longitude.
*/
lng: number
/**
* Altitude (optional).
*/
alt?: number
}
MapClickEventMap click event data.
interface MapClickEvent {
/**
* Original event.
*/
originalEvent: MouseEvent
/**
* Coordinates.
*/
coordinates: Coordinates
/**
* Point on screen.
*/
point: { x: number; y: number }
}
MapConfigMap initialization options (container, center, zoom, style, interaction controls, bounds).
interface MapConfig {
/**
* Container element.
*/
container: HTMLElement | string
/**
* Initial center.
*/
center?: Coordinates
/**
* Initial zoom level.
*/
zoom?: number
/**
* Min zoom level.
*/
minZoom?: number
/**
* Max zoom level.
*/
maxZoom?: number
/**
* Initial bearing.
*/
bearing?: number
/**
* Initial pitch.
*/
pitch?: number
/**
* Map style (URL or style object).
*/
style?: string | Record<string, unknown>
/**
* Enable scroll zoom.
*/
scrollZoom?: boolean
/**
* Enable drag pan.
*/
dragPan?: boolean
/**
* Enable drag rotate.
*/
dragRotate?: boolean
/**
* Enable double-click zoom.
*/
doubleClickZoom?: boolean
/**
* Enable keyboard navigation.
*/
keyboard?: boolean
/**
* Enable touch zoom/rotate.
*/
touchZoomRotate?: boolean
/**
* Enable touch pitch.
*/
touchPitch?: boolean
/**
* Max bounds (pan limits).
*/
maxBounds?: Bounds
/**
* Render world copies.
*/
renderWorldCopies?: boolean
/**
* Attribution control.
*/
attributionControl?: boolean
/**
* Locale for controls.
*/
locale?: string
}
MapInstanceLive map instance providing viewport control, marker/layer management, and event handling.
interface MapInstance {
/**
* Gets the current viewport.
*/
getViewport(): Viewport
/**
* Sets the viewport.
*/
setViewport(viewport: Partial<Viewport>, options?: { animate?: boolean; duration?: number }): void
/**
* Flies to a location.
*/
flyTo(
location: Partial<Viewport>,
options?: { duration?: number; curve?: number; easing?: (t: number) => number },
): void
/**
* Fits the map to bounds.
*/
fitBounds(bounds: Bounds, options?: { padding?: number; animate?: boolean }): void
/**
* Gets the current bounds.
*/
getBounds(): Bounds
/**
* Adds a marker.
*/
addMarker(marker: MarkerConfig): void
/**
* Removes a marker.
*/
removeMarker(id: string): void
/**
* Updates a marker.
*/
updateMarker(id: string, updates: Partial<MarkerConfig>): void
/**
* Gets all markers.
*/
getMarkers(): MarkerConfig[]
/**
* Clears all markers.
*/
clearMarkers(): void
/**
* Adds a polyline.
*/
addPolyline(polyline: PolylineConfig): void
/**
* Removes a polyline.
*/
removePolyline(id: string): void
/**
* Adds a polygon.
*/
addPolygon(polygon: PolygonConfig): void
/**
* Removes a polygon.
*/
removePolygon(id: string): void
/**
* Adds a circle.
*/
addCircle(circle: CircleConfig): void
/**
* Removes a circle.
*/
removeCircle(id: string): void
/**
* Opens a popup.
*/
openPopup(popup: PopupConfig): void
/**
* Closes all popups.
*/
closePopups(): void
/**
* Adds an event listener.
*/
on<T = unknown>(event: MapEvent, handler: (data: T) => void): () => void
/**
* Removes an event listener.
*/
off<T = unknown>(event: MapEvent, handler: (data: T) => void): void
/**
* Adds a click listener for a marker.
*/
onMarkerClick(markerId: string, handler: (marker: MarkerConfig) => void): () => void
/**
* Projects coordinates to pixel position.
*/
project(coordinates: Coordinates): { x: number; y: number }
/**
* Unprojects pixel position to coordinates.
*/
unproject(point: { x: number; y: number }): Coordinates
/**
* Resizes the map.
*/
resize(): void
/**
* Gets the map container element.
*/
getContainer(): HTMLElement
/**
* Gets the canvas element.
*/
getCanvas(): HTMLCanvasElement
/**
* Takes a screenshot.
*/
getSnapshot(options?: { width?: number; height?: number }): Promise<string>
/**
* Gets the underlying map instance.
*/
getInstance(): unknown
/**
* Destroys the map.
*/
destroy(): void
}
MapMoveEventMap move event data.
interface MapMoveEvent {
/**
* New viewport.
*/
viewport: Viewport
/**
* Original event.
*/
originalEvent?: Event
}
MapProviderMap provider interface.
interface MapProvider {
/**
* Create a new map instance with the given configuration.
* @returns A MapInstance that can be used to control the map.
*/
createMap(config: MapConfig): MapInstance | Promise<MapInstance>
/**
* Get the display name of this map provider (e.g., 'Google Maps', 'Mapbox').
* @returns The provider name string.
*/
getName(): string
/**
* Check if the map provider's SDK/library has been loaded and is ready.
* @returns Whether the map provider is loaded and ready to create maps.
*/
isLoaded(): boolean
/**
* Get the list of available map styles for this provider.
* @returns Array of style objects with id, name, and URL.
*/
getStyles(): { id: string; name: string; url: string }[]
/**
* Geocodes an address.
*/
geocode?(address: string): Promise<Coordinates[]>
/**
* Reverse geocodes coordinates.
*/
reverseGeocode?(coordinates: Coordinates): Promise<string>
/**
* Gets directions between points.
*/
getDirections?(
origin: Coordinates,
destination: Coordinates,
options?: { mode?: 'driving' | 'walking' | 'cycling' },
): Promise<{ route: Coordinates[]; duration: number; distance: number }>
}
MarkerConfigMap marker configuration (position, icon, popup, draggable, color, metadata).
interface MarkerConfig {
/**
* Unique ID.
*/
id: string
/**
* Position.
*/
position: Coordinates
/**
* Title (tooltip on hover).
*/
title?: string
/**
* Custom icon URL.
*/
icon?: string
/**
* Icon size.
*/
iconSize?: [number, number]
/**
* Icon anchor point.
*/
iconAnchor?: [number, number]
/**
* Popup content.
*/
popup?: string | HTMLElement
/**
* Draggable.
*/
draggable?: boolean
/**
* Opacity (0-1).
*/
opacity?: number
/**
* Color (for default markers).
*/
color?: string
/**
* Additional metadata.
*/
meta?: Record<string, unknown>
}
PolygonConfigMap polygon overlay (closed path, fill/stroke colors, opacity).
interface PolygonConfig {
/**
* Unique ID.
*/
id: string
/**
* Polygon coordinates.
*/
path: Coordinates[]
/**
* Fill color.
*/
fillColor?: string
/**
* Fill opacity (0-1).
*/
fillOpacity?: number
/**
* Stroke color.
*/
strokeColor?: string
/**
* Stroke width.
*/
strokeWidth?: number
/**
* Stroke opacity (0-1).
*/
strokeOpacity?: number
/**
* Additional metadata.
*/
meta?: Record<string, unknown>
}
PolylineConfigMap polyline overlay (path coordinates, stroke color/width/opacity, dash pattern).
interface PolylineConfig {
/**
* Unique ID.
*/
id: string
/**
* Path coordinates.
*/
path: Coordinates[]
/**
* Stroke color.
*/
strokeColor?: string
/**
* Stroke width.
*/
strokeWidth?: number
/**
* Stroke opacity (0-1).
*/
strokeOpacity?: number
/**
* Dash pattern.
*/
dashArray?: number[]
/**
* Additional metadata.
*/
meta?: Record<string, unknown>
}
PopupConfigMap popup configuration (position, HTML content, offset, close behavior, max width).
interface PopupConfig {
/**
* Position.
*/
position: Coordinates
/**
* Content.
*/
content: string | HTMLElement
/**
* Offset from position.
*/
offset?: [number, number]
/**
* Close button.
*/
closeButton?: boolean
/**
* Close on click.
*/
closeOnClick?: boolean
/**
* Max width.
*/
maxWidth?: number
/**
* Class name.
*/
className?: string
}
ViewportMap viewport state (center coordinates, zoom, bearing, pitch, and bounds).
interface Viewport {
/**
* Center coordinates.
*/
center: Coordinates
/**
* Zoom level.
*/
zoom: number
/**
* Bearing (rotation) in degrees.
*/
bearing?: number
/**
* Pitch (tilt) in degrees.
*/
pitch?: number
}
MapEventMap event types.
type MapEvent =
| 'click'
| 'dblclick'
| 'contextmenu'
| 'mouseenter'
| 'mouseleave'
| 'mousemove'
| 'movestart'
| 'move'
| 'moveend'
| 'zoomstart'
| 'zoom'
| 'zoomend'
| 'load'
| 'idle'
| 'resize'
calculateBounds(coordinates)Calculate the bounding box that contains all given coordinates.
function calculateBounds(coordinates: Coordinates[]): Bounds
coordinates — Array of coordinates to compute bounds for.Returns: The southwest and northeast corners of the bounding box.
calculateCenter(coordinates)Calculate the geographic center (centroid) of multiple coordinates.
function calculateCenter(coordinates: Coordinates[]): Coordinates
coordinates — Array of coordinates to average.Returns: The center point as a Coordinates object.
calculateDistance(from, to)Calculate the Haversine distance between two geographic coordinates.
function calculateDistance(from: Coordinates, to: Coordinates): number
from — The starting coordinates.to — The destination coordinates.Returns: The distance in meters.
createMap(config)Create a new map instance using the current provider.
function createMap(config: MapConfig): MapInstance | Promise<MapInstance>
config — Map configuration (container element, center, zoom, style, etc.).Returns: A MapInstance for controlling the map.
createSimpleMapProvider(options)Create the built-in PLACEHOLDER map provider. It does NOT render a real map:
it draws a static grey panel that identifies itself as a placeholder and
console.warns once when it first engages, because no real map SDK ships with
the fleet. Markers are tracked in memory but never drawn; overlays, popups,
events and projection are no-ops; getSnapshot() returns an empty string. It
exists only so map-using screens render something honest until a real
MapProvider (Mapbox GL / Google Maps / Leaflet / MapLibre) is implemented
and bonded via setProvider().
function createSimpleMapProvider(options?: SimpleMapProviderOptions): MapProvider
options — Optional placeholder title and description text.Returns: A MapProvider that renders an honest, self-identifying placeholder.
getProvider()Get the current map provider. Falls back to the built-in placeholder provider
if none has been bonded — no real map SDK ships with the fleet, so the
placeholder renders a grey panel (not a map) and console.warns once when it
engages rather than silently faking a working map. Use hasProvider() to
detect whether a real provider was actually wired.
function getProvider(): MapProvider
Returns: The active MapProvider instance.
hasProvider()Check if a map provider has been registered.
function hasProvider(): boolean
Returns: Whether a MapProvider has been bonded.
isWithinBounds(coordinates, bounds)Check if a coordinate point falls within a geographic bounding box.
function isWithinBounds(coordinates: Coordinates, bounds: Bounds): boolean
coordinates — The point to test.bounds — The bounding box with southwest and northeast corners.Returns: Whether the point is inside the bounds.
setProvider(provider)Set the map provider.
function setProvider(provider: MapProvider): void
provider — MapProvider implementation to register.defaultStylesDefault Mapbox style URLs for common map appearances.
const defaultStyles: {
streets: string
outdoors: string
light: string
dark: string
satellite: string
satelliteStreets: string
navigationDay: string
navigationNight: string
}
providerDefault map provider — the built-in simple provider. Exported so apps can wire
it with bond('maps', provider) (equivalent to setProvider), matching the
convention of other bondable feature packages (e.g. @molecule/app-data-table-*).
Bond a richer MapProvider instead to replace it.
const provider: MapProvider
Peer dependencies:
@molecule/app-bond ^1.0.1@molecule/app-i18n ^1.0.1@molecule/app-bond
@molecule/app-i18n
getProvider() falls back to the placeholder when nothing is wired — a
forgotten setProvider does not throw; the placeholder renders a grey
panel AND console.warns once (naming the gap and the fix) so the omission
is visible, not silent. Use hasProvider() to detect real wiring.
The map fills 100% of its container: the container element must have an explicit height or the map/placeholder is invisible (a zero-height parent is the classic blank-screen trap).
When config.container is an element-id string, the element must already
exist in the DOM at createMap() time or it crashes.
Placeholder text is localizable via @molecule/app-locales-maps
(maps.placeholder.title / maps.placeholder.description) or the
createSimpleMapProvider({ placeholderTitle, placeholderDescription })
options.
Custom providers must return the normalized MapInstance shape — never
leak the underlying SDK object except through getInstance().
Translation strings are provided by @molecule/app-locales-maps.