Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

VenueLayoutCanvas

A self-managed semantic-zoom venue map for meters-based layouts. Renders rect / polygon / polyline (roads + closed track band) / circle footprints in a meters viewBox, in painter's-algorithm layers (areas → roads → track → grandstands → objects). It drives its own pan/zoom (wheel / drag / pinch / double-click + a zoom-control cluster), so it knows meters-per-pixel and stays readable at every scale: only landmark labels at fit, more revealed as you zoom (LOD tiers + greedy collision), per-kind icons at object centers, a dot-collapse for tiny features, a scale bar, a north compass, and an optional legend that also filters kinds. Theme-token driven.

Import

import { VenueLayoutCanvas, type LayoutZone, SAMPLE_LAYOUT } from "@roger-emerson/ploxum-venue";

Peer deps: react, react-dom, motion, lucide-react; depends on @roger-emerson/ploxum-utils. SAMPLE_LAYOUT is a ready-made synthetic festival layout for trying it out.

Usage

Give it a sized, overflow-hidden parent — it fills h-full w-full and owns pan/zoom itself (do not wrap it in MapViewport).

<div className="h-[560px] overflow-hidden">
  <VenueLayoutCanvas
    objects={SAMPLE_LAYOUT.objects}
    bounds={SAMPLE_LAYOUT.bounds}
    defaultView={SAMPLE_LAYOUT.defaultView}
    anchorBearingDeg={SAMPLE_LAYOUT.anchorBearingDeg}
    focusedZoneId={focused}
    onZoneSelect={setFocused}
    badgeLabel="Live"
    badgeAccent
    showLegend
    showScaleBar
    showZoomControls
  />
</div>;

For a glanceable mini-map, drop the chrome (showLegend/showScaleBar/showZoomControls default to false) — it still fit-frames and shows landmark labels.

Props

VenueLayoutCanvas

PropTypeDefaultDescription
objectsLayoutZone[]Footprint objects to render.
bounds{ width: number; height: number }Meters extent; the view frames the larger dimension at fit.
defaultView{ center: { x; y }; spanM }Initial framing center (span is overridden to fit bounds).
anchorBearingDegnumber0Compass bearing map-up points toward; rotates the north arrow.
focusedZoneIdstring | nullnullControlled selection (glow + emphasis).
onZoneSelect(id: string | null) => voidSelection handler; null on background click / Escape.
showLabelsbooleantrueZoom-aware LOD labels with collision.
showCompassbooleantrueNorth arrow (rotated by anchorBearingDeg).
showLegendbooleanfalseCollapsible legend — a vertical dropdown menu whose rows toggle per-kind visibility.
showScaleBarbooleanfalseLive meters scale bar (bottom-left).
showZoomControlsbooleanfalse+ / / reset cluster (bottom-right).
isolatedZoneIdstring | nullnullDim every object except this id.
badgeLabel / badgeAccentstring / boolean— / falseTop-left badge text + amber accent.
titlestring"Venue map"Accessible <title>.
classNamestringMerged onto the root <div>.

LayoutZone

{ id, label?, kind, status, footprint, intensity? }. kind is a VenueLayoutKind (stage, gate, vendor, amenity, parking, security, vip, track, grandstand, art, artCar, ride, medical, water, restroom, camping, road, tower, zone) — drives color + icon + layer. footprint is one of:

  • { kind: "rect", center, width, depth, rotationDeg? }
  • { kind: "polygon", points }
  • { kind: "polyline", points, widthM, closed? } — open = road centerline; closed = a band ring (track)
  • { kind: "circle", center, radius }

All coordinates are meters.

Design rules

  • Semantic zoom is the readability mechanism: at fit only stage/track/gate (+ big areas) are labeled; vendor/medical/etc. labels appear as you zoom in, always sized in constant pixels. A greedy collision pass drops any label that would overlap.
  • Small features collapse to an icon marker below ~9px on screen, so nothing becomes an illegible squiggle.
  • The component owns pan/zoom (viewBox-based). It is not wrapped in MapViewport — that's for the rect-only VenueCanvas.
  • Per-kind palette mirrors the e2c2 venue colors, with stage taking the ploxum amber brand; status ≠ ok colors the stroke.

Storybook

Venue/VenueLayoutCanvas ↗