Skip to content

Commit e8aac0f

Browse files
Merge pull request #27 from eventjet/high-level-component
Add high-level SeatmapLayout component
2 parents 607e92d + a021fb9 commit e8aac0f

23 files changed

Lines changed: 6651 additions & 2857 deletions

.claude/skills/address-pr-comments/SKILL.md

Lines changed: 0 additions & 47 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ examples/**/package-lock.json
1010
packages/**/package-lock.json
1111
junit.xml
1212
temp
13+
test-results
1314
!.claude/
1415
.claude/*
1516
!.claude/skills/

knip.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
"ignoreDependencies": [
44
"@babel/preset-typescript",
55
"babel-plugin-react-compiler"
6-
],
7-
"ignoreBinaries": ["api:check"]
6+
]
87
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"typecheck": "pnpm -r typecheck"
2222
},
2323
"devDependencies": {
24+
"@playwright/test": "^1.58.1",
2425
"husky": "^9.0.0",
2526
"knip": "^5.84.0",
2627
"lint-staged": "^16.2.7",

packages/seatmaps/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ api/
22
lib/
33
node_modules/
44
temp/
5+
test-results/

packages/seatmaps/.storybook/main.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { StorybookConfig } from '@storybook/react-vite';
22

33
const config: StorybookConfig = {
44
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
5-
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
65
framework: { name: '@storybook/react-vite', options: {} },
76
};
87

packages/seatmaps/README.md

Lines changed: 73 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -28,72 +28,88 @@ pnpm add react react-dom @emotion/react @emotion/styled
2828

2929
## Quick Start
3030

31+
The easiest way to render a seatmap is with the `SeatmapLayout` component, which takes a plain data object:
32+
3133
```tsx
32-
import { Seatmap, Block, Row, Seat, Volume, SeatShape } from '@eventjet/react-seatmaps';
34+
import { SeatmapLayout, SeatmapLayoutData, SeatShape } from '@eventjet/react-seatmaps';
35+
36+
const data: SeatmapLayoutData = {
37+
areas: [
38+
{
39+
name: 'Main Hall',
40+
blocks: [
41+
{
42+
rows: [
43+
{
44+
name: 'A',
45+
showLabels: 'both',
46+
seats: [
47+
{ id: 'a-1', name: '1', x: 0, color: '#ff9900', shape: SeatShape.CIRCLE },
48+
{ id: 'a-2', name: '2', x: 100, color: '#ff9900', shape: SeatShape.CIRCLE },
49+
{ id: 'a-3', name: '3', x: 200, color: '#ff9900', shape: SeatShape.CIRCLE },
50+
],
51+
},
52+
],
53+
},
54+
],
55+
volumes: [
56+
{
57+
id: 'ga-1',
58+
label: 'Standing Area',
59+
x: 500,
60+
width: 300,
61+
height: 150,
62+
color: '#00ff99',
63+
availableSeats: 250,
64+
},
65+
],
66+
},
67+
],
68+
};
3369

3470
function MyVenue() {
3571
return (
36-
<Seatmap className="my-seatmap">
37-
<Block>
38-
<Row
39-
leftLabel="A"
40-
rightLabel="A"
41-
>
42-
<Seat
43-
name="1"
44-
x={0}
45-
color="#ff9900"
46-
/>
47-
<Seat
48-
name="2"
49-
x={100}
50-
color="#ff9900"
51-
/>
52-
<Seat
53-
name="3"
54-
x={200}
55-
color="#ff9900"
56-
/>
57-
</Row>
58-
<Row
59-
y={100}
60-
leftLabel="B"
61-
rightLabel="B"
62-
>
63-
<Seat
64-
name="1"
65-
x={0}
66-
color="#ff9900"
67-
shape={SeatShape.CIRCLE}
68-
/>
69-
<Seat
70-
name="2"
71-
x={100}
72-
color="#ff9900"
73-
shape={SeatShape.CIRCLE}
74-
/>
75-
<Seat
76-
name="3"
77-
x={200}
78-
color="#ff9900"
79-
shape={SeatShape.CIRCLE}
80-
/>
81-
</Row>
82-
</Block>
83-
<Volume
84-
x={500}
85-
width={300}
86-
height={150}
87-
label="General Admission"
88-
color="#00ff99"
89-
/>
90-
</Seatmap>
72+
<SeatmapLayout
73+
data={data}
74+
onBookableClick={({ id, type, disabled }) => {
75+
if (disabled) {
76+
alert('This is no longer available');
77+
return;
78+
}
79+
console.log(`${type} clicked:`, id);
80+
}}
81+
/>
9182
);
9283
}
9384
```
9485

86+
`SeatmapLayout` handles seat name visibility, badge placement, row labels, and decorations automatically. For full control, compose the low-level components directly (see below).
87+
9588
## Components
9689

90+
### SeatmapLayout
91+
92+
High-level component that renders an entire seatmap from a `SeatmapLayoutData` object.
93+
94+
```tsx
95+
<SeatmapLayout
96+
data={data}
97+
onBookableClick={({ id, type, disabled }) => console.log(id, type, disabled)}
98+
className="my-seatmap"
99+
ariaLabel="Venue seating"
100+
/>
101+
```
102+
103+
**Props:** `data`, `onBookableClick`, `className`, `ariaLabel`, `formatAreaName`, `formatRowName`, `formatSeatName`, `formatVolumeLabel`
104+
105+
The `onBookableClick` callback receives a `SeatmapBookableClickEvent` with `id` (string), `type` (`'seat'` | `'volume'`), and `disabled` (boolean). It fires for both enabled and disabled elements, so you can show a message like "this seat is no longer available" when a disabled element is clicked.
106+
107+
**Data types:** `SeatmapLayoutData`, `SeatmapAreaData`, `SeatmapBlockData`, `SeatmapRowData`, `SeatmapSeatData`, `SeatmapVolumeData`, `SeatmapDecoration`
108+
109+
### Low-Level Components
110+
111+
For cases where `SeatmapLayout` doesn't provide enough flexibility, compose the individual components directly:
112+
97113
### Seatmap
98114

99115
Root SVG container that auto-calculates viewBox based on children.
@@ -183,7 +199,7 @@ General admission area (rectangle or ellipse).
183199
/>
184200
```
185201

186-
**Props:** `x`, `y`, `width`, `height`, `label`, `color`, `shape` (`'rectangle'` | `'ellipse'`), `angle`, `active`, `disabled`, `fontWeight`, `onClick`, `children`
202+
**Props:** `x`, `y`, `width`, `height`, `label`, `color`, `shape` (`'rectangle'` | `'ellipse'`), `angle`, `active`, `disabled`, `fontWeight`, `onClick`, `onDisabledClick`, `children`
187203

188204
### SeatCountBadge
189205

0 commit comments

Comments
 (0)