Skip to content

Repository files navigation

seatcharts

CIjsDelivr

Interactive seat charts — no jQuery required.

A TypeScript rewrite of jQuery-Seat-Charts with a modern class-based API, full type declarations, and zero runtime dependencies.

Installation

npm install @orpham/seatcharts

Quick Start

<divid="seat-map"></div><divid="legend"></div>
import{SeatCharts}from'@orpham/seatcharts';constsc=newSeatCharts(document.getElementById('seat-map')!,{map: ['aaaaaaaaaaaa','aaaaaaaaaaaa','bbbbbbbbbb__','bbbbbbbbbb__','bbbbbbbbbbbb','cccccccccccc'],seats: {a: {classes: ['front-seat'],price: 99.99},b: {price: 49.99},c: {price: 19.99}},legend: {node: document.getElementById('legend')!,items: [['a','available','Front (€ 99.99)'],['b','available','Middle (€ 49.99)'],['c','available','Back (€ 19.99)'],['a','unavailable','Unavailable'],['a','selected','Your selection']]},click(){if(this.status()==='available')return'selected';if(this.status()==='selected')return'available';returnthis.style();}});// mark already booked seatssc.find('c.available').status('unavailable');// read price of a seatconsole.log('Seat 1_2 costs '+sc.get('1_2').data().price);

CDN (no bundler)

<scriptsrc="https://cdn.jsdelivr.net/npm/@orpham/seatcharts/dist/seatcharts.umd.cjs"></script><script>constsc=newseatcharts.SeatCharts(container,options);</script>

Map

The map option is an array of strings where each string represents one row. Every character represents a seat type of your choice — you can use any letter except _, which is reserved as a blank spacer:

map: ['aaaaaa__DDDDD','aaaaaa__aaaaa','bbbbbb__bbbbb','ccccccccccccc']

Custom ID and label per seat

By default, each seat gets an id of row_column (e.g. 1_3) and a label equal to its column number. You can override both inline using square-bracket notation:

map: ['a[myId,My Label]aa',// custom id and label'a[onlyId]aa',// custom id, default label'a[,Just a label]aa'// default id, custom label]

IDs may contain letters, digits, and underscores. Labels may additionally contain spaces.


Seat Setup

The seats option maps each character to a SeatSetup object:

PropertyTypeDescription
classesstring[]Extra CSS classes added to every seat of this type
dataRecord<string, unknown>Arbitrary data attached to the seat (price, etc.)
pricenumberShorthand stored in data.price
click(this: ISeat) => SeatStatusPer-character click handler (overrides global)
focus(this: ISeat) => SeatStatusPer-character focus handler (overrides global)
blur(this: ISeat) => SeatStatusPer-character blur handler (overrides global)
seats: {v: {classes: ['vip'],price: 300,click(){if(this.status()==='available')return'selected';if(this.status()==='selected')return'available';returnthis.style();}},e: {classes: ['economy'],price: 50}}

Events

Three events drive every interaction — mouse, touch, and keyboard are unified behind the same handlers:

EventTriggered by
clickMouse click · Spacebar
focusMouse enter · Arrow keys
blurMouse leave · Arrow keys

Each handler is called with this bound to the seat (ISeat) and must return the new seat status. You can define handlers globally on the chart or per character in seats — the per-character handler takes precedence.

Default handlers

click(){if(this.status()==='available')return'selected';if(this.status()==='selected')return'available';returnthis.style();// keep current visual state (e.g., unavailable)}focus(){if(this.status()==='available')return'focused';returnthis.style();}blur(){returnthis.status();// revert to actual status}

Note on focused:focused is a visual-only style — it does not change the seat's underlying status. Calling seat.status() on a focused seat returns its real status (available, selected, etc.). Use seat.style() when you need the current visual state.


Options

Constructor

newSeatCharts(container: HTMLElement,options: SeatChartsOptions)

map(required)

string[] — Row definitions. See Map.

naming

Controls row and column labels and ID generation.

PropertyTypeDefaultDescription
topbooleantrueShow a column-header row above the map
leftbooleantrueShow a row-label column left of the map
rows(string | number)[][1, 2, 3, ...]Row label for each row
columns(string | number)[][1, 2, 3, ...]Column label for each column
getId(character, row, column) => string`${row}_${column}`ID generator — also used as the DOM id
getLabel(character, row, column) => string | number(_c, _r, column) => columnLabel displayed inside the seat cell
naming: {rows: ['A','B','C','D'],columns: [1,2,3,4,5,6,7,8,9,10],getId: (char,row,col)=>`${row}${col}`,getLabel: (_char,row,col)=>`${row}${col}`}

seats

Record<string, SeatSetup> — Per-character configuration. See Seat Setup.

legend

PropertyTypeDescription
nodeHTMLElement | nullContainer for the legend. If omitted, a <div> is inserted after the map.
itemsLegendItem[]Array of [character, cssClass, label] tuples.
legend: {node: document.getElementById('legend')!,items: [['a','available','Available'],['a','unavailable','Unavailable'],['a','selected','Selected']]}

click / focus / blur

Global event handlers. See Events.

onChange

onChange?: (seat: ISeat,newStatus: SeatStatus)=>void

Called after every click-triggered status change, with the seat and its new status already applied. Use this to react to selections without needing to read state inside the click handler:

onChange(seat,newStatus){if(newStatus==='selected'){cart.add(seat.data().price);}else{cart.remove(seat.data().price);}}

i18n

i18n: {t: (key: string)=>string}

Translation function used for seat aria-label attributes. Use the bundled createI18n helper or wire in any i18n library. See i18n.


API

SeatCharts

status(id, newStatus?) / status(ids, newStatus)

Read or update the status of one or more seats by ID:

sc.status('1_3');// 'available'sc.status('1_3','unavailable');// set one seatsc.status(['1_3','1_4'],'unavailable');// set multiple seats

get(id) / get(ids)

Retrieve a single ISeat or a ISeatSet:

constseat=sc.get('1_3');constset=sc.get(['1_3','1_4']);

find(query)

Search the entire map. Returns an ISeatSet. See Selectors.

each(callback)

Iterate over every seat. Return false from the callback to break early:

sc.each(function(id){console.log(id,this.status());});

node()

Returns an HTMLElement[] of every seat node in map order.

destroy()

Removes all DOM elements created by the chart and clears event listeners. The container is restored to its original state.

sc.destroy();

Live Updates

Use sc.status() to reflect new bookings in real time — for example by polling an endpoint every few seconds:

setInterval(async()=>{constbookings: {seatId: string}[]=awaitfetch('/bookings').then(r=>r.json());sc.status(bookings.map(b=>b.seatId),'unavailable');},10_000);

Selectors

find() and get() return an ISeatSet that exposes the same methods, so they can be chained:

sc.find('a');// all 'a' seatssc.find('unavailable');// all unavailable seatssc.find('a.available');// available 'a' seatssc.find(/^2_/);// all seats in row 2 (regex on id)sc.get(['1_2','1_3']).find('available');// available seats within a subset

Set methods

MethodDescription
status()Returns the status of the single seat in the set (length must be 1)
status(newStatus)Sets the status of every seat in the set
node()Returns HTMLElement[] for every seat in the set
each(callback)Iterates the set; return false to break
find(query)Searches within the set (not the whole map)
get(id|ids)Retrieves seat(s) within the set by ID

Seat Methods

Every ISeat instance exposes the following methods. Inside event handlers this is the seat.

MethodSignatureDescription
status()() => SeatStatusReturns the current seat status
status(s)(s: SeatStatus) => SeatStatusSets a new status and returns it
style()() => stringReturns the current visual style (may be 'focused')
style(s)(s: string) => stringSets the visual style without touching the status
node()() => HTMLElementReturns the seat's DOM element
data()() => SeatDataReturns the arbitrary data object attached to the seat
char()() => stringReturns the seat's map character
click()() => SeatStatusProgrammatically fires the click handler
focus()() => SeatStatusProgrammatically fires the focus handler
blur()() => SeatStatusProgrammatically fires the blur handler

Seat statuses

StatusMeaning
availableSeat can be selected by the user
unavailableSeat is already taken or blocked
selectedSeat has been selected by the current user
focusedVisual-only hover/keyboard focus style

You can also return any custom string from an event handler to use your own status / style.


Styling

The library generates plain HTML with predictable CSS classes — no styles are bundled, so you have full control.

ClassElement
.seatCharts-containerThe root container element
.seatCharts-rowEach row <div>
.seatCharts-headerThe column-header row (when naming.top is true)
.seatCharts-cellEvery cell — seats, spacers, and header cells
.seatCharts-seatSeat cells only
.seatCharts-spaceSpacer cells (_ in the map)
.seatCharts-seat.availableAvailable seats
.seatCharts-seat.selectedSelected seats
.seatCharts-seat.unavailableUnavailable seats
.seatCharts-seat.focusedFocused seats (hover / keyboard)
.seatCharts-legendThe legend container
.seatCharts-legendListThe <ul> inside the legend
.seatCharts-legendItemEach <li> in the legend
.seatCharts-legendDescriptionThe text label next to each legend icon

To style seat types differently, combine the status class with your custom class:

/* vip seats */
.seatCharts-seat.available.vip {
background:#ffd700;
}
.seatCharts-seat.selected.vip {
background:#ff8c00;
}
.seatCharts-seat.focused.vip {
background:#ffe680;
}
.seatCharts-seat.unavailable.vip {
background:#ccc;
}
/* economy seats */
.seatCharts-seat.available.economy {
background:#90ee90;
}
.seatCharts-seat.selected.economy {
background:#228b22;
}
seats: {v: {classes: ['vip'],price: 300},e: {classes: ['economy'],price: 50}}

i18n

The library uses translation keys for seat aria-label attributes, making the chart fully accessible in any language.

KeyDefault (fallback to key)Usage
available"available"aria-label of available seats
unavailable"unavailable"aria-label of unavailable seats
selected"selected"aria-label of selected seats
focused"focused"aria-label of focused seats

Use the bundled createI18n helper with one of the included locale files:

import{SeatCharts,createI18n}from'@orpham/seatcharts';importdefrom'@orpham/seatcharts/locales/de.json';constsc=newSeatCharts(container,{map: ['aaa'],i18n: createI18n(de)});

Available locales: cs, de, el, en, es, fr, it, nl, no, pl, pt, sk, tr.

You can also wire in any i18n library directly:

importi18nextfrom'i18next';constsc=newSeatCharts(container,{map: ['aaa'],i18n: {t: (key)=>i18next.t(key)}});

Build

npm run build # produces dist/seatcharts.js and dist/seatcharts.umd.cjs

Test

npm test# run once
npm run test:watch # watch mode

License

MIT — see LICENSE.txt. Based on jQuery-Seat-Charts by Mateusz Markowski — see NOTICE.

About

Interactive seat charts.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages