ticket-selector
v1.0.8
Published
A professional stadium seat selection widget with multi-language support
Maintainers
Readme
TicketSelector Widget
A professional stadium seat selection widget with multi-language support and comprehensive functionality. Perfect for event ticketing systems, stadium booking platforms, and venue management applications.
✨ Features
- 🎯 Interactive seat selection with real-time feedback and visual indicators
- 🌍 Multi-language support (English, Azerbaijani, and more) with automatic browser detection
- 🔄 Pan and zoom controls with smooth animations and fullscreen mode
- 📱 Responsive design that works flawlessly on all devices and screen sizes
- 🎨 Customizable styling with BEM CSS methodology and SCSS variables
- ⚡ Professional build system with minification, tree-shaking, and optimization
- 🧩 Modular architecture with ES6 modules and bundled dependencies
- 🔧 Event-driven API for seamless integration with any backend system
- 🎪 Dynamic sector loading with loading states and error handling
- 🖱️ Advanced tooltips with seat information and availability status
🚀 Installation
Via Local Files (Recommended)
Download and include the built files from the dist directory:
<!-- Include CSS -->
<link rel="stylesheet" href="../dist/ticket-selector.min.css">
<!-- Include JavaScript (Panzoom is bundled) -->
<script src="../dist/ticket-selector.js"></script>Alternative Installation
Clone the repository and build the project:
git clone https://github.com/IBP-LLC/ticket-selector.git cd ticket-selector npm install npm run buildInclude the CSS and JS files in your project:
<link rel="stylesheet" href="path/to/dist/ticket-selector.min.css"> <script src="path/to/dist/ticket-selector.js"></script>
📖 Quick Start
1. HTML Structure
<div id="ticket-selector" class="ticket-select">
<div class="ticket-select__container">
<div class="ticket-select__wrapper">
<div class="ticket-select__viewport">
<div class="ticket-select__content">
<div class="ticket-select__stadium">
<img src="stadium.jpg" alt="Stadium" class="ticket-select__stadium-image">
<div class="ticket-select__stadium-overlay">
<svg class="ticket-select__stadium-svg" viewBox="0 0 1000 600">
<!-- Define your stadium sectors -->
<path class="ticket-select__sector"
data-sector-id="1"
data-sector-color="blue"
d="M100,100 L300,100 L300,200 L100,200 Z"/>
<path class="ticket-select__sector"
data-sector-id="2"
data-sector-color="red"
d="M100,400 L300,400 L300,500 L100,500 Z"/>
<path class="ticket-select__sector"
data-sector-id="3"
data-sector-color="green"
d="M400,100 L600,100 L600,200 L400,200 Z"/>
<!-- Add more sectors... -->
</svg>
</div>
</div>
</div>
</div>
<!-- Info bar with seat counter -->
<div class="ticket-select__info">
<span class="ticket-select__selected-count">No seats selected</span>
<a href="#" disabled class="ticket-select__info-btn">Buy <span data-ticket-selector-count style="display: none;"></span></a>
</div>
</div>
</div>
</div>2. Initialize the Widget
const ticketSelector = new TicketSelector('#ticket-selector', {
lang: 'en', // 'en', 'az' or auto-detect
showControls: true,
showInfo: true,
maxSeat: Infinity // Maximum number of seats that can be selected (default: Infinity)
});
// Initialize sector summaries for dynamic loading
ticketSelector.addEventListener('ready', (event) => {
console.log(`TicketSelector v${event.detail.version} is ready!`);
// Load sector summaries to update HTML attributes dynamically
const sectorSummaries = {
'1': {
name: 'North Stand',
price: '100 USD',
availability: { available: 45, total: 50 },
disabled: false
},
'2': {
name: 'South Stand',
price: '80 USD',
availability: { available: 0, total: 30 },
disabled: true
}
};
ticketSelector.loadSectorSummaries(sectorSummaries);
});
// Listen for sector clicks
ticketSelector.addEventListener('sectorClick', async (event) => {
const { sectorId, sectorName, element } = event.detail;
// Show loading state
ticketSelector.showLoading('Loading seats...');
try {
// Load sector data from your API
const response = await fetch(`/api/sectors/${sectorId}/seats`);
const sectorData = await response.json();
// Load seats into the widget
ticketSelector.setSectorData(sectorData, {
sectorId,
sectorName,
sectorColor: element.dataset.sectorColor
});
} catch (error) {
console.error('Failed to load sector:', error);
alert('Failed to load seat information. Please try again.');
}
});
// Get selected seats when user is ready to proceed
document.querySelector('.ticket-select__info-btn').addEventListener('click', () => {
const selectedSeats = ticketSelector.getSelectedSeats();
console.log('User selected:', selectedSeats);
// Process the selection...
processTicketSelection(selectedSeats);
});📚 API Reference
Constructor Options
new TicketSelector(container, options)| Option | Type | Default | Description |
|--------|------|---------|-------------|
| lang | string | auto-detect | Language code ('en', 'az', 'tr') |
| showControls | boolean | true | Show zoom/pan/fullscreen controls |
| showInfo | boolean | true | Show selected seats counter |
| maxSeat | number | Infinity | Maximum number of seats that can be selected |
Methods
setSectorData(sectorData, sectorInfo?)
Load seats for a specific sector with detailed configuration.
const sectorData = {
id: 'north-stand',
name: 'North Stand',
price: '100 USD',
availability: { available: 45, total: 50 },
disabled: false,
seats: [
{
id: 1,
name: 'Row 1',
data: [
{ id: 1001, available: true },
{ id: 1002, available: false },
{ id: 1003, available: true, skipLeft: 2 }, // Gap before seat
{ id: 1004, available: true, skipRight: 1 }, // Gap after seat
{ id: 1005, available: true, number: 15 } // Manual seat number
]
},
{
id: 2,
name: 'Row 2',
data: [
{ id: 2001, available: true },
{ id: 2002, available: true }
]
}
]
};
ticketSelector.setSectorData(sectorData, {
sectorId: '1',
sectorName: 'North Stand',
sectorColor: 'blue' // Applies color theme to seats
});Seat Data Properties:
| Property | Type | Description |
|----------|------|-------------|
| id | number | Unique seat identifier (required) |
| available | boolean | Whether seat is available for selection |
| skipLeft | number | Number of empty spaces before this seat |
| skipRight | number | Number of empty spaces after this seat |
| number | number | Manual seat number (optional - if not provided, auto-numbered) |
getSelectedSeats()
Returns array of selected seats with comprehensive information.
const selectedSeats = ticketSelector.getSelectedSeats();
// Returns:
// [
// {
// sector: "North Stand",
// row: 1,
// seat: 3,
// seatId: "seat-1003",
// price: "50 USD",
// category: "Standard"
// }
// ]setLanguage(lang)
Change language dynamically with instant UI updates.
ticketSelector.setLanguage('az'); // Switch to Azerbaijani
ticketSelector.setLanguage('en'); // Switch to EnglishshowLoading(message?)
Display loading state with optional custom message.
ticketSelector.showLoading('Loading seat data...');destroy()
Clean up the widget and remove all event listeners.
ticketSelector.destroy();Events
sectorClick
Triggered when a sector/zone is clicked in the stadium view.
ticketSelector.addEventListener('sectorClick', (event) => {
const { sectorId, sectorName, element } = event.detail;
console.log(`User clicked on ${sectorName} (ID: ${sectorId})`);
});🛠️ Development
Prerequisites
- Node.js 16+
- npm, yarn, or pnpm
Setup
git clone https://github.com/IBP-LLC/ticket-selector.git
cd ticket-selector
npm install # or yarn install / pnpm installDevelopment Server
npm run devThis will start:
- 📦 Rollup in watch mode (JavaScript bundling)
- 🎨 Sass in watch mode (CSS compilation)
- 🔄 PostCSS processor (CSS optimization)
- 🌐 Live server at
http://localhost:8080with hot reload
Build Commands
npm run build # Production build
npm run clean # Clean dist folder
npm run lint # Run ESLint
npm run lint:fix # Fix linting issues
npm run format # Format code with Prettier
npm run test # Run tests (when available)Package Manager Support
The project works with any modern package manager:
# npm
npm install && npm run dev
# yarn
yarn install && yarn dev
# pnpm
pnpm install && pnpm dev📁 Project Structure
src/
├── TicketSelector.js # Main widget class
├── i18n/ # Internationalization
│ ├── index.js # I18n system core
│ └── translations.js # All language translations
└── styles/ # SCSS styles
├── main.scss # Main stylesheet
└── _variables.scss # SCSS variables & theming
dist/ # Built files (generated)
├── ticket-selector.js # UMD bundle
├── ticket-selector.esm.js # ES module bundle
├── ticket-selector.min.css # Minified CSS
└── *.map # Source maps
examples/ # Usage examples
├── index.html # Basic implementation example
└── stadium.jpg # Sample stadium image
docs/ # Documentation (generated)
tests/ # Test files (when available)🌐 Browser Support
| Browser | Version | |---------|---------| | Chrome/Chromium | 60+ | | Firefox | 55+ | | Safari | 12+ | | Edge | 79+ | | iOS Safari | 12+ | | Android WebView | 60+ |
🎨 Customization
CSS Variables
:root {
--ticket-select-primary: #2175bf;
--ticket-select-secondary: #f8ad02;
--ticket-select-success: #0a8837;
--ticket-select-danger: #bb2932;
--ticket-select-bg: #f1f1f1;
--ticket-select-white: #ffffff;
}Theming
The widget supports custom color schemes through SCSS variables and CSS custom properties. See src/styles/_variables.scss for all available options.
📈 Performance
- Bundle size: ~56KB minified (includes Panzoom)
- CSS size: ~8KB minified
- Zero external dependencies - everything is bundled
- Tree-shakeable ES modules
- Optimized rendering with efficient DOM updates
- Smooth initialization with loading states
- Mobile optimized with touch event support
🔄 Changelog
v1.0.8 (Latest)
- 🎯 Maximum Seat Selection: New
maxSeatoption to limit the number of seats that can be selected - ⚠️ Seat Limit Warnings: Informative tooltip notifications when maximum seat limit is reached
- 🌍 Multi-language Seat Limits: Added translations for seat limit messages in English, Azerbaijani, and Turkish
- 🔧 Sector Drag Fix: Fixed click event triggering after pan/zoom drag on sector paths
- 🖱️ Improved Drag Detection: Separate drag handling for sectors and seats to prevent false clicks
- ✨ Better UX for Limits: Visual feedback with cursor changes and informative tooltips
- 📏 Drag Threshold Optimization: Consistent 5px drag threshold for both sectors and seats
v1.0.7
- 🔢 Manual Seat Numbering: Added optional
numberproperty for custom seat numbering from backend - 🔧 Flexible Numbering System: Backend can now override automatic seat numbers
- 📈 Enhanced Data Structure: Seats can now have custom numbers from backend
- 📚 Documentation Updates: Added seat data properties table with
numberparameter - 💡 Example Implementation: Updated examples to show manual seat numbering usage
- 🔄 Backward Compatibility: Automatic numbering still works when
numberis not provided
v1.0.6
- 🖼️ Stadium Image Support: Added stadium image support in the widget
- ✨ Enhanced Seat Styling: Visual improvements and seat styling enhancements
- 🐛 Bug Fixes: Various minor bugs and performance optimizations
v1.0.5
- 🎯 Dynamic Seat Counter: Purchase button now shows selected seat count (e.g., "Buy (3)")
- 🔄 Enhanced Button States: Purchase button automatically enables/disables based on seat selection
- 🚫 Improved Disabled Sectors: Better visual feedback for unavailable sectors
- 🎨 Button UX Improvements: Disabled by default when no seats selected, dynamic count display
- ✨ Sector Interaction Refinements: Better hover states and cursor feedback
- 🛠️ CSS & JavaScript Enhancements: Improved styling consistency and responsive behavior
v1.0.4
- 🔄 Dynamic Sector Loading: Backend/mock data integration for sector information
- 🚫 Disabled Sector Functionality: Visual feedback and proper state management
- 📄 Custom HTML Preservation: Info section content maintained during updates
- 🤖 Automated Version Updates: Husky hooks for version management
- 🏷️ Enhanced Tooltips: Improved sector tooltip system with price and availability
- 🌍 Better Internationalization: Enhanced multi-language support
v1.0.3
- 🌍 Multi-language Support: Added English, Azerbaijani, and Turkish language support
- 🔧 Dynamic Internationalization: Professional i18n system implementation
- ⚡ Professional Build System: Automated workflows and build optimizations
- 🎨 Code Formatting: Improved style consistency and formatting
- 🛠️ Error Handling: Better user feedback and error management
- 📖 Enhanced Documentation: Improved examples and documentation
v1.0.2
- 📱 Mobile Touch Support: Added touchend events for perfect mobile interaction
- ⚡ Instant UI Feedback: Viewport class applied immediately on sector click for responsive UI
- 🔄 Improved Loading States: CSS-based loading control with instant visual feedback
- 🚫 Touch/Click Conflict Fix: Prevented event conflicts with preventDefault for smooth mobile experience
- 🎯 Enhanced Mobile UX: Complete mobile user experience optimization
v1.0.1
- 📦 Bundled Panzoom: No more external CDN dependencies - panzoom is now included in the bundle
- 🔄 Initial Loading: Added professional loading screen during widget initialization
- 🎯 Version Tracking: Added version property to constructor for debugging and tracking
- 🚀 Ready Event: Widget now emits 'ready' event when fully initialized
- 🛠️ Improved Error Handling: Better error messages and loading states
- ⚡ Self-contained: Widget is now completely standalone with no external dependencies
v1.0.0
- ✨ Initial release with full functionality
- 🌍 Multi-language support (EN, AZ)
- 🖼️ Fullscreen mode with cross-browser compatibility
- 📱 Responsive design for all screen sizes
- ⚡ Professional build system with hot reload
- 🎯 Interactive seat selection with visual feedback
- 🔧 Event-driven API for easy integration
- 📖 Comprehensive documentation and examples
🤝 Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes and add tests if applicable
- Run
npm run lintandnpm run buildto ensure code quality - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow the existing code style and use Prettier for formatting
- Add JSDoc comments for new public methods
- Ensure backward compatibility when possible
- Update documentation for any API changes
📄 License
MIT License - see LICENSE file for details.
🆘 Support
- 📧 Email: [email protected]
- 🐛 Bug reports: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📖 Documentation: GitHub Wiki
🙏 Acknowledgments
- Panzoom for zoom and pan functionality
- All contributors who help improve this project
