d3-seating-chart
TypeScript icon, indicating that this package has built-in type declarations

2.1.0 • Public • Published

D3 Seating Chart

A simple but pleasant seating chart written using d3js

Setup

Install

npm install d3-seating-chart

Attach to the SVG-

HTML

<svg id="x" width="640" height="480" xmlns="http://www.w3.org/2000/svg"></svg>

JS

import { D3SeatingChart } from './D3SeatingChart';

let d3sc = D3SeatingChart.attach(document.getElementById('x'));

2.1.0

Changes

  • added getClosestSeats method

2.0.0

Breaking Changes

  • type="Board" changed to attribute board
  • add stage attribute to stage element
  • type="SeatingArea" name="main" changed to zoom-control="main"
  • type="SeatingChartExpose" name="main" change to seating-area zoom-target="main"
  • public methods getSeatingAreas, getSeats removed. use selectElement, and selectElements with a dom query selector
  • this library now implements seat selection logic instead of leaving that up to you

Changes

  • added registerSelectionChangeListener method
  • added select, deselect, lock, unlock methods
  • added allowManualSelection to config

Try It

Clone this repo. Run webpack -w

SVG Format

There are some rules the svg needs to follow to behave properly. Don't worry about positioning your svg perfectly in the center, this library will zoom/position the board so that it fills the svg dimensions with a margin.

To get the svg seen in the examples, I used http://editor.method.ac/

  • Your svg should have a g element child with a board attribute. All objects that should be affected by transitions should go in here.
  • Your stage element should have a stage attribute.
  • Seating areas should have a seating-area attribute.
  • Seats should have a seat attribute.
  • Use zoom controls with zoom-control="{targetName}" and zoom-target="{targetName}"
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
     <g board>
      <rect stage height="80.38871" width="126.153836" y="206.825647" x="206.459768"/>
      <rect zoom-control="right" height="80.175489" width="57.875103" y="205.119891" x="338.511515"/>
      <rect zoom-control="main" height="95.687786" width="126.854046" y="105.769235" x="206.180359"/>
      <rect zoom-control="left" height="79.962273" width="57.448662" y="205.119891" x="142.930951"/>
      <g seating-area zoom-target="left">
       <rect selected seat height="14.560438" width="13" y="206.529233" x="158.181482"/>
       <rect locked seat height="14.560438" width="13" y="206.529233" x="172.049076"/>
       <rect locked="reserved" seat height="14.560438" width="13" y="206.475552" x="185.969085"/>
       
      ...

      </g>
      <g seating-area zoom-target="right">
       <rect seat height="14.560438" width="13" y="206.529233" x="354.251359"/>
       <rect seat height="14.560438" width="13" y="206.529233" x="368.118954"/>
       <rect seat height="14.560438" width="13" y="206.475552" x="382.038963"/>
       
      ...

      </g>
      <g seating-area zoom-target="main">
       <rect seat class="reserved" height="14.560438" width="13" y="106.863558" x="207.345423"/>
       <rect seat class="locked" height="14.560438" width="13" y="106.863558" x="221.213018"/>
       <rect seat height="14.560438" width="13" y="106.809877" x="235.133027"/>
       
      ...

      </g>
     </g>
    </svg>

Styling

This package doesn't do any styling. To style elements you should use plain ol' css.

body {
  background-color: #1d74aa;
}

svg {
  border: 1px solid #000;
}

[stage] {
  fill: #000;
}

[zoom-control] {
  cursor: pointer;
  fill: transparent;
}

[seat] {
  fill: #efefef;
}

[seat][locked] {
  fill: #888888;
}

[seat][locked="reserved"] {
  fill: #00bfff;
}

[seat][selected] {
  fill: #ffe100;
}

[seat]:not([selected]):not([locked]):hover {
  fill: #ffffff;
  cursor: pointer;
}

Public Methods

goToBoard

Returns to the top view of seating areas.

document.getElementById('goToBoard').onclick = function() {
  d3sc.goToBoard();
}

select(elementSelector, emitEvents = true)

Selects the given set of seats. Can be given a single element, an array of elements, or a dom query selector. If the selection contains a seat that is locked or reserved and error will be thrown. If the selected seats actually change, an event will be emitted.

d3sc.select(element1);
d3sc.select([element1, element2]);
d3sc.select('[seat="10"]');

deselect

Deselects the given set of seats. Can be given a single element, an array of elements, or a dom query selector. If the selected seats actually change, an event will be emitted.

d3sc.deselect(element1);
d3sc.deselect([element1, element2]);
d3sc.deselect('[seat="10"]');

deselectAll

Deselects all selected seats.

lock(elementSelector, class = '', emitEvents = true)

Locks the given set of seats. Can be given a single element, an array of elements, or a dom query selector. If the selection contains seats that are already selected, the will be force locked and a change event will be emitted with the LockOverride reason.

d3sc.lock(element1);
d3sc.lock([element1, element2]);
d3sc.lock('[seat="10"]');
d3sc.lock(element1, "reserved");
d3sc.lock([element1, element2], "reserved");
d3sc.lock('[seat="10"]', "reserved");

unlockAll(class = '')

Unlocks all locked seats. Given a class, only unlocks seats of that given class. e.g. unlockAll('reserved')

unlock

Unlocks the given set of seats. Can be given a single element, an array of elements, or a dom query selector.

d3sc.unlock(element1);
d3sc.unlock([element1, element2]);
d3sc.unlock('[seat="10"]');

selectElement

Given a dom query selector, returns the first element within the svg that matches the query selector. See d3js select

d3sc.selectElement('[seat="10"]');

selectElements

Given a dom query selector, returns the elements within the svg that matches the query selector. See d3js selectAll

d3sc.selectElements('[seat]');

refresh

Resets the position of items inside the csv. You should use this if the svg itself changes size.

window.onresize = function() {
  d3sc.refresh();
}

zoom

Zooms into an element. Makes the element and it's direct children visible. Any element on the board that is not this element or one of it's direct children will be hidden.

d3sc.zoom(d3sc.selectElement('.the-best-chair')); // animates
d3sc.zoom(d3sc.selectElement('.the-best-chair'), false); // does not animate

canGoBack

All non Board zooms are remembered in a history chain. If you are not at the root level (Board) this will return true.

goBack

Will zoom to the last selection in history.

if(d3sc.canGoBack()) {
  d3sc.goBack()
}

Config

When attaching D3SeatingChart to an svg, an optional configuration can be passed in.

D3SeatingChart.attach(element, { ... config ... });

Options

Data Type Default Description
showBehavior enum ShowBehavior.DirectDecendants Changes the show behavior for zooming. Possible values are All, DirectDecendants, AllDecendants
allowManualSelection boolean true Implements manual seat selection seen in the demo

ShowBehavior

Changing the ShowBehavior changes the elements that are shown/hidden when zoom is called. This does not affect pointer-events.

import {D3SeatingChart, ShowBehavior, SelectionBehavior} from 'd3-seating-chart';

D3SeatingChart.attach(element, {
  showBehavior: ShowBehavior.DirectDecendants,
  allowManualSelection: true
});

To hide the seating area you can set it's fill color to transparent.

[seating-area] {
  cursor: pointer;
  fill: transparent;
}

ShowAll

AllDecendants

Events

This package is built using d3js which provides events for svg elements. You can use the selectElement and selectElements methods to get the d3 elements themselves. With those, you can bind events using d3.

Elements that are not direct decendants of the zoomed element will have pointer-events set to none.

  d3sc.getBoard().on('click', function() {});

  d3sc.getElement('#myCustomEvent').on('click', function() {
    console.log(this); // the element
  });

  d3sc.getElements('[seat]').on('click', function() {
    console.log(this); // the element
  });

However, there are some d3SeatingChart events.

zoom change

Register a callback listener for when the zoom is changed. This method returns an unregister function.

var unreg = d3sc.registerZoomChangeListener(() => {
  console.log('should run once');
  unreg();
});

d3sc.registerZoomChangeListener(() => {
  console.log('should run everytime');
});

selection change

Register a callback listener for when the seat selection is changed. This method return an unregister function.

The SelectChangeEvent sends the list of selected seats (not just the changes). It also sends the reason for the change. If select is used then the reason will be SelectionChanged. If the selection has changed because a lock was set, then the reason will be LockOverride. This is useful if updating locks/reservations can overwrite the users selection at runtime- at which point the user should be notified in some way.

import { SelectionChangeEvent, SelectionChangeEventReason } from 'd3-seating-chart';

var unreg = d3sc.registerSelectionChangeListener((e: SelectionChangeEvent) => {
  console.log('select evt should run once');
  unreg();
});

d3sc.registerSelectionChangeListener((e: SelectionChangeEvent) => {
  console.log(e.selection); // All current selections

  if(e.reason === SelectionChangeEventReason.SelectionChanged) {
    console.log('number of selected seats: ' + e.selection.length)
  } else if (e.reason === SelectionChangeEventReason.LockOverride) {
    alert('Some of the seats you had selected are now taken.');
  }
});

Get Closest Seats

In some cases you may not want to give the user the option to select seats but auto assign them seats closest to the stage. This is used to prevent holes in the audience due to users giving themselves a one seat buffer between their group and others.

This method looks at the seating area in relation to the stage and determines if it's above, right of, below, or left of the stage. With that it "knows" which seats are closest to the stage and automatically sorts them. This method will always return as many seats it can match even if the amount of seats it finds does not equal the requested numSeats. You should account for this in your implementation.

This method allows specifying whether to search for contiguous seating, where it will attempt to only match adjacent seats. If not enough adjacent seats are found, and scatterFallback is set to true, it will select the closest seats possible and ignore the contiguous rule. If scatterFallback is false in this scenario, an empty array will be returned.

getClosestSeats(seatingArea, numSeats, contiguous = true, scatterFallback = true)

d3sc.select(d3sc.getClosestSeats('left', 3));

d3sc.select(d3sc.getClosestSeats('left', 3))

d3sc.select(d3sc.getClosestSeats('left', 3, false))

d3sc.select(d3sc.getClosestSeats('left', 5))

d3sc.select(d3sc.getClosestSeats('left', 8))

Package Sidebar

Install

npm i d3-seating-chart

Weekly Downloads

4

Version

2.1.0

License

MIT

Last publish

Collaborators

  • iamchairs