Score Display Component
A simple score counter component that increments when receiving click events.
Overview
Component Name: score-displayLatest Version: 1.0.0Author: BoardAPI Team License: MIT
The Score Display shows a numeric counter that increments automatically when it receives action:click events from other components on the board.
Use Cases
- Game score tracking
- Click counters
- Progress indicators
- Event counting
- Analytics dashboards
- Leaderboards
Component Preview
┌─────────────────┐
│ │
│ Score │
│ │
│ 15 │
│ │
└─────────────────┘Props Schema
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
label | string | No | "Score" | Label text above the counter |
Props Examples
Default score display:
{
"label": "Score"
}Custom label:
{
"label": "Total Clicks"
}No label (empty string):
{
"label": ""
}Points counter:
{
"label": "Points"
}Events
Listened Events
action:click
The score increments by 1 when it receives this event.
Expected Event Structure:
{
type: 'board-event',
event: 'action:click',
data: {
clickCount: 5, // Current click count (optional)
timestamp: 1700000000
}
}Behavior:
- Each
action:clickincrements score by +1 - Score persists until component is reset or removed
- No maximum score limit
Emitted Events
This component does not emit any events. It only listens and displays the count.
Future Enhancement Idea: You could modify it to emit score:milestone events at certain thresholds (e.g., every 10 points).
Dimensions
- Width: 180px
- Height: 360px
- Resizable: No
manifest.json
{
"name": "score-display",
"version": "1.0.0",
"description": "Score counter display that increments on click events",
"author": "BoardAPI Team",
"license": "MIT",
"entry": "index.html",
"component": {
"width": 180,
"height": 360,
"resizable": false,
"schema": {
"type": "object",
"properties": {
"label": {
"type": "string",
"default": "Score",
"description": "Label text above counter"
}
}
}
}
}Implementation Example
index.html (Simplified)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
}
.score-label {
font-size: 18px;
font-weight: 600;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
opacity: 0.9;
}
.score-value {
font-size: 72px;
font-weight: bold;
line-height: 1;
transition: transform 0.2s ease;
}
.score-value.increment {
animation: scoreIncrement 0.3s ease;
}
@keyframes scoreIncrement {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
</style>
</head>
<body>
<div class="score-label" id="scoreLabel">Score</div>
<div class="score-value" id="scoreValue">0</div>
<script>
// Get props from parent
let props = window.componentProps || {};
const label = props.label !== undefined ? props.label : 'Score';
// State
let score = 0;
// Set label
const labelElement = document.getElementById('scoreLabel');
if (label === '') {
labelElement.style.display = 'none';
} else {
labelElement.textContent = label;
}
// Update score display
function updateScore() {
const scoreElement = document.getElementById('scoreValue');
scoreElement.textContent = score;
// Trigger increment animation
scoreElement.classList.add('increment');
setTimeout(() => {
scoreElement.classList.remove('increment');
}, 300);
}
// Listen for click events from other components
window.addEventListener('message', (event) => {
if (event.data.type === 'board-event' &&
event.data.event === 'action:click') {
score++;
updateScore();
}
});
// Initialize
document.getElementById('scoreValue').textContent = score;
</script>
</body>
</html>Usage on a Board
Adding via API
curl -X POST http://localhost:4000/api/v1/boards/{boardId}/objects \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"objectId": "score-1",
"type": "external-component",
"position": { "x": 600, "y": 100 },
"width": 180,
"height": 360,
"data": {
"componentUrl": "http://localhost:4000/components/score-display@1.0.0/index.html",
"props": {
"label": "Total Clicks"
}
}
}'Combining with Other Components
The score-display works perfectly with any component that emits action:click events.
Example: Balloon Game Setup
{
"objects": [
{
"objectId": "clicker-btn-1",
"type": "external-component",
"position": { "x": 70, "y": 100 },
"data": {
"componentUrl": ".../clicker-button@1.0.0/index.html"
}
},
{
"objectId": "balloon-1",
"type": "external-component",
"position": { "x": 290, "y": 100 },
"data": {
"componentUrl": ".../balloon-viewer@1.0.0/index.html"
}
},
{
"objectId": "score-1",
"type": "external-component",
"position": { "x": 590, "y": 100 },
"data": {
"componentUrl": ".../score-display@1.0.0/index.html",
"props": {
"label": "Clicks"
}
}
}
]
}Customization Ideas
Color Themes
Dark theme:
body {
background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%);
color: #f7fafc;
}Neon theme:
body {
background: #000;
color: #0ff;
text-shadow: 0 0 10px #0ff;
}
.score-value {
color: #0ff;
text-shadow: 0 0 20px #0ff, 0 0 40px #0ff;
}Milestone Celebrations
Add visual effects at certain score milestones:
function updateScore() {
score++;
document.getElementById('scoreValue').textContent = score;
// Check for milestones
if (score % 10 === 0) {
celebrateMilestone();
}
}
function celebrateMilestone() {
// Flash background
document.body.style.background = 'linear-gradient(135deg, #ffd700 0%, #ff8c00 100%)';
setTimeout(() => {
document.body.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
}, 300);
// Play sound
playMilestoneSound();
}High Score Tracking
Store and display high score using localStorage:
let highScore = parseInt(localStorage.getItem('highScore') || '0');
function updateScore() {
score++;
document.getElementById('scoreValue').textContent = score;
// Update high score
if (score > highScore) {
highScore = score;
localStorage.setItem('highScore', highScore);
document.getElementById('highScoreValue').textContent = highScore;
}
}
// Add to HTML
<div class="high-score">Best: <span id="highScoreValue">0</span></div>Reset Button
Add a reset button to clear the score:
<button class="reset-btn" onclick="resetScore()">Reset</button>
<style>
.reset-btn {
margin-top: 20px;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
cursor: pointer;
}
</style>
<script>
function resetScore() {
score = 0;
document.getElementById('scoreValue').textContent = score;
}
</script>Animated Number Counter
Use counting animation for score changes:
function animateScoreChange(from, to) {
const duration = 300;
const steps = 10;
const increment = (to - from) / steps;
let current = from;
let step = 0;
const interval = setInterval(() => {
current += increment;
step++;
document.getElementById('scoreValue').textContent = Math.floor(current);
if (step >= steps) {
clearInterval(interval);
document.getElementById('scoreValue').textContent = to;
}
}, duration / steps);
}Testing
Unit Test (Jest)
describe('Score Display Component', () => {
test('increments score on action:click event', () => {
const initialScore = score;
// Simulate click event from board
window.postMessage({
type: 'board-event',
event: 'action:click',
data: { clickCount: 1 }
}, '*');
expect(score).toBe(initialScore + 1);
});
test('displays custom label from props', () => {
const labelElement = document.getElementById('scoreLabel');
expect(labelElement.textContent).toBe(props.label || 'Score');
});
test('hides label when prop is empty string', () => {
props.label = '';
// Re-initialize component
const labelElement = document.getElementById('scoreLabel');
expect(labelElement.style.display).toBe('none');
});
});Integration Test
describe('Score Display Integration', () => {
test('increments score when clicker-button emits click', async () => {
// Setup: Add clicker-button and score-display to board
// Click button
// Assert: Score increments
const button = document.querySelector('#clickBtn');
button.click();
await waitFor(() => {
expect(document.getElementById('scoreValue').textContent).toBe('1');
});
});
});Variants
Compact Score Display
Smaller version for sidebars:
{
"component": {
"width": 120,
"height": 80
}
}Leaderboard Display
Show top 3 scores:
<div class="leaderboard">
<div class="leader">1st: 150</div>
<div class="leader">2nd: 120</div>
<div class="leader current">3rd: 95 (You)</div>
</div>Multi-Metric Display
Track multiple scores:
<div class="metrics">
<div class="metric">
<div class="label">Clicks</div>
<div class="value">15</div>
</div>
<div class="metric">
<div class="label">Pops</div>
<div class="value">3</div>
</div>
</div>Related Components
- Clicker Button - Button that emits clicks
- Balloon Viewer - Animated balloon
- Progress Bar - Visual progress
Related Documentation
Download
- Source Code: score-display-1.0.0.zip
- Live Demo: Try it on BoardAPI
See Also: