Skip to content

goma-top-competitions

Sidebar "Top Competitions" list — a section title and a vertical list of competition rows (sport icon + region flag + name). One-shot fetch per sport, single-selection state, emits goma:top-competition-select on tap.

Live demo

Open in the playground → — toggle Top Competitions in the widget rail.

Install

@gomagaming/top-competitions is a scoped private package. See Installation for the one-time .npmrc setup.

bash
npm install @gomagaming/top-competitions

You also need the shared peer dependencies installed at your app level:

bash
npm install vue pinia vue-router vue-i18n @vueuse/core autobahn-browser swiper vue-toastification

Element

html
<goma-top-competitions id="tc"></goma-top-competitions>

Configuration

js
const el = document.getElementById('tc')
el.config = {
  socketUrl: 'wss://sportsapi.example.com/v2',
  socketRealm: 'www.example.com',
  apiBaseUrl: 'https://api.example.com',
  bettingApiBaseUrl: 'https://sports-api.example.com',
  ucsOperatorId: '2838',
  locale: 'en',
}
KeyTypeRequiredDescription
socketUrlstringWhen dataSource !== 'prop'WAMP socket URL. The widget hits popularTournaments over this transport.
socketRealmstringWhen dataSource !== 'prop'WAMP realm.
apiBaseUrlstringWhen dataSource !== 'prop'REST base URL.
bettingApiBaseUrlstringNoREST base URL for betting offers.
ucsOperatorIdstringWhen dataSource !== 'prop'Operator ID — used to build per-tenant URLs.
localestringNoi18n locale. Defaults to 'en'.
themeobjectNoCSS variable overrides — see Theming.
messagesobjectNoi18n overrides per locale.
debugbooleanNoVerbose console logging.

Data sources

'prop' — host-fed

The widget renders whatever the host writes to el.competitions. Use this when the competition list comes from a REST endpoint or a CMS layer you already own.

js
el.dataSource = 'prop'
el.competitions = [
  { id: 106, name: 'UEFA Champions League', sportId: 1, venueId: 9  },
  { id: 102, name: 'La Liga',                sportId: 1, venueId: 11 },
  { id: 110, name: 'Primeira Liga',          sportId: 1, venueId: 16 },
]

'rpc' — one-shot WAMP call (default)

The widget calls apiSession.$wcall(popularTournaments, { sportId?, numberOfCompetitions, lang, liveStatus: 'BOTH', sortByPopularity: true }) once the WAMP connection is up. sportId is optional on the backend:

  • Cross-sport (default, sportId: null) — omit sportId from the kwargs. Backend returns top tournaments aggregated across every sport.
  • Sport-scoped (sportId: 1 etc.) — pass sportId. Backend returns the top N for that sport.

Re-runs whenever sportId or numberOfCompetitions changes.

js
// Cross-sport (default — no sportId)
el.dataSource = 'rpc'
el.config = { /* socket + REST creds */ }

// Sport-scoped
el.dataSource = 'rpc'
el.sportId = 1
el.numberOfCompetitions = 5
el.config = { /* socket + REST creds */ }

'subscribe' — live WAMP subscription (placeholder)

Note: the backend topic for live top-competition updates is not yet available. The widget accepts dataSource: 'subscribe' but internally falls back to the one-shot RPC. Once the topic ships, the fallback flips automatically.

Props (HTML attributes + JS properties)

PropertyAttributeDefaultDescription
dataSourcedata-source'rpc''prop' / 'rpc' / 'subscribe'.
sportIdsport-idnullSport id. null / 0 / missing (default) → cross-sport REST. A real number → sport-scoped WAMP RPC.
numberOfCompetitionsnumber-of-competitions5Max number of competitions to request (sport-scoped path only).
competitions[]Array of GomaCompetition objects. Populated by the WAMP composable when dataSource !== 'prop'.
selectedCompetitionIdselected-competition-idnullId of the currently-selected row. Two-way: clicks update this value.
showTitleshow-titletrueRender the "Top Competitions" header above the list.
skeletonCountskeleton-count5Number of placeholder rows shown until the first non-empty competitions assignment.
flagBaseUrlflag-base-url'https://static.glastcoper.com/omfe-widgets/s/assets/1.10.2/om1/icons/flag/'Base URL for country-flag PNG assets. Flags resolve to ${flagBaseUrl}{venueId}.png.
iconBaseUrlicon-base-url''Base URL for sport-icon SVG assets. Sport icons resolve to ${iconBaseUrl}/icons/sports/dark_mode/{sportId}.svg.
highlightedImageshighlighted-images (JSON){}Map of competition-id → banner image URL. The first competition in the list whose id appears here is rendered as the featured card (banner + gradient divider + row).

GomaCompetition shape

ts
interface GomaCompetition {
  id: number
  name: string
  translatedName?: string
  shortTranslatedName?: string
  sportId: number
  sportName?: string
  venueId?: number      // country/region id — used to resolve the flag PNG
  venueName?: string
}

Events

StatusEventDetailEmitted when
Canonicalready{}Widget mounted.
Canonicalgoma:connection{ state, … }Connection state transition.
Canonicalgoma:error{ message, code, component? }Render or data error.
Canonicalgoma:top-competition-select{ competitionId, competitionName, competitionSportId, competitionVenueId, isFeatured, previousCompetitionId }User taps a competition row. isFeatured: true when the tapped row was the featured card.

Embedding examples

Vanilla HTML

html
<goma-top-competitions id="tc" sport-id="1" number-of-competitions="5"></goma-top-competitions>
<script type="module">
  import '@gomagaming/top-competitions'
  document.getElementById('tc').config = {
    socketUrl: 'wss://sportsapi.example.com/v2',
    socketRealm: 'www.example.com',
    apiBaseUrl: 'https://api.example.com',
    ucsOperatorId: '2838',
    locale: 'en',
  }
  document.getElementById('tc').addEventListener('goma:top-competition-select', (e) => {
    console.log('selected', e.detail.competitionId, e.detail.competitionName)
  })
</script>

Host-fed

html
<goma-top-competitions id="tc" data-source="prop"></goma-top-competitions>
<script type="module">
  import '@gomagaming/top-competitions'
  const el = document.getElementById('tc')
  el.competitions = await fetch('/api/top-competitions').then((r) => r.json())
</script>

React

jsx
import '@gomagaming/top-competitions'
import { useEffect, useRef } from 'react'

function TopCompetitions({ config, sportId = 1 }) {
  const ref = useRef(null)
  useEffect(() => {
    if (!ref.current) return
    ref.current.sportId = sportId
    ref.current.config = config
  }, [config, sportId])
  return (
    <goma-top-competitions
      ref={ref}
      onGoma:top-competition-select={(e) => console.log(e.detail)}
    />
  )
}

Vue 3

vue
<script setup>
import '@gomagaming/top-competitions'
import { ref, onMounted } from 'vue'

const el = ref(null)
const props = defineProps(['config'])

onMounted(() => {
  el.value.sportId = 1
  el.value.config = props.config
})
</script>

<template>
  <goma-top-competitions ref="el" @goma:top-competition-select="onCompetitionSelect" />
</template>

Theming

Override via el.theme = { … }.

The outer panel paints with backgroundSecondary; the competition rows, the featured card, and the loading skeleton paint with backgroundPrimary, so the rows read as recessed against the panel.

TokenElement
--goma-backgroundSecondaryOuter panel background
--goma-backgroundPrimaryRow / featured-card / skeleton background
--goma-textPrimaryRow label + title
--goma-textSecondaryFlag fallback glyph
--goma-iconSecondarySport icon colour
--goma-highlightPrimarySelected row ring
--goma-highlightGradientStartFeatured-card divider gradient start (cyan default)
--goma-highlightGradientEndFeatured-card divider gradient end (purple default)

Accessibility

  • role="list" outer container, role="listitem" + tabindex="0" + aria-selected on each row.
  • Keyboard parity: Enter / Space activate selection.
  • Row aria-label is the competition name.

Source layout

LayerFile
Element classpackages/top-competitions/src/TopCompetitionsElement.js
App wrapperpackages/top-competitions/src/TopCompetitionsApp.vue
Component treepackages/top-competitions/src/components/TopCompetitions/{TopCompetitionsList,TopCompetitionItem,TopCompetitionFeaturedItem}.vue
Source composablepackages/sports-domain/src/composables/ui/useTopCompetitionsSource.js
Top-competitions RPCpackages/sports-domain/src/api/everymatrix/modules/betting.js (getTopCompetitions — sportId optional)
Manifestpackages/top-competitions/custom-elements.json

Pass an object map of competition-id → banner image URL to mark specific competitions as featured. Every competition in the list whose id appears in the map renders with the banner image on top + a cyan→purple gradient divider + the same info row underneath; the rest render as plain rows. The featured cards keep the natural feed order — the map only flips the row variant, it does not reorder. An empty map (the default) renders the entire list as plain rows.

js
el.highlightedImages = {
  '106': 'https://photobucket.com/comp/ucl.png',
  '999': 'https://photobucket.com/comp/europa.png',
}
html
<goma-top-competitions
  highlighted-images='{"106":"https://photobucket.com/comp/ucl.png"}'
></goma-top-competitions>

The selection event detail includes isFeatured: boolean so hosts can fork their handler on whether the tapped row was the banner variant.

What's deferred

  • Real subscribe path. Backend topic for live top-competition updates not yet known; dataSource: 'subscribe' falls back to one-shot RPC.