@seatmap.pro/renderer
Version:
Seatmap renderer library for booking and admin interfaces by Seatmap.pro
1,939 lines (1,930 loc) • 97.9 kB
TypeScript
import KDBush from 'kdbush';
import { Machine, Service } from 'robot3';
/**
* Base interface for seat properties.
*/
interface IBaseSeat {
/**
* The unique identifier of the seat.
*/
id: number;
/**
* The ID of the row this seat belongs to.
*/
rowId: number;
/**
* The ID of the sector this seat belongs to.
*/
sectorId: number;
/**
* The X coordinate of the seat within its section.
*/
x: number;
/**
* The Y coordinate of the seat within its section.
*/
y: number;
/**
* The name or number of the seat.
*/
name: string;
/**
* Whether the seat is accessible for people with disabilities.
*/
isAccessible?: boolean;
/**
* Whether the seat is marked with a special status.
*/
isMarked?: boolean;
}
/**
* Base interface for sector properties.
*/
interface IBaseSector {
/**
* The unique identifier of the sector.
*/
id: number;
/**
* The globally unique identifier for the sector.
*/
guid?: string;
/**
* Whether this is a general admission (GA) sector.
*/
isGa: boolean;
/**
* The name of the sector.
*/
name: string;
/**
* The rotation angle of the sector in degrees.
*/
angle?: number | null;
/**
* The type of the sector.
*/
type?: string | null;
/**
* Whether the sector is disabled and cannot be interacted with.
*/
disabled?: boolean;
/**
* Whether the sector is filtered out by current filter criteria.
*/
filtered?: boolean;
/**
* Whether the sector is currently selected.
*/
selected?: boolean;
/**
* Shape metadata for the sector (GA section shapes with text, color, etc.)
*/
shapes?: ReadonlyArray<IShapeMetadata>;
/** Label anchor X offset from section center (SEAT-624) */
labelOffsetX?: number;
/** Label anchor Y offset from section center (SEAT-624) */
labelOffsetY?: number;
/** Label style overrides (SEAT-624) */
labelStyle?: ILabelStyle;
/** Whether the section name label should render (SEAT-895) */
labelVisible?: boolean;
}
/**
* Label style overrides for GA section labels (SEAT-624).
*/
interface ILabelStyle {
fontScale?: number;
color?: string;
opacity?: number;
fontWeight?: 'normal' | 'bold';
background?: string;
visible?: boolean;
showPrice?: boolean;
}
/**
* Shape metadata from the API response.
*/
interface IShapeMetadata {
id?: string;
text?: string;
textColor?: string;
textPosition?: string;
fill?: string;
width?: number;
height?: number;
top?: number;
left?: number;
angle?: number;
order?: number;
fontScale?: number;
}
/**
* Interface representing the schema data transfer object from the API.
* @hidden
*/
interface ISchemaDTO {
plainSeats?: IPlainSeatsDTO;
seats: ISeatDTO[];
rows: IRowDTO[];
sectors: ISectorDTO[];
background: ISVGBackgroundDTO;
requestTime?: number;
responseSize?: number;
configuration?: IConfigurationDTO;
instanceId?: string;
componentVersion?: string;
}
/**
* Interface representing the venue data transfer object from the API.
* @hidden
*/
interface IVenueDTO {
guid: string;
name: string;
gaCapacity?: number;
seatsCapacity?: number;
seatmap: ISchemaDTO;
schema: ISchemaDTO;
}
/**
* Interface representing the plain seats data transfer object from the API.
* @hidden
*/
interface IPlainSeatsDTO {
ids: number[];
x: number[];
y: number[];
rowIds: [];
sectorIds: [];
names: string[];
}
/**
* Interface representing the base seat data transfer object from the API.
* Contains the core properties of a seat as received from the backend.
* @hidden
*/
type ISeatDTO = IBaseSeat;
/**
* Interface representing a row in the venue.
* @hidden
*/
interface IRowDTO {
id: number;
rowNumber: number;
sectorId: number;
name: string;
seatName: string;
}
/**
* Interface representing the base sector data transfer object from the API.
* Contains the core properties of a sector as received from the backend.
* @hidden
*/
type ISectorDTO = IBaseSector;
/**
* Interface representing the SVG background data transfer object from the API.
* @hidden
*/
interface ISVGBackgroundDTO {
svg?: Nullable<string>;
viewBox: {
x: number;
y: number;
width: number;
height: number;
};
svgLink?: string;
images?: IPngBackgroundDTO;
outlineSvg?: Nullable<string>;
}
/**
* Interface representing the configuration data transfer object from the API.
* @hidden
*/
interface IConfigurationDTO {
accessible: number[];
marked: number[];
eventName?: string;
}
/**
* Interface representing a special price option for seats, sections, or sectors.
* Contains information about a named price point with its identifier.
*/
interface ISpecialPrice {
/**
* The name or label of the special price option.
*/
name: string;
/**
* The unique identifier for this price option.
*/
priceId: number;
}
/**
* Interface representing special state information for seats, sections, or sectors.
* Contains additional properties that affect rendering or behavior.
*/
interface ISpecialState {
/**
* Special state flag 1, used for custom state indicators.
*/
s1?: number;
/**
* Array of special prices associated with this item.
*/
prices?: ISpecialPrice[];
/**
* Category identifier for grouping items with similar special states.
*/
category?: number;
}
/**
* Interface representing a price list data transfer object from the API.
* @hidden
*/
interface IPriceListDTO {
seats: [number, IPriceId, ISpecialState?][];
groupOfSeats: [number, number, number, ISpecialState?][];
prices: IPriceDTO[];
requestTime?: number;
responseSize?: number;
}
/**
* Interface representing a price data transfer object from the API.
* @hidden
*/
interface IPriceDTO {
id: IPriceId;
name: string;
}
/**
* Type representing blurred image data.
* @hidden
*/
type IBlurred = {
data: string;
shrink_factor: number;
size: number;
status: string;
};
/**
* Type representing a PNG image loaded from a URL.
* @hidden
*/
type IPngFromUrl = {
height: number;
size: number;
width: number;
path: string;
status: string;
};
/**
* Interface representing PNG background images in different resolutions.
* @hidden
*/
interface IPngBackgroundDTO {
blurred: IBlurred;
preview: IPngFromUrl;
full: IPngFromUrl;
}
/**
* Settings for the BookingApiClient.
* @hidden
*/
interface IBookingApiClientSettings {
baseUrl: string;
publicKey: string;
debug?: boolean;
forceSvg?: boolean;
}
/**
* Metrics for API requests.
* @hidden
*/
interface RequestMetrics {
data?: string;
requestTime: number;
responseSize: number;
}
/**
* Error thrown when the booking API returns a non-2xx response.
* Preserves the HTTP status and the backend error code (e.g. EVENT_ARCHIVED, EVENT_NOT_PUBLISHED).
*/
declare class ApiError extends Error {
readonly status: number;
readonly errorCode: string | undefined;
constructor(status: number, statusText: string, errorCode?: string);
}
/**
* API client for fetching schema and price data from the booking API.
* @hidden
*/
declare class BookingApiClient {
private settings;
constructor(settings: IBookingApiClientSettings);
/**
* Returns schema data
* @param schemaId Schema ID
*/
fetchSchema(schemaId: number): Promise<ISchemaDTO>;
/**
* Returns schema data
* @param venueId Venue GUID
*/
fetchSchemaForVenue(venueId: string): Promise<Omit<IVenueDTO, 'seatmap'>>;
/**
* Returns schema data
* @param eventId Event GUID
*/
fetchSchemaForEvent(eventId: string): Promise<ISchemaDTO>;
private unpackSchemaDTO;
/**
* Return prices information
* @param eventId Event GUID
*/
fetchPricesForEvent(eventId: string): Promise<IPriceListDTO>;
/**
* Return rows SVG information
* @param eventId Event GUID
*/
fetchRowsSvgForEvent(eventId: string): Promise<RequestMetrics>;
/**
* Makes request to booking API
* @param url Relative API endpoint URL, e.g. 'event/prices/?id=XXX'
*/
request<T>(url: string): Promise<T>;
/**
* Makes request to booking API
* @param url Relative API endpoint URL, e.g. 'event/prices/?id=XXX'
*/
requestPlain<T extends RequestMetrics>(url: string): Promise<T>;
private restoreSeats;
private restoreIds;
}
interface IPoint {
readonly x: number;
readonly y: number;
}
type ById$1<T> = {
[id: number]: T;
};
interface IPrice {
id: IPriceId;
name: string;
}
interface IColoredPrice extends IPrice {
color: string;
}
/**
* @hidden
*/
type ColorSequenceSettings = string[][];
/**
* @hidden
*/
declare const sortPrices: <T extends IPrice>(prices: T[]) => T[];
/**
* @hidden
*/
declare const convertPricesToColored: (prices: IPrice[], colorSettings: ColorSequenceSettings) => IColoredPrice[];
/**
* @hidden
*/
declare const convertPricesToColoredById: (prices: IPrice[], colorSettings: ColorSequenceSettings) => ById$1<IColoredPrice>;
interface IVisibilityStatus {
selectable: boolean;
visible: boolean;
}
interface IVisibilityStatuses {
outline: IVisibilityStatus;
seat: IVisibilityStatus;
marker: IVisibilityStatus;
}
/**
* @hidden
*/
interface IRendererMachineContext {
mode?: string;
scale: number;
isEagleView: boolean;
events: DestEvent[];
hovered?: {
targetType: RendererTargetType;
id: number;
};
visibility?: IVisibilityStatuses;
enable3DView?: boolean;
rotationZ?: number;
perspectiveZ?: number;
tiltX?: number;
getWidth?: () => number;
getHeight?: () => number;
/** Zoom strategy configuration for eagle eye view interactions */
interactionZoomStrategy?: InteractionZoomStrategy;
}
/**
* @hidden
*/
type RendererMachineReducer<T> = (ctx: IRendererMachineContext, src: T) => IRendererMachineContext;
/**
* @hidden
*/
type RendererMachine = Machine<any, IRendererMachineContext, any>;
/**
* @hidden
*/
type RendererMachineService = Service<RendererMachine>;
/**
* @hidden
*/
declare enum RendererTargetType {
SEAT = "seat",
SECTION = "section"
}
/**
* @hidden
*/
declare enum RendererSelectMode {
REPLACE = "replace",
ADD = "add",
SUBTRACT = "subtract"
}
/**
* Source events
*/
/**
* @hidden
*/
declare enum SrcEventType {
DRAG_START = "srcDragStart",
DRAG_MOVE = "srcDragMove",
DRAG_END = "srcDragEnd",
CLICK = "srcClick",
MOUSE_MOVE = "srcMouseMove"
}
/**
* @hidden
*/
type SrcEvent = IDragStartSrcEvent | IDragMoveSrcEvent | IDragEndSrcEvent | IClickSrcEvent | IMouseMoveSrcEvent;
/**
* @hidden
*/
interface IDragSrcEvent<T> {
type: T;
origin: {
x: number;
y: number;
};
delta: {
x: number;
y: number;
};
shiftKey?: boolean;
altKey?: boolean;
metaKey?: boolean;
}
/**
* @hidden
*/
type IDragStartSrcEvent = IDragSrcEvent<SrcEventType.DRAG_START>;
/**
* @hidden
*/
type IDragMoveSrcEvent = IDragSrcEvent<SrcEventType.DRAG_MOVE>;
/**
* @hidden
*/
type IDragEndSrcEvent = IDragSrcEvent<SrcEventType.DRAG_END>;
/**
* @hidden
*/
interface IClickSrcEvent {
type: SrcEventType.CLICK;
target: HTMLElement;
point: {
x: number;
y: number;
};
section?: ISection;
seat?: ISeat;
shiftKey?: boolean;
altKey?: boolean;
metaKey?: boolean;
}
/**
* @hidden
*/
interface IMouseMoveSrcEvent {
type: SrcEventType.MOUSE_MOVE;
target: HTMLElement;
section?: ISection;
seat?: ISeat;
}
/**
* Output events
*/
/**
* @hidden
*/
declare enum DestEventType {
PAN = "destPan",
RECT_SELECT = "destRectSelect",
DESELECT = "destDeselect",
PAN_ZOOM = "destPanZoom",
SEAT_SELECT = "destSeatSelect",
SEAT_CART_SWITCH = "destSeatCartSwitch",
SECTION_CLICK = "destSectionClick",
SEAT_MOUSE_ENTER = "destSeatMouseEnter",
SECTION_MOUSE_ENTER = "destSectionMouseEnter",
SEAT_MOUSE_LEAVE = "destSeatMouseLeave",
SECTION_MOUSE_LEAVE = "destSectionMouseLeave"
}
/**
* @hidden
*/
type DestEvent = IPanDestEvent | IRectSelectDestEvent | IPanZoomDestEvent | IDeselectDestEvent | ISeatSelectDestEvent | ISeatCartSwitchDestEvent | ISectionClickDestEvent | ISeatMouseEnterDestEvent | ISectionMouseEnterDestEvent | ISeatMouseLeaveDestEvent | ISectionMouseLeaveDestEvent;
/**
* @hidden
*/
interface IPanDestEvent {
type: DestEventType.PAN;
isStart?: boolean;
isFinish?: boolean;
delta: {
x: number;
y: number;
};
}
/**
* @hidden
*/
interface IRectSelectDestEvent {
type: DestEventType.RECT_SELECT;
selectMode: RendererSelectMode;
isStart?: boolean;
isFinish?: boolean;
isRowsMode?: boolean;
isSectionsMode?: boolean;
rect: {
x: number;
y: number;
width: number;
height: number;
};
}
/**
* Pan-zoom destination event.
* Triggered when clicking in eagle eye view to zoom into the venue.
* @hidden
*/
interface IPanZoomDestEvent {
type: DestEventType.PAN_ZOOM;
/** Target zoom scale */
scale: number;
/** Origin point in viewport coordinates */
origin?: {
x: number;
y: number;
};
/** Section at the clicked point, if any */
section?: ISection;
/** Zoom strategy to apply */
strategy?: InteractionZoomStrategy;
}
/**
* @hidden
*/
interface ISeatMouseEnterDestEvent {
type: DestEventType.SEAT_MOUSE_ENTER;
isRowsMode?: boolean;
seat: ISeat;
}
/**
* @hidden
*/
interface ISectionMouseEnterDestEvent {
type: DestEventType.SECTION_MOUSE_ENTER;
section: ISection;
}
/**
* @hidden
*/
interface ISeatMouseLeaveDestEvent {
type: DestEventType.SEAT_MOUSE_LEAVE;
}
/**
* @hidden
*/
interface ISectionMouseLeaveDestEvent {
type: DestEventType.SECTION_MOUSE_LEAVE;
}
/**
* @hidden
*/
interface IDeselectDestEvent {
type: DestEventType.DESELECT;
}
/**
* @hidden
*/
interface ISeatSelectDestEvent {
type: DestEventType.SEAT_SELECT;
selectMode: RendererSelectMode;
point: {
x: number;
y: number;
};
seat: ISeat;
isRowsMode?: boolean;
}
/**
* @hidden
*/
interface ISeatCartSwitchDestEvent {
type: DestEventType.SEAT_CART_SWITCH;
point: {
x: number;
y: number;
};
seat: ISeat;
}
/**
* @hidden
*/
interface ISectionClickDestEvent {
type: DestEventType.SECTION_CLICK;
point: {
x: number;
y: number;
};
section: ISection;
isSectionsMode?: boolean;
}
type GpuTier = 'high' | 'constrained';
interface GpuProbeResult {
renderer: string;
maxTextureSize: number;
deviceMemory: number | undefined;
/** Texture upload throughput benchmark score (ms). Lower = faster GPU. -1 if benchmark failed. */
benchmarkMs: number;
}
interface DeviceCapabilities {
tier: GpuTier;
maxCanvasDimension: number;
gpu: GpuProbeResult | null;
}
/**
* Label positioning and style data for a section outline (GA, seated, or background-bound).
* @hidden
*/
interface ISectionLabelInfo {
id: number;
name: string;
transform: TransformArray;
isTable?: boolean;
textColor?: string;
/** Relative offset from section center for label positioning (SEAT-624) */
labelOffset?: {
x: number;
y: number;
};
/** Style overrides for the label (SEAT-624) */
labelStyle?: {
fontScale?: number;
color?: string;
opacity?: number;
fontWeight?: 'normal' | 'bold';
background?: string;
visible?: boolean;
showPrice?: boolean;
};
/** When true, label is centered on shape centroid (no vertical shift above seats) */
centroid?: boolean;
hideText?: boolean;
hasPrice?: boolean;
fontScale?: number;
}
/**
* @hidden
*/
declare class Context {
private redrawHandler;
element: HTMLElement;
settings: IRendererSettings;
seatImages: {
[id: string]: HTMLImageElement;
};
cart: ICart;
gaCategories: {
[id: number]: number;
};
categoriesColor: {
[id: number]: string | undefined;
};
scale: number;
translate: IPoint;
isEagleView: boolean;
visibility: IVisibilityStatuses;
width: number;
height: number;
physicalWidth: number;
physicalHeight: number;
capabilities: DeviceCapabilities;
enable3DView: boolean;
rotationZ: number;
perspectiveZ: number;
tiltX: number;
private _seats;
seatsIndex: KDBush;
seatsById: ById<ISeat>;
seatsByRowId: {
[rowId: number]: ISeat[];
};
rowsById: ById<IRowDTO>;
sectionsById: ById<ISector>;
pricesById: ById<IColoredPrice>;
seatsKeysMissedOnPriceSet: string[];
seatsIdsMissedOnPriceSet: number[];
rowsPolylines?: Array<{
points: {
x: number;
y: number;
}[];
widthPx?: number;
color?: string;
}>;
rowsPolylinesVersion: number;
/**
* @hidden
*/
private _pricesDTO;
private _selectedSeatIds;
private _selectedGaId?;
underlay: {
svgString?: Nullable<string>;
viewBox: {
x: number;
y: number;
width: number;
height: number;
};
svgUrl?: string;
outlineSvg?: Nullable<string>;
pngBackground?: IPngBackgroundDTO;
};
hoveredSeat?: ISeat;
hoveredRow?: IRowDTO;
sectionLabelInfo: ISectionLabelInfo[];
_selectedAt: {
[seatId: number]: number;
};
_deselectedAt: {
[seatId: number]: number;
};
constructor(element: HTMLElement, settings: IRendererSettings, redrawHandler: () => void);
set selectedSeatIds(value: number[]);
get selectedSeatIds(): number[];
set selectedGaId(value: number | undefined);
get selectedGaId(): number | undefined;
destroy(): void;
/**
* @hidden
*/
setPricesDTO(value: IPriceListDTO): void;
/**
* @hidden
*/
getPricesDTO(): IPriceListDTO;
setHovered(seat: ISeat | undefined, isRowsMode?: boolean): void;
private createSeatImages;
initCart(cart: ICart): void;
get seats(): ISeat[];
set seats(value: ISeat[]);
getPositionByOffset: (offset: IPoint) => IPoint;
getOffsetByPosition: (position: IPoint) => IPoint;
addGaToCart(ga: ICartGa): void;
removeGaFromCart(removedGa: IRemovedCartGa): void;
/**
* Clears the cart and records deselection timestamps for all seats.
*/
clearCart(): void;
/**
* Removes seats from the cart by internal seat IDs and records deselection times.
*/
removeSeatsFromCartByIds(ids: number[]): void;
/**
* Triggers a selection pulse animation for a seat and clears any pending deselection.
*/
triggerSeatSelectionPulse(seatId: number): void;
addSeatsToCart(seats: ICartSeat[]): void;
rectSelectSeats(rect: {
x: number;
y: number;
width: number;
height: number;
}, mode: RendererSelectMode): void;
selectSeats(seats: number[], mode: RendererSelectMode): void;
rectSelectRows(rect: {
x: number;
y: number;
width: number;
height: number;
}, mode: RendererSelectMode): void;
repairSeat: (s: ICartSeat) => ICartSeat | undefined;
afterCartUpdate: () => void;
repairGa: (ga: ICartGa) => ICartGa | undefined;
findSeatByKey(key: string): ISeat | undefined;
getCart(): ICart;
isSeatInCart(seatId: number): boolean;
isSectionSelected(sectorId: number): boolean;
addSeatToCart(seatId: number): void;
removeSeatFromCart(seatId: number): void;
getCartSeats(): ISeat[];
getSeatSelection(): ISeat[];
setSectionSelection(sections?: number[] | string[]): (number | undefined)[] | null;
getSvgSectionBySelection(): ISectorDTO[];
getGaSectors(): ISector[];
seatToCartSeat: (seat: ISeat, origCartSeat?: ICartSeat) => ICartSeat;
seatToExtendedSeat: (seat: ISeat) => IExtendedSeat;
calculateAbsolutePoint(point: IPoint): IPoint;
getSeatByOffset: (offset: IPoint) => ISeat | undefined;
/**
* Hit-test a seat using a viewport-relative point (CSS px) while accounting for 3D view.
* When 3D is enabled, the point is unprojected back to the stage before querying KDBush.
*/
getSeatByViewportPoint: (viewportPoint: IPoint, viewportSize: {
width: number;
height: number;
}) => ISeat | undefined;
}
type Nullable<T> = T | null | undefined;
type DeepPartial<T> = T extends object ? {
[P in keyof T]?: DeepPartial<T[P]>;
} : T;
/**
* Type for mapping objects by their ID.
*/
type ById<T> = {
[id: number]: T;
};
type ColorById<T> = {
[id: number | string]: T;
};
/**
* Type representing a price identifier, which can be either a number or a string.
* Used to uniquely identify price points in the system.
*/
type IPriceId = number | string;
/**
* Type representing a transformation matrix as a 6-element array.
*/
type TransformArray = [number, number, number, number, number, number];
/**
* Interface representing a seat in the venue with extended properties.
* Contains all the base seat properties plus additional properties for rendering and interaction.
*/
interface ISeat extends IBaseSeat {
/**
* The price ID associated with this seat.
*/
priceId?: IPriceId;
/**
* Whether the seat is locked and cannot be selected.
*/
locked?: boolean;
/**
* Whether the seat is filtered out by current filter criteria.
*/
filtered?: boolean;
/**
* Special state information for the seat.
*/
special?: ISpecialState;
/**
* The state of the seat.
*/
state?: SeatInteractionState;
}
/**
* Interface representing a sector in the venue with extended properties.
* Contains all the base sector properties plus additional properties for rendering and interaction.
*/
interface ISector extends IBaseSector {
/**
* The price ID associated with this sector.
*/
priceId?: IPriceId;
/**
* Special state information for the sector.
*/
special?: ISpecialState;
}
/**
* Interface representing a section in the venue.
* A section is a logical grouping of seats or a general admission area.
*/
interface ISection {
/**
* The unique ID of the section.
*/
id?: number;
/**
* The name of the section.
*/
name: string;
/**
* Whether this is a general admission section.
*/
ga: boolean;
/**
* The rectangular bounds of the section.
*/
rect: ISectionRect;
/**
* The price for the section.
*/
price?: number;
/**
* Special state information for the section.
*/
special?: ISpecialState;
/**
* The type of the section.
*/
type?: string | null;
/**
* The total number of seats in the section.
*/
seatCount?: number;
/**
* Text color for the section label (e.g., GA section name).
*/
textColor?: string;
}
/**
* Visual and interaction states that can be applied to entities
*/
interface IEntityStates {
highlighted: boolean;
selected: boolean;
unavailable: boolean;
filtered: boolean;
}
/**
* Metadata for a section including visual states, bounds, and rendering info
*/
interface ISectionMetadata {
id: number;
name: string;
guid?: string;
type?: string;
elementCount: number;
seatCount?: number;
priceId?: number | string;
isGa?: boolean;
disabled?: boolean;
filtered?: boolean;
bounds?: DOMRect | null;
center?: {
x: number;
y: number;
} | null;
centerOutsideViewBox?: boolean;
outlineSource?: 'svg' | 'shape' | 'auto' | 'fallback';
states: IEntityStates;
}
/**
* Metadata for a seat including visual states, position, and availability
*/
interface ISeatMetadata {
id: number;
key: string;
sectionId: number;
sectionName?: string;
row?: string;
seat?: string;
x: number;
y: number;
priceId?: number | string;
available: boolean;
locked?: boolean;
filtered?: boolean;
inCart: boolean;
states: IEntityStates;
}
/**
* Interface representing a section with additional coordinate information.
* Extends ISection with absolute x and y coordinates.
*/
interface ISectionWithCoords {
/**
* The X coordinate of the section.
*/
x: number;
/**
* The Y coordinate of the section.
*/
y: number;
/**
* The unique ID of the section.
*/
id?: number | undefined;
/**
* The name of the section.
*/
name: string;
/**
* Whether this is a general admission section.
*/
ga: boolean;
/**
* The rectangular bounds of the section.
*/
rect: ISectionRect;
/**
* The price for the section.
*/
price?: number | undefined;
/**
* Special state information for the section.
*/
special?: ISpecialState | undefined;
/**
* The type of the section.
*/
type?: string | undefined | null;
}
/**
* Interface representing the rectangular bounds of a section.
*/
interface ISectionRect {
/**
* The left coordinate of the rectangle.
*/
left: number;
/**
* The top coordinate of the rectangle.
*/
top: number;
/**
* The width of the rectangle.
*/
width: number;
/**
* The height of the rectangle.
*/
height: number;
}
/**
* Interface representing a seat in the shopping cart.
*/
interface ICartSeat {
/**
* The unique identifier of the seat.
*/
id?: number;
/**
* The unique key for the seat, typically combining section, row, and seat information.
*/
key: string;
/**
* The price of the seat.
*/
price?: number;
/**
* Special state information for the seat.
*/
special?: ISpecialState;
}
/**
* Extended interface for a seat with additional coordinate and descriptive information.
* Extends ICartSeat with position and section details.
*/
interface IExtendedSeat extends ICartSeat {
/**
* The X coordinate of the seat.
*/
x: number;
/**
* The Y coordinate of the seat.
*/
y: number;
/**
* The absolute X coordinate of the seat.
*/
ax: number;
/**
* The absolute Y coordinate of the seat.
*/
ay: number;
/**
* The ID of the section containing this seat.
*/
sectionId: number;
/**
* The ID of the sector containing this seat.
*/
sectorId: number;
/**
* The name or number of the seat.
*/
seatName: string;
/**
* The row number of the seat.
*/
rowNumber: number;
/**
* The name of the section containing this seat.
*/
sectionName: string;
/**
* Whether the seat is accessible for people with disabilities.
*/
isAccessible?: boolean;
}
/**
* Interface representing a general admission (GA) item in the shopping cart.
*/
interface ICartGa {
/**
* The ID of the sector for this GA item.
*/
sectorId?: number;
/**
* The unique key for the GA item.
*/
key: string;
/**
* The number of GA tickets in this item.
*/
count: number;
/**
* The price per GA ticket.
*/
price?: number;
}
/**
* Interface representing a removed general admission (GA) item from the cart.
*/
interface IRemovedCartGa {
/**
* The ID of the sector for the removed GA item.
*/
sectorId: number;
/**
* The price of the removed GA item.
*/
price?: number;
}
/**
* Interface representing the shopping cart containing selected seats and GA items.
*/
interface ICart {
/**
* Array of selected seats in the cart.
*/
seats: ICartSeat[];
/**
* Array of general admission items in the cart.
*/
ga: ICartGa[];
}
/**
* Type definition for a function that filters seats based on custom criteria.
* @param seat - The seat to evaluate against the filter criteria
* @returns A boolean indicating whether the seat passes the filter (true) or not (false)
*/
type SeatFilter = (seat: ISeat) => boolean;
/**
* Type definition for a seat price scheme, which associates a price with a specific seat.
*/
type ISeatPriceScheme = {
/**
* Optional unique identifier for the price scheme.
*/
id?: number;
/**
* The unique key of the seat this price applies to.
*/
seatKey: string;
/**
* The price value for the seat.
*/
price: number;
/**
* The price ID reference for the seat.
*/
priceId: IPriceId;
};
interface IZoomSettings {
presets: number[];
maxZoomToFitScale: number;
minPinchZoom: number;
maxPinchZoom: number;
}
interface IRange {
from?: number;
to?: number;
}
interface IVisibleSetting {
visible?: IRange;
selectable?: IRange;
}
/**
* Visibility settings for different elements based on zoom level.
*/
interface IVisibilitySettings {
/**
* Visibility configuration for seats.
*/
seats?: IVisibleSetting;
/**
* Visibility configuration for section outlines.
*/
outlines?: IVisibleSetting;
/**
* Visibility configuration for custom markers.
* Controls when markers are visible and interactive based on zoom level.
*/
markers?: IVisibleSetting;
}
/**
* Minimap position options.
*/
type MinimapPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
/**
* Minimap configuration settings.
*/
interface IMinimapSettings {
/**
* Whether the minimap is enabled.
*/
enabled: boolean;
/**
* Position of the minimap on the screen.
* @default 'bottom-right'
*/
position?: MinimapPosition;
/**
* Width of the minimap in pixels. Height is calculated automatically based on aspect ratio.
* @default 200
*/
width?: number;
/**
* Maximum minimap height as a percentage (0..100) of the seatmap container height.
* When set and the computed minimap height exceeds this limit, minimap width is reduced proportionally
* to preserve the venue aspect ratio.
*/
maxHeightPercent?: number;
/**
* Opacity of the minimap.
* @default 0.8
*/
opacity?: number;
/**
* Background color of the minimap.
*/
backgroundColor?: string;
/**
* Border color of the minimap.
*/
borderColor?: string;
/**
* Color of the viewport indicator rectangle.
* @default '#FF5722'
*/
viewportColor?: string;
/**
* Border width in pixels.
* @default 2
*/
borderWidth?: number;
/**
* Margin from the edge of the screen in pixels.
* @default 16
*/
margin?: number;
/**
* Color of the dim overlay outside the viewport on the minimap.
* @default '#000000'
*/
offscreenColor?: string;
/**
* Opacity of the dim overlay outside the viewport on the minimap (0..1).
* @default 0.4
*/
offscreenOpacity?: number;
/**
* Show cart pins on the minimap.
* @default true
*/
showCartPins?: boolean;
/**
* Show custom markers on the minimap.
* @default true
*/
showMarkers?: boolean;
}
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>;
}
/**
* Individual zoom strategy option for eagle eye view interactions.
*
* @public
*/
type InteractionZoomStrategyType = 'default' | 'section' | 'next-scale';
/**
* Zoom strategy configuration for eagle eye view interactions.
* Can be a single strategy or an array of strategies for fallback behavior.
*
* When an array is provided, strategies are tried in order until one succeeds.
* If all strategies fail, falls back to default zoom behavior (scale 1).
*
* The `'section'` strategy only works in eagle eye view. When already zoomed to a section,
* it will fall through to the next strategy, enabling progressive zoom behavior.
*
* @example
* ```typescript
* // Single strategy
* interactionZoomStrategy: 'section'
*
* // Progressive zoom with fallback
* interactionZoomStrategy: ['section', 'next-scale']
* // First click (eagle view): zooms to section
* // Second click (at section): zooms to next scale step
* ```
*
* @public
*/
type InteractionZoomStrategy = InteractionZoomStrategyType | InteractionZoomStrategyType[];
/**
* Default settings for markers.
*/
interface IMarkerSettings {
/**
* Default appearance for markers when not specified per-marker.
* @default { type: 'pin' }
*/
defaultAppearance?: MarkerAppearance;
/**
* Whether markers are shown on the main canvas by default.
* @default true
*/
defaultShowOnCanvas?: boolean;
/**
* Whether markers are shown on the minimap by default.
* @default true
*/
defaultShowOnMinimap?: boolean;
/**
* Whether markers are interactive (clickable/hoverable) by default.
* @default false
*/
defaultInteractive?: boolean;
}
interface IResolvedMarker {
marker: IMarker;
x: number;
y: number;
}
/**
* Loading phase identifiers for progress tracking.
*/
type LoadingPhase = 'schema' | 'prices' | 'rows' | 'background-blurred' | 'background-preview' | 'background-full' | 'processing';
/**
* Progress event data passed to onLoadProgress callback.
*/
interface ILoadProgressEvent {
phase: LoadingPhase;
progress: number;
isIndeterminate: boolean;
message: string;
}
/**
* Background image loaded event data.
* @hidden
*/
interface IBackgroundImageLoadedEvent {
type: 'blurred' | 'preview' | 'full' | 'detail-crop';
loadTimeMs: number;
sizeBytes?: number;
/** Native image width in pixels */
width?: number;
/** Native image height in pixels */
height?: number;
/** Estimated uncompressed GPU VRAM usage in bytes (width * height * 4 for RGBA) */
gpuBytes?: number;
}
/**
* Loader display style.
*/
type LoaderStyle = 'overlay' | 'top-bar';
/**
* Loader theme configuration.
*/
interface ILoaderTheme {
overlayColor?: string;
overlayOpacity?: number;
progressBarColor?: string;
progressBarBackgroundColor?: string;
progressBarHeight?: number;
textColor?: string;
fontSize?: number;
}
/**
* Loader configuration settings.
*/
/**
* Customizable error message with title and optional subtitle.
*/
interface IErrorMessage {
title: string;
subtitle?: string;
}
interface ILoaderSettings {
enabled: boolean;
style?: LoaderStyle;
theme?: ILoaderTheme;
showText?: boolean;
texts?: {
schema?: string;
prices?: string;
rows?: string;
backgroundBlurred?: string;
backgroundPreview?: string;
backgroundFull?: string;
processing?: string;
};
/**
* Customizable error messages shown when event loading fails.
* Each key corresponds to a backend error code.
*/
errorTexts?: {
/** Shown when the event has been archived (HTTP 410). */
eventArchived?: IErrorMessage;
/** Shown when the event is not published (HTTP 422). */
eventNotPublished?: IErrorMessage;
/** Shown when the event does not exist (HTTP 404). */
eventNotFound?: IErrorMessage;
/** Shown for any other API error. */
genericError?: IErrorMessage;
};
}
/**
* Configuration settings for the renderer.
* Defines various options that control the behavior and appearance of the renderer.
*/
interface IRendererSettings {
/**
* Debug mode for the renderer.
* @hidden
*/
debug?: boolean;
/**
* Environment setting that determines which API endpoint to use.
* Can be 'local', 'stage', or 'production' (default).
*/
env?: string;
/**
* Base URL prefix for background images.
* Used to resolve relative image paths to absolute URLs.
* Absolute URLs (starting with http://, https://, or data:) are used as-is.
*/
backgroundImageBaseUrl?: string;
/**
* External price information for seats.
*/
seatsPrices?: ISeatPriceScheme[];
/**
* Theme settings for the renderer.
*/
theme?: IRendererTheme;
markers?: IMarkerSettings;
/**
* Delay in milliseconds for debounced events.
*/
debounceDelay?: number;
/**
* Number of seats to select as a group.
*/
groupSize?: number;
/**
* Fixed height for the renderer in pixels.
*/
height?: number;
/**
* Fixed width for the renderer in pixels.
* If not provided, the renderer will use the container element's width.
*/
width?: number;
/**
* Initial padding around the venue when first loaded.
*/
initialPadding?: number;
/**
* Padding around the venue during normal operation.
*/
padding?: number;
/**
* Minimum zoom level required to enable seat selection.
*
* @deprecated
* Use visibilitySettings instead
*/
seatSelectionMinZoom?: number;
/**
* Maximum number of seats that can be selected.
*/
selectionLimit?: number;
/**
* Duration of transform animations in milliseconds.
*/
transformAnimationDuration?: number;
/**
* Duration of zoom animations in milliseconds.
*/
zoomAnimationDuration?: number;
/**
* Zoom mode for zoomToSection method.
* 'fit' - Adaptively fit section to screen with padding
* 'scale' - Zoom to a specific scale value
* @default 'fit'
*/
zoomToSectionMode?: 'fit' | 'scale';
/**
* Default scale value when zoomToSectionMode is 'scale' and no custom scale provided.
* @default 1.0
*/
zoomToSectionDefaultScale?: number;
/**
* Padding percentage around section when fitting to screen (0.0 to 1.0).
* 0.1 means 10% padding on all sides.
* @default 0.1
*/
zoomToSectionFitPadding?: number;
/**
* Threshold dimensions for adaptive zoom behavior.
* Small sections get capped zoom, large sections get minimum zoom.
* @default { small: 200, large: 1000 }
*/
zoomToSectionAdaptiveThreshold?: {
small: number;
large: number;
};
/**
* If true, disables zoom when clicking on empty space.
*/
disableZoomToEmptySpace?: boolean;
/**
* If true, disables cart changed when clicking on sections.
*/
disableCartInteractions?: boolean;
/**
*
*/
disableOutlinesInHelicopterView?: boolean;
/**
* If true, hides all seats from view.
*/
hideSeats?: boolean;
/**
* If true, shows the outline layer during animations.
*/
showOutlineLayerOnAnimation?: boolean;
/**
* If true, shows row labels.
*/
showRows?: boolean;
/**
* If true, shows outlines for unavailable seats.
*/
showUnavailableOutlines?: boolean;
/**
* If true, uses WebGL for rendering instead of Canvas.
*/
switchToWebGL?: boolean;
/**
* Force the GPU capability tier instead of auto-detecting from device hardware.
* Since SEAT-990 the two-layer texture system (detail crop on zoom) runs on every
* WebGL device regardless of tier; the flag now only caps physical canvas dimensions
* to 2048px on 'constrained'.
* Also settable via URL parameter `?gpuTier=constrained`.
* @hidden
*/
gpuTier?: 'high' | 'constrained';
/**
* If true, the renderer skips preview and full PNG background loads and uses
* the SVG background instead. The blurred PNG (inline base64) is still loaded
* as an instant low-quality placeholder until SVG rasterization completes.
* Useful when the PNG storage is not CORS-configured.
* Also settable via URL parameter `?forceSvg=true` and propagated to the
* booking-service `/event/` request as `forceSvg=true`.
*/
forceSvg?: boolean;
/**
* If true, shows the debug overlay layer (diagnostic lines from sections to venue center).
*/
showDebugLayer?: boolean;
/**
* If true, enables 3D view mode for enhanced visualization.
*/
enable3DView?: boolean;
/**
* If true, highlights all section outlines with a border.
*/
highlightAllOutlines?: boolean;
/**
* Minimap configuration settings.
*/
minimap?: IMinimapSettings;
/**
* Loader configuration settings.
* Controls the loading overlay with progress bar during event loading.
*/
loader?: ILoaderSettings;
/**
* Option to configure zoom settings
*/
zoomSettings?: IZoomSettings;
/**
* Option to configure visibility settings
*/
visibilitySettings?: IVisibilitySettings;
/**
* Zoom strategy when clicking on the canvas in eagle eye view.
* Can be a single strategy or an array of strategies for fallback behavior.
*
* Available strategies:
* - `'default'`: Zoom to scale 1.0 (current behavior)
* - `'section'`: Zoom to the clicked section using adaptive fit (only works in eagle eye view)
* - `'next-scale'`: Zoom to the next scale step in zoom presets
*
* When multiple strategies are provided, they are tried in order.
* The first strategy that can be applied is executed.
* If all strategies fail, falls back to default zoom behavior.
*
* **Note:** The `'section'` strategy only works when in eagle eye view (zoomed out).
* If already zoomed to a section, clicking again will fall through to the next strategy,
* allowing progressive zoom (e.g., section → next-scale → deeper zoom).
*
* @example Single strategy
* ```typescript
* interactionZoomStrategy: 'section'
* ```
*
* @example Multiple strategies with progressive zoom
* ```typescript
* interactionZoomStrategy: ['section', 'next-scale']
* // First click: zooms to section (from eagle view)
* // Second click: zooms to next scale (already at section, falls through)
* ```
*
* @example Explicit fallback to default
* ```typescript
* interactionZoomStrategy: ['section', 'default']
* // Tries to zoom to section first, if no section clicked or not in eagle view, zooms to scale 1
* ```
*
* @default 'default'
*/
interactionZoomStrategy?: InteractionZoomStrategy;
/**
* Rises when the mouse pointer moves above a seat.
*
* @remarks
*
* Seat is passed as a param to the handler (see IExtendedSeat).
*/
onSeatMouseEnter?: (seat: IExtendedSeat) => void;
/**
* Same as `onSeatMouseEnter` but with debounce.
*
* @remarks
*
* Seat is passed as a param to the handler (see IExtendedSeat).
*/
onSeatDebouncedEnter?: (seat: IExtendedSeat) => void;
/**
* Fires when the mouse pointer leaves a seat.
*/
onSeatMouseLeave?: () => void;
/**
* Fires when the user marks a seat as selected.
*
* @remarks
*
* Seat is passed as a param to the handler (see IExtendedSeat).
*
* To cancel seat selection you can return `false` or Promise resolving to `false`.
*/
onSeatSelect?: (seat: IExtendedSeat) => void | boolean | Promise<void | boolean>;
/**
* Fires when the user deselects a seat.
*
* @remarks
*
* Seat is passed as a param to the handler (see IExtendedSeat).
*
* To cancel seat deselection you can return `false` or Promise resolving to `false`.
*/
onSeatDeselect?: (seat: IExtendedSeat) => void | boolean | Promise<void | boolean>;
/**
* Fires when the user marks a seat or seats as selected.
*
* @remarks
*
* Seats are passed as a param to the handler (see ISeat).
*
*/
onSeatsSelect?: (seats: ISeat[]) => void;
/**
* Fires when the user deselects a seat or seats.
*
* @remarks
*
* Seats are passed as a param to the handler (see ISeat).
*
*/
onSeatsDeselect?: (seats: ISeat[]) => void;
/**
* Fires when the cart was modified.
*
* @remarks
*
* Cart state is passed as a param to the handler (see ICart).
*/
onCartChange?: (cart: ICart, prevCart?: ICart) => void;
onMarkerClick?: (marker: IMarker, event: MouseEvent) => void;
onMarkerMouseEnter?: (marker: IMarker) => void;
onMarkerMouseLeave?: (marker: IMarker) => void;
/**
* Fires when the mouse pointer moves above a section.
*
* @remarks
*
* Section is passed as a param to the handler (see ISection).
*/
onSectorMouseEnter?: (section: ISection) => void;
/**
* Fires when the mouse pointer leaves a section.
*/
onSectorMouseLeave?: () => void;
/**
* Fires when the user clicks on a section.
*
* @deprecated
* Use onSectionClick instead
*/
onSectorClick?: (section: ISection) => void;
/**
* Fires when the user clicks on a section.
*
* @remarks
*
* Section is passed as a param to the handler (see ISection).
*/
onSectionClick?: (section: ISection) => void;
/**
* Fires after section selection was updated in selectSections mode.
*
* @remarks
*
* Called with the full array of currently selected sections after any
* selection change (single click toggle or rect drag selection).
*/
onSectionsSelectionChange?: (sections: ISection[]) => void;
/**
* Fires before the zoom animation started.
*/
onZoomStart?: (newZoom: number, oldZoom: number) => void;
/**
* Fires after the zoom animation ended.
*/
onZoomEnd?: (newZoom: number, oldZoom?: number) => void;
/**
* Fires when component full redrawing starts.
*/
onRedrawStart?: () => void;
/**
* Fires when component full redrawing ends.
*/
onRedrawEnd?: () => void;
/**
* Fires when schema data has been successfully loaded and processed.
*
* @remarks
*
* This event is triggered after the schema data is fully loaded and all internal
* processing is complete. It can be used to perform actions that depend on the
* schema being fully initialized.
*/
onSchemaDataLoaded?: () => void;
/**
* Fires during loading to report progress.
*
* @remarks
*
* Progress event contains phase, progress percentage (0-100),
* whether the phase is indeterminate, and a status message.
*/
onLoadProgress?: (event: ILoadProgressEvent) => void;
/**
* Fires when a background image has been loaded.
*
* @remarks
*
* Provides the image type (blurred, preview, full), load time in ms, and size in bytes.
* @hidden
*/
onBackgroundImageLoaded?: (event: IBackgroundImageLoadedEvent) => void;
/**
* Fires when the WebGL context is lost (e.g., GPU memory pressure on mobile).
* Host apps can use this to show fallback UI.
* @hidden
*/
onWebGLContextLost?: () => void;
/**
* Fires when the WebGL context is restored after a loss event.
* @hidden
*/
onWebGLContextRestored?: () => void;
/**
* Fires while panning.
*/
onPan?: (delta: IPoint, isFinish?: boolean) => void;
/**
* Fires after seat selection was updated.
*/
onSeatSelectionChange?: () => void;
/**
* Fires after seats selection was updated.
*/
onSeatsSelectionChange?: (seats: ISeat[]) => void;
/**
* You can control seats' styling by returning custom style for each seat
*/
onBeforeSeatDraw?: (event: IBeforeSeatDrawEvent) => ISeatStyle;
lockedSeatsFilter?: (seat: ISeat) => boolean;
/**
* Suppress console warnings for deprecated API methods.
* Useful for gradual migration in production environments.
* @default false
*/
suppressDeprecationWarnings?: boolean;
/**
* Control visibility of outline types based on source.
* Each source can be set to 'always', 'eagle-only', or 'hidden'.
*
* Source meanings:
* - svg: bound to the background svg
* - fallback: fallback outline generated from seats
* - shape: editor-made basic shapes
* - auto: editor-generated outline
*/
outlineVisibility?: {
auto?: 'always' | 'eagle-only' | 'hidden';
fallback?: 'always' | 'eagle-only' | 'hidden';
svg?: 'always' | 'eagle-only' | 'hidden';
shape?: 'always' | 'eagle-only' | 'hidden';
};
}
/**
* Represents the possible interaction states of a seat.
* Used to determine how a seat should be rendered based on user interaction.
*/
type SeatInteractionState = 'default' | 'hovered' | 'selected' | 'unavailable' | 'loading' | 'error';
/**
* Interface for the event data passed to the onBeforeSeatDraw callba