seat-picker
Version:
> π [Full Documentation & Live Demo](https://seat-picker-docs.vercel.app/)
386 lines (313 loc) β’ 14.8 kB
Markdown
# [Seat Picker](https://seat-picker-docs.vercel.app/)
> π [Full Documentation & Live Demo](https://seat-picker-docs.vercel.app/)
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.
---
## β¨ Features
### Canvas Editing & Layout
- **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 & Zone Management
- **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, Import, and Sharing
- **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.
### Customer-Facing Features
- **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.
### Developer Experience
- **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).
---
## π Getting Started
### Installation
```bash
npm install seat-picker
# or
yarn add seat-picker
```
---
## π οΈ Usage (Admin/Editor)
```tsx
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',
}}
/>
);
}
```
### Toolbar Actions
- **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.
### Sidebar Actions
- **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.
### Keyboard Shortcuts
- **Ctrl+Z:** Undo
- **Ctrl+Y:** Redo
- **Del:** Delete selected
- **Shift+Click:** Multi-select
- **Ctrl+K:** Open command palette
---
## π₯ Customer-Facing Seat Viewer
A dedicated, safe page for customers to view and purchase seats. This page is read-only and does not allow editing.
### How to Use
1. **Start the development server:**
```bash
npm run dev
# or
yarn dev
```
2. **Visit the customer page:**
- Go to [http://localhost:5173/seats](http://localhost:5173/seats)
3. **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.
4. **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.
### Customer Features
- **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.
### πΌοΈ Rendering a Saved Layout (Read-Only)
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.
### Example
```tsx
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>
);
}
```
---
## βοΈ Props Reference & Customization
### SeatPicker
| 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) |
#### Example: Customizing Style and Labels
```tsx
<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',
}}
/>
```
#### Example: Custom Toolbar
```tsx
<SeatPicker
renderToolbar={({ onSave, onBgLayout }) => (
<div>
<button onClick={onSave}>Custom Save</button>
<button onClick={onBgLayout}>Background</button>
{/* ...add more custom controls */}
</div>
)}
/>
```
#### Example: Custom Sidebar
```tsx
<SeatPicker
renderSidebar={() => (
<div>
<h3>Custom Sidebar</h3>
{/* Add your own controls or info */}
</div>
)}
/>
```
#### Example: Custom Seat Details Modal
```tsx
<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>
)}
/>
```
---
### SeatLayoutRenderer
| 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 |
#### Example: Read-Only Renderer with Custom Modal
```tsx
<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>
)}
/>
```
---
### Customization Tips
- **Styling:**
Use the `style` prop to override canvas, seat, and label styles. You can use Tailwind, CSS-in-JS, or plain CSS.
- **Labels:**
Pass a `labels` object to override any button or field text for localization or branding.
- **Component Composition:**
Use the `renderToolbar`, `renderSidebar`, and `renderSeatDetails` props to inject your own React components for a fully custom UI.
- **Callbacks:**
Use `onChange`, `onSave`, `onSeatClick`, and `onSeatAction` to hook into all user actions and integrate with your backend or analytics.
---
## π§βπ» Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## π License
MIT Β© Emmanuel Michael