goma-time-filter
Horizontal pill row of time-interval filters with a hardcoded "All" pill on the left. Pure host-fed: each interval is a { label, value } pair, where value matches the matches-aggregator topic's hoursInterval segment.
Lighter than the rest of the filter family: no WAMP transport, no sport-scoping, no API container — just a host-fed array, a selection state, and a goma:time-select event. Pair with <goma-events-horizontal> (followTimePicker: true) to drive the events feed.
Live demo
Open in the playground → — toggle Time filter in the widget rail. The playground ships a default 8-pill mock interval set (1H / 3H / 6H / 12H / Today / Tomorrow / 3 days / Week); paste your own JSON into the inspector's "Intervals JSON" field to swap.
Install
bash
npm install @gomagaming/time-filterYou also need the shared peer dependencies installed at your app level:
bash
npm install vue pinia vue-router vue-i18n @vueuse/core swiperElement
html
<goma-time-filter id="tf"></goma-time-filter>Configuration
The time-filter has no socketUrl / apiBaseUrl requirement — there's no WAMP transport. Only the standard locale / theme / messages knobs apply.
js
const el = document.getElementById('tf')
el.config = { locale: 'en' } // optional; defaults to 'en'The intervals array
Drive the widget by setting el.intervals to an array of { label, value } pairs:
js
el.intervals = [
{ label: '1H', value: '0-1' },
{ label: '3H', value: '0-3' },
{ label: '6H', value: '0-6' },
{ label: '12H', value: '0-12' },
{ label: 'Today', value: '0-24' },
{ label: 'Tomorrow', value: '24-48' },
{ label: '3 days', value: '0-72' },
{ label: 'Week', value: '0-168' },
]labelis the visible pill text. The widget renders it verbatim — hosts pre-localise (the widget itself only localises the hardcoded "All" pill via the bundledtime_filter_all_labelkey).valueis the canonical identifier. Format matches the matches-aggregator topic's hoursInterval segment:'{start}-{end}'where both are non-negative integers in hours. Use'all'for the no-filter sentinel — but you don't need to supply that yourself, the hardcoded "All" pill already covers it.
Entries missing value are dropped silently; duplicates by value keep the first occurrence. Malformed payloads don't crash the render.
Props (HTML attributes + JS properties)
| Property | Attribute | Default | Description |
|---|---|---|---|
intervals | intervals | [] | Array of { label, value } pairs. Set via JS or as a JSON-encoded HTML attribute. |
selectedTimeValue | selected-time-value | 'all' | The currently-selected interval's value field. 'all' selects the hardcoded leftmost pill. Two-way: clicks update this. |
spaceBetween | space-between | 8 | Pixel gap between pills. |
showLabels | show-labels | true | Render the label on each pill (otherwise pills shrink to icon-free dots — useful for ultra-compact layouts). |
The hardcoded "All" pill
Always rendered as the leftmost pill. Clicking it emits:
js
{
timeValue: 'all',
timeLabel: 'All', // locale-aware via `t('time_filter_all_label')`
previousTimeValue: <whatever was selected>,
}timeValue === 'all' is the canonical "no filter" sentinel — maps straight to the matches-aggregator topic's literal all hoursInterval segment.
Events
| Status | Event | Detail | Emitted when |
|---|---|---|---|
| Canonical | ready | {} | Widget mounted. |
| Canonical | goma:error | { message, code, component? } | Render error. |
| Canonical | goma:time-select | { timeValue, timeLabel, previousTimeValue } | User taps a time-interval pill or the "All" pill. The widget also mutates selectedTimeValue so the pill flips immediately. |
Embedding examples
Vanilla HTML — drive an events-horizontal feed
html
<goma-time-filter id="times"></goma-time-filter>
<goma-events-horizontal
id="feed"
follow-time-picker
topic="custom-matches-aggregator/1/all/all/all/POPULAR/LIVE/10/5"
></goma-events-horizontal>
<script type="module">
import '@gomagaming/time-filter'
import '@gomagaming/events-horizontal'
const times = document.getElementById('times')
times.intervals = [
{ label: '1H', value: '0-1' },
{ label: 'Today', value: '0-24' },
{ label: 'Week', value: '0-168' },
]
document.getElementById('feed').config = { /* socket + REST creds */ }
</script>Tap a pill → events-horizontal rewrites segment 3 (hoursInterval) of its topic → WAMP subscription rebinds transparently, no reconnect.
React
jsx
import '@gomagaming/time-filter'
import { useEffect, useRef } from 'react'
const INTERVALS = [
{ label: '1H', value: '0-1' },
{ label: 'Today', value: '0-24' },
{ label: 'Week', value: '0-168' },
]
function TimeFilter({ onSelect }) {
const ref = useRef(null)
useEffect(() => {
if (!ref.current) return
ref.current.intervals = INTERVALS
}, [])
return (
<goma-time-filter ref={ref} onGoma:time-select={(e) => onSelect(e.detail)} />
)
}Vue 3
vue
<script setup>
import '@gomagaming/time-filter'
import { ref, onMounted } from 'vue'
const el = ref(null)
const INTERVALS = [
{ label: '1H', value: '0-1' },
{ label: 'Today', value: '0-24' },
]
onMounted(() => { el.value.intervals = INTERVALS })
</script>
<template>
<goma-time-filter ref="el" @goma:time-select="onTimeSelect" />
</template>Theming
Same --goma-* tokens as goma-region-filter / goma-competition-filter. Override via el.theme = { … }. Time-filter doesn't render a flag/icon ring or a live-count badge, so a slightly narrower set:
| Token | Element |
|---|---|
--goma-backgroundPrimary | Rail background |
--goma-backgroundCards | Inactive pill background |
--goma-textPrimary | Inactive pill label |
--goma-highlightPrimary | Selected pill background |
--goma-highlightPrimaryContrast | Selected pill label |
i18n
Three bundled keys (en / fr / pt):
| Key | Default (en) | Used in |
|---|---|---|
time_filter_all_label | All | The hardcoded leftmost pill |
time_filter_aria | Time filter | <swiper aria-label> |
time_filter_empty | No time intervals available | (Reserved — currently only the All pill renders when intervals is empty.) |
Pill labels themselves are never localised by the widget — hosts pre-localise their intervals array before passing it in. This keeps the prop-shape contract minimal.
Accessibility
| Element | Role | Attributes |
|---|---|---|
| Swiper container | tablist | aria-label="Time filter" (i18n key time_filter_aria) |
| Time pill | tab | tabindex="0", aria-selected, aria-label="{label}" |
| Error banner | — | role="alert" for screen-reader announcement |
Keyboard: Tab to focus pills, Enter / Space to select. :focus-visible ring uses the --goma-highlightPrimary token.
Source layout
| Layer | File |
|---|---|
| Element class | packages/time-filter/src/TimeFilterElement.js |
| App wrapper | packages/time-filter/src/TimeFilterApp.vue |
| Component tree | packages/time-filter/src/components/TimeFilter/{TimeList,TimeItem}.vue |
| Manifest | packages/time-filter/custom-elements.json |
Unlike the sport / region / competition filters, there's no shared composable in @gomagaming/sports-domain — the widget is purely host-driven and has no WAMP plumbing of its own.
What's deferred
- A future v2 surface could ship a default
intervalsset bundled at the locale layer (e.g.intervals: 'preset:standard'resolves to[{label: t('time_1h'), value: '0-1'}, …]). Out of scope for v1 — hosts supply their own arrays today, which gives them full control over localisation and the set of options.