<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.
| Key | Type | Default | Purpose |
|---|---|---|---|
locale | string | 'en' | i18n locale. |
theme | object | — | --goma-* overrides (see Theming). |
open | boolean | false | Open state of the modal. Drive from your own control. |
showTrigger | boolean | true | Render the built-in settings trigger button. Set false to open only via el.open = true. |
triggerIcon | string | '' | Raw inline-SVG string for the trigger (uses currentColor). |
mobileBreakpoint | string | 'sm' | Below this width the modal is a bottom-sheet. Tailwind token (sm..2xl) or px. |
iconBaseUrl | string | '' | CDN base for per-option glyphs (sport / market icons). |
showTimeFilter | boolean | true | Show 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)
| Property | Shape | Purpose |
|---|---|---|
ticks / ticksJson | Array<{ hours, label }> | The time-slider ticks (both thumbs snap to these). Same contract as <goma-time-filter>. |
selectedTimeValue | string | Confirmed time window: 'all' or '{startHours}-{endHours}'. |
sports | Array<{ 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'). |
selectedSportId | string | number | The sport the filters are scoped to (displayed read-only; set by the host / a sibling picker). |
marketOptions | Array<{ 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. |
marketOptionsBySport | Record<sportId, marketOptions[]> | Optional (prop mode). When present, the widget swaps the market list locally when selectedSportId changes — no host round-trip. |
selectedMarketId | string | number | Confirmed 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 frombettingStore.mainMarkets(intersected with the betting types present on the loaded events), refetching wheneverselectedSportIdchanges. A skeleton shows while the feed loads.prop— the host drives it: supplymarketOptionsBySport[sportId](swapped locally whenselectedSportIdchanges) or updatemarketOptionsdirectly.
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.
| Event | detail | When |
|---|---|---|
goma:dialog-open | {} | The modal opened. |
goma:dialog-close | {} | The modal closed (any cause). |
goma:filters-reset | FiltersPayload | Reset 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 / error | — | Lifecycle (mount / error boundary). |
Interaction model
- The trigger (or
el.open = true) opens the modal; pending state is seeded from the confirmed selection. - Reset returns the pending filter values to their defaults (does not close, does not commit).
- Cancel closes the modal, discarding pending edits.
- 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.
| Token | Role | Default |
|---|---|---|
--goma-dialogBackground | Modal panel surface | #1b1d2b |
--goma-backgroundCards | Filter card background | #2a2f3e |
--goma-highlightPrimary | Accent (trigger, radios, in-range slider, CTA) | #29f2ff |
--goma-highlightPrimaryContrast | Text / ring on the accent | #062225 |
--goma-textPrimary | Primary text + slider thumbs | #ffffff |
--goma-textSecondary | Odds subtitle / muted text | #ade4ff |
--goma-separatorLine | Dividers + dim slider track | #3d425a |
Source layout
| Layer | File |
|---|---|
| Element class | packages/filters-settings/src/FiltersSettingsElement.js |
| App wrapper | packages/filters-settings/src/FiltersSettingsApp.vue |
| Modal | packages/filters-settings/src/components/FiltersSettings/FiltersSettingsDialog.vue |
| Native time slider | packages/filters-settings/src/components/FiltersSettings/FiltersTimeSlider.vue |
| Trigger / card / radio | packages/filters-settings/src/components/FiltersSettings/{FiltersSettingsTrigger,FilterCard,RadioOptionRow}.vue |
| Pending-state composable | packages/filters-settings/src/composables/useFiltersSettings.js |
| Per-widget CSS | packages/filters-settings/src/styles/dialog.css |
| Tests | packages/filters-settings/tests/{FiltersSettings,playground-manifest}.test.js |