A flexible and customizable seating arrangement GUI component for React applications. This library provides an interactive canvas for creating and managing seat layouts, perfect for event venues, theaters, restaurants, and more.
- Interactive Canvas: Drag, drop, and arrange seats, shapes, and text.
- Multi-Select & Grouping: Select multiple objects to move, align, or edit as a group.
- Grid Arrangement: Instantly arrange selected seats into a grid with adjustable rows, columns, and spacing.
- Customizable Properties: Edit seat number, color, size, shape, and more from the sidebar.
- Shape & Text Tools: Add rectangles, circles, and text labels to your layout.
- Undo/Redo: Easily revert or reapply changes with keyboard shortcuts or toolbar buttons.
- Delete, Copy, Cut, Paste: Full clipboard support for objects and groups.
- Lock/Unlock Selection: Prevent accidental changes by locking selected objects.
- Zoom & Pan: Zoom in/out (80%β120%) and pan to focus on any part of your layout.
- Snap to Grid: Enable smart snapping for precise alignment.
- Background Image: Upload a background image (e.g., venue map) and adjust its opacity.
- Seat Attributes: Assign seat numbers, categories, prices, and status (available, sold, etc.).
- Zone Management: Group seats into zones for pricing or sectioning.
- Bulk Editing: Edit properties for multiple selected seats at once.
- Export Layout: Save your seat map as a JSON file for backup or sharing.
- Import Layout: Load a previously saved layout to continue editing.
- Download: Download the current layout instantly from the toolbar.
- Read-Only Viewer: Customers can view seat layouts without editing.
- Drag & Drop Upload: Customers can upload a seat layout JSON to view and interact.
- Seat Details Modal: Clicking a seat opens a modal with all details and purchase options.
- Purchase Integration: Hook into seat actions (buy, reserve, etc.) with your own logic.
- No Edit Mode: Customers cannot move, edit, or delete seats.
- TypeScript Support: Full typings for all components and props.
- Customizable Styling: Easily override styles with your own CSS or Tailwind classes.
- Composable Components: Use only what you need (e.g., just the renderer for read-only views).
npm install seat-picker
# or
yarn add seat-picker
import { SeatPicker } from 'seat-picker';
function App() {
const handleChange = (layout) => {
console.log('Layout updated:', layout);
};
const handleSave = (layout) => {
console.log('Saving layout:', layout);
// Save to your backend or file system
};
return (
<SeatPicker
onChange={handleChange}
onSave={handleSave}
style={{
width: 800,
height: 600,
backgroundColor: '#f8fafc',
showSeatNumbers: true,
seatNumberStyle: {
fontSize: 14,
fill: '#222',
fontWeight: 'bold',
},
seatStyle: {
fill: 'transparent',
stroke: 'black',
strokeWidth: 1,
radius: 10,
},
}}
labels={{
buyButton: 'Buy Seat',
cancelButton: 'Cancel',
seatNumber: 'Seat Number',
category: 'Category',
price: 'Price',
status: 'Status',
}}
/>
);
}
- Open/Save/Download: Import/export layouts as JSON.
- Select/Move: Use the pointer tool to select and move objects.
- Add Seat/Shape/Text: Quickly add new elements to your layout.
- Grid Tool: Arrange selected seats in a grid.
- Undo/Redo: Step backward or forward through your changes.
- Clipboard: Cut, copy, paste, and delete selected objects.
- Zoom: Zoom in/out (80%β120%) and keep the layout centered.
- Lock/Unlock: Lock selection to prevent changes.
- No Selection: See helpful tips and keyboard shortcuts.
- Single Selection: Edit all properties of the selected object.
- Multiple Selection: Edit properties in bulk and use the grid arrangement tool.
- Ctrl+Z: Undo
- Ctrl+Y: Redo
- Del: Delete selected
- Shift+Click: Multi-select
- Ctrl+K: Open command palette
A dedicated, safe page for customers to view and purchase seats. This page is read-only and does not allow editing.
-
Start the development server:
npm run dev # or yarn dev
- Visit the customer page:
-
Upload a seat layout:
- Drag and drop your exported seat JSON file onto the upload area, or click to browse and select the file.
- Only JSON files exported from the admin/editor are supported.
-
View and purchase seats:
- The seat layout will be displayed on a canvas.
- Click any seat to view its details in a modal.
- Use the Buy or Cancel buttons in the modal (customize the buy logic as needed).
- Seats and other objects are not editable or selectable by customers.
- Drag & Drop Upload: Easily upload seat layouts by dragging your JSON file onto the upload area.
- Modal Details: Clicking a seat opens a modal with seat number, category, price, status, and purchase options.
- Read-Only: Customers cannot move, edit, or select seatsβonly view and purchase.
You can render a saved seat layout in read-only mode using the provided SeatPicker
component with the readOnly
prop set to true
. This is ideal for customer-facing pages or embeddable widgets where you want to display a seat map and allow seat selection/purchase, but prevent editing or uploading.
import { SeatPicker } from 'seat-picker';
function CustomerView() {
const [layout, setLayout] = useState(null);
const handleSeatAction = (action, seat) => {
console.log('Action:', action, 'on seat:', seat);
// Implement your buy functionality here
};
return (
<div className="min-h-screen bg-gray-100 p-8">
<h1 className="mb-4 text-2xl font-bold">Customer Seat Viewer</h1>
{/* Upload area for layout JSON */}
<div className="mb-6">
<div className="rounded-lg border-2 border-dashed border-gray-300 p-6">
{/* Your file upload UI here */}
</div>
</div>
{layout ? (
<SeatPicker
layout={layout}
readOnly
style={{
width: 800,
height: 600,
backgroundColor: '#f8fafc',
showSeatNumbers: true,
seatNumberStyle: {
fontSize: 14,
fill: '#222',
fontWeight: 'bold',
},
seatStyle: {
fill: 'transparent',
stroke: 'black',
strokeWidth: 1,
radius: 10,
},
}}
labels={{
buyButton: 'Buy Seat',
cancelButton: 'Cancel',
seatNumber: 'Seat Number',
category: 'Category',
price: 'Price',
status: 'Status',
}}
onSeatAction={handleSeatAction}
/>
) : (
<div className="rounded-lg border bg-white p-4 shadow">
<div className="flex h-[600px] items-center justify-center text-gray-500">
Please upload a seat layout file
</div>
</div>
)}
</div>
);
}
Prop | Type | Default | Description |
---|---|---|---|
width |
number |
800 | Canvas width in pixels |
height |
number |
600 | Canvas height in pixels |
layout |
object |
β | (Optional) Layout JSON to load (for editing an existing layout) |
initialSeats |
Seat[] |
[] |
(Deprecated, use layout ) Initial seats to render |
initialZones |
Zone[] |
[] |
(Deprecated, use layout ) Initial zones for grouping seats |
onChange |
(layout: object) => void |
β | Callback when the layout changes (add, move, edit, delete, etc.) |
onSave |
(layout: object) => void |
β | Callback when the user clicks the Save button |
onZoneChange |
(zones: Zone[]) => void |
β | Callback when zones change |
className |
string |
β | Additional CSS class name for the root container |
style |
object |
β | Additional inline styles or style overrides (see below) |
labels |
object |
β | Override default button/label text (see below) |
readOnly |
boolean |
false |
If true, disables all editing and only allows viewing |
renderToolbar |
(props) => ReactNode |
β | Custom render function for the toolbar |
renderSidebar |
() => ReactNode |
β | Custom render function for the sidebar |
renderSeatDetails |
({ seat, onClose, onAction }) => ReactNode |
β | Custom render function for the seat details modal |
onSeatClick |
(seat: SeatData) => void |
β | Callback when a seat is clicked (read-only mode) |
onSeatAction |
(action: string, seat: SeatData) => void |
β | Callback for seat actions (e.g., buy, reserve) |
<SeatPicker
width={1000}
height={700}
style={{
backgroundColor: '#fffbe6',
seatNumberStyle: { fontSize: 16, fill: '#333' },
seatStyle: { fill: '#e0e7ff', stroke: '#6366f1', radius: 12 },
}}
labels={{
buyButton: 'Purchase',
cancelButton: 'Back',
seatNumber: 'No.',
category: 'Section',
price: 'Cost',
status: 'Availability',
}}
/>
<SeatPicker
renderToolbar={({ onSave, onBgLayout }) => (
<div>
<button onClick={onSave}>Custom Save</button>
<button onClick={onBgLayout}>Background</button>
{/* ...add more custom controls */}
</div>
)}
/>
<SeatPicker
renderSidebar={() => (
<div>
<h3>Custom Sidebar</h3>
{/* Add your own controls or info */}
</div>
)}
/>
<SeatPicker
renderSeatDetails={({ seat, onClose, onAction }) => (
<div>
<h2>Seat {seat.number}</h2>
<p>Price: {seat.price}</p>
<button onClick={() => onAction('buy', seat)}>Buy</button>
<button onClick={onClose}>Close</button>
</div>
)}
/>
Prop | Type | Default | Description |
---|---|---|---|
layout |
object | β | The seat layout JSON exported from the editor |
width |
number | 800 | Canvas width in pixels |
height |
number | 600 | Canvas height in pixels |
labels |
object | β | Override default button/label text |
onSeatClick |
(seat: SeatData) => void |
β | Callback when a seat is clicked (read-only) |
onSeatAction |
(action: string, seat: SeatData) => void |
β | Callback for seat actions (e.g., buy) |
renderSeatDetails |
({ seat, onClose, onAction }) => ReactNode |
β | Custom seat modal |
<SeatLayoutRenderer
layout={seatLayoutJson}
width={900}
height={700}
labels={{ buyButton: 'Book Now' }}
renderSeatDetails={({ seat, onClose, onAction }) => (
<div>
<h2>VIP Seat {seat.number}</h2>
<button onClick={() => onAction('buy', seat)}>Book</button>
<button onClick={onClose}>Close</button>
</div>
)}
/>
-
Styling:
Use thestyle
prop to override canvas, seat, and label styles. You can use Tailwind, CSS-in-JS, or plain CSS. -
Labels:
Pass alabels
object to override any button or field text for localization or branding. -
Component Composition:
Use therenderToolbar
,renderSidebar
, andrenderSeatDetails
props to inject your own React components for a fully custom UI. -
Callbacks:
UseonChange
,onSave
,onSeatClick
, andonSeatAction
to hook into all user actions and integrate with your backend or analytics.
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Β© Emmanuel Michael