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

@alonso-cancino/dcel-map-frontend

v0.9.3

Published

An interactive, zoomable map viewer for hierarchical zone maps. Built with React and D3, this component renders SVG maps generated by [`dcel-map-generator`](https://pypi.org/project/dcel-map-generator/) with smooth zoom, depth-aware progressive detail, ho

Downloads

163

Readme

@alonso-cancino/dcel-map-frontend

An interactive, zoomable map viewer for hierarchical zone maps. Built with React and D3, this component renders SVG maps generated by dcel-map-generator with smooth zoom, depth-aware progressive detail, hover highlights, and zone labels.

Live Demo | GitHub

Installation

npm install @alonso-cancino/dcel-map-frontend

Peer Dependencies

  • react ^18.3.1
  • react-dom ^18.3.1

Quick Example

Minimal usage — just pass a bundle:

import { MapView, loadBundle } from "@alonso-cancino/dcel-map-frontend";
import "@alonso-cancino/dcel-map-frontend/style.css";
import { useEffect, useState } from "react";
import type { FrontendBundle } from "@alonso-cancino/dcel-map-frontend";

function App() {
  const [bundle, setBundle] = useState<FrontendBundle | null>(null);

  useEffect(() => {
    loadBundle("/map_bundle.json").then(setBundle);
  }, []);

  if (!bundle) return <p>Loading...</p>;
  return <MapView bundle={bundle} />;
}

With theme and side panel:

<MapView
  bundle={bundle}
  theme={{ palette: "parchment", texture: "crosshatch", hover: "glow" }}
  panel={{ enabled: true, defaultState: "half", position: "right" }}
  panelContent={<aside>Zone inspector</aside>}
  onZoneClick={(zone) => console.log(zone.name)}
/>

API

<MapView />

The main React component. Renders an interactive SVG map with:

  • D3-powered zoom and pan
  • Depth-aware zone visibility — child zones reveal as you zoom in
  • Hover highlights with zone name tooltips
  • Smooth animated transitions between zoom levels
  • Responsive container sizing
  • Configurable color palettes, SVG textures, and hover animations
  • Optional resizable side panel

Props:

| Prop | Type | Description | |---|---|---| | bundle | FrontendBundle | Parsed map bundle (see loadBundle or parseBundle) | | renderConfig | Partial<MapRenderConfig> | Optional rendering behavior config | | theme | MapTheme | Optional theme config (palette, texture, hover) | | panel | SidePanelConfig | Optional side panel configuration | | panelContent | React.ReactNode | Content rendered inside the side panel | | onZoneClick | (zone: ZoneRecord) => void | Callback when a zone is clicked | | onZoneHover | (zone: ZoneRecord \| null) => void | Callback when the hovered zone changes |

loadBundle(path: string): Promise<FrontendBundle>

Fetch and parse a map bundle JSON file from a URL or path.

const bundle = await loadBundle("/map_bundle.json");

parseBundle(raw: RawFrontendBundle): FrontendBundle

Parse an already-loaded raw JSON object into a FrontendBundle. Useful when you have the JSON data from a source other than fetch.

import rawData from "./my-bundle.json";
const bundle = parseBundle(rawData as RawFrontendBundle);

Theme

The theme prop controls visual appearance. All fields are optional and default to the current look.

<MapView
  bundle={bundle}
  theme={{
    palette: "parchment",
    texture: "dots",
    textureOpacity: 0.2,
    hover: "glow",
  }}
/>

Color Palettes

Set theme.palette to a built-in name or a custom function:

| Palette | Description | |---|---| | "default" | Full hue rotation (current behavior) | | "parchment" | Warm sepia tones | | "forest" | Greens and earth browns | | "ocean" | Blues and teals | | "volcanic" | Reds and dark oranges | | "arctic" | Cool grays and pale blues |

Custom palette function:

<MapView
  bundle={bundle}
  theme={{
    palette: (zoneId, depth) => `hsl(${(zoneId * 30) % 360}, 60%, ${50 + depth * 5}%)`,
  }}
/>

SVG Textures

Set theme.texture to a built-in name or a custom SvgPatternDef:

| Texture | Description | |---|---| | "none" | No texture overlay (default) | | "crosshatch" | Diagonal crosshatch lines | | "dots" | Regular dot grid | | "waves" | Sinusoidal wave pattern | | "parchment-noise" | Irregular scattered marks | | "stipple" | Pseudo-random dot placement |

textureOpacity controls the overlay intensity (0–1, default 0.15).

Custom texture:

<MapView
  bundle={bundle}
  theme={{
    texture: {
      id: "my-pattern",
      width: 10,
      height: 10,
      content: '<circle cx="5" cy="5" r="2" fill="currentColor" />',
    },
    textureOpacity: 0.1,
  }}
/>

Hover Animations

Set theme.hover to a built-in preset or a custom animation:

| Preset | Description | |---|---| | "pulse" | Opacity pulse with golden glow (default) | | "glow" | Expanding golden halo | | "lift" | Slight scale-up with shadow | | "none" | No hover animation |

Custom hover animation:

<MapView
  bundle={bundle}
  theme={{
    hover: {
      className: "my-hover",
      keyframes: `
        @keyframes myEffect {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.6; }
        }
        .region.my-hover.is-hovered {
          animation: myEffect 1s ease infinite;
        }
      `,
    },
  }}
/>

Side Panel

The panel prop enables a resizable side panel alongside the map. The map viewport automatically resizes to accommodate the panel.

<MapView
  bundle={bundle}
  panel={{
    enabled: true,
    defaultState: "half",
    position: "right",
  }}
  panelContent={<div>Zone details here</div>}
/>

SidePanelConfig:

| Field | Type | Default | Description | |---|---|---|---| | enabled | boolean | — | Whether the panel is rendered | | defaultState | PanelState | "hidden" | Initial state (uncontrolled mode) | | state | PanelState | — | Controlled panel state | | onStateChange | (state: PanelState) => void | — | Called when the panel state changes | | position | "left" \| "right" | "right" | Which side the panel appears on |

PanelState: "hidden" | "half" | "full"

The panel supports both controlled and uncontrolled modes. Pass state to control it externally, or use defaultState to let the component manage its own state. A toggle button on the panel edge cycles through the three states.

Render Config

The renderConfig prop controls rendering engine behavior:

<MapView
  bundle={bundle}
  renderConfig={{
    detailCommitMode: "animation-frame",
    interactionSettleMs: 80,
    disableEffectsWhileInteracting: true,
  }}
/>

| Field | Type | Default | Description | |---|---|---|---| | detailCommitMode | "animation-frame" \| "interaction-end" | "animation-frame" | When to commit viewport detail updates | | interactionSettleMs | number | 80 | Delay after interaction before settling | | disableEffectsWhileInteracting | boolean | true | Disable hover/focus effects during pan/zoom |

Event Callbacks

<MapView
  bundle={bundle}
  onZoneClick={(zone) => console.log("Clicked:", zone.name)}
  onZoneHover={(zone) => console.log("Hovered:", zone?.name ?? "none")}
/>

Both callbacks receive a ZoneRecord (or null for onZoneHover when the pointer leaves all zones).

TypeScript Types

FrontendBundle

The parsed bundle consumed by MapView:

interface FrontendBundle {
  rootId: number;
  maxDepth: number;
  worldBBox: [number, number, number, number];
  zoomDepthThresholds: Map<number, number>;
  levels: Map<number, number[]>;
  borders: BorderRecord[];
  zones: Map<number, ZoneRecord>;
}

ZoneRecord

A single zone (territory) in the map:

interface ZoneRecord {
  id: number;
  name: string;
  parentId: number | null;
  depth: number;
  childIds: number[];
  isLeaf: boolean;
  bbox: [number, number, number, number];
  area: number;
  path: string;              // SVG path data
  childrenRevealDepth: number | null;
}

BorderRecord

A shared border between two zones:

interface BorderRecord {
  id: string;
  zoneIds: [number, number];
  path: string;              // SVG path data
}

MapTheme

interface MapTheme {
  palette?: BuiltinPalette | ((zoneId: number, depth: number) => string);
  texture?: BuiltinTexture | SvgPatternDef;
  textureOpacity?: number;
  hover?: BuiltinHoverPreset | CustomHoverAnimation;
}

SvgPatternDef

interface SvgPatternDef {
  id: string;
  width: number;
  height: number;
  content: string; // SVG markup for the pattern
}

CustomHoverAnimation

interface CustomHoverAnimation {
  className: string;
  keyframes?: string;
  style?: Record<string, string>;
}

SidePanelConfig

interface SidePanelConfig {
  enabled: boolean;
  defaultState?: PanelState;
  state?: PanelState;
  onStateChange?: (state: PanelState) => void;
  position?: "left" | "right";
}

Preset Constants

The library exports arrays of available built-in options:

import {
  BUILTIN_PALETTES,       // ["default", "parchment", "forest", "ocean", "volcanic", "arctic"]
  BUILTIN_TEXTURES,       // ["none", "crosshatch", "dots", "waves", "parchment-noise", "stipple"]
  BUILTIN_HOVER_PRESETS,  // ["pulse", "glow", "lift", "none"]
} from "@alonso-cancino/dcel-map-frontend";

Raw Types

RawFrontendBundle, RawZoneRecord, and RawBorderRecord represent the JSON wire format (snake_case keys) before parsing. These are useful if you need to work with the raw JSON directly.

Generating Map Bundles

Map bundles are generated by the Python package dcel-map-generator:

pip install dcel-map-generator

dcel-map \
  --zone-edges zone_edges.json \
  --tree-stats zone_tree_stats.json \
  --zone-index zone_index.json \
  --frontend-bundle map_bundle.json

Or via the Python API:

from dcel_builder import generate_frontend_bundle

bundle, report = generate_frontend_bundle(
    "zone_edges.json",
    "zone_tree_stats.json",
    "zone_index.json",
    seed=42,
)

CSS

Import the stylesheet to get default zone and border styling:

import "@alonso-cancino/dcel-map-frontend/style.css";

License

MIT