@sensiblestats/widget-react 0.22.0 → 0.23.1
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 +21 -0
- package/dist/index.cjs +442 -57
- 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 +434 -49
- package/dist/index.js.map +1 -1
- package/dist/styles.css +112 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/StatsWidget.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
3
3
|
import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
|
|
4
4
|
|
|
5
5
|
// src/kickoff.ts
|
|
@@ -322,8 +322,9 @@ function toCardVM(card) {
|
|
|
322
322
|
function toActionVM(action) {
|
|
323
323
|
const label = str(action.actionLabel) ?? str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
|
|
324
324
|
const actionId = str(action.actionId) ?? str(action.id);
|
|
325
|
+
const actionRef = str(action.actionRef);
|
|
325
326
|
const message = str(action.actionValue) ?? str(action.message) ?? str(action.query) ?? label;
|
|
326
|
-
return { label, actionId, message };
|
|
327
|
+
return { label, actionId, actionRef, message };
|
|
327
328
|
}
|
|
328
329
|
function toChipVM(intent) {
|
|
329
330
|
const text = str(intent.text) ?? str(intent.label) ?? str(intent.title);
|
|
@@ -519,8 +520,8 @@ function applyEvent(state, event) {
|
|
|
519
520
|
return { ...state, actionsLoading: true, status: event.data.message };
|
|
520
521
|
case "turn_complete": {
|
|
521
522
|
const active = activeTurn(state);
|
|
522
|
-
const
|
|
523
|
-
const popupText = active?.isGreeting && !state.isOpen &&
|
|
523
|
+
const greetingLine = event.data.clubFanDashboard?.greeting?.trim() || (active?.text.trim() ?? "");
|
|
524
|
+
const popupText = active?.isGreeting && !state.isOpen && greetingLine ? greetingLine.slice(0, POPUP_MAX) : state.popupText;
|
|
524
525
|
return {
|
|
525
526
|
...state,
|
|
526
527
|
isStreaming: false,
|
|
@@ -531,7 +532,8 @@ function applyEvent(state, event) {
|
|
|
531
532
|
messages: mapActive(state, (t) => ({
|
|
532
533
|
...t,
|
|
533
534
|
done: true,
|
|
534
|
-
greetingDashboard: event.data.dashboard ?? void 0
|
|
535
|
+
greetingDashboard: event.data.dashboard ?? void 0,
|
|
536
|
+
clubFanDashboard: event.data.clubFanDashboard ?? void 0
|
|
535
537
|
}))
|
|
536
538
|
};
|
|
537
539
|
}
|
|
@@ -728,7 +730,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
728
730
|
}
|
|
729
731
|
|
|
730
732
|
// src/components/ChatPanel.tsx
|
|
731
|
-
import { useEffect as
|
|
733
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
732
734
|
|
|
733
735
|
// src/components/ChatHeader.tsx
|
|
734
736
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -741,7 +743,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
741
743
|
}
|
|
742
744
|
|
|
743
745
|
// src/components/MessageList.tsx
|
|
744
|
-
import { useEffect as
|
|
746
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
745
747
|
|
|
746
748
|
// src/components/Markdown.tsx
|
|
747
749
|
import ReactMarkdown from "react-markdown";
|
|
@@ -1287,28 +1289,299 @@ function GreetingDashboard({ dashboard, onTap }) {
|
|
|
1287
1289
|
] });
|
|
1288
1290
|
}
|
|
1289
1291
|
|
|
1292
|
+
// src/components/dashboard/FixtureCard.tsx
|
|
1293
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1294
|
+
function resultClass(result) {
|
|
1295
|
+
const upper = result.toUpperCase();
|
|
1296
|
+
if (upper === "W") return "ss-w-fandash-form-pill--win";
|
|
1297
|
+
if (upper === "L") return "ss-w-fandash-form-pill--loss";
|
|
1298
|
+
return "ss-w-fandash-form-pill--draw";
|
|
1299
|
+
}
|
|
1300
|
+
function FormStrip({ form }) {
|
|
1301
|
+
return /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-form", children: form.map((r, i) => /* @__PURE__ */ jsx17("span", { className: `ss-w-fandash-form-pill ${resultClass(r)}`, children: r }, `${r}-${i}`)) });
|
|
1302
|
+
}
|
|
1303
|
+
function FormRow({ name, form }) {
|
|
1304
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-formrow", children: [
|
|
1305
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-formrow-name", children: name }),
|
|
1306
|
+
/* @__PURE__ */ jsx17(FormStrip, { form })
|
|
1307
|
+
] });
|
|
1308
|
+
}
|
|
1309
|
+
function FixtureCard({ card, onTap }) {
|
|
1310
|
+
const { data } = card;
|
|
1311
|
+
const h2h = data.h2H;
|
|
1312
|
+
return /* @__PURE__ */ jsxs13(Fragment5, { children: [
|
|
1313
|
+
/* @__PURE__ */ jsxs13("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-fixture", onClick: () => onTap(card), children: [
|
|
1314
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-eyebrow", children: [
|
|
1315
|
+
card.title,
|
|
1316
|
+
" \xB7 ",
|
|
1317
|
+
data.competition
|
|
1318
|
+
] }),
|
|
1319
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-teams", children: [
|
|
1320
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-fixture-team", children: [
|
|
1321
|
+
/* @__PURE__ */ jsx17(Crest2, { url: data.homeCrestUrl ?? void 0, name: data.homeName, kind: "team" }),
|
|
1322
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-name", children: data.homeName })
|
|
1323
|
+
] }),
|
|
1324
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-vs", children: "v" }),
|
|
1325
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-fixture-team ss-w-fandash-fixture-team--away", children: [
|
|
1326
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-name", children: data.awayName }),
|
|
1327
|
+
/* @__PURE__ */ jsx17(Crest2, { url: data.awayCrestUrl ?? void 0, name: data.awayName, kind: "team" })
|
|
1328
|
+
] })
|
|
1329
|
+
] }),
|
|
1330
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-meta", children: [
|
|
1331
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
|
|
1332
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.dateLabel }),
|
|
1333
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.kickoffLabel })
|
|
1334
|
+
] }),
|
|
1335
|
+
data.venue ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
|
|
1336
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.venueLabel }),
|
|
1337
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.venue })
|
|
1338
|
+
] }) : null
|
|
1339
|
+
] }),
|
|
1340
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
|
|
1341
|
+
] }),
|
|
1342
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-lastfive", children: [
|
|
1343
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-eyebrow", children: data.formLabel }),
|
|
1344
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-form", children: [
|
|
1345
|
+
/* @__PURE__ */ jsx17(FormRow, { name: data.homeName, form: data.homeForm }),
|
|
1346
|
+
/* @__PURE__ */ jsx17(FormRow, { name: data.awayName, form: data.awayForm })
|
|
1347
|
+
] })
|
|
1348
|
+
] }),
|
|
1349
|
+
h2h || data.fact ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-h2h", children: [
|
|
1350
|
+
h2h ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
|
|
1351
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-eyebrow", children: h2h.label }),
|
|
1352
|
+
/* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-split", children: [
|
|
1353
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
|
|
1354
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.wins }),
|
|
1355
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.homeWinsLabel })
|
|
1356
|
+
] }),
|
|
1357
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
|
|
1358
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.draws }),
|
|
1359
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.drawsLabel })
|
|
1360
|
+
] }),
|
|
1361
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
|
|
1362
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.losses }),
|
|
1363
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.awayWinsLabel })
|
|
1364
|
+
] })
|
|
1365
|
+
] })
|
|
1366
|
+
] }) : null,
|
|
1367
|
+
data.fact ? /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fact", children: data.fact }) : null
|
|
1368
|
+
] }) : null
|
|
1369
|
+
] });
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/components/dashboard/CountdownCard.tsx
|
|
1373
|
+
import { Fragment as Fragment6, useEffect as useEffect3, useState as useState6 } from "react";
|
|
1374
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1375
|
+
var DAY_MS = 864e5;
|
|
1376
|
+
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
1377
|
+
const totalSeconds = Math.floor((kickoffUnix * 1e3 - nowMs) / 1e3);
|
|
1378
|
+
if (totalSeconds <= 0) return null;
|
|
1379
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
1380
|
+
const hours = Math.floor(totalSeconds % 86400 / 3600);
|
|
1381
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
1382
|
+
const seconds = totalSeconds % 60;
|
|
1383
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1384
|
+
if (days > 0) return [{ value: pad(days), unit: "d" }, { value: pad(hours), unit: "h" }];
|
|
1385
|
+
const segs = [{ value: pad(hours), unit: "h" }, { value: pad(minutes), unit: "m" }];
|
|
1386
|
+
if (withSeconds) segs.push({ value: pad(seconds), unit: "s" });
|
|
1387
|
+
return segs;
|
|
1388
|
+
}
|
|
1389
|
+
function CountdownCard({ card }) {
|
|
1390
|
+
const { data } = card;
|
|
1391
|
+
const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
|
|
1392
|
+
const [nowMs, setNowMs] = useState6(() => Date.now());
|
|
1393
|
+
const [reduced] = useState6(
|
|
1394
|
+
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
1395
|
+
);
|
|
1396
|
+
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
1397
|
+
useEffect3(() => {
|
|
1398
|
+
if (reduced) return;
|
|
1399
|
+
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
1400
|
+
return () => window.clearInterval(id);
|
|
1401
|
+
}, [reduced, withinDay]);
|
|
1402
|
+
const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
|
|
1403
|
+
return /* @__PURE__ */ jsx18("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ jsxs14("div", { className: "ss-w-fandash-countdown-top", children: [
|
|
1404
|
+
/* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
|
|
1405
|
+
segments ? /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ jsxs14(Fragment6, { children: [
|
|
1406
|
+
i > 0 ? /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
|
|
1407
|
+
/* @__PURE__ */ jsxs14("span", { className: "ss-w-fandash-countdown-seg", children: [
|
|
1408
|
+
/* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
|
|
1409
|
+
/* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-unit", children: unitLabel[s.unit] })
|
|
1410
|
+
] })
|
|
1411
|
+
] }, s.unit)) }) : null
|
|
1412
|
+
] }) });
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// src/components/dashboard/InjuriesCard.tsx
|
|
1416
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1417
|
+
function ColHead({ name, crestUrl }) {
|
|
1418
|
+
return /* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injuries-col-head", children: [
|
|
1419
|
+
/* @__PURE__ */ jsx19(Crest2, { url: crestUrl ?? void 0, name, kind: "team" }),
|
|
1420
|
+
/* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-col-head-name", children: name })
|
|
1421
|
+
] });
|
|
1422
|
+
}
|
|
1423
|
+
function InjuryList({ rows, emptyLabel }) {
|
|
1424
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-empty", children: emptyLabel });
|
|
1425
|
+
return /* @__PURE__ */ jsx19("ul", { className: "ss-w-fandash-injury-list", children: rows.map((r, i) => /* @__PURE__ */ jsxs15("li", { className: "ss-w-fandash-injury-row", children: [
|
|
1426
|
+
/* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-name", children: r.playerName }),
|
|
1427
|
+
/* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injury-meta", children: [
|
|
1428
|
+
/* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-status", children: r.status }),
|
|
1429
|
+
r.injuryType ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-type", children: r.injuryType }) : null,
|
|
1430
|
+
r.expectedReturnLabel ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-return", children: r.expectedReturnLabel }) : null
|
|
1431
|
+
] })
|
|
1432
|
+
] }, `${r.playerName}-${i}`)) });
|
|
1433
|
+
}
|
|
1434
|
+
function InjuriesCard({ card }) {
|
|
1435
|
+
const { data } = card;
|
|
1436
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-card ss-w-fandash-injuries", children: [
|
|
1437
|
+
card.title ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1438
|
+
/* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-cols", children: [
|
|
1439
|
+
/* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
|
|
1440
|
+
/* @__PURE__ */ jsx19(ColHead, { name: data.oursName, crestUrl: data.oursCrestUrl }),
|
|
1441
|
+
/* @__PURE__ */ jsx19(InjuryList, { rows: data.ours, emptyLabel: data.emptyLabel })
|
|
1442
|
+
] }),
|
|
1443
|
+
/* @__PURE__ */ jsx19("div", { className: "ss-w-fandash-injuries-divider" }),
|
|
1444
|
+
/* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
|
|
1445
|
+
/* @__PURE__ */ jsx19(ColHead, { name: data.theirsName, crestUrl: data.theirsCrestUrl }),
|
|
1446
|
+
/* @__PURE__ */ jsx19(InjuryList, { rows: data.theirs, emptyLabel: data.emptyLabel })
|
|
1447
|
+
] })
|
|
1448
|
+
] })
|
|
1449
|
+
] });
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// src/components/dashboard/ReasonToBetCard.tsx
|
|
1453
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1454
|
+
function ReasonToBetCard({ card }) {
|
|
1455
|
+
return /* @__PURE__ */ jsxs16("div", { className: "ss-w-fandash-card ss-w-fandash-reasons", children: [
|
|
1456
|
+
card.title ? /* @__PURE__ */ jsx20("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1457
|
+
/* @__PURE__ */ jsx20("ul", { className: "ss-w-fandash-reason-list", children: card.data.angles.map((angle, i) => /* @__PURE__ */ jsx20("li", { className: "ss-w-fandash-reason-item", children: angle }, `${angle}-${i}`)) })
|
|
1458
|
+
] });
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// src/components/dashboard/WhereToWatchCard.tsx
|
|
1462
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1463
|
+
function WhereToWatchCard({ card }) {
|
|
1464
|
+
return /* @__PURE__ */ jsxs17("div", { className: "ss-w-fandash-card ss-w-fandash-watch", children: [
|
|
1465
|
+
card.title ? /* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1466
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-stations", children: card.data.stations.map((station, i) => /* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-station-chip", children: station }, `${station}-${i}`)) })
|
|
1467
|
+
] });
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/components/dashboard/CongestedWeekCard.tsx
|
|
1471
|
+
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1472
|
+
function CongestedWeekCard({ card, onTap }) {
|
|
1473
|
+
return /* @__PURE__ */ jsxs18("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-congested", onClick: () => onTap(card), children: [
|
|
1474
|
+
card.title ? /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1475
|
+
/* @__PURE__ */ jsx22("div", { className: "ss-w-fandash-congested-grid", children: card.data.entries.map((entry, i) => /* @__PURE__ */ jsxs18("span", { className: "ss-w-fandash-congested-cell", children: [
|
|
1476
|
+
/* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-day", children: entry.dayLabel }),
|
|
1477
|
+
/* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-opponent", children: entry.opponent }),
|
|
1478
|
+
/* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-comp", children: entry.competition }),
|
|
1479
|
+
/* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-time", children: entry.timeLabel })
|
|
1480
|
+
] }, `${entry.opponent}-${i}`)) }),
|
|
1481
|
+
/* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
|
|
1482
|
+
] });
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// src/components/dashboard/TransfersCard.tsx
|
|
1486
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1487
|
+
function TransferList({ rows }) {
|
|
1488
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-empty", children: "None" });
|
|
1489
|
+
return /* @__PURE__ */ jsx23("ul", { className: "ss-w-fandash-transfer-list", children: rows.map((r, i) => /* @__PURE__ */ jsxs19("li", { className: "ss-w-fandash-transfer-row", children: [
|
|
1490
|
+
/* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-name", children: r.playerName }),
|
|
1491
|
+
/* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-club", children: r.otherClub }),
|
|
1492
|
+
r.type ? /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-type", children: r.type }) : null
|
|
1493
|
+
] }, `${r.playerName}-${i}`)) });
|
|
1494
|
+
}
|
|
1495
|
+
function TransfersCard({ card }) {
|
|
1496
|
+
const { data } = card;
|
|
1497
|
+
return /* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-card ss-w-fandash-transfers", children: [
|
|
1498
|
+
card.title ? /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1499
|
+
/* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-cols", children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-col", children: [
|
|
1501
|
+
/* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-col-head", children: "In" }),
|
|
1502
|
+
/* @__PURE__ */ jsx23(TransferList, { rows: data.in })
|
|
1503
|
+
] }),
|
|
1504
|
+
/* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-col", children: [
|
|
1505
|
+
/* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-col-head", children: "Out" }),
|
|
1506
|
+
/* @__PURE__ */ jsx23(TransferList, { rows: data.out })
|
|
1507
|
+
] })
|
|
1508
|
+
] })
|
|
1509
|
+
] });
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// src/components/dashboard/LinksCard.tsx
|
|
1513
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1514
|
+
function LinksCard({ card }) {
|
|
1515
|
+
return /* @__PURE__ */ jsxs20("div", { className: "ss-w-fandash-card ss-w-fandash-links", children: [
|
|
1516
|
+
card.title ? /* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1517
|
+
/* @__PURE__ */ jsx24("div", { className: "ss-w-fandash-link-grid", children: card.data.links.map((link, i) => /* @__PURE__ */ jsxs20("a", { className: "ss-w-fandash-link-tile", href: link.url, target: "_blank", rel: "noopener noreferrer", children: [
|
|
1518
|
+
/* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-link-label", children: link.label }),
|
|
1519
|
+
/* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-link-chevron", children: /* @__PURE__ */ jsx24(ChevronIcon, {}) })
|
|
1520
|
+
] }, `${link.url}-${i}`)) })
|
|
1521
|
+
] });
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// src/components/dashboard/ClubFanDashboard.tsx
|
|
1525
|
+
import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1526
|
+
function ClubFanDashboard({ dashboard, onTap, ui }) {
|
|
1527
|
+
const orderedCards = [
|
|
1528
|
+
...dashboard.cards.filter((c) => c.cardType === "countdown"),
|
|
1529
|
+
...dashboard.cards.filter((c) => c.cardType !== "countdown")
|
|
1530
|
+
];
|
|
1531
|
+
return /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash", children: [
|
|
1532
|
+
dashboard.greeting ? /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
|
|
1533
|
+
/* @__PURE__ */ jsx25("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ jsx25(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
|
|
1534
|
+
] });
|
|
1535
|
+
}
|
|
1536
|
+
function FanDashboardCard({ card, onTap, ui }) {
|
|
1537
|
+
switch (card.cardType) {
|
|
1538
|
+
case "fixture":
|
|
1539
|
+
return /* @__PURE__ */ jsx25(FixtureCard, { card, onTap });
|
|
1540
|
+
case "countdown":
|
|
1541
|
+
return /* @__PURE__ */ jsx25(CountdownCard, { card });
|
|
1542
|
+
case "injuries":
|
|
1543
|
+
return /* @__PURE__ */ jsx25(InjuriesCard, { card });
|
|
1544
|
+
case "reasonToBet":
|
|
1545
|
+
return /* @__PURE__ */ jsx25(ReasonToBetCard, { card });
|
|
1546
|
+
case "whereToWatch":
|
|
1547
|
+
return /* @__PURE__ */ jsx25(WhereToWatchCard, { card });
|
|
1548
|
+
case "congestedWeek":
|
|
1549
|
+
return /* @__PURE__ */ jsx25(CongestedWeekCard, { card, onTap });
|
|
1550
|
+
case "transfers":
|
|
1551
|
+
return /* @__PURE__ */ jsx25(TransfersCard, { card });
|
|
1552
|
+
case "links":
|
|
1553
|
+
return /* @__PURE__ */ jsx25(LinksCard, { card });
|
|
1554
|
+
default:
|
|
1555
|
+
return null;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1290
1559
|
// src/components/ChatMessage.tsx
|
|
1291
|
-
import { jsx as
|
|
1560
|
+
import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1292
1561
|
function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
|
|
1293
1562
|
if (message.role === "user") {
|
|
1294
|
-
return /* @__PURE__ */
|
|
1563
|
+
return /* @__PURE__ */ jsx26("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx26("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1295
1564
|
}
|
|
1296
|
-
|
|
1297
|
-
message.
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
/* @__PURE__ */
|
|
1301
|
-
/* @__PURE__ */
|
|
1302
|
-
/* @__PURE__ */
|
|
1303
|
-
/* @__PURE__ */
|
|
1565
|
+
const onFanDashboardTap = (card) => {
|
|
1566
|
+
onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
|
|
1567
|
+
};
|
|
1568
|
+
return /* @__PURE__ */ jsxs22("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1569
|
+
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx26(Markdown, { text: message.text }) : null,
|
|
1570
|
+
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx26(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1571
|
+
message.clubFanDashboard ? /* @__PURE__ */ jsx26(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
|
|
1572
|
+
/* @__PURE__ */ jsx26(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1573
|
+
/* @__PURE__ */ jsx26(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1574
|
+
/* @__PURE__ */ jsx26(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1575
|
+
/* @__PURE__ */ jsx26(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1576
|
+
/* @__PURE__ */ jsx26(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1304
1577
|
] });
|
|
1305
1578
|
}
|
|
1306
1579
|
|
|
1307
1580
|
// src/components/IntentChips.tsx
|
|
1308
|
-
import { jsx as
|
|
1581
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
1309
1582
|
function IntentChips({ chips, onPick }) {
|
|
1310
1583
|
if (chips.length === 0) return null;
|
|
1311
|
-
return /* @__PURE__ */
|
|
1584
|
+
return /* @__PURE__ */ jsx27("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx27("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1312
1585
|
}
|
|
1313
1586
|
|
|
1314
1587
|
// src/scroll.ts
|
|
@@ -1318,18 +1591,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1318
1591
|
}
|
|
1319
1592
|
|
|
1320
1593
|
// src/components/MessageList.tsx
|
|
1321
|
-
import { jsx as
|
|
1594
|
+
import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1322
1595
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1323
1596
|
const { state, send } = controller;
|
|
1324
1597
|
const listRef = useRef2(null);
|
|
1325
1598
|
const endRef = useRef2(null);
|
|
1326
1599
|
const pinnedRef = useRef2(true);
|
|
1327
|
-
|
|
1600
|
+
useEffect4(() => {
|
|
1328
1601
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1329
1602
|
}, [state.messages, state.status]);
|
|
1330
1603
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1331
|
-
const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
|
|
1332
|
-
return /* @__PURE__ */
|
|
1604
|
+
const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
|
|
1605
|
+
return /* @__PURE__ */ jsxs23(
|
|
1333
1606
|
"div",
|
|
1334
1607
|
{
|
|
1335
1608
|
className: "ss-w-list",
|
|
@@ -1339,12 +1612,12 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1339
1612
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1340
1613
|
},
|
|
1341
1614
|
children: [
|
|
1342
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
1615
|
+
state.messages.map((m) => /* @__PURE__ */ jsx28(
|
|
1343
1616
|
ChatMessage,
|
|
1344
1617
|
{
|
|
1345
1618
|
message: m,
|
|
1346
1619
|
disabled: state.isStreaming,
|
|
1347
|
-
onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
|
|
1620
|
+
onAct: (a) => send(a.message, { actionId: a.actionId, actionRef: a.actionRef, displayText: a.label }),
|
|
1348
1621
|
onTap: (tile, index) => {
|
|
1349
1622
|
send(tile.actionValue, { displayText: tile.title });
|
|
1350
1623
|
if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
|
|
@@ -1354,25 +1627,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1354
1627
|
},
|
|
1355
1628
|
m.id
|
|
1356
1629
|
)),
|
|
1357
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
1630
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx28(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1358
1631
|
} }) : null,
|
|
1359
|
-
state.status ? /* @__PURE__ */
|
|
1360
|
-
state.error ? /* @__PURE__ */
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1632
|
+
state.status ? /* @__PURE__ */ jsx28("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1633
|
+
state.error ? /* @__PURE__ */ jsxs23("div", { className: "ss-w-error", role: "alert", children: [
|
|
1634
|
+
/* @__PURE__ */ jsx28("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1635
|
+
/* @__PURE__ */ jsx28("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1363
1636
|
] }) : null,
|
|
1364
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */
|
|
1365
|
-
/* @__PURE__ */
|
|
1637
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx28(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1638
|
+
/* @__PURE__ */ jsx28("div", { ref: endRef })
|
|
1366
1639
|
]
|
|
1367
1640
|
}
|
|
1368
1641
|
);
|
|
1369
1642
|
}
|
|
1370
1643
|
|
|
1371
1644
|
// src/components/ChatInput.tsx
|
|
1372
|
-
import { useState as
|
|
1373
|
-
import { jsx as
|
|
1645
|
+
import { useState as useState7 } from "react";
|
|
1646
|
+
import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1374
1647
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1375
|
-
const [value, setValue] =
|
|
1648
|
+
const [value, setValue] = useState7("");
|
|
1376
1649
|
const submit = () => {
|
|
1377
1650
|
const t = value.trim();
|
|
1378
1651
|
if (!t) return;
|
|
@@ -1385,18 +1658,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1385
1658
|
submit();
|
|
1386
1659
|
}
|
|
1387
1660
|
};
|
|
1388
|
-
return /* @__PURE__ */
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
streaming ? /* @__PURE__ */
|
|
1661
|
+
return /* @__PURE__ */ jsxs24("div", { className: "ss-w-input", children: [
|
|
1662
|
+
/* @__PURE__ */ jsx29("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1663
|
+
streaming ? /* @__PURE__ */ jsx29("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx29("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx29("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx29(SendIcon, {}) })
|
|
1391
1664
|
] });
|
|
1392
1665
|
}
|
|
1393
1666
|
|
|
1394
1667
|
// src/components/ChatPanel.tsx
|
|
1395
|
-
import { jsx as
|
|
1668
|
+
import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1396
1669
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1397
1670
|
function useIsModal() {
|
|
1398
|
-
const [modal, setModal] =
|
|
1399
|
-
|
|
1671
|
+
const [modal, setModal] = useState8(false);
|
|
1672
|
+
useEffect5(() => {
|
|
1400
1673
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1401
1674
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1402
1675
|
const sync = () => setModal(mql.matches);
|
|
@@ -1409,17 +1682,17 @@ function useIsModal() {
|
|
|
1409
1682
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1410
1683
|
const { state, close, stop, send } = controller;
|
|
1411
1684
|
const isModal = useIsModal();
|
|
1412
|
-
|
|
1685
|
+
useEffect5(() => {
|
|
1413
1686
|
const onKey = (e) => {
|
|
1414
1687
|
if (e.key === "Escape") close();
|
|
1415
1688
|
};
|
|
1416
1689
|
window.addEventListener("keydown", onKey);
|
|
1417
1690
|
return () => window.removeEventListener("keydown", onKey);
|
|
1418
1691
|
}, [close]);
|
|
1419
|
-
return /* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
/* @__PURE__ */
|
|
1692
|
+
return /* @__PURE__ */ jsxs25("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1693
|
+
/* @__PURE__ */ jsx30(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1694
|
+
/* @__PURE__ */ jsx30(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1695
|
+
/* @__PURE__ */ jsx30(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1423
1696
|
] });
|
|
1424
1697
|
}
|
|
1425
1698
|
|
|
@@ -1772,10 +2045,122 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1772
2045
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
|
|
1773
2046
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
|
|
1774
2047
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
|
|
2048
|
+
|
|
2049
|
+
/* club fan pre-match dashboard */
|
|
2050
|
+
.ss-w-fandash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
2051
|
+
.ss-w-fandash-greeting { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--ss-w-ink); }
|
|
2052
|
+
.ss-w-fandash-cards { display: flex; flex-direction: column; gap: 8px; }
|
|
2053
|
+
|
|
2054
|
+
/* Shared card chrome -- a plain <div> for [data] cards, a <button> for [prefill] cards. Both
|
|
2055
|
+
share the same box so the two families read as one system, not two ad hoc layouts. */
|
|
2056
|
+
.ss-w-fandash-card {
|
|
2057
|
+
display: flex; flex-direction: column; gap: 8px;
|
|
2058
|
+
padding: 12px; border-radius: 14px; border: 1px solid var(--ss-w-line);
|
|
2059
|
+
background: var(--ss-w-panel); text-align: left;
|
|
2060
|
+
color: inherit; font: inherit; width: 100%; min-width: 0;
|
|
2061
|
+
}
|
|
2062
|
+
.ss-w-fandash-card--tap { cursor: pointer; border-left: 3px solid var(--ss-w-accent); }
|
|
2063
|
+
.ss-w-fandash-card--tap:hover { background: var(--ss-w-bubble); }
|
|
2064
|
+
.ss-w-fandash-card-cta { align-self: flex-start; font-size: 11.5px; font-weight: 700; color: var(--ss-w-accent); }
|
|
2065
|
+
|
|
2066
|
+
/* Shared alignment primitives: a small letterspaced section eyebrow, and a 3-column value/label
|
|
2067
|
+
split reused by the fixture head-to-head (and any future stat-row card). */
|
|
2068
|
+
.ss-w-fandash-eyebrow { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
|
|
2069
|
+
.ss-w-fandash-split { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
|
|
2070
|
+
.ss-w-fandash-split-cell { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; min-width: 0; }
|
|
2071
|
+
.ss-w-fandash-split-value { font-size: 20px; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); line-height: 1; }
|
|
2072
|
+
.ss-w-fandash-split-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
2073
|
+
|
|
2074
|
+
/* fixture card */
|
|
2075
|
+
.ss-w-fandash-fixture-teams { display: flex; align-items: center; gap: 10px; }
|
|
2076
|
+
.ss-w-fandash-fixture-team { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
|
|
2077
|
+
.ss-w-fandash-fixture-team--away { justify-content: flex-end; }
|
|
2078
|
+
.ss-w-fandash-fixture-team .ss-w-bi-crest { width: 30px; height: 30px; }
|
|
2079
|
+
.ss-w-fandash-fixture-team .ss-w-bi-crest-mono { font-size: 12px; }
|
|
2080
|
+
.ss-w-fandash-fixture-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 650; }
|
|
2081
|
+
.ss-w-fandash-fixture-team--away .ss-w-fandash-fixture-name { text-align: right; }
|
|
2082
|
+
.ss-w-fandash-fixture-vs { flex: none; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); }
|
|
2083
|
+
/* date + venue as labelled rows, so a long stadium name gets its own line to ellipsis in */
|
|
2084
|
+
.ss-w-fandash-fixture-meta { display: flex; flex-direction: column; gap: 1px; border-top: 1px solid var(--ss-w-line); padding-top: 8px; }
|
|
2085
|
+
.ss-w-fandash-fixture-metarow { display: flex; align-items: baseline; gap: 9px; }
|
|
2086
|
+
.ss-w-fandash-fixture-metakey { flex: none; width: 44px; font-size: 8.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
|
|
2087
|
+
.ss-w-fandash-fixture-metaval { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
|
|
2088
|
+
.ss-w-fandash-fact { font-size: 12px; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 8px; padding: 6px 8px; }
|
|
2089
|
+
/* each head-to-head figure sits on its own shaded tile, so the grid gap separates wins/draws/losses */
|
|
2090
|
+
.ss-w-fandash-h2h .ss-w-fandash-split-cell { background: var(--ss-w-bubble); border-radius: 10px; padding: 12px 6px; }
|
|
2091
|
+
/* extra breathing room between the tally row and the fact beyond the card's own gap */
|
|
2092
|
+
.ss-w-fandash-h2h .ss-w-fandash-fact { margin-top: 4px; }
|
|
2093
|
+
|
|
2094
|
+
/* last-five: one row per side, team name left, form pills right */
|
|
2095
|
+
.ss-w-fandash-fixture-form { display: flex; flex-direction: column; gap: 5px; }
|
|
2096
|
+
.ss-w-fandash-formrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2097
|
+
.ss-w-fandash-formrow-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; }
|
|
2098
|
+
.ss-w-fandash-form { display: inline-flex; gap: 3px; flex: none; }
|
|
2099
|
+
.ss-w-fandash-form-pill {
|
|
2100
|
+
display: inline-grid; place-items: center; width: 16px; height: 16px;
|
|
2101
|
+
border-radius: 4px; font-size: 9.5px; font-weight: 700; color: #fff;
|
|
2102
|
+
}
|
|
2103
|
+
.ss-w-fandash-form-pill--win { background: var(--ss-w-pos); }
|
|
2104
|
+
.ss-w-fandash-form-pill--loss { background: var(--ss-w-neg); }
|
|
2105
|
+
.ss-w-fandash-form-pill--draw { background: var(--ss-w-faint); }
|
|
2106
|
+
|
|
2107
|
+
/* countdown card: a compact top card -- section eyebrow left, colon-separated clock right. Each
|
|
2108
|
+
segment stacks its figure over a small unit label (d/h/m/s). The exact kickoff date is not
|
|
2109
|
+
repeated here; the fixture card below carries it. */
|
|
2110
|
+
.ss-w-fandash-countdown-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2111
|
+
.ss-w-fandash-countdown-clock { display: inline-flex; align-items: flex-start; gap: 6px; color: var(--ss-w-accent); }
|
|
2112
|
+
.ss-w-fandash-countdown-seg { display: inline-flex; flex-direction: column; align-items: center; }
|
|
2113
|
+
.ss-w-fandash-countdown-value { font-size: 26px; font-weight: 750; font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.01em; }
|
|
2114
|
+
.ss-w-fandash-countdown-unit { font-size: 8px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); margin-top: 3px; }
|
|
2115
|
+
.ss-w-fandash-countdown-colon { font-size: 22px; font-weight: 750; line-height: 1; color: var(--ss-w-faint); }
|
|
2116
|
+
|
|
2117
|
+
/* injuries + transfers: two-column ours/theirs, in/out */
|
|
2118
|
+
.ss-w-fandash-transfers-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
|
2119
|
+
/* injuries: a 1px divider column sits between the two team sections, each headed by crest + name */
|
|
2120
|
+
.ss-w-fandash-injuries-cols { display: grid; grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); gap: 12px; }
|
|
2121
|
+
.ss-w-fandash-injuries-divider { background: var(--ss-w-line); }
|
|
2122
|
+
.ss-w-fandash-injuries-col, .ss-w-fandash-transfers-col { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
2123
|
+
.ss-w-fandash-transfers-col-head { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--ss-w-faint); }
|
|
2124
|
+
.ss-w-fandash-injuries-col-head { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; color: var(--ss-w-ink); }
|
|
2125
|
+
.ss-w-fandash-injuries-col-head .ss-w-bi-crest { width: 18px; height: 18px; }
|
|
2126
|
+
.ss-w-fandash-injuries-col-head .ss-w-bi-crest-mono { font-size: 9px; }
|
|
2127
|
+
.ss-w-fandash-injuries-col-head-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2128
|
+
.ss-w-fandash-injuries-empty, .ss-w-fandash-transfers-empty { font-size: 11.5px; color: var(--ss-w-faint); }
|
|
2129
|
+
.ss-w-fandash-injury-list, .ss-w-fandash-transfer-list { list-style: none; display: flex; flex-direction: column; gap: 5px; }
|
|
2130
|
+
.ss-w-fandash-injury-row, .ss-w-fandash-transfer-row { display: flex; flex-direction: column; gap: 1px; }
|
|
2131
|
+
.ss-w-fandash-injury-name, .ss-w-fandash-transfer-name { font-size: 12px; font-weight: 600; overflow-wrap: anywhere; }
|
|
2132
|
+
.ss-w-fandash-injury-meta { display: flex; flex-wrap: wrap; gap: 4px; font-size: 10.5px; color: var(--ss-w-faint); }
|
|
2133
|
+
.ss-w-fandash-injury-status { font-weight: 700; color: var(--ss-w-neg); }
|
|
2134
|
+
.ss-w-fandash-transfer-club { font-size: 11px; color: var(--ss-w-faint); }
|
|
2135
|
+
.ss-w-fandash-transfer-type { align-self: flex-start; font-size: 9.5px; font-weight: 700; text-transform: uppercase; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 13%, var(--ss-w-panel)); border-radius: 999px; padding: 1px 7px; margin-top: 2px; }
|
|
2136
|
+
|
|
2137
|
+
/* reason-to-bet */
|
|
2138
|
+
.ss-w-fandash-reason-list { list-style: none; display: flex; flex-direction: column; gap: 6px; }
|
|
2139
|
+
.ss-w-fandash-reason-item { font-size: 12.5px; padding-left: 14px; position: relative; }
|
|
2140
|
+
.ss-w-fandash-reason-item::before { content: "\u2022"; position: absolute; left: 0; color: var(--ss-w-accent); font-weight: 700; }
|
|
2141
|
+
|
|
2142
|
+
/* where-to-watch */
|
|
2143
|
+
.ss-w-fandash-stations { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
2144
|
+
.ss-w-fandash-station-chip { font-size: 11.5px; font-weight: 600; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 999px; padding: 4px 10px; }
|
|
2145
|
+
|
|
2146
|
+
/* congested week: a fixed 3-column grid of upcoming fixtures (day / opponent / comp / time) */
|
|
2147
|
+
.ss-w-fandash-congested-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
|
|
2148
|
+
.ss-w-fandash-congested-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 8px; border-radius: 10px; background: var(--ss-w-bubble); }
|
|
2149
|
+
.ss-w-fandash-congested-day { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); }
|
|
2150
|
+
.ss-w-fandash-congested-opponent { font-size: 12.5px; font-weight: 650; overflow-wrap: anywhere; }
|
|
2151
|
+
.ss-w-fandash-congested-comp { font-size: 10px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
2152
|
+
.ss-w-fandash-congested-time { font-size: 10.5px; color: var(--ss-w-faint); font-variant-numeric: tabular-nums; }
|
|
2153
|
+
|
|
2154
|
+
/* links: 2-column grid of tappable tiles */
|
|
2155
|
+
.ss-w-fandash-link-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
2156
|
+
.ss-w-fandash-link-tile { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 11px 13px; border-radius: 10px; border: 1px solid transparent; text-decoration: none; color: inherit; background: color-mix(in srgb, var(--ss-w-accent) 10%, var(--ss-w-panel)); }
|
|
2157
|
+
.ss-w-fandash-link-tile:hover { background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
|
|
2158
|
+
.ss-w-fandash-link-label { font-size: 12.5px; font-weight: 650; overflow-wrap: anywhere; }
|
|
2159
|
+
.ss-w-fandash-link-chevron { flex: none; width: 12px; height: 12px; color: var(--ss-w-accent); }
|
|
1775
2160
|
`;
|
|
1776
2161
|
|
|
1777
2162
|
// src/StatsWidget.tsx
|
|
1778
|
-
import { jsx as
|
|
2163
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1779
2164
|
function buildClient(props) {
|
|
1780
2165
|
try {
|
|
1781
2166
|
if ("client" in props && props.client) {
|
|
@@ -1821,14 +2206,14 @@ function StatsWidget(props) {
|
|
|
1821
2206
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
1822
2207
|
[built]
|
|
1823
2208
|
);
|
|
1824
|
-
|
|
2209
|
+
useEffect6(() => {
|
|
1825
2210
|
if (!built || !ui.injectStyles) return;
|
|
1826
2211
|
const node = rootRef.current?.getRootNode();
|
|
1827
2212
|
if (node) injectStyles(node);
|
|
1828
2213
|
}, [built, ui.injectStyles]);
|
|
1829
2214
|
if (!built) return null;
|
|
1830
2215
|
const attrs = themeAttrs(ui);
|
|
1831
|
-
return /* @__PURE__ */
|
|
2216
|
+
return /* @__PURE__ */ jsx31("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__ */ jsx31(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx31(
|
|
1832
2217
|
ChatFab,
|
|
1833
2218
|
{
|
|
1834
2219
|
label: ui.launcherLabel,
|