npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mappedin/static-maps

v6.31.0-beta.0

Published

A POC SDK that produces styled, top-down SVG renderings of a map floor from a [`MapData`](https://www.npmjs.com/package/@mappedin/mappedin-js) instance.

Readme

@mappedin/static-maps

A POC SDK that produces styled, top-down SVG renderings of a map floor from a MapData instance.

Usage

Installation

With NPM:

npm install @mappedin/static-maps

With Yarn:

yarn add @mappedin/static-maps

Getting Started

import { getMapData } from '@mappedin/mappedin-js';
import { StaticMaps } from '@mappedin/static-maps';

const mapData = await getMapData({ key, secret, mapId });
const staticMaps = new StaticMaps(mapData);

const svg = staticMaps.getSVG(mapData.getByType('floor')[0], {
	cameraTransform: { pitch: 0, bearing: 0, zoomLevel: 16 },
	width: 600,
	height: 400,
});

document.getElementById('map')!.innerHTML = svg;

The cameraTransform uses the same units as mapView.Camera:

  • pitch — degrees (0 = top-down).
  • bearing — degrees.
  • zoomLevel — mercator zoom level (0–24). mappedin-demo-mall fits at ~16.

extras/basic-app

@mappedin/static-maps/extras/basic-app ships a fully-wired, printed-map app on top of the SDK. Instead of calling getSVG yourself, you describe the page with a JSON config — two side-by-side floor columns (each [SVG | legend]), an optional QR info card, per-category label rules, and a "you are here" marker — and BasicStaticApp loads the map, runs the label pipeline, and mounts the page for you. Call export() to trigger the high-res, Illustrator-compatible SVG download from your own button.

mapData accepts either getMapData credentials (the app loads the venue for you) or a ready MapData instance you already have. When you pass credentials, the key / secret are reused for the optional popularity ranking call; a pre-built MapData instance skips that call since no credentials are available.

This entry point pulls in the qrcode package (a runtime dependency of @mappedin/static-maps) only when you import extras/basic-app. Core @mappedin/static-maps consumers that never import the basic-app do not need to use it.

import { getMapData } from '@mappedin/mappedin-js';
import { BasicStaticApp, cornersToPolygon } from '@mappedin/static-maps/extras/basic-app';

// Option A: pass credentials and let BasicStaticApp call getMapData for you.
// const mapData = { key, secret, mapId };

// Option B: reuse a MapData instance you already loaded.
const mapData = await getMapData({ key, secret, mapId });

const app = new BasicStaticApp({
	mapData,
	container: '#map', // HTMLElement or selector
	config: {
		// Exactly two columns; the first renders on the left.
		columns: [
			{ floorId: 'm_floor_1', showQRCard: true },
			{
				floorId: 'm_floor_2',
				// Optionally crop a column to a sub-region and label it.
				crop: {
					name: 'Food Court',
					bbox: [-79.39, 43.64, -79.38, 43.65], // [minLon, minLat, maxLon, maxLat]
				},
				// Group locations into labelled zones in the legend.
				zones: [
					{
						name: 'East Wing',
						polygon: cornersToPolygon([
							[43.6487, -79.3899],
							[43.6491, -79.3881],
						]),
					},
				],
			},
		],
		qrCode: {
			url: 'https://app.mappedin.com/',
			heading: 'Looking for information?',
			body: 'Scan the QR code to open the live map.',
		},
		// Per-category rendering rules; unlisted categories render as numbered pills.
		categoryRules: {
			Washrooms: { kind: 'icon-on-map-only', iconSvg: '<svg viewBox="0 0 24 24">…</svg>', perSpace: true },
			Parking: { kind: 'name-on-map-only' },
			Walls: { kind: 'ignored' },
		},
		youAreHere: {
			coordinate: { latitude: 43.6489, longitude: -79.389 },
			floorId: 'm_floor_1',
			bearing: 90,
		},
	},
});

await app.render();

// Wire export() to your own UI affordance (button, menu item, shortcut).
document.getElementById('download')!.addEventListener('click', () => app.export());

Call app.destroy() to empty the container and release the cached export data.

Styling, camera & ranking

Beyond the layout, the config exposes three optional blocks for fine-tuning how each floor renders. All fields have sensible defaults, so add only what you need.

const config: BasicStaticAppConfig = {
	// Schema version of this config. Stamp new configs so the SDK can migrate
	// older payloads forward; omitting it is treated as version 1.
	version: 1,
	columns: [
		/* … */
	],
	qrCode: { url: 'https://app.mappedin.com/' },

	// Pin specific categories above/below the popularity-derived collision rank.
	// 'low' | 'medium' | 'high' still collide; 'always-visible' never hides.
	rankOverrides: {
		Gates: 'high',
		Washrooms: 'low',
	},

	// Camera applies to every column (cropped or not).
	camera: {
		pitch: 15, // degrees; 0 = top-down. Raise to tilt into a 2.5D view.
		extrusionMultiplier: 0.5, // 1 = full MVF height, 0 = flat. Visible only at pitch > 0.
	},

	// Visual effects layered onto each floor map.
	effects: {
		// Soft drop shadow under each floor (offset + blur + colour).
		mapShadow: { x: 0, y: 0, blur: 2 },
		// The border drawn around the whole floor footprint.
		mapOutline: { width: 1, color: '#777777' },
		// The outlines drawn around each individual space polygon.
		polygonOutlines: { color: '#777777', opacity: 0.8 },
	},
};
  • rankByPopularity (boolean, default true) — log-normalise label collision rank by location popularity. Set false to rank every label evenly and skip the analytics network call.
  • rankOverrides (Record<string, BasicStaticAppRank>) — per-category rank overrides keyed by category name. Wins over the popularity rank.
  • camera.pitch / camera.extrusionMultiplier — tilt and wall-height scale, applied to all columns. Extrusion is only visible when pitch > 0.
  • effects.mapShadow — x/y offset, blur, and color of the floor's drop shadow. Omit for a default soft shadow; { blur: 0 } (no offset) disables it.
  • effects.mapOutline — width (0 hides it) and color of the footprint border around the whole floor.
  • effects.polygonOutlines — color and opacity of the per-space outlines (defaults to a darkened version of each space's own fill).

React

BasicStaticApp manages its own DOM, so in React you give it a host <div> ref on mount and let it own the page chrome, label pipeline, and SVG export. A small wrapper component is all you need; use the onReady callback to grab the app instance and wire up your own "download" affordance.

import { useEffect, useRef } from 'react';
import {
	BasicStaticApp,
	type BasicStaticAppConfig,
	type BasicStaticAppOptions,
} from '@mappedin/static-maps/extras/basic-app';

interface BasicStaticAppViewProps extends Omit<BasicStaticAppOptions, 'container'> {
	/** Fires once `render()` resolves, with the live app instance. */
	onReady?: (app: BasicStaticApp) => void;
}

function BasicStaticAppView({ mapData, config, onReady }: BasicStaticAppViewProps) {
	const ref = useRef<HTMLDivElement>(null);
	useEffect(() => {
		if (!ref.current) return;
		const app = new BasicStaticApp({ mapData, container: ref.current, config });
		void app.render().then(() => onReady?.(app));
		return () => app.destroy();
	}, [mapData, config, onReady]);
	return <div ref={ref} style={{ width: '100%', height: '100%' }} />;
}

function App() {
	const appRef = useRef<BasicStaticApp | null>(null);

	// `mapData` accepts credentials (shown here) or a ready `MapData` instance.
	const mapData = { key, secret, mapId };
	const config: BasicStaticAppConfig = {
		columns: [{ floorId: 'm_floor_1', showQRCard: true }, { floorId: 'm_floor_2' }],
		qrCode: { url: 'https://app.mappedin.com/' },
	};

	return (
		<>
			<button type="button" onClick={() => appRef.current?.export()}>
				Download SVG
			</button>
			<BasicStaticAppView mapData={mapData} config={config} onReady={app => (appRef.current = app)} />
		</>
	);
}

Keep config (and mapData) referentially stable — e.g. memoize them or hold them in state — so the effect only re-renders the map when the inputs actually change.