Skip to content

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

PropTypeRequiredDefaultDescription
labelstringNo"Score"Label text above the counter

Props Examples

Default score display:

json
{
  "label": "Score"
}

Custom label:

json
{
  "label": "Total Clicks"
}

No label (empty string):

json
{
  "label": ""
}

Points counter:

json
{
  "label": "Points"
}

Events

Listened Events

action:click

The score increments by 1 when it receives this event.

Expected Event Structure:

javascript
{
  type: 'board-event',
  event: 'action:click',
  data: {
    clickCount: 5,    // Current click count (optional)
    timestamp: 1700000000
  }
}

Behavior:

  • Each action:click increments 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

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)

html
<!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

bash
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

json
{
  "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"
        }
      }
    }
  ]
}

See: Balloon Game Tutorial

Customization Ideas

Color Themes

Dark theme:

css
body {
  background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%);
  color: #f7fafc;
}

Neon theme:

css
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:

javascript
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:

javascript
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:

html
<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:

javascript
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)

javascript
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

javascript
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:

json
{
  "component": {
    "width": 120,
    "height": 80
  }
}

Leaderboard Display

Show top 3 scores:

html
<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:

html
<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>

Download


See Also: