@sensiblestats/widget-react 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/index.cjs +530 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +529 -173
- package/dist/index.js.map +1 -1
- package/dist/styles.css +98 -35
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/StatsWidget.tsx
|
|
39
|
-
var
|
|
39
|
+
var import_react17 = require("react");
|
|
40
40
|
var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
|
|
41
41
|
|
|
42
42
|
// src/kickoff.ts
|
|
@@ -608,7 +608,14 @@ function reducer(state, action) {
|
|
|
608
608
|
return {
|
|
609
609
|
...state,
|
|
610
610
|
messages: state.messages.map(
|
|
611
|
-
(m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards } } : m
|
|
611
|
+
(m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards, greeting: action.greeting ?? m.clubFanDashboard.greeting } } : m
|
|
612
|
+
)
|
|
613
|
+
};
|
|
614
|
+
case "LIVE_TRANSITION":
|
|
615
|
+
return {
|
|
616
|
+
...state,
|
|
617
|
+
messages: state.messages.map(
|
|
618
|
+
(m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: action.dashboard } : m
|
|
612
619
|
)
|
|
613
620
|
};
|
|
614
621
|
}
|
|
@@ -617,26 +624,74 @@ function reducer(state, action) {
|
|
|
617
624
|
// src/useLiveDashboardPoll.ts
|
|
618
625
|
var import_react = require("react");
|
|
619
626
|
var POLL_INTERVAL_MS = 6e4;
|
|
620
|
-
|
|
627
|
+
var LEAD_MS = 12e4;
|
|
628
|
+
var GIVE_UP_MS = 30 * 6e4;
|
|
629
|
+
function extractKickoffMs(dashboard) {
|
|
630
|
+
const countdown = dashboard.cards.find((c) => c.cardType === "countdown");
|
|
631
|
+
if (countdown) return countdown.data.kickoffUnix * 1e3;
|
|
632
|
+
for (const c of dashboard.cards) {
|
|
633
|
+
const d = c.data;
|
|
634
|
+
if (typeof d?.kickoffUnix === "number") return d.kickoffUnix * 1e3;
|
|
635
|
+
}
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
function findAnchor(messages) {
|
|
639
|
+
const now = Date.now();
|
|
621
640
|
for (const m of messages.slice().reverse()) {
|
|
622
|
-
if (m.role
|
|
641
|
+
if (m.role !== "assistant" || !m.clubFanDashboard) continue;
|
|
642
|
+
const { phase } = m.clubFanDashboard;
|
|
643
|
+
if (phase === "live") return { messageId: m.id, kind: "live" };
|
|
644
|
+
if (phase === "preMatch") {
|
|
645
|
+
const kickoffMs = extractKickoffMs(m.clubFanDashboard);
|
|
646
|
+
if (kickoffMs === null) continue;
|
|
647
|
+
if (now >= kickoffMs - LEAD_MS && now <= kickoffMs + GIVE_UP_MS) {
|
|
648
|
+
return { messageId: m.id, kind: "preMatch", kickoffMs };
|
|
649
|
+
}
|
|
650
|
+
}
|
|
623
651
|
}
|
|
624
652
|
return null;
|
|
625
653
|
}
|
|
654
|
+
function findEarliestWakeupMs(messages) {
|
|
655
|
+
const now = Date.now();
|
|
656
|
+
let earliest = null;
|
|
657
|
+
for (const m of messages) {
|
|
658
|
+
if (m.role !== "assistant" || m.clubFanDashboard?.phase !== "preMatch") continue;
|
|
659
|
+
const kickoffMs = extractKickoffMs(m.clubFanDashboard);
|
|
660
|
+
if (kickoffMs === null) continue;
|
|
661
|
+
const wakeAt = kickoffMs - LEAD_MS;
|
|
662
|
+
if (wakeAt > now && (earliest === null || wakeAt < earliest)) earliest = wakeAt;
|
|
663
|
+
}
|
|
664
|
+
return earliest;
|
|
665
|
+
}
|
|
626
666
|
function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
627
|
-
const
|
|
667
|
+
const [, forceUpdate] = (0, import_react.useReducer)((x) => x + 1, 0);
|
|
668
|
+
const anchor = findAnchor(messages);
|
|
669
|
+
const anchorRef = (0, import_react.useRef)(anchor);
|
|
670
|
+
anchorRef.current = anchor;
|
|
628
671
|
const inFlightRef = (0, import_react.useRef)(false);
|
|
672
|
+
const anchorKey = anchor ? `${anchor.messageId}:${anchor.kind}` : null;
|
|
629
673
|
(0, import_react.useEffect)(() => {
|
|
630
|
-
if (!
|
|
674
|
+
if (!anchorKey || !anchorRef.current) return;
|
|
631
675
|
const tick = async () => {
|
|
632
676
|
if (inFlightRef.current) return;
|
|
633
677
|
inFlightRef.current = true;
|
|
634
678
|
try {
|
|
635
679
|
const payload = await getLiveDashboard();
|
|
636
|
-
|
|
637
|
-
|
|
680
|
+
const current = anchorRef.current;
|
|
681
|
+
if (!current) return;
|
|
682
|
+
if (payload?.phase === "live") {
|
|
683
|
+
if (current.kind === "preMatch") {
|
|
684
|
+
dispatch({ kind: "LIVE_TRANSITION", messageId: current.messageId, dashboard: payload });
|
|
685
|
+
window.clearInterval(id);
|
|
686
|
+
} else {
|
|
687
|
+
dispatch({ kind: "LIVE_REFRESH", messageId: current.messageId, cards: payload.cards, greeting: payload.greeting });
|
|
688
|
+
}
|
|
638
689
|
} else {
|
|
639
|
-
|
|
690
|
+
if (current.kind === "live") {
|
|
691
|
+
window.clearInterval(id);
|
|
692
|
+
} else if (Date.now() > current.kickoffMs + GIVE_UP_MS) {
|
|
693
|
+
window.clearInterval(id);
|
|
694
|
+
}
|
|
640
695
|
}
|
|
641
696
|
} catch {
|
|
642
697
|
} finally {
|
|
@@ -647,7 +702,16 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
|
647
702
|
void tick();
|
|
648
703
|
}, POLL_INTERVAL_MS);
|
|
649
704
|
return () => window.clearInterval(id);
|
|
650
|
-
}, [
|
|
705
|
+
}, [anchorKey, dispatch, getLiveDashboard]);
|
|
706
|
+
(0, import_react.useEffect)(() => {
|
|
707
|
+
if (anchorKey !== null) return;
|
|
708
|
+
const wakeAt = findEarliestWakeupMs(messages);
|
|
709
|
+
if (wakeAt === null) return;
|
|
710
|
+
const delay = wakeAt - Date.now();
|
|
711
|
+
if (delay <= 0) return;
|
|
712
|
+
const id = window.setTimeout(forceUpdate, delay);
|
|
713
|
+
return () => window.clearTimeout(id);
|
|
714
|
+
}, [anchorKey, messages, forceUpdate]);
|
|
651
715
|
}
|
|
652
716
|
|
|
653
717
|
// src/useChatStream.ts
|
|
@@ -812,7 +876,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
812
876
|
}
|
|
813
877
|
|
|
814
878
|
// src/components/ChatPanel.tsx
|
|
815
|
-
var
|
|
879
|
+
var import_react16 = require("react");
|
|
816
880
|
|
|
817
881
|
// src/components/ChatHeader.tsx
|
|
818
882
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -825,7 +889,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
825
889
|
}
|
|
826
890
|
|
|
827
891
|
// src/components/MessageList.tsx
|
|
828
|
-
var
|
|
892
|
+
var import_react14 = require("react");
|
|
829
893
|
|
|
830
894
|
// src/components/Markdown.tsx
|
|
831
895
|
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
@@ -1135,22 +1199,44 @@ function ReasonStrip({ it, ui }) {
|
|
|
1135
1199
|
) : null
|
|
1136
1200
|
] });
|
|
1137
1201
|
}
|
|
1202
|
+
function CardFixture({ it }) {
|
|
1203
|
+
if (it.homeTeamName && it.awayTeamName) {
|
|
1204
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.homeCrest, name: it.homeTeamName }),
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.homeTeamName }),
|
|
1207
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-vs", "aria-hidden": "true", children: "vs" }),
|
|
1208
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.awayTeamName }),
|
|
1209
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.awayCrest, name: it.awayTeamName })
|
|
1210
|
+
] });
|
|
1211
|
+
}
|
|
1212
|
+
if (it.fixture) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-fixture-txt", children: it.fixture });
|
|
1213
|
+
if (it.competition) {
|
|
1214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
|
|
1217
|
+
] });
|
|
1218
|
+
}
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1138
1221
|
function InsightCard({ it, ui, slip }) {
|
|
1139
1222
|
const live = it.isLive || it.phase === "live";
|
|
1140
1223
|
const kickoff = it.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(it.kickoffUtc) : void 0;
|
|
1141
1224
|
const hasNamedTeams = !!it.homeTeamName && !!it.awayTeamName;
|
|
1142
|
-
const
|
|
1225
|
+
const leftIsFixture = hasNamedTeams || !!it.fixture;
|
|
1226
|
+
const compInMeta = leftIsFixture && !!it.competition;
|
|
1227
|
+
const showHeader = leftIsFixture || !!it.competition || live || !!kickoff;
|
|
1143
1228
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-binsight", children: [
|
|
1144
1229
|
showHeader ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-header", children: [
|
|
1145
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-header-left", children:
|
|
1146
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
|
|
1147
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
|
|
1148
|
-
] }) : null }),
|
|
1230
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-header-left", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CardFixture, { it }) }),
|
|
1149
1231
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-header-right", children: [
|
|
1150
1232
|
live ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-live", children: [
|
|
1151
1233
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
|
|
1152
1234
|
ui?.liveLabel ?? "Live"
|
|
1153
1235
|
] }) : null,
|
|
1236
|
+
compInMeta ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-comp-meta", children: [
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
|
|
1239
|
+
] }) : null,
|
|
1154
1240
|
kickoff ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-kick", children: [
|
|
1155
1241
|
kickoff.dayLabel,
|
|
1156
1242
|
" ",
|
|
@@ -1158,28 +1244,22 @@ function InsightCard({ it, ui, slip }) {
|
|
|
1158
1244
|
] }) : null
|
|
1159
1245
|
] })
|
|
1160
1246
|
] }) : null,
|
|
1161
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-
|
|
1162
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
1163
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1164
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
] })
|
|
1169
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("
|
|
1170
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
it.odds ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-odds", children: [
|
|
1176
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: it.odds }),
|
|
1177
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
|
|
1178
|
-
it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
|
|
1179
|
-
] }) : null,
|
|
1180
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AddToSlipButton, { it, slip, ui })
|
|
1181
|
-
] })
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-action-row", children: [
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-pick", children: [
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
|
|
1250
|
+
it.selection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-pick-dot", "aria-hidden": "true", children: "\xB7" }),
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-sel", children: it.selection })
|
|
1253
|
+
] }) : null
|
|
1254
|
+
] }),
|
|
1255
|
+
it.odds ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-odds", children: [
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: it.odds }),
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
|
|
1258
|
+
it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
|
|
1259
|
+
] }) : null,
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AddToSlipButton, { it, slip, ui })
|
|
1182
1261
|
] }),
|
|
1262
|
+
it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null,
|
|
1183
1263
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ReasonStrip, { it, ui }),
|
|
1184
1264
|
it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
|
|
1185
1265
|
] });
|
|
@@ -1739,139 +1819,352 @@ function RestingFormCard({ card }) {
|
|
|
1739
1819
|
] });
|
|
1740
1820
|
}
|
|
1741
1821
|
|
|
1822
|
+
// src/components/dashboard/LiveScoreCard.tsx
|
|
1823
|
+
var import_react11 = require("react");
|
|
1824
|
+
|
|
1825
|
+
// src/useLiveClock.ts
|
|
1826
|
+
var import_react10 = require("react");
|
|
1827
|
+
var TICK_MS = 6e4;
|
|
1828
|
+
var MAX_LIVE_TICK_MINUTES = 15;
|
|
1829
|
+
function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
1830
|
+
if (!clockRunning) return minute;
|
|
1831
|
+
const elapsedMinutes = Math.floor((Date.now() - receivedAtMs) / TICK_MS);
|
|
1832
|
+
return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
|
|
1833
|
+
}
|
|
1834
|
+
function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
1835
|
+
const [displayMinute, setDisplayMinute] = (0, import_react10.useState)(
|
|
1836
|
+
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
1837
|
+
);
|
|
1838
|
+
(0, import_react10.useEffect)(() => {
|
|
1839
|
+
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
1840
|
+
if (!clockRunning) return;
|
|
1841
|
+
const id = window.setInterval(() => {
|
|
1842
|
+
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
1843
|
+
}, TICK_MS);
|
|
1844
|
+
return () => window.clearInterval(id);
|
|
1845
|
+
}, [minute, clockRunning, receivedAtMs]);
|
|
1846
|
+
return displayMinute;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1742
1849
|
// src/components/dashboard/LiveScoreCard.tsx
|
|
1743
1850
|
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1744
|
-
function
|
|
1851
|
+
function initials3(name) {
|
|
1852
|
+
return name.trim().slice(0, 3).toUpperCase();
|
|
1853
|
+
}
|
|
1854
|
+
function LiveCrest({ url, name, side }) {
|
|
1855
|
+
const [failed, setFailed] = (0, import_react11.useState)(false);
|
|
1856
|
+
const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
|
|
1857
|
+
if (url && !failed) {
|
|
1858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
1859
|
+
}
|
|
1860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: `${cls} ss-w-fandash-live-crest--fallback`, "aria-hidden": "true", children: initials3(name) });
|
|
1861
|
+
}
|
|
1862
|
+
function scorersLine(goalEvents) {
|
|
1863
|
+
if (!goalEvents || goalEvents.length === 0) return null;
|
|
1864
|
+
const named = goalEvents.filter((e) => e.type === "goal" && e.player);
|
|
1865
|
+
if (named.length === 0) return null;
|
|
1866
|
+
return named.slice().sort((a, b) => a.minute - b.minute).map((e) => `${e.player} ${e.minuteLabel}`).join(", ");
|
|
1867
|
+
}
|
|
1868
|
+
function LiveScoreCard({
|
|
1869
|
+
card,
|
|
1870
|
+
goalEvents
|
|
1871
|
+
}) {
|
|
1745
1872
|
const { data } = card;
|
|
1873
|
+
const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
|
|
1874
|
+
const seedRef = (0, import_react11.useRef)(null);
|
|
1875
|
+
if (!seedRef.current || seedRef.current.key !== seedKey) {
|
|
1876
|
+
seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
|
|
1877
|
+
}
|
|
1878
|
+
const displayMinute = useLiveClock(data.minute, data.clockRunning, seedRef.current.receivedAtMs);
|
|
1879
|
+
const minuteSuffix = data.minuteLabel.replace(/^\d+/, "");
|
|
1880
|
+
const scorers = scorersLine(goalEvents);
|
|
1746
1881
|
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
|
|
1747
1882
|
card.title ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1748
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-
|
|
1749
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-
|
|
1754
|
-
] }),
|
|
1755
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-meta", children: [
|
|
1756
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: data.minuteLabel }),
|
|
1757
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-status", children: data.statusLabel }),
|
|
1883
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-top", children: [
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "ss-w-fandash-live-pill", children: [
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-dot", "aria-hidden": "true" }),
|
|
1886
|
+
data.statusLabel
|
|
1887
|
+
] }),
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: `${displayMinute}${minuteSuffix}` }),
|
|
1758
1889
|
data.competition ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
|
|
1759
|
-
] })
|
|
1890
|
+
] }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-main", children: [
|
|
1892
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-side ss-w-fandash-live-side--home", children: [
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LiveCrest, { url: data.homeCrestUrl, name: data.homeName, side: "home" }),
|
|
1894
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-team-name", children: data.homeName })
|
|
1895
|
+
] }),
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-score-main", children: [
|
|
1897
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-value", children: data.homeScore }),
|
|
1898
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-sep", children: "\u2013" }),
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-value", children: data.awayScore })
|
|
1900
|
+
] }),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-side ss-w-fandash-live-side--away", children: [
|
|
1902
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LiveCrest, { url: data.awayCrestUrl, name: data.awayName, side: "away" }),
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-team-name", children: data.awayName })
|
|
1904
|
+
] })
|
|
1905
|
+
] }),
|
|
1906
|
+
scorers ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-scorers", children: [
|
|
1907
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-scorers-icon", "aria-hidden": "true", children: "\u26BD" }),
|
|
1908
|
+
scorers
|
|
1909
|
+
] }) : null
|
|
1760
1910
|
] });
|
|
1761
1911
|
}
|
|
1762
1912
|
|
|
1763
1913
|
// src/components/dashboard/LiveTimelineCard.tsx
|
|
1914
|
+
var import_react12 = require("react");
|
|
1764
1915
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1765
|
-
|
|
1916
|
+
var SHOWN = 3;
|
|
1917
|
+
function EventIcon({ type }) {
|
|
1918
|
+
if (type === "goal") {
|
|
1919
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico ss-w-fandash-live-tl-ico--goal", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ball" }) });
|
|
1920
|
+
}
|
|
1921
|
+
if (type === "yellow" || type === "red") {
|
|
1922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: `ss-w-fandash-live-tl-card ss-w-fandash-live-tl-card--${type}` }) });
|
|
1923
|
+
}
|
|
1924
|
+
if (type === "sub") {
|
|
1925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-subarrows", children: "\u21C5" }) });
|
|
1926
|
+
}
|
|
1927
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-dot" }) });
|
|
1928
|
+
}
|
|
1929
|
+
function EventLabel({ e }) {
|
|
1930
|
+
if (e.type === "sub") {
|
|
1931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-who", children: [
|
|
1932
|
+
e.player ?? e.text,
|
|
1933
|
+
e.playerOff ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-off", children: [
|
|
1934
|
+
" for ",
|
|
1935
|
+
e.playerOff
|
|
1936
|
+
] }) : null
|
|
1937
|
+
] });
|
|
1938
|
+
}
|
|
1939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-who", children: e.player ?? e.text });
|
|
1940
|
+
}
|
|
1941
|
+
function EventRow({ e }) {
|
|
1942
|
+
const cls = [
|
|
1943
|
+
"ss-w-fandash-live-tl-ev",
|
|
1944
|
+
e.side === "home" ? "ss-w-fandash-live-tl-ev--home" : "ss-w-fandash-live-tl-ev--away",
|
|
1945
|
+
e.type === "goal" ? "ss-w-fandash-live-tl-ev--goal" : ""
|
|
1946
|
+
].filter(Boolean).join(" ");
|
|
1947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cls, children: [
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-txt", children: [
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventLabel, { e }),
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventIcon, { type: e.type })
|
|
1951
|
+
] }),
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-badge", children: e.minuteLabel })
|
|
1953
|
+
] });
|
|
1954
|
+
}
|
|
1955
|
+
function LiveTimelineCard({ card, ui }) {
|
|
1956
|
+
const [expanded, setExpanded] = (0, import_react12.useState)(false);
|
|
1957
|
+
const events = card.data.events;
|
|
1958
|
+
if (!events || events.length === 0) return null;
|
|
1959
|
+
const sorted = [...events].sort((a, b) => b.minute - a.minute);
|
|
1960
|
+
const rest = sorted.length - SHOWN;
|
|
1961
|
+
const shown = expanded ? sorted : sorted.slice(0, SHOWN);
|
|
1962
|
+
const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
|
|
1963
|
+
const showLess = ui?.showLess ?? "Show less";
|
|
1766
1964
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
|
|
1767
1965
|
card.title ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1768
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "ss-w-fandash-live-tl", children: shown.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventRow, { e }, `${e.minute}-${e.side}-${e.type}-${i}`)) }),
|
|
1967
|
+
rest > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1968
|
+
"button",
|
|
1969
|
+
{
|
|
1970
|
+
type: "button",
|
|
1971
|
+
className: "ss-w-fandash-live-tl-more",
|
|
1972
|
+
"aria-expanded": expanded,
|
|
1973
|
+
onClick: () => setExpanded((v) => !v),
|
|
1974
|
+
children: expanded ? showLess : showMore(rest)
|
|
1975
|
+
}
|
|
1976
|
+
) : null
|
|
1772
1977
|
] });
|
|
1773
1978
|
}
|
|
1774
1979
|
|
|
1775
|
-
// src/components/dashboard/
|
|
1980
|
+
// src/components/dashboard/MomentumWave.tsx
|
|
1981
|
+
var import_react13 = require("react");
|
|
1776
1982
|
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1983
|
+
var WIDTH = 270;
|
|
1984
|
+
var HEIGHT = 56;
|
|
1985
|
+
var BASELINE = HEIGHT / 2;
|
|
1986
|
+
var SCALE = 0.26;
|
|
1987
|
+
function buildPaths(points) {
|
|
1988
|
+
const lastIndex = points.length - 1;
|
|
1989
|
+
const px = (i) => lastIndex === 0 ? 0 : i / lastIndex * WIDTH;
|
|
1990
|
+
const py = (v) => BASELINE - v * SCALE;
|
|
1991
|
+
const linePts = points.map((p, i) => `${px(i).toFixed(1)},${py(p.value).toFixed(1)}`);
|
|
1992
|
+
const area = `M0,${BASELINE} L${linePts.join(" L")} L${WIDTH},${BASELINE} Z`;
|
|
1993
|
+
const line = `M${linePts.join(" L")}`;
|
|
1994
|
+
const lastValue = points[lastIndex]?.value ?? 0;
|
|
1995
|
+
return { area, line, lastX: px(lastIndex).toFixed(1), lastY: py(lastValue).toFixed(1) };
|
|
1996
|
+
}
|
|
1997
|
+
function MomentumWave({
|
|
1998
|
+
points,
|
|
1999
|
+
caption,
|
|
2000
|
+
clubIsHome
|
|
2001
|
+
}) {
|
|
2002
|
+
if (points.length === 0) return null;
|
|
2003
|
+
const { area, line, lastX, lastY } = buildPaths(points);
|
|
2004
|
+
const clipId = (0, import_react13.useId)();
|
|
2005
|
+
const topClipId = `${clipId}-top`;
|
|
2006
|
+
const bottomClipId = `${clipId}-bottom`;
|
|
2007
|
+
const topClass = clubIsHome ? "ss-w-fandash-live-mom-home" : "ss-w-fandash-live-mom-away";
|
|
2008
|
+
const bottomClass = clubIsHome ? "ss-w-fandash-live-mom-away" : "ss-w-fandash-live-mom-home";
|
|
2009
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-live-mom", children: [
|
|
2010
|
+
caption ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-mom-caption", children: caption }) : null,
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
2012
|
+
"svg",
|
|
2013
|
+
{
|
|
2014
|
+
className: "ss-w-fandash-live-mom-svg",
|
|
2015
|
+
viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
|
|
2016
|
+
preserveAspectRatio: "none",
|
|
2017
|
+
role: "img",
|
|
2018
|
+
"aria-label": caption ?? "Momentum",
|
|
2019
|
+
children: [
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("defs", { children: [
|
|
2021
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("clipPath", { id: topClipId, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("rect", { x: 0, y: 0, width: WIDTH, height: BASELINE }) }),
|
|
2022
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("clipPath", { id: bottomClipId, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("rect", { x: 0, y: BASELINE, width: WIDTH, height: HEIGHT - BASELINE }) })
|
|
2023
|
+
] }),
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: area, className: topClass, clipPath: `url(#${topClipId})` }),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: area, className: bottomClass, clipPath: `url(#${bottomClipId})` }),
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("line", { x1: 0, y1: BASELINE, x2: WIDTH, y2: BASELINE, className: "ss-w-fandash-live-mom-base" }),
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: line, className: "ss-w-fandash-live-mom-line", vectorEffect: "non-scaling-stroke" }),
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: lastX, cy: lastY, r: 3.2, className: "ss-w-fandash-live-mom-now", vectorEffect: "non-scaling-stroke" })
|
|
2029
|
+
]
|
|
2030
|
+
}
|
|
2031
|
+
)
|
|
2032
|
+
] });
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// src/components/dashboard/LiveStatsCard.tsx
|
|
2036
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2037
|
+
function splitWidths(homeNum, awayNum) {
|
|
2038
|
+
const total = homeNum + awayNum;
|
|
2039
|
+
if (total <= 0) return { home: "50%", away: "50%" };
|
|
2040
|
+
const homePct = Math.round(homeNum / total * 1e3) / 10;
|
|
2041
|
+
return { home: `${homePct}%`, away: `${Math.round((100 - homePct) * 10) / 10}%` };
|
|
2042
|
+
}
|
|
1777
2043
|
function LiveStatsCard({ card }) {
|
|
1778
2044
|
const { data } = card;
|
|
1779
|
-
return /* @__PURE__ */ (0,
|
|
1780
|
-
card.title ? /* @__PURE__ */ (0,
|
|
1781
|
-
/* @__PURE__ */ (0,
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2045
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
|
|
2046
|
+
card.title ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "ss-w-fandash-live-stat-rows", children: data.rows.map((r, i) => {
|
|
2048
|
+
const homeValue = data.clubIsHome ? r.clubValue : r.oppValue;
|
|
2049
|
+
const awayValue = data.clubIsHome ? r.oppValue : r.clubValue;
|
|
2050
|
+
const homeNum = data.clubIsHome ? r.clubNum : r.oppNum;
|
|
2051
|
+
const awayNum = data.clubIsHome ? r.oppNum : r.clubNum;
|
|
2052
|
+
const { home, away } = splitWidths(homeNum, awayNum);
|
|
2053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-stat", children: [
|
|
2054
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-stat-row", children: [
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-value ss-w-fandash-live-stat-value--home", children: homeValue }),
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
|
|
2057
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-value ss-w-fandash-live-stat-value--away", children: awayValue })
|
|
2058
|
+
] }),
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-bar", children: [
|
|
2060
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-bar-home", style: { width: home } }),
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-bar-away", style: { width: away } })
|
|
2062
|
+
] })
|
|
2063
|
+
] }, `${r.label}-${i}`);
|
|
2064
|
+
}) }),
|
|
2065
|
+
data.momentum.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MomentumWave, { points: data.momentum, caption: data.momentumLabel, clubIsHome: data.clubIsHome }) : data.momentumLabel ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-momentum", children: data.momentumLabel }) : null,
|
|
2066
|
+
data.rows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-legend", children: [
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "ss-w-fandash-live-legend-item", children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("i", { className: "ss-w-fandash-live-legend-swatch ss-w-fandash-live-legend-swatch--home" }),
|
|
2069
|
+
data.homeName
|
|
2070
|
+
] }),
|
|
2071
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "ss-w-fandash-live-legend-item", children: [
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("i", { className: "ss-w-fandash-live-legend-swatch ss-w-fandash-live-legend-swatch--away" }),
|
|
2073
|
+
data.awayName
|
|
2074
|
+
] })
|
|
2075
|
+
] }) : null
|
|
1787
2076
|
] });
|
|
1788
2077
|
}
|
|
1789
2078
|
|
|
1790
2079
|
// src/components/dashboard/ClubFanDashboard.tsx
|
|
1791
|
-
var
|
|
2080
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1792
2081
|
function ClubFanDashboard({ dashboard, onTap, ui }) {
|
|
1793
2082
|
const orderedCards = [
|
|
1794
2083
|
...dashboard.cards.filter((c) => c.cardType === "countdown"),
|
|
1795
2084
|
...dashboard.cards.filter((c) => c.cardType !== "countdown")
|
|
1796
2085
|
];
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2086
|
+
const timelineCard = dashboard.cards.find(
|
|
2087
|
+
(c) => c.cardType === "liveTimeline"
|
|
2088
|
+
);
|
|
2089
|
+
const goalEvents = timelineCard?.data.events.filter((e) => e.type === "goal");
|
|
2090
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-fandash", children: [
|
|
2091
|
+
dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FanDashboardCard, { card, onTap, ui, goalEvents }, `${card.cardType}-${i}`)) })
|
|
1800
2093
|
] });
|
|
1801
2094
|
}
|
|
1802
|
-
function FanDashboardCard({ card, onTap, ui }) {
|
|
2095
|
+
function FanDashboardCard({ card, onTap, ui, goalEvents }) {
|
|
1803
2096
|
switch (card.cardType) {
|
|
1804
2097
|
case "fixture":
|
|
1805
|
-
return /* @__PURE__ */ (0,
|
|
2098
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FixtureCard, { card, onTap });
|
|
1806
2099
|
case "countdown":
|
|
1807
|
-
return /* @__PURE__ */ (0,
|
|
2100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CountdownCard, { card });
|
|
1808
2101
|
case "injuries":
|
|
1809
|
-
return /* @__PURE__ */ (0,
|
|
2102
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(InjuriesCard, { card });
|
|
1810
2103
|
case "reasonToBet":
|
|
1811
|
-
return /* @__PURE__ */ (0,
|
|
2104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReasonToBetCard, { card });
|
|
1812
2105
|
case "whereToWatch":
|
|
1813
|
-
return /* @__PURE__ */ (0,
|
|
2106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(WhereToWatchCard, { card });
|
|
1814
2107
|
case "congestedWeek":
|
|
1815
|
-
return /* @__PURE__ */ (0,
|
|
2108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CongestedWeekCard, { card, onTap });
|
|
1816
2109
|
case "transfers":
|
|
1817
|
-
return /* @__PURE__ */ (0,
|
|
2110
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TransfersCard, { card });
|
|
1818
2111
|
case "links":
|
|
1819
|
-
return /* @__PURE__ */ (0,
|
|
2112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LinksCard, { card });
|
|
1820
2113
|
case "postMatchResult":
|
|
1821
|
-
return /* @__PURE__ */ (0,
|
|
2114
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchResultCard, { card });
|
|
1822
2115
|
case "postMatchGoals":
|
|
1823
|
-
return /* @__PURE__ */ (0,
|
|
2116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchGoalsCard, { card, onTap });
|
|
1824
2117
|
case "postMatchStats":
|
|
1825
|
-
return /* @__PURE__ */ (0,
|
|
2118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchStatsCard, { card, onTap });
|
|
1826
2119
|
case "postMatchTopPerformer":
|
|
1827
|
-
return /* @__PURE__ */ (0,
|
|
2120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchTopPerformerCard, { card, onTap });
|
|
1828
2121
|
case "postMatchNext":
|
|
1829
|
-
return /* @__PURE__ */ (0,
|
|
2122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchNextCard, { card });
|
|
1830
2123
|
case "restingNext":
|
|
1831
|
-
return /* @__PURE__ */ (0,
|
|
2124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingNextCard, { card, onTap });
|
|
1832
2125
|
case "restingStandings":
|
|
1833
|
-
return /* @__PURE__ */ (0,
|
|
2126
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingStandingsCard, { card, onTap });
|
|
1834
2127
|
case "restingSeason":
|
|
1835
|
-
return /* @__PURE__ */ (0,
|
|
2128
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingSeasonCard, { card, onTap });
|
|
1836
2129
|
case "restingForm":
|
|
1837
|
-
return /* @__PURE__ */ (0,
|
|
2130
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingFormCard, { card });
|
|
1838
2131
|
case "liveScore":
|
|
1839
|
-
return /* @__PURE__ */ (0,
|
|
2132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveScoreCard, { card, goalEvents });
|
|
1840
2133
|
case "liveTimeline":
|
|
1841
|
-
return /* @__PURE__ */ (0,
|
|
2134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveTimelineCard, { card, ui });
|
|
1842
2135
|
case "liveStats":
|
|
1843
|
-
return /* @__PURE__ */ (0,
|
|
2136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveStatsCard, { card });
|
|
1844
2137
|
default:
|
|
1845
2138
|
return null;
|
|
1846
2139
|
}
|
|
1847
2140
|
}
|
|
1848
2141
|
|
|
1849
2142
|
// src/components/ChatMessage.tsx
|
|
1850
|
-
var
|
|
2143
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1851
2144
|
function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
|
|
1852
2145
|
if (message.role === "user") {
|
|
1853
|
-
return /* @__PURE__ */ (0,
|
|
2146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-msg ss-w-user", "data-msg-id": message.id, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1854
2147
|
}
|
|
1855
2148
|
const onFanDashboardTap = (card) => {
|
|
1856
2149
|
onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
|
|
1857
2150
|
};
|
|
1858
|
-
return /* @__PURE__ */ (0,
|
|
1859
|
-
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1860
|
-
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1861
|
-
message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1862
|
-
/* @__PURE__ */ (0,
|
|
1863
|
-
/* @__PURE__ */ (0,
|
|
1864
|
-
/* @__PURE__ */ (0,
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1866
|
-
/* @__PURE__ */ (0,
|
|
2151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
|
|
2152
|
+
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Markdown, { text: message.text }) : null,
|
|
2153
|
+
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
2154
|
+
message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
|
|
2155
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
2156
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
2157
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
2158
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
2159
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1867
2160
|
] });
|
|
1868
2161
|
}
|
|
1869
2162
|
|
|
1870
2163
|
// src/components/IntentChips.tsx
|
|
1871
|
-
var
|
|
2164
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1872
2165
|
function IntentChips({ chips, onPick }) {
|
|
1873
2166
|
if (chips.length === 0) return null;
|
|
1874
|
-
return /* @__PURE__ */ (0,
|
|
2167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1875
2168
|
}
|
|
1876
2169
|
|
|
1877
2170
|
// src/scroll.ts
|
|
@@ -1881,7 +2174,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1881
2174
|
}
|
|
1882
2175
|
|
|
1883
2176
|
// src/components/MessageList.tsx
|
|
1884
|
-
var
|
|
2177
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
1885
2178
|
function turnAnchor(messages) {
|
|
1886
2179
|
const last = messages[messages.length - 1];
|
|
1887
2180
|
if (!last || last.role !== "assistant") return null;
|
|
@@ -1890,11 +2183,11 @@ function turnAnchor(messages) {
|
|
|
1890
2183
|
}
|
|
1891
2184
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1892
2185
|
const { state, send } = controller;
|
|
1893
|
-
const listRef = (0,
|
|
1894
|
-
const endRef = (0,
|
|
1895
|
-
const pinnedRef = (0,
|
|
1896
|
-
const anchoredTurnRef = (0,
|
|
1897
|
-
(0,
|
|
2186
|
+
const listRef = (0, import_react14.useRef)(null);
|
|
2187
|
+
const endRef = (0, import_react14.useRef)(null);
|
|
2188
|
+
const pinnedRef = (0, import_react14.useRef)(true);
|
|
2189
|
+
const anchoredTurnRef = (0, import_react14.useRef)(null);
|
|
2190
|
+
(0, import_react14.useEffect)(() => {
|
|
1898
2191
|
const anchor = turnAnchor(state.messages);
|
|
1899
2192
|
const list = listRef.current;
|
|
1900
2193
|
if (anchor && anchor.turnId !== anchoredTurnRef.current) {
|
|
@@ -1909,7 +2202,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1909
2202
|
}, [state.messages, state.status, state.isStreaming]);
|
|
1910
2203
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1911
2204
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
|
|
1912
|
-
return /* @__PURE__ */ (0,
|
|
2205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
1913
2206
|
"div",
|
|
1914
2207
|
{
|
|
1915
2208
|
className: "ss-w-list",
|
|
@@ -1919,7 +2212,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1919
2212
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1920
2213
|
},
|
|
1921
2214
|
children: [
|
|
1922
|
-
state.messages.map((m) => /* @__PURE__ */ (0,
|
|
2215
|
+
state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
1923
2216
|
ChatMessage,
|
|
1924
2217
|
{
|
|
1925
2218
|
message: m,
|
|
@@ -1934,25 +2227,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1934
2227
|
},
|
|
1935
2228
|
m.id
|
|
1936
2229
|
)),
|
|
1937
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0,
|
|
2230
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1938
2231
|
} }) : null,
|
|
1939
|
-
state.status ? /* @__PURE__ */ (0,
|
|
1940
|
-
state.error ? /* @__PURE__ */ (0,
|
|
1941
|
-
/* @__PURE__ */ (0,
|
|
1942
|
-
/* @__PURE__ */ (0,
|
|
2232
|
+
state.status ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
|
|
2233
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
|
|
2234
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
2235
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1943
2236
|
] }) : null,
|
|
1944
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0,
|
|
1945
|
-
/* @__PURE__ */ (0,
|
|
2237
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
2238
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { ref: endRef, className: "ss-w-turn-spacer", "aria-hidden": "true" })
|
|
1946
2239
|
]
|
|
1947
2240
|
}
|
|
1948
2241
|
);
|
|
1949
2242
|
}
|
|
1950
2243
|
|
|
1951
2244
|
// src/components/ChatInput.tsx
|
|
1952
|
-
var
|
|
1953
|
-
var
|
|
2245
|
+
var import_react15 = require("react");
|
|
2246
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
1954
2247
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1955
|
-
const [value, setValue] = (0,
|
|
2248
|
+
const [value, setValue] = (0, import_react15.useState)("");
|
|
1956
2249
|
const submit = () => {
|
|
1957
2250
|
const t = value.trim();
|
|
1958
2251
|
if (!t) return;
|
|
@@ -1965,18 +2258,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1965
2258
|
submit();
|
|
1966
2259
|
}
|
|
1967
2260
|
};
|
|
1968
|
-
return /* @__PURE__ */ (0,
|
|
1969
|
-
/* @__PURE__ */ (0,
|
|
1970
|
-
streaming ? /* @__PURE__ */ (0,
|
|
2261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-input", children: [
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
2263
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SendIcon, {}) })
|
|
1971
2264
|
] });
|
|
1972
2265
|
}
|
|
1973
2266
|
|
|
1974
2267
|
// src/components/ChatPanel.tsx
|
|
1975
|
-
var
|
|
2268
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
1976
2269
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1977
2270
|
function useIsModal() {
|
|
1978
|
-
const [modal, setModal] = (0,
|
|
1979
|
-
(0,
|
|
2271
|
+
const [modal, setModal] = (0, import_react16.useState)(false);
|
|
2272
|
+
(0, import_react16.useEffect)(() => {
|
|
1980
2273
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1981
2274
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1982
2275
|
const sync = () => setModal(mql.matches);
|
|
@@ -1989,17 +2282,17 @@ function useIsModal() {
|
|
|
1989
2282
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1990
2283
|
const { state, close, stop, send } = controller;
|
|
1991
2284
|
const isModal = useIsModal();
|
|
1992
|
-
(0,
|
|
2285
|
+
(0, import_react16.useEffect)(() => {
|
|
1993
2286
|
const onKey = (e) => {
|
|
1994
2287
|
if (e.key === "Escape") close();
|
|
1995
2288
|
};
|
|
1996
2289
|
window.addEventListener("keydown", onKey);
|
|
1997
2290
|
return () => window.removeEventListener("keydown", onKey);
|
|
1998
2291
|
}, [close]);
|
|
1999
|
-
return /* @__PURE__ */ (0,
|
|
2000
|
-
/* @__PURE__ */ (0,
|
|
2001
|
-
/* @__PURE__ */ (0,
|
|
2002
|
-
/* @__PURE__ */ (0,
|
|
2292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
2003
2296
|
] });
|
|
2004
2297
|
}
|
|
2005
2298
|
|
|
@@ -2012,6 +2305,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2012
2305
|
--ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;
|
|
2013
2306
|
--ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
|
|
2014
2307
|
--ss-w-pos: #1aa563; --ss-w-neg: #e0524b;
|
|
2308
|
+
--ss-w-home: #3a63a8; --ss-w-away: #c8102e; --ss-w-track: #e9edf1;
|
|
2015
2309
|
--ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;
|
|
2016
2310
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
2017
2311
|
font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);
|
|
@@ -2022,6 +2316,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2022
2316
|
--ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
|
|
2023
2317
|
--ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
|
|
2024
2318
|
--ss-w-pos: #37c891; --ss-w-neg: #f0655d;
|
|
2319
|
+
--ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
|
|
2025
2320
|
}
|
|
2026
2321
|
}
|
|
2027
2322
|
.ss-w-root[data-ss-w-theme="dark"] {
|
|
@@ -2029,11 +2324,13 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2029
2324
|
--ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
|
|
2030
2325
|
--ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
|
|
2031
2326
|
--ss-w-pos: #37c891; --ss-w-neg: #f0655d;
|
|
2327
|
+
--ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
|
|
2032
2328
|
}
|
|
2033
2329
|
.ss-w-root[data-ss-w-theme="light"] {
|
|
2034
2330
|
--ss-w-accent: #17936a; --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
|
|
2035
2331
|
--ss-w-ink: #14181d; --ss-w-faint: #6b7580;
|
|
2036
2332
|
--ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
|
|
2333
|
+
--ss-w-home: #3a63a8; --ss-w-away: #c8102e; --ss-w-track: #e9edf1;
|
|
2037
2334
|
}
|
|
2038
2335
|
|
|
2039
2336
|
/* launcher */
|
|
@@ -2148,7 +2445,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2148
2445
|
|
|
2149
2446
|
/* row 1: competition logo + name on the left, live badge + kickoff on the right */
|
|
2150
2447
|
.ss-w-bi-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; padding-bottom: 7px; border-bottom: 1px solid var(--ss-w-line); }
|
|
2151
|
-
.ss-w-bi-header-left { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
|
|
2448
|
+
.ss-w-bi-header-left { display: inline-flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12.5px; font-weight: 650; color: var(--ss-w-ink); }
|
|
2152
2449
|
.ss-w-bi-header-right { display: inline-flex; align-items: center; gap: 8px; flex: none; }
|
|
2153
2450
|
.ss-w-bi-comp { font-size: 10.5px; color: var(--ss-w-faint); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2154
2451
|
.ss-w-bi-kick { font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); white-space: nowrap; flex: none; }
|
|
@@ -2161,23 +2458,26 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2161
2458
|
.ss-w-bi-crest { width: 24px; height: 24px; flex: none; display: block; border-radius: 50%; object-fit: contain; }
|
|
2162
2459
|
.ss-w-bi-crest-mono { display: grid; place-items: center; font-size: 10px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
|
|
2163
2460
|
|
|
2164
|
-
/*
|
|
2165
|
-
.ss-w-bi-body { display: flex; align-items: center; gap: 10px; }
|
|
2166
|
-
.ss-w-bi-fixture { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12px; font-weight: 650; color: var(--ss-w-ink); }
|
|
2461
|
+
/* header fixture atoms (team hero lives in .ss-w-bi-header-left; comp demotes to the meta) */
|
|
2167
2462
|
.ss-w-bi-team { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2168
2463
|
.ss-w-bi-vs { font-size: 10px; font-weight: 600; color: var(--ss-w-faint); flex: none; }
|
|
2169
2464
|
.ss-w-bi-fixture-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2170
|
-
.ss-w-bi-
|
|
2171
|
-
|
|
2172
|
-
.ss-w-bi-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
.ss-w-bi-
|
|
2176
|
-
.ss-w-bi-
|
|
2177
|
-
.ss-w-bi-
|
|
2465
|
+
.ss-w-bi-comp-meta { display: inline-flex; align-items: center; gap: 5px; flex: none; min-width: 0; }
|
|
2466
|
+
/* shared crest-flanked team row, still consumed by MarketOddsCardList */
|
|
2467
|
+
.ss-w-bi-fixture { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12px; font-weight: 650; color: var(--ss-w-ink); }
|
|
2468
|
+
|
|
2469
|
+
/* row 2: one action line -- market \xB7 selection (left, market ellipsises first), odds, slip button */
|
|
2470
|
+
.ss-w-bi-action-row { display: flex; align-items: center; gap: 9px; }
|
|
2471
|
+
.ss-w-bi-pick { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; }
|
|
2472
|
+
.ss-w-bi-market { font-size: 12px; font-weight: 600; line-height: 1.2; color: var(--ss-w-faint); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2473
|
+
.ss-w-bi-pick-dot { flex: none; color: var(--ss-w-faint); font-weight: 700; }
|
|
2474
|
+
.ss-w-bi-sel { flex: none; font-size: 12.5px; font-weight: 700; line-height: 1.2; color: var(--ss-w-ink); white-space: nowrap; }
|
|
2178
2475
|
.ss-w-bi-odds { flex: none; display: flex; align-items: baseline; justify-content: flex-end; gap: 5px; }
|
|
2179
|
-
.ss-w-bi-odds b { font-size:
|
|
2476
|
+
.ss-w-bi-odds b { font-size: 20px; font-family: ui-monospace, monospace; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.05; }
|
|
2180
2477
|
.ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }
|
|
2478
|
+
.ss-w-bi-action-row .ss-w-slip { flex: none; }
|
|
2479
|
+
/* let the slip toast size to its own text and anchor to the card's right edge, not the button width */
|
|
2480
|
+
.ss-w-bi-action-row .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
|
|
2181
2481
|
|
|
2182
2482
|
/* row 3: bar-chart glyph + reasoning prose + stats-expander chevron, in a subtle strip */
|
|
2183
2483
|
.ss-w-bi-reason { display: flex; align-items: flex-start; gap: 8px; background: var(--ss-w-bubble); border-radius: 10px; padding: 8px 10px; }
|
|
@@ -2519,34 +2819,90 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2519
2819
|
/* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
|
|
2520
2820
|
.ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
|
|
2521
2821
|
|
|
2522
|
-
/* live score:
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
.ss-w-fandash-live-
|
|
2527
|
-
.ss-w-fandash-live-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
.ss-w-fandash-live-
|
|
2532
|
-
|
|
2533
|
-
.ss-w-fandash-live-
|
|
2534
|
-
.ss-w-fandash-live-
|
|
2535
|
-
.ss-w-fandash-live-
|
|
2536
|
-
.ss-w-fandash-live-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
.ss-w-fandash-live-
|
|
2541
|
-
.ss-w-fandash-live-
|
|
2822
|
+
/* live score: pulsing Live pill, crests (real image or a coloured initials fallback), a big
|
|
2823
|
+
tabular score, and an optional scorers line. Also where the shared home/away venue-colour
|
|
2824
|
+
tokens (--ss-w-home / --ss-w-away / --ss-w-track) are defined above -- the timeline and stats
|
|
2825
|
+
cards reuse them. */
|
|
2826
|
+
.ss-w-fandash-live-top { display: flex; align-items: center; gap: 8px; }
|
|
2827
|
+
.ss-w-fandash-live-pill { display: inline-flex; flex: none; align-items: center; gap: 5px; font-size: 10px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: var(--ss-w-neg); border-radius: 6px; padding: 3px 7px; }
|
|
2828
|
+
.ss-w-fandash-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; animation: ss-w-fandash-live-pulse 1.3s ease-in-out infinite; }
|
|
2829
|
+
@keyframes ss-w-fandash-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
|
|
2830
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2831
|
+
.ss-w-fandash-live-dot { animation: none; }
|
|
2832
|
+
}
|
|
2833
|
+
.ss-w-fandash-live-minute { flex: none; font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
|
|
2834
|
+
.ss-w-fandash-live-competition { margin-left: auto; font-size: 11px; font-weight: 650; color: var(--ss-w-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2835
|
+
.ss-w-fandash-live-main { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
|
|
2836
|
+
.ss-w-fandash-live-side { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
2837
|
+
.ss-w-fandash-live-side--away { flex-direction: row-reverse; text-align: right; }
|
|
2838
|
+
.ss-w-fandash-live-crest { width: 30px; height: 30px; border-radius: 8px; flex: none; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 10.5px; object-fit: cover; }
|
|
2839
|
+
.ss-w-fandash-live-crest--fallback.ss-w-fandash-live-crest--home { background: var(--ss-w-home); }
|
|
2840
|
+
.ss-w-fandash-live-crest--fallback.ss-w-fandash-live-crest--away { background: var(--ss-w-away); }
|
|
2841
|
+
.ss-w-fandash-live-team-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
|
|
2842
|
+
.ss-w-fandash-live-score-main { display: flex; align-items: baseline; justify-content: center; font-size: 26px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; color: var(--ss-w-ink); }
|
|
2843
|
+
.ss-w-fandash-live-score-sep { color: var(--ss-w-faint); font-weight: 500; margin: 0 4px; }
|
|
2844
|
+
.ss-w-fandash-live-scorers { margin-top: 2px; display: flex; justify-content: flex-end; gap: 6px; align-items: center; font-size: 11.5px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
2845
|
+
.ss-w-fandash-live-scorers-icon { flex: none; color: var(--ss-w-away); }
|
|
2846
|
+
|
|
2847
|
+
/* live timeline: a compact two-sided event list down a centre spine -- each club's events sit on
|
|
2848
|
+
its own side (home left, away right), most-recent 3 shown with a "See more" toggle for the
|
|
2849
|
+
rest. A 3-column grid (txt | badge | txt) per row; the spine itself is a ::before on the list. */
|
|
2850
|
+
.ss-w-fandash-live-tl { position: relative; display: flex; flex-direction: column; gap: 6px; }
|
|
2851
|
+
.ss-w-fandash-live-tl::before { content: ""; position: absolute; left: 50%; top: 1px; bottom: 1px; width: 2px; background: var(--ss-w-line); transform: translateX(-1px); }
|
|
2852
|
+
.ss-w-fandash-live-tl-ev { display: grid; grid-template-columns: 1fr 32px 1fr; align-items: center; gap: 6px; }
|
|
2853
|
+
.ss-w-fandash-live-tl-txt { display: flex; align-items: center; gap: 6px; font-size: 12.5px; min-width: 0; }
|
|
2854
|
+
.ss-w-fandash-live-tl-ev--home .ss-w-fandash-live-tl-txt { grid-column: 1; justify-content: flex-end; }
|
|
2855
|
+
.ss-w-fandash-live-tl-ev--away .ss-w-fandash-live-tl-txt { grid-column: 3; justify-content: flex-start; flex-direction: row-reverse; }
|
|
2856
|
+
.ss-w-fandash-live-tl-who { font-weight: 650; color: var(--ss-w-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2857
|
+
.ss-w-fandash-live-tl-off { font-weight: 500; color: var(--ss-w-faint); }
|
|
2858
|
+
.ss-w-fandash-live-tl-ev--goal .ss-w-fandash-live-tl-who { font-weight: 800; }
|
|
2859
|
+
.ss-w-fandash-live-tl-badge { grid-column: 2; justify-self: center; min-width: 27px; height: 18px; padding: 0 4px; border-radius: 6px; display: grid; place-items: center; font-size: 10.5px; font-weight: 800; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); font-variant-numeric: tabular-nums; z-index: 1; }
|
|
2860
|
+
.ss-w-fandash-live-tl-ev--goal .ss-w-fandash-live-tl-badge { background: var(--ss-w-accent); color: #fff; border-color: transparent; }
|
|
2861
|
+
.ss-w-fandash-live-tl-ico { width: 16px; height: 16px; flex: none; display: grid; place-items: center; }
|
|
2862
|
+
.ss-w-fandash-live-tl-ico--goal { border-radius: 50%; background: color-mix(in srgb, var(--ss-w-away) 16%, transparent); }
|
|
2863
|
+
.ss-w-fandash-live-tl-ball { width: 11px; height: 11px; border-radius: 50%; background: var(--ss-w-ink); border: 1px solid var(--ss-w-ink); }
|
|
2864
|
+
.ss-w-fandash-live-tl-card { width: 8px; height: 11px; border-radius: 2px; display: block; }
|
|
2865
|
+
.ss-w-fandash-live-tl-card--yellow { background: var(--ss-w-cta); }
|
|
2866
|
+
.ss-w-fandash-live-tl-card--red { background: var(--ss-w-away); }
|
|
2867
|
+
.ss-w-fandash-live-tl-subarrows { font-size: 11px; font-weight: 900; line-height: 1; letter-spacing: -1px; color: var(--ss-w-accent); }
|
|
2868
|
+
.ss-w-fandash-live-tl-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ss-w-faint); }
|
|
2869
|
+
.ss-w-fandash-live-tl-more { margin-top: 4px; width: 100%; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 9px; padding: 7px; font: inherit; font-size: 11.5px; font-weight: 700; color: var(--ss-w-accent); cursor: pointer; }
|
|
2870
|
+
.ss-w-fandash-live-tl-more:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
|
|
2871
|
+
|
|
2872
|
+
/* live stats: club value / stat label / opponent value, each row backed by a home/away split
|
|
2873
|
+
bar, plus an optional momentum wave (or its text fallback) and a club/opponent colour legend.
|
|
2874
|
+
No side signal reaches this card, so the club is always the home token and the opponent is
|
|
2875
|
+
always the away token -- see LiveStatsCard.tsx. */
|
|
2876
|
+
.ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 14px; }
|
|
2877
|
+
.ss-w-fandash-live-stat { display: flex; flex-direction: column; gap: 6px; }
|
|
2878
|
+
.ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 8px; }
|
|
2542
2879
|
.ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
|
|
2543
|
-
.ss-w-fandash-live-stat-
|
|
2544
|
-
.ss-w-fandash-live-stat-
|
|
2880
|
+
.ss-w-fandash-live-stat-value--home { color: var(--ss-w-home); }
|
|
2881
|
+
.ss-w-fandash-live-stat-value--away { color: var(--ss-w-away); text-align: right; }
|
|
2882
|
+
.ss-w-fandash-live-stat-label { font-size: 10px; font-weight: 700; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .04em; }
|
|
2883
|
+
.ss-w-fandash-live-bar { position: relative; height: 7px; border-radius: 999px; background: var(--ss-w-track); overflow: hidden; display: flex; }
|
|
2884
|
+
.ss-w-fandash-live-bar-home { height: 100%; background: var(--ss-w-home); }
|
|
2885
|
+
.ss-w-fandash-live-bar-away { height: 100%; background: var(--ss-w-away); margin-left: auto; }
|
|
2886
|
+
|
|
2545
2887
|
.ss-w-fandash-live-momentum { font-size: 11.5px; font-weight: 650; color: var(--ss-w-accent); }
|
|
2888
|
+
.ss-w-fandash-live-mom { margin-top: 15px; padding-top: 13px; border-top: 1px solid var(--ss-w-line); }
|
|
2889
|
+
.ss-w-fandash-live-mom-caption { display: block; margin-bottom: 5px; font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); }
|
|
2890
|
+
.ss-w-fandash-live-mom-svg { width: 100%; height: 50px; display: block; overflow: visible; }
|
|
2891
|
+
.ss-w-fandash-live-mom-home { fill: var(--ss-w-home); fill-opacity: .85; }
|
|
2892
|
+
.ss-w-fandash-live-mom-away { fill: var(--ss-w-away); fill-opacity: .85; }
|
|
2893
|
+
.ss-w-fandash-live-mom-line { fill: none; stroke: color-mix(in srgb, var(--ss-w-ink) 55%, transparent); stroke-width: 1.5; }
|
|
2894
|
+
.ss-w-fandash-live-mom-base { stroke: var(--ss-w-line); stroke-width: 1; stroke-dasharray: 3 3; }
|
|
2895
|
+
.ss-w-fandash-live-mom-now { fill: var(--ss-w-panel); stroke: var(--ss-w-ink); stroke-width: 1.5; }
|
|
2896
|
+
|
|
2897
|
+
.ss-w-fandash-live-legend { display: flex; gap: 14px; margin-top: 13px; padding-top: 13px; border-top: 1px solid var(--ss-w-line); font-size: 11px; color: var(--ss-w-faint); }
|
|
2898
|
+
.ss-w-fandash-live-legend-item { display: inline-flex; align-items: center; gap: 6px; }
|
|
2899
|
+
.ss-w-fandash-live-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
|
|
2900
|
+
.ss-w-fandash-live-legend-swatch--home { background: var(--ss-w-home); }
|
|
2901
|
+
.ss-w-fandash-live-legend-swatch--away { background: var(--ss-w-away); }
|
|
2546
2902
|
`;
|
|
2547
2903
|
|
|
2548
2904
|
// src/StatsWidget.tsx
|
|
2549
|
-
var
|
|
2905
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2550
2906
|
function buildClient(props) {
|
|
2551
2907
|
try {
|
|
2552
2908
|
if ("client" in props && props.client) {
|
|
@@ -2571,11 +2927,11 @@ function injectStyles(root) {
|
|
|
2571
2927
|
function StatsWidget(props) {
|
|
2572
2928
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
2573
2929
|
const cfg = props.config;
|
|
2574
|
-
const built = (0,
|
|
2575
|
-
const ui = (0,
|
|
2576
|
-
const rootRef = (0,
|
|
2930
|
+
const built = (0, import_react17.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
2931
|
+
const ui = (0, import_react17.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
2932
|
+
const rootRef = (0, import_react17.useRef)(null);
|
|
2577
2933
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
|
|
2578
|
-
const slip = (0,
|
|
2934
|
+
const slip = (0, import_react17.useMemo)(
|
|
2579
2935
|
() => ({
|
|
2580
2936
|
onAddToSlip: props.config?.onAddToSlip,
|
|
2581
2937
|
conversationId: controller.state.conversationId ?? void 0,
|
|
@@ -2588,18 +2944,18 @@ function StatsWidget(props) {
|
|
|
2588
2944
|
}),
|
|
2589
2945
|
[props.config?.onAddToSlip, controller.state.conversationId, built]
|
|
2590
2946
|
);
|
|
2591
|
-
const onDashboardTap = (0,
|
|
2947
|
+
const onDashboardTap = (0, import_react17.useMemo)(
|
|
2592
2948
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
2593
2949
|
[built]
|
|
2594
2950
|
);
|
|
2595
|
-
(0,
|
|
2951
|
+
(0, import_react17.useEffect)(() => {
|
|
2596
2952
|
if (!built || !ui.injectStyles) return;
|
|
2597
2953
|
const node = rootRef.current?.getRootNode();
|
|
2598
2954
|
if (node) injectStyles(node);
|
|
2599
2955
|
}, [built, ui.injectStyles]);
|
|
2600
2956
|
if (!built) return null;
|
|
2601
2957
|
const attrs = themeAttrs(ui);
|
|
2602
|
-
return /* @__PURE__ */ (0,
|
|
2958
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2603
2959
|
ChatFab,
|
|
2604
2960
|
{
|
|
2605
2961
|
label: ui.launcherLabel,
|