Booking Client (Renderer) API Changes - v1.58.0

Release: v1.58.0
Package: @seatmap.pro/renderer
Date: 2026-01-20


Summary

Breaking Changes: NO
New Features: 4
Deprecations: 0
Bug Fixes: 3
Architecture Improvements: Major refactoring (DataManager, StateManager extraction)

Migration Required: NO - All changes are backwards compatible


New Features

Minimap Height Restriction (SEAT-883)

Status: New

Description: Set a maximum height for the minimap as a percentage of the container height to prevent it from becoming too large on narrow viewports.

API:

interface IMinimapSettings {
  maxHeightPercent?: number;  // NEW: Maximum height as percentage (0-100)
}

Example:

// Before: Minimap could become too tall
const renderer = new SeatmapBookingRenderer({
  minimap: {
    enabled: true,
    width: 200  // Height calculated from aspect ratio, could be very tall
  }
});

// After: Limit minimap height to 30% of container
const renderer = new SeatmapBookingRenderer({
  minimap: {
    enabled: true,
    width: 200,
    maxHeightPercent: 30  // Minimap won't exceed 30% of container height
  }
});

Parameters:

Parameter Type Required Default Description
maxHeightPercent number No undefined Maximum minimap height as percentage of container (0-100)

Behavior:

  • When maxHeightPercent is set and the computed minimap height exceeds this limit, the minimap width is reduced proportionally to preserve the venue aspect ratio
  • Useful for responsive layouts where the container height varies
  • Prevents minimap from dominating the viewport on narrow screens

Browser Support: All modern browsers (Chrome, Firefox, Safari, Edge)

Performance Impact: LOW - Minimal overhead for percentage calculations

Related Files:

  • src/layers/minimap/computeMinimapSize.ts - Size calculation logic
  • src/layers/MinimapLayer.ts - Minimap rendering
  • src/models.ts - Interface definitions

Flex Layout Support

Status: New

Description: Renderer automatically adapts to CSS flexbox layouts using ResizeObserver without manual resize calculations.

What Changed:

  • Added ResizeObserver to detect container size changes
  • Hybrid sizing logic: prioritizes explicit settings, then container dimensions, then window fallback
  • Container dimensions stored in Context for consistent access across layers
  • Only applies explicit dimensions to container when set in settings (allows CSS-based sizing)
  • Removed forced height: 100% from playground CSS to enable flex layouts

API Changes:

interface IRendererSettings {
  width?: number;   // NEW: Explicit width in pixels (optional)
  height?: number;  // EXISTING: Explicit height in pixels (optional)
}

// Internal Context now tracks computed dimensions
interface Context {
  width: number;   // NEW: Computed viewport width
  height: number;  // NEW: Computed viewport height
}

Example:

<style>
  .container {
    display: flex;
    flex-direction: column;
    height: 100vh;
  }
  .header { height: 60px; }
  .seatmap { flex: 1; } /* Renderer adapts automatically */
</style>

<div class="container">
  <div class="header">Header</div>
  <div id="seatmap" class="seatmap"></div>
</div>
// No special configuration needed - renderer detects container size
const renderer = new SeatmapBookingRenderer({
  container: '#seatmap'
  // Automatically works with flex: 1 via ResizeObserver
});

// Or with explicit dimensions (overrides container size)
const renderer = new SeatmapBookingRenderer({
  container: '#seatmap',
  width: 800,   // Fixed width
  height: 600   // Fixed height
});

Sizing Priority:

  1. Explicit settings.width / settings.height
  2. Container clientWidth / clientHeight
  3. Window fallback (height only, for backward compatibility)

Backward Compatibility: Fully compatible with existing implementations

Related Files:

  • src/Renderer.ts - Core sizing logic, ResizeObserver initialization
  • src/Context.ts - Width/height storage
  • src/layers/Layer.ts - Base layer width/height getters
  • All layer classes - Updated to use context.width/context.height

Interaction Zoom Strategy

Status: New

Description: Configure how zoom interactions behave when clicking on the canvas in eagle eye view, with support for progressive zoom strategies.

API:

type InteractionZoomStrategyType = 'default' | 'section' | 'next-scale';
type InteractionZoomStrategy = InteractionZoomStrategyType | InteractionZoomStrategyType[];

interface IRendererSettings {
  interactionZoomStrategy?: InteractionZoomStrategy;
}

Example:

// Single strategy: zoom to clicked section
const renderer = new SeatmapBookingRenderer({
  interactionZoomStrategy: 'section'
});

// Progressive zoom: section first, then next scale step
const renderer = new SeatmapBookingRenderer({
  interactionZoomStrategy: ['section', 'next-scale']
  // First click (eagle view): zooms to section
  // Second click (at section): zooms to next scale step
});

// Explicit fallback
const renderer = new SeatmapBookingRenderer({
  interactionZoomStrategy: ['section', 'default']
  // Tries section first, falls back to scale 1.0
});

Strategy Options:

Strategy Behavior Use Case
'default' Zoom to scale 1.0 Standard zoom behavior
'section' Zoom to clicked section (adaptive fit) Section-focused navigation (only works in eagle view)
'next-scale' Zoom to next scale step in presets Progressive zoom levels

Default: 'default'

Behavior Notes:

  • When an array is provided, strategies are tried in order until one succeeds
  • The 'section' strategy only works in eagle eye view (zoomed out)
  • If already zoomed to a section, clicking again falls through to the next strategy
  • This enables progressive zoom behavior (e.g., section → next-scale → deeper zoom)
  • If all strategies fail, falls back to default zoom (scale 1.0)

Related Files:

  • src/models.ts - Type definitions
  • src/booking-renderer/machine.ts - State machine integration
  • src/Renderer.ts - Zoom execution logic

Visual Markers System

Status: New

Description: Add visual markers to highlight specific seats, sections, or arbitrary points on the venue for user guidance.

API:

type MarkerTarget =
  | { type: 'seat'; seatId: number }
  | { type: 'section'; sectionId: number }
  | { type: 'point'; x: number; y: number };

type MarkerAppearance =
  | { type: 'pin'; color?: string }
  | { type: 'circle'; radius?: number; color?: string; label?: string }
  | { type: 'custom'; svg: string; width?: number; height?: number };

interface IMarker {
  id: string;
  target: MarkerTarget;
  appearance?: MarkerAppearance;
  showOnCanvas?: boolean;
  showOnMinimap?: boolean;
  interactive?: boolean;
  data?: Record<string, unknown>;
}

// Renderer methods
renderer.addMarker(marker: IMarker): void;
renderer.removeMarker(markerId: string): void;
renderer.clearMarkers(): void;
renderer.getMarkers(): IMarker[];
renderer.getResolvedMarkers(): IResolvedMarker[];

Example:

// Add a pin marker to a seat
renderer.addMarker({
  id: 'best-seat-1',
  target: { type: 'seat', seatId: 123 },
  appearance: { type: 'pin', color: '#ff0000' },
  showOnCanvas: true,
  showOnMinimap: true
});

// Add a circle marker to a section
renderer.addMarker({
  id: 'vip-section',
  target: { type: 'section', sectionId: 5 },
  appearance: { 
    type: 'circle', 
    radius: 20, 
    color: '#00ff00',
    label: 'VIP'
  }
});

// Add a custom SVG marker at a specific point
renderer.addMarker({
  id: 'entrance',
  target: { type: 'point', x: 100, y: 200 },
  appearance: { 
    type: 'custom', 
    svg: '<svg>...</svg>',
    width: 32,
    height: 32
  }
});

// Remove specific marker
renderer.removeMarker('best-seat-1');

// Clear all markers
renderer.clearMarkers();

// Get all markers
const markers = renderer.getMarkers();

// Get markers with resolved coordinates
const resolved = renderer.getResolvedMarkers();

Marker Configuration:

interface IMarkerSettings {
  defaultAppearance?: MarkerAppearance;
  defaultShowOnCanvas?: boolean;  // default: true
  defaultShowOnMinimap?: boolean; // default: true
  defaultInteractive?: boolean;   // default: false
}

const renderer = new SeatmapBookingRenderer({
  markers: {
    defaultAppearance: { type: 'pin', color: '#ff0000' },
    defaultShowOnCanvas: true,
    defaultShowOnMinimap: true,
    defaultInteractive: false
  }
});

Related Files:

  • src/markers/MarkerManager.ts - Marker state management
  • src/markers/MarkerResolver.ts - Coordinate resolution
  • src/layers/MarkerLayer.ts - Marker rendering
  • src/models.ts - Type definitions
  • src/Renderer.ts - Public API methods

Data and State Management APIs

Status: New

Description: New public APIs for accessing entity metadata and managing visual states through DataManager and StateManager classes. These were extracted from OutlineLayer as part of the architecture refactoring.

DataManager - Metadata Access

Purpose: Provides read-only access to cached entity metadata with change subscriptions

API:

// Events
type DataManagerEvent = 
  | 'sectionsChanged'
  | 'seatsChanged'
  | 'dataInvalidated'
  | 'dataLoaded';

type DataManagerEventCallback = (entityId?: number) => void;

// Section metadata
interface ISectionMetadata {
  id: number;
  name: string;
  guid: string;
  type?: string;
  elementCount: number;
  seatCount?: number;
  priceId?: number;
  isGa?: boolean;
  disabled?: boolean;
  filtered?: boolean;
  bounds?: { x: number; y: number; width: number; height: number };
  center?: { x: number; y: number };
  centerOutsideViewBox?: boolean;
  outlineSource?: 'svg' | 'shape' | 'auto' | 'fallback';
  states: IEntityStates;
}

// Seat metadata
interface ISeatMetadata {
  id: number;
  key: string;
  sectionId: number;
  sectionName?: string;
  row?: string;
  seat?: string;
  x: number;
  y: number;
  priceId?: number;
  available: boolean;
  locked?: boolean;
  filtered?: boolean;
  inCart: boolean;
  states: IEntityStates;
}

interface IEntityStates {
  highlighted: boolean;
  selected: boolean;
  unavailable: boolean;
  filtered: boolean;
}

Access via Renderer:

const dataManager = renderer.getDataManager();

Example Usage:

// Get the data manager
const dataManager = renderer.getDataManager();

// Subscribe to section changes
dataManager.on('sectionsChanged', (sectionId) => {
  if (sectionId) {
    console.log(`Section ${sectionId} changed`);
  } else {
    console.log('All sections changed');
  }
});

// Get section metadata
const metadata = dataManager.getSectionMetadata(5);
if (metadata) {
  console.log(`Section: ${metadata.name}`);
  console.log(`Seats: ${metadata.seatCount}`);
  console.log(`Center: ${metadata.center?.x}, ${metadata.center?.y}`);
  console.log(`Selected: ${metadata.states.selected}`);
}

// Get all sections
const allSections = dataManager.getAllSectionMetadata();
console.log(`Total sections: ${allSections.length}`);

// Get seat metadata
const seatMeta = dataManager.getSeatMetadata(123);
if (seatMeta) {
  console.log(`Seat: ${seatMeta.key}`);
  console.log(`Available: ${seatMeta.available}`);
  console.log(`In Cart: ${seatMeta.inCart}`);
}

// Get all seats in a section
const sectionSeats = dataManager.getAllSeatMetadata(5);
console.log(`Seats in section: ${sectionSeats.length}`);

// Unsubscribe
const callback = (id) => console.log(`Changed: ${id}`);
dataManager.on('sectionsChanged', callback);
dataManager.off('sectionsChanged', callback);

Events:

  • sectionsChanged - Fired when section data changes (with optional sectionId)
  • seatsChanged - Fired when seat data changes (with optional seatId)
  • dataInvalidated - Fired when all data is invalidated
  • dataLoaded - Fired when new data is loaded

StateManager - State Mutations

Purpose: Manages visual and interaction states for entities with change notifications

API:

// Events
type StateManagerEvent = 'sectionStateChanged' | 'seatStateChanged';
type StateManagerEventCallback = (entityId: number) => void;

// State updates
interface IEntityStates {
  highlighted?: boolean;
  selected?: boolean;
  unavailable?: boolean;
  filtered?: boolean;
}

Access via Renderer:

const stateManager = renderer.getStateManager();

Example Usage:

// Get the state manager
const stateManager = renderer.getStateManager();

// Subscribe to state changes
stateManager.on('sectionStateChanged', (sectionId) => {
  console.log(`Section ${sectionId} state changed`);
  const states = stateManager.getSectionStates(sectionId);
  console.log('New states:', states);
});

// Update section state
stateManager.updateSectionState(5, {
  highlighted: true,
  selected: false
});

// Toggle state
const isHighlighted = stateManager.toggleSectionState(5, 'highlighted');
console.log(`Section 5 highlighted: ${isHighlighted}`);

// Get current states
const states = stateManager.getSectionStates(5);
if (states) {
  console.log(`Highlighted: ${states.highlighted}`);
  console.log(`Selected: ${states.selected}`);
  console.log(`Unavailable: ${states.unavailable}`);
  console.log(`Filtered: ${states.filtered}`);
}

// Clear all states for a section
stateManager.clearSectionStates(5);

// Clear all highlights
stateManager.clearAllHighlights();

// Unsubscribe
const callback = (id) => console.log(`State changed: ${id}`);
stateManager.on('sectionStateChanged', callback);
stateManager.off('sectionStateChanged', callback);

State Types:

  • highlighted - Temporary hover/focus state
  • selected - Persistent selection state
  • unavailable - Disabled/unavailable state
  • filtered - Hidden by filter state

Integration Notes

Architecture:

  • Both managers were extracted from OutlineLayer during refactoring
  • DataManager provides read-only metadata access
  • StateManager handles state mutations
  • Both support event subscriptions for reactive programming
  • Changes automatically propagate to rendering layers

Use Cases:

  • DataManager:

    • Build custom UI panels with live section/seat information
    • Export venue data for analytics
    • Monitor data changes in real-time
    • Build reactive UI components
    • Access computed metadata (bounds, centers, states)
  • StateManager:

    • Programmatic state control for sections
    • Custom interaction patterns
    • Automated testing and demos
    • External state synchronization
    • Custom highlight/selection behaviors

Public API Access:

// Both managers are accessible via public methods
const dataManager = renderer.getDataManager();
const stateManager = renderer.getStateManager();

Related Commit: 37007f45 - “refactor: extract DataManager and StateManager, decompose OutlineLayer”

Related Files:

  • src/DataManager.ts - NEW: Entity metadata management
  • src/StateManager.ts - NEW: Entity state management
  • src/layers/OutlineLayer.ts - Refactored to use managers
  • src/Renderer.ts - Manager initialization and lifecycle
  • src/models.ts - Type definitions

Bug Fixes

Resize Observer Memory Leak

Issue: ResizeObserver not properly cleaned up in destroy() method, causing memory leaks in long-running applications

Impact: Long-running pages with multiple renderer instances or frequent create/destroy cycles

Fixed In: v1.58.0

Root Cause: ResizeObserver instance was created but not disconnected during cleanup

Fix:

// Added in Renderer.ts destroy() method
if (this.resizeObserver) {
  this.resizeObserver.disconnect();
  this.resizeObserver = undefined;
}

Behavior Change:

  • Before: Memory usage increased over time with each renderer instance
  • After: Proper cleanup on destroy() - observer disconnected and reference cleared

User Action Required: NO - Automatic improvement

Related Commit: efc51022 - “Renderer sizing behaviour improved (flex support)”


Filtering Functionality

Issue: Playground filtering didn’t work correctly in some scenarios

Impact: Development/testing only (playground environment)

Fixed In: v1.58.0

Root Cause: Filter state management issue in React playground

Related Commit: 74cce1ad - “Filtering in the Playground fixed”


Outline Rendering

Issue: Section outlines not rendered correctly after refactoring, sometimes disappeared or rendered incorrectly

Impact: Visual display of section outlines and boundaries

Fixed In: v1.58.0

Root Cause: Refactoring of OutlineLayer broke rendering pipeline

Fix: Major refactoring to decompose OutlineLayer into specialized modules:

  • DataManager - Data state management
  • StateManager - Rendering state management
  • Specialized outline style modules

Behavior Change:

  • Before: Outlines sometimes disappeared or rendered incorrectly after zoom/pan
  • After: Consistent, reliable outline rendering with better code organization

Related Commits:

  • 37007f45 - “refactor: extract DataManager and StateManager, decompose OutlineLayer”
  • 37b004d0 - “Outlines refactoring in Renderer”
  • 887e8613 - “Outlines appearance fixed”

Related Files:

  • src/DataManager.ts - NEW: Extracted data management
  • src/StateManager.ts - NEW: Extracted state management
  • src/layers/OutlineLayer.ts - Refactored and simplified

Configuration Changes

Constructor Options

New Options

interface IRendererSettings {
  // New in v1.58.0
  width?: number;  // Explicit width in pixels
  interactionZoomStrategy?: InteractionZoomStrategy;
  markers?: IMarkerSettings;
  
  // Enhanced in v1.58.0
  minimap?: IMinimapSettings;  // Added maxHeightPercent property
}

interface IMinimapSettings {
  maxHeightPercent?: number;  // NEW: Maximum height as percentage (0-100)
  // ... existing properties
}

interface IMarkerSettings {
  defaultAppearance?: MarkerAppearance;
  defaultShowOnCanvas?: boolean;
  defaultShowOnMinimap?: boolean;
  defaultInteractive?: boolean;
}

No removed or deprecated options


Method Changes

New Methods

addMarker(marker)

addMarker(marker: IMarker): void

Purpose: Add a visual marker to highlight seats, sections, or points

Parameters:

Name Type Required Description
marker IMarker Yes Complete marker configuration
marker.id string Yes Unique marker identifier
marker.target MarkerTarget Yes Target location (seat/section/point)
marker.appearance MarkerAppearance No Visual appearance (defaults to settings)
marker.showOnCanvas boolean No Show on main canvas (default: true)
marker.showOnMinimap boolean No Show on minimap (default: true)
marker.interactive boolean No Enable interactions (default: false)
marker.data Record<string, unknown> No Custom data attached to marker

Returns: void

Example:

renderer.addMarker({
  id: 'best-seat-1',
  target: { type: 'seat', seatId: 123 },
  appearance: { type: 'pin', color: '#ffcc00' },
  showOnCanvas: true,
  showOnMinimap: true
});

Notes:

  • Marker ID must be unique
  • Target can be seat (by ID), section (by ID), or arbitrary point (x, y coordinates)
  • Appearance defaults to settings.markers.defaultAppearance if not provided

removeMarker(markerId)

removeMarker(markerId: string): void

Purpose: Remove a specific marker by ID

Parameters:

Name Type Required Description
markerId string Yes Marker ID (from IMarker.id)

Example:

renderer.removeMarker('best-seat-1');

Notes: Silently succeeds if marker doesn’t exist

clearMarkers()

clearMarkers(): void

Purpose: Remove all markers from the renderer

Example:

renderer.clearMarkers();

Notes: Optimized to skip work if no markers exist

getMarkers()

getMarkers(): IMarker[]

Purpose: Get all current markers

Returns: Array of all marker objects

Example:

const markers = renderer.getMarkers();
console.log(`Total markers: ${markers.length}`);

getResolvedMarkers()

getResolvedMarkers(): IResolvedMarker[]

Purpose: Get all markers with resolved screen coordinates

Returns: Array of markers with computed x, y positions

Example:

const resolved = renderer.getResolvedMarkers();
resolved.forEach(({ marker, x, y }) => {
  console.log(`Marker ${marker.id} at (${x}, ${y})`);
});

Notes: Only returns markers that could be successfully resolved (e.g., seat exists, section exists)

getDataManager()

getDataManager(): DataManager

Purpose: Get the data manager for accessing entity metadata and subscribing to data changes

Returns: DataManager instance

Example:

const dataManager = renderer.getDataManager();

// Get section metadata
const section = dataManager.getSectionMetadata(5);

// Subscribe to changes
dataManager.on('sectionsChanged', (sectionId) => {
  console.log('Section changed:', sectionId);
});

Notes:

  • Provides read-only access to cached metadata
  • Supports event subscriptions for reactive updates
  • See “Data and State Management APIs” section for full API

getStateManager()

getStateManager(): StateManager

Purpose: Get the state manager for manipulating visual states and subscribing to state changes

Returns: StateManager instance

Example:

const stateManager = renderer.getStateManager();

// Toggle section highlight
stateManager.toggleSectionState(5, 'highlighted');

// Subscribe to state changes
stateManager.on('sectionStateChanged', (sectionId) => {
  console.log('State changed:', sectionId);
});

Notes:

  • Manages highlighted, selected, unavailable, and filtered states
  • Supports event subscriptions for reactive updates
  • See “Data and State Management APIs” section for full API

Type Definitions

New Types

// Marker system types
export type MarkerTarget =
  | { type: 'seat'; seatId: number }
  | { type: 'section'; sectionId: number }
  | { type: 'point'; x: number; y: number };

export type MarkerAppearance =
  | { type: 'pin'; color?: string }
  | { type: 'circle'; radius?: number; color?: string; label?: string }
  | { type: 'custom'; svg: string; width?: number; height?: number };

export interface IMarker {
  id: string;
  target: MarkerTarget;
  appearance?: MarkerAppearance;
  showOnCanvas?: boolean;
  showOnMinimap?: boolean;
  interactive?: boolean;
  data?: Record<string, unknown>;
}

export interface IMarkerSettings {
  defaultAppearance?: MarkerAppearance;
  defaultShowOnCanvas?: boolean;
  defaultShowOnMinimap?: boolean;
  defaultInteractive?: boolean;
}

export interface IResolvedMarker {
  marker: IMarker;
  x: number;
  y: number;
}

// Zoom strategy types
export type InteractionZoomStrategyType = 'default' | 'section' | 'next-scale';
export type InteractionZoomStrategy = InteractionZoomStrategyType | InteractionZoomStrategyType[];

Modified Types

// Updated in v1.58.0
export interface IRendererSettings {
  width?: number;  // NEW: Explicit width in pixels
  interactionZoomStrategy?: InteractionZoomStrategy;  // NEW
  markers?: IMarkerSettings;  // NEW
  minimap?: IMinimapSettings;  // ENHANCED
  // ... existing properties
}

export interface IMinimapSettings {
  maxHeightPercent?: number;  // NEW: Maximum height as percentage
  // ... existing properties
}

Performance Changes

Benchmarks

Operation v1.57.0 (ms) v1.58.0 (ms) Change
Initial render ~250 ~245 Slightly faster
Resize handling ~50 ~35 30% faster (ResizeObserver)
Outline rendering ~80 ~75 Slightly faster (refactoring)

Note: Benchmarks are approximate and vary by venue complexity and device

Memory Usage

Improvements:

  • Fixed memory leak in ResizeObserver (proper cleanup in destroy())
  • Reduced memory footprint through better component separation (DataManager, StateManager)
  • Estimated ~10% reduction in memory usage for long-running instances
  • Better garbage collection due to proper observer cleanup

Impact: Most noticeable in:

  • Single-page applications with long sessions
  • Applications that create/destroy multiple renderer instances
  • Mobile devices with limited memory

Bundle Size

  • Main Bundle: ~156 KB minified (no significant change from v1.57.0)
  • WebGL Module: ~45 KB minified (no change)
  • Total Minified + Gzipped: ~58 KB (no significant change)

Note: Minor increases due to new features (markers, zoom strategies) offset by refactoring improvements


Migration Guide

Quick Migration

From v1.57.0 to v1.58.0

  1. Update Package

    npm install @seatmap.pro/renderer@1.58.0
    # or
    yarn add @seatmap.pro/renderer@1.58.0
    
  2. No Code Changes Required

    • All changes are backwards compatible
    • Existing code will continue to work
  3. Optional: Use New Features

    // Try minimap height restriction
    const renderer = new SeatmapBookingRenderer({
      minimap: {
        enabled: true,
        width: 200,
        maxHeightPercent: 30  // NEW: Limit minimap height
      }
    });
    
    // Try new zoom strategy
    const renderer = new SeatmapBookingRenderer({
      interactionZoomStrategy: ['section', 'next-scale']  // NEW: Progressive zoom
    });
    
    // Try markers
    renderer.addMarker({
      id: 'marker-1',
      target: { type: 'seat', seatId: 123 },
      appearance: { type: 'pin', color: '#ff0000' }
    });
    

No Breaking Changes

Yes - All existing code continues to work
Yes - No TypeScript errors
Yes - No configuration changes required
Yes - No event handler updates needed


Examples

Basic Usage with New Features

import { SeatmapBookingRenderer } from '@seatmap.pro/renderer';

// Initialize with new v1.58.0 options
const renderer = new SeatmapBookingRenderer(
  document.getElementById('seatmap'),
  {
    publicKey: 'your-api-key',
    
    // NEW: Minimap height restriction
    minimap: {
      enabled: true,
      width: 200,
      maxHeightPercent: 30
    },
    
    // NEW: Progressive zoom strategy
    interactionZoomStrategy: ['section', 'next-scale'],
    
    // NEW: Marker defaults
    markers: {
      defaultAppearance: { type: 'pin', color: '#ff0000' },
      defaultShowOnCanvas: true,
      defaultShowOnMinimap: true
    }
  }
);

// Load event data
await renderer.loadEvent('event-123');

// NEW: Use markers feature to highlight best seats
const bestSeats = [123, 124, 125];  // Seat IDs
bestSeats.forEach((seatId, index) => {
  renderer.addMarker({
    id: `best-${seatId}`,
    target: { type: 'seat', seatId },
    appearance: { 
      type: 'circle',
      radius: 15,
      color: '#00ff00',
      label: ''
    }
  });
});

// NEW: Highlight a VIP section
renderer.addMarker({
  id: 'vip-section',
  target: { type: 'section', sectionId: 5 },
  appearance: {
    type: 'pin',
    color: '#ffd700'
  }
});

// Clean up markers when done
setTimeout(() => {
  renderer.clearMarkers();
}, 5000);

Data and State Management Integration

import { SeatmapBookingRenderer } from '@seatmap.pro/renderer';

const renderer = new SeatmapBookingRenderer(
  document.getElementById('seatmap'),
  { publicKey: 'your-api-key' }
);

await renderer.loadEvent('event-123');

// Get managers
const dataManager = renderer.getDataManager();
const stateManager = renderer.getStateManager();

// Build a custom section info panel
function updateSectionPanel(sectionId) {
  const metadata = dataManager.getSectionMetadata(sectionId);
  if (!metadata) return;
  
  const panel = document.getElementById('section-info');
  panel.innerHTML = `
    <h3>${metadata.name}</h3>
    <p>Seats: ${metadata.seatCount || 'N/A'}</p>
    <p>Available: ${metadata.seatCount - metadata.states.unavailable}</p>
    <p>Selected: ${metadata.states.selected ? 'Yes' : 'No'}</p>
    <p>Center: (${metadata.center?.x.toFixed(0)}, ${metadata.center?.y.toFixed(0)})</p>
  `;
}

// Subscribe to section hover events
renderer.onSectionMouseEnter = (section) => {
  // Highlight the section
  stateManager.updateSectionState(section.id, { highlighted: true });
  
  // Update info panel
  updateSectionPanel(section.id);
};

renderer.onSectionMouseLeave = (section) => {
  // Clear highlight
  stateManager.updateSectionState(section.id, { highlighted: false });
};

// Subscribe to state changes for analytics
stateManager.on('sectionStateChanged', (sectionId) => {
  const states = stateManager.getSectionStates(sectionId);
  if (states?.selected) {
    console.log('Section selected for analytics:', sectionId);
    // Send to analytics service
  }
});

// Build a section list with real-time updates
function renderSectionList() {
  const sections = dataManager.getAllSectionMetadata();
  const listEl = document.getElementById('section-list');
  
  listEl.innerHTML = sections
    .filter(s => !s.filtered)
    .map(s => `
      <div class="section-item ${s.states.selected ? 'selected' : ''}">
        <span>${s.name}</span>
        <span>${s.seatCount || 0} seats</span>
        <button onclick="zoomToSection(${s.id})">Zoom</button>
      </div>
    `)
    .join('');
}

// Re-render list when sections change
dataManager.on('sectionsChanged', renderSectionList);
stateManager.on('sectionStateChanged', renderSectionList);

// Initial render
renderSectionList();

// Programmatic section control
window.zoomToSection = (sectionId) => {
  renderer.zoomToSection(sectionId);
  stateManager.updateSectionState(sectionId, { selected: true });
};

// Export venue data
function exportVenueData() {
  const sections = dataManager.getAllSectionMetadata();
  const data = sections.map(s => ({
    id: s.id,
    name: s.name,
    seats: s.seatCount,
    bounds: s.bounds,
    center: s.center,
    selected: s.states.selected
  }));
  
  console.log('Venue data:', data);
  // Download as JSON, send to API, etc.
  return data;
}

Flex Layout Integration

<!DOCTYPE html>
<html>
<head>
  <style>
    body { margin: 0; font-family: Arial; }
    .app {
      display: flex;
      flex-direction: column;
      height: 100vh;
    }
    .header {
      background: #333;
      color: white;
      padding: 1rem;
    }
    .main {
      flex: 1;
      display: flex;
    }
    .sidebar {
      width: 250px;
      background: #f0f0f0;
      padding: 1rem;
    }
    .seatmap {
      flex: 1;
      /* Renderer adapts automatically */
    }
  </style>
</head>
<body>
  <div class="app">
    <div class="header">My Booking App</div>
    <div class="main">
      <div class="sidebar">Filters...</div>
      <div id="seatmap" class="seatmap"></div>
    </div>
  </div>
  
  <script>
    // No special configuration needed for flex
    const renderer = new SeatmapBookingRenderer({
      container: '#seatmap'
      // Automatically works with flex layout
    });
  </script>
</body>
</html>


Support

Questions or Issues?

Reporting Bugs

Include:

  • Renderer version: 1.58.0
  • Browser and version
  • Minimal reproduction code
  • Console errors
  • Steps to reproduce