Vanilla JavaScript UI components for tennis tournament management and competition displays.
This library provides a comprehensive set of UI components for building tournament management applications. Components are framework-agnostic (vanilla JavaScript) and designed to work seamlessly with the Competition Factory engine.
Used by:
- TMX (Tournament Management eXperience) - Production tournament management application
- Competition Factory Documentation - Interactive examples and demonstrations
npm install courthive-components
# or
yarn add courthive-components
# or
pnpm add courthive-componentsimport { renderMatchUp, scoringModal, compositions } from 'courthive-components';
// Render a match display
const matchUpElement = renderMatchUp({
matchUp: myMatchUpData,
composition: compositions.Australian
});
document.getElementById('container').appendChild(matchUpElement);
// Open scoring modal
scoringModal({
matchUp: myMatchUpData,
callback: (outcome) => {
console.log('Score submitted:', outcome);
}
});Display a single match with scores, participants, and status indicators.
import { renderMatchUp, compositions } from 'courthive-components';
const element = renderMatchUp({
matchUp: matchUpData,
composition: compositions.Wimbledon,
isLucky: false
});Display a complete draw structure (bracket, round robin, etc.).
import { renderStructure } from 'courthive-components';
const element = renderStructure({
structure: drawStructure,
config: { showSeeds: true }
});Display a single round of matches within a structure.
import { renderRound } from 'courthive-components';
const element = renderRound({
round: roundData,
roundNumber: 1
});Display participant information (name, rating, country, etc.).
import { renderParticipant } from 'courthive-components';
const element = renderParticipant({
participant: participantData,
showRating: true
});Display round headers for draw structures.
import { renderRoundHeader } from 'courthive-components';
const element = renderRoundHeader({
roundName: 'Quarterfinals',
roundNumber: 3
});Wrapper component for draw containers with scrolling and layout.
Autocomplete input for participant selection.
import { renderParticipantInput } from 'courthive-components';
const input = renderParticipantInput({
participants: participantsList,
onSelect: (participant) => console.log('Selected:', participant)
});Base modal system with flexible configuration.
import { cModal } from 'courthive-components';
cModal.open({
title: 'Confirm Action',
content: 'Are you sure?',
buttons: [
{ label: 'Cancel', close: true },
{ label: 'Confirm', onClick: handleConfirm, close: true }
]
});Interactive score entry with multiple input approaches.
import { scoringModal, setScoringConfig } from 'courthive-components';
// Configure scoring behavior
setScoringConfig({
scoringApproach: 'dynamicSets', // 'dynamicSets' | 'freeScore' | 'dialPad'
smartComplements: true,
composition: 'Australian'
});
// Open scoring modal
scoringModal({
matchUp: matchUpData,
callback: (outcome) => {
// outcome: { isValid, sets, winningSide, matchUpStatus }
}
});Scoring Approaches:
- dynamicSets - Set-by-set entry with real-time validation
- freeScore - Flexible text-based entry (e.g., "6-4 6-3")
- dialPad - Touch-friendly numeric keypad
Interactive modal for selecting/editing match formats.
import { getMatchUpFormatModal } from 'courthive-components';
getMatchUpFormatModal({
existingMatchUpFormat: 'SET3-S:6/TB7',
callback: (newFormat) => {
console.log('Format:', newFormat);
}
});Render dynamic forms from configuration.
import { renderForm } from 'courthive-components';
const inputs = renderForm(container, [
{ field: 'name', label: 'Name', type: 'text' },
{ field: 'age', label: 'Age', type: 'number' }
]);Render individual form fields.
Render button groups with consistent styling.
Render dropdown/context menus.
Form validation utilities.
Slide-out drawer component for side panels.
import { drawer, initDrawer } from 'courthive-components';
initDrawer(); // Initialize once
drawer.open({
title: 'Details',
content: myContent,
side: 'right' // 'left' | 'right'
});Tooltip/popover system using Tippy.js.
import { tipster } from 'courthive-components';
tipster({
target: buttonElement,
content: 'Click to edit',
placement: 'top'
});Pre-configured visual themes matching Grand Slam tournaments:
import { compositions } from 'courthive-components';
// Available compositions:
compositions.Australian; // Australian Open colors
compositions.French; // Roland Garros colors
compositions.Wimbledon; // Wimbledon colors
compositions.US; // US Open colorsPre-defined match format codes.
import { MATCH_FORMATS } from 'courthive-components';
console.log(MATCH_FORMATS.BEST_OF_3_TB7);
// 'SET3-S:6/TB7'Get the current package version.
import { courthiveComponentsVersion } from 'courthive-components';
console.log(courthiveComponentsVersion());📚 Interactive Documentation: Storybook
The Storybook includes:
- Live component demos
- Interactive examples
- Configuration options
- Usage patterns
- Integration guides
Components use Bulma CSS framework. Include Bulma in your project:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css" />Or import the bundled CSS:
import 'courthive-components/dist/courthive-components.css';The package includes TypeScript definitions:
import { renderMatchUp, ScoringModalParams, ScoreOutcome } from 'courthive-components';
const params: ScoringModalParams = {
matchUp: myMatchUp,
callback: (outcome: ScoreOutcome) => {
if (outcome.isValid) {
console.log('Winner:', outcome.winningSide);
}
}
};Components are designed to work with TODS (Tennis Open Data Standards) data structures from tods-competition-factory:
import { tournamentEngine } from 'tods-competition-factory';
import { renderMatchUp, scoringModal } from 'courthive-components';
// Get match data from factory
const { matchUp } = tournamentEngine.findMatchUp({ matchUpId });
// Render with components
const display = renderMatchUp({ matchUp });
// Score with modal
scoringModal({
matchUp,
callback: (outcome) => {
// Update tournament using factory
tournamentEngine.setMatchUpStatus({
matchUpId,
outcome: {
score: { sets: outcome.sets },
winningSide: outcome.winningSide,
matchUpStatus: outcome.matchUpStatus
}
});
}
});# Install dependencies
pnpm install
# Start Storybook
pnpm storybook
# Build library
pnpm build
# Run tests
pnpm test
# Build Storybook for deployment
pnpm build-storybookContributions are welcome! This library is actively used in production tournament management applications.
- Maintain framework-agnostic vanilla JavaScript
- Follow existing component patterns
- Add Storybook stories for new components
- Include TypeScript types
- Test with Competition Factory integration
MIT © Charles Allen
- Competition Factory: https://github.com/CourtHive/tods-competition-factory
- TMX: https://github.com/CourtHive/tmx