Skip to content

<goma-filters-settings>

A settings/filters modal that aggregates four controls behind a single trigger: a time-window range slider, a read-only sport-scope indicator, a default-format market radio, and an odds-range radio — with a Reset / Cancel header and an Apply CTA.

The widget is self-contained — the time slider and sport section are native to it (no embedded child widgets). The sport the filters apply to is chosen elsewhere (the host, or a sibling sport picker) and set via selectedSportId; this modal only displays it.

The dialog holds pending state and commits nothing until the user taps Apply, which emits a single consolidated goma:filters-apply event — an "edit-then-confirm" filter pattern.

html
<goma-filters-settings id="fs"></goma-filters-settings>
<script type="module">
  import '@gomagaming/filters-settings'

  const el = document.getElementById('fs')
  el.config = { locale: 'en' }
  el.sports = [
    { id: 1, name: 'Soccer' },
    { id: 2, name: 'Basketball' },
  ]
  el.selectedSportId = 1
  el.marketOptions = [
    { id: 1, label: 'Result', subtitle: 'Home Draw Away' },
    { id: 2, label: 'Winner' },
    { id: 3, label: 'Total Goals' },
  ]
  el.selectedMarketId = 1

  el.addEventListener('goma:filters-apply', (e) => {
    console.log(e.detail)
    // { time: { timeValue, timeLabel }, sportId, sportName, marketId, marketLabel, oddsFilter }
  })
</script>

Element tag

<goma-filters-settings>

Configuration

All keys can be set via the config object, as individual JS properties, or as kebab-cased HTML attributes.

KeyTypeDefaultPurpose
localestring'en'i18n locale.
themeobject--goma-* overrides (see Theming).
openbooleanfalseOpen state of the modal. Drive from your own control.
showTriggerbooleantrueRender the built-in settings trigger button. Set false to open only via el.open = true.
triggerIconstring''Raw inline-SVG string for the trigger (uses currentColor).
mobileBreakpointstring'sm'Below this width the modal is a bottom-sheet. Tailwind token (sm..2xl) or px.
iconBaseUrlstring''CDN base for per-option glyphs (sport / market icons).
showTimeFilterbooleantrueShow the Time-window section (native range slider). Set false to hide it — e.g. on a live / in-play surface where a time window is moot.

Data sources (host-fed)

PropertyShapePurpose
ticks / ticksJsonArray<{ hours, label }>The time-slider ticks (both thumbs snap to these). Same contract as <goma-time-filter>.
selectedTimeValuestringConfirmed time window: 'all' or '{startHours}-{endHours}'.
sportsArray<{ id, name, icon?, iconId? }>Sport list used to resolve the displayed sport name/icon (dataSource: 'prop').
dataSource'prop' | 'rpc' | 'subscribe'Where the sport list comes from (to resolve the displayed sport name + the sport-scoped market list). 'prop' (default) = host-fed sports; 'rpc' = one-shot WAMP call on connect; 'subscribe' = live WAMP topic. rpc/subscribe need socketUrl / socketRealm (+ apiBaseUrl / ucsOperatorId) in config.
liveStatus'LIVE' | 'NOT_LIVE' | 'BOTH'Which sports the rpc/subscribe feed returns (default 'LIVE').
selectedSportIdstring | numberThe sport the filters are scoped to (displayed read-only; set by the host / a sibling picker).
marketOptionsArray<{ id, label, icon?, subtitle? }>Default-format markets. In prop mode host-fed; in rpc/subscribe mode the widget derives these from the active sport's matches-aggregator feed (bettingStore.mainMarkets ∩ present markets) and refetches when the sport changes.
marketOptionsBySportRecord<sportId, marketOptions[]>Optional (prop mode). When present, the widget swaps the market list locally when selectedSportId changes — no host round-trip.
selectedMarketIdstring | numberConfirmed default-format market id.
oddsFilter'all' | 'between_2_3' | 'big_odds'Confirmed odds-range filter.

Sport-scoped markets

The default-format market list is sport-dependent. The sport itself is not changed inside this modal — it's set via selectedSportId — and the market list re-scopes to it:

  • rpc / subscribe — the widget fetches the active sport's matches-aggregator feed and derives the market list from bettingStore.mainMarkets (intersected with the betting types present on the loaded events), refetching whenever selectedSportId changes. A skeleton shows while the feed loads.
  • prop — the host drives it: supply marketOptionsBySport[sportId] (swapped locally when selectedSportId changes) or update marketOptions directly.

If the previously selected market isn't offered for the new sport, the widget falls back to the first available option (stale-selection guard). The time and odds-range sections are not sport-dependent.

Events

Every event is composed: true and bubbles: true.

EventdetailWhen
goma:dialog-open{}The modal opened.
goma:dialog-close{}The modal closed (any cause).
goma:filters-resetFiltersPayloadReset tapped — pending values reset to defaults (not committed; dialog stays open).
goma:filters-apply{ time: { timeValue, timeLabel }, sportId, sportName, marketId, marketLabel, oddsFilter }Apply tapped — pending state committed to the confirmed selection; dialog closes.
ready / errorLifecycle (mount / error boundary).

Interaction model

  1. The trigger (or el.open = true) opens the modal; pending state is seeded from the confirmed selection.
  2. Reset returns the pending filter values to their defaults (does not close, does not commit).
  3. Cancel closes the modal, discarding pending edits.
  4. Apply commits pending → confirmed, emits goma:filters-apply, and closes.

Per-sport persistence

The confirmed filter set ({ timeValue, marketId, oddsFilter }) is remembered per sport in localStorage under a single key goma:filters-settings:v1 (a map { [sportId]: { … } }). Apply writes the active sport's entry; when selectedSportId changes (host / sibling picker), that sport's saved entry is restored onto the confirmed selection (a stale marketId falls back via the stale-market guard on open). No saved entry → the host-provided defaults stand.

Embedding

React

jsx
import '@gomagaming/filters-settings'

<goma-filters-settings
  ref={(el) => {
    if (!el) return
    el.config = { locale: 'en' }
    el.sports = sports
    el.marketOptions = markets
    el.selectedSportId = 1
    el.addEventListener('goma:filters-apply', (e) => onApply(e.detail))
  }}
/>

Vue

vue
<script setup>
import '@gomagaming/filters-settings'
</script>

<template>
  <goma-filters-settings
    :sports.prop="sports"
    :market-options.prop="markets"
    :selected-sport-id.prop="1"
    @goma:filters-apply="onApply"
  />
</template>

Theming

All colours map to existing --goma-* tokens — no new tokens.

TokenRoleDefault
--goma-dialogBackgroundModal panel surface#1b1d2b
--goma-backgroundCardsFilter card background#2a2f3e
--goma-highlightPrimaryAccent (trigger, radios, in-range slider, CTA)#29f2ff
--goma-highlightPrimaryContrastText / ring on the accent#062225
--goma-textPrimaryPrimary text + slider thumbs#ffffff
--goma-textSecondaryOdds subtitle / muted text#ade4ff
--goma-separatorLineDividers + dim slider track#3d425a

Source layout

LayerFile
Element classpackages/filters-settings/src/FiltersSettingsElement.js
App wrapperpackages/filters-settings/src/FiltersSettingsApp.vue
Modalpackages/filters-settings/src/components/FiltersSettings/FiltersSettingsDialog.vue
Native time sliderpackages/filters-settings/src/components/FiltersSettings/FiltersTimeSlider.vue
Trigger / card / radiopackages/filters-settings/src/components/FiltersSettings/{FiltersSettingsTrigger,FilterCard,RadioOptionRow}.vue
Pending-state composablepackages/filters-settings/src/composables/useFiltersSettings.js
Per-widget CSSpackages/filters-settings/src/styles/dialog.css
Testspackages/filters-settings/tests/{FiltersSettings,playground-manifest}.test.js