@sensiblestats/widget-react 0.29.2 → 0.29.4

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/dist/index.js CHANGED
@@ -839,7 +839,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
839
839
  }
840
840
 
841
841
  // src/components/ChatPanel.tsx
842
- import { useEffect as useEffect7, useState as useState11 } from "react";
842
+ import { useEffect as useEffect7, useState as useState13 } from "react";
843
843
 
844
844
  // src/components/ChatHeader.tsx
845
845
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -1432,17 +1432,20 @@ function resultClass(result) {
1432
1432
  function FormStrip({ form }) {
1433
1433
  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}`)) });
1434
1434
  }
1435
- function FormRow({ name, form }) {
1435
+ function FormRow({ name, form, crestUrl }) {
1436
1436
  return /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-formrow", children: [
1437
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-formrow-name", children: name }),
1437
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-formrow-team", children: [
1438
+ /* @__PURE__ */ jsx17(Crest2, { url: crestUrl ?? void 0, name, kind: "team" }),
1439
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-formrow-name", children: name })
1440
+ ] }),
1438
1441
  /* @__PURE__ */ jsx17(FormStrip, { form })
1439
1442
  ] });
1440
1443
  }
1441
- function FixtureCard({ card, onTap }) {
1444
+ function FixtureCard({ card }) {
1442
1445
  const { data } = card;
1443
1446
  const h2h = data.h2H;
1444
1447
  return /* @__PURE__ */ jsxs13(Fragment5, { children: [
1445
- /* @__PURE__ */ jsxs13("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-fixture", onClick: () => onTap(card), children: [
1448
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-fixture", children: [
1446
1449
  /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-eyebrow", children: [
1447
1450
  card.title,
1448
1451
  " \xB7 ",
@@ -1460,22 +1463,18 @@ function FixtureCard({ card, onTap }) {
1460
1463
  ] })
1461
1464
  ] }),
1462
1465
  /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-meta", children: [
1463
- /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
1464
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.dateLabel }),
1465
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.kickoffLabel })
1466
- ] }),
1467
- data.venue ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
1468
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.venueLabel }),
1469
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.venue })
1466
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-kickoff", children: data.kickoffLabel }),
1467
+ data.venue ? /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-fixture-venue", children: [
1468
+ " \xB7 ",
1469
+ data.venue
1470
1470
  ] }) : null
1471
- ] }),
1472
- /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1471
+ ] })
1473
1472
  ] }),
1474
1473
  /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-lastfive", children: [
1475
1474
  /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-eyebrow", children: data.formLabel }),
1476
1475
  /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-form", children: [
1477
- /* @__PURE__ */ jsx17(FormRow, { name: data.homeName, form: data.homeForm }),
1478
- /* @__PURE__ */ jsx17(FormRow, { name: data.awayName, form: data.awayForm })
1476
+ /* @__PURE__ */ jsx17(FormRow, { name: data.homeName, form: data.homeForm, crestUrl: data.homeCrestUrl }),
1477
+ /* @__PURE__ */ jsx17(FormRow, { name: data.awayName, form: data.awayForm, crestUrl: data.awayCrestUrl })
1479
1478
  ] })
1480
1479
  ] }),
1481
1480
  h2h || data.fact ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-h2h", children: [
@@ -1545,16 +1544,19 @@ function CountdownCard({ card }) {
1545
1544
  }
1546
1545
 
1547
1546
  // src/components/dashboard/InjuriesCard.tsx
1547
+ import { useState as useState7 } from "react";
1548
1548
  import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
1549
+ var SHOWN = 3;
1549
1550
  function ColHead({ name, crestUrl }) {
1550
1551
  return /* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injuries-col-head", children: [
1551
1552
  /* @__PURE__ */ jsx19(Crest2, { url: crestUrl ?? void 0, name, kind: "team" }),
1552
1553
  /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-col-head-name", children: name })
1553
1554
  ] });
1554
1555
  }
1555
- function InjuryList({ rows, emptyLabel }) {
1556
+ function InjuryList({ rows, emptyLabel, limit }) {
1556
1557
  if (rows.length === 0) return /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-empty", children: emptyLabel });
1557
- 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: [
1558
+ const shown = limit === void 0 ? rows : rows.slice(0, limit);
1559
+ return /* @__PURE__ */ jsx19("ul", { className: "ss-w-fandash-injury-list", children: shown.map((r, i) => /* @__PURE__ */ jsxs15("li", { className: "ss-w-fandash-injury-row", children: [
1558
1560
  /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-name", children: r.playerName }),
1559
1561
  /* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injury-meta", children: [
1560
1562
  /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-status", children: r.status }),
@@ -1563,21 +1565,36 @@ function InjuryList({ rows, emptyLabel }) {
1563
1565
  ] })
1564
1566
  ] }, `${r.playerName}-${i}`)) });
1565
1567
  }
1566
- function InjuriesCard({ card }) {
1568
+ function InjuriesCard({ card, ui }) {
1567
1569
  const { data } = card;
1570
+ const [expanded, setExpanded] = useState7(false);
1571
+ const hidden = Math.max(0, data.ours.length - SHOWN) + Math.max(0, data.theirs.length - SHOWN);
1572
+ const limit = expanded ? void 0 : SHOWN;
1573
+ const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
1574
+ const showLess = ui?.showLess ?? "Show less";
1568
1575
  return /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-card ss-w-fandash-injuries", children: [
1569
1576
  card.title ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1570
1577
  /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-cols", children: [
1571
1578
  /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
1572
1579
  /* @__PURE__ */ jsx19(ColHead, { name: data.oursName, crestUrl: data.oursCrestUrl }),
1573
- /* @__PURE__ */ jsx19(InjuryList, { rows: data.ours, emptyLabel: data.emptyLabel })
1580
+ /* @__PURE__ */ jsx19(InjuryList, { rows: data.ours, emptyLabel: data.emptyLabel, limit })
1574
1581
  ] }),
1575
1582
  /* @__PURE__ */ jsx19("div", { className: "ss-w-fandash-injuries-divider" }),
1576
1583
  /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
1577
1584
  /* @__PURE__ */ jsx19(ColHead, { name: data.theirsName, crestUrl: data.theirsCrestUrl }),
1578
- /* @__PURE__ */ jsx19(InjuryList, { rows: data.theirs, emptyLabel: data.emptyLabel })
1585
+ /* @__PURE__ */ jsx19(InjuryList, { rows: data.theirs, emptyLabel: data.emptyLabel, limit })
1579
1586
  ] })
1580
- ] })
1587
+ ] }),
1588
+ hidden > 0 ? /* @__PURE__ */ jsx19(
1589
+ "button",
1590
+ {
1591
+ type: "button",
1592
+ className: "ss-w-fandash-injuries-more",
1593
+ "aria-expanded": expanded,
1594
+ onClick: () => setExpanded((v) => !v),
1595
+ children: expanded ? showLess : showMore(hidden)
1596
+ }
1597
+ ) : null
1581
1598
  ] });
1582
1599
  }
1583
1600
 
@@ -1654,21 +1671,60 @@ function LinksCard({ card }) {
1654
1671
  }
1655
1672
 
1656
1673
  // src/components/dashboard/PostMatchResultCard.tsx
1674
+ import { useState as useState8 } from "react";
1657
1675
  import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1676
+ function initials3(name) {
1677
+ return name.trim().slice(0, 3).toUpperCase();
1678
+ }
1679
+ function PostMatchCrest({
1680
+ url,
1681
+ name,
1682
+ side
1683
+ }) {
1684
+ const [failed, setFailed] = useState8(false);
1685
+ const cls = `ss-w-fandash-postmatch-crest ss-w-fandash-postmatch-crest--${side}`;
1686
+ if (url && !failed) {
1687
+ return /* @__PURE__ */ jsx25("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
1688
+ }
1689
+ return /* @__PURE__ */ jsx25("span", { className: `${cls} ss-w-fandash-postmatch-crest--fallback`, "aria-hidden": "true", children: initials3(name) });
1690
+ }
1658
1691
  function PostMatchResultCard({ card }) {
1659
1692
  const { data } = card;
1693
+ const homeWon = data.homeScore >= data.awayScore;
1694
+ const awayWon = data.awayScore >= data.homeScore;
1660
1695
  return /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-card ss-w-fandash-postmatch-result", children: [
1661
- card.title ? /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1662
- /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-scoreboard", children: [
1663
- /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-team", children: data.homeName }),
1664
- /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-score", children: data.homeScore }),
1665
- /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-sep", children: "\u2013" }),
1666
- /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-score", children: data.awayScore }),
1667
- /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-team ss-w-fandash-postmatch-team--away", children: data.awayName })
1696
+ /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-top", children: [
1697
+ card.title ? /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-pill", children: card.title }) : null,
1698
+ /* @__PURE__ */ jsx25("span", { className: `ss-w-fandash-postmatch-tag ss-w-fandash-postmatch-tag--${data.outcome}`, children: data.resultLabel })
1699
+ ] }),
1700
+ /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-main", children: [
1701
+ /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-side ss-w-fandash-postmatch-side--home", children: [
1702
+ /* @__PURE__ */ jsx25(PostMatchCrest, { url: data.homeCrestUrl, name: data.homeName, side: "home" }),
1703
+ /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-team-name", children: data.homeName })
1704
+ ] }),
1705
+ /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-score-main", children: [
1706
+ /* @__PURE__ */ jsx25(
1707
+ "span",
1708
+ {
1709
+ className: `ss-w-fandash-postmatch-score-value ss-w-fandash-postmatch-score-value--${homeWon ? "win" : "lose"}`,
1710
+ children: data.homeScore
1711
+ }
1712
+ ),
1713
+ /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-score-sep", children: "\u2013" }),
1714
+ /* @__PURE__ */ jsx25(
1715
+ "span",
1716
+ {
1717
+ className: `ss-w-fandash-postmatch-score-value ss-w-fandash-postmatch-score-value--${awayWon ? "win" : "lose"}`,
1718
+ children: data.awayScore
1719
+ }
1720
+ )
1721
+ ] }),
1722
+ /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-side ss-w-fandash-postmatch-side--away", children: [
1723
+ /* @__PURE__ */ jsx25(PostMatchCrest, { url: data.awayCrestUrl, name: data.awayName, side: "away" }),
1724
+ /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-postmatch-team-name", children: data.awayName })
1725
+ ] })
1668
1726
  ] }),
1669
1727
  /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash-postmatch-meta", children: [
1670
- data.resultLabel,
1671
- " \xB7 ",
1672
1728
  data.kickoffLabel,
1673
1729
  data.competition ? ` \xB7 ${data.competition}` : ""
1674
1730
  ] })
@@ -1680,38 +1736,75 @@ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1680
1736
  function PostMatchGoalsCard({ card, onTap }) {
1681
1737
  return /* @__PURE__ */ jsxs22("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-postmatch-goals", onClick: () => onTap(card), children: [
1682
1738
  card.title ? /* @__PURE__ */ jsx26("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1683
- /* @__PURE__ */ jsx26("ul", { className: "ss-w-fandash-postmatch-goal-list", children: card.data.goals.map((g, i) => /* @__PURE__ */ jsx26("li", { className: g.scoredByClub ? "ss-w-fandash-postmatch-goal ss-w-fandash-postmatch-goal--club" : "ss-w-fandash-postmatch-goal", children: g.text }, `${g.text}-${i}`)) }),
1739
+ /* @__PURE__ */ jsx26("ul", { className: "ss-w-fandash-postmatch-goal-list", children: card.data.goals.map((g, i) => /* @__PURE__ */ jsxs22("li", { className: g.scoredByClub ? "ss-w-fandash-postmatch-goal ss-w-fandash-postmatch-goal--club" : "ss-w-fandash-postmatch-goal", children: [
1740
+ /* @__PURE__ */ jsx26("span", { className: "ss-w-fandash-postmatch-goal-ball" }),
1741
+ /* @__PURE__ */ jsx26("span", { children: g.text })
1742
+ ] }, `${g.text}-${i}`)) }),
1684
1743
  /* @__PURE__ */ jsx26("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1685
1744
  ] });
1686
1745
  }
1687
1746
 
1688
1747
  // src/components/dashboard/PostMatchStatsCard.tsx
1689
1748
  import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
1749
+ function splitWidths(homeNum, awayNum) {
1750
+ const total = homeNum + awayNum;
1751
+ if (total <= 0) return { home: "50%", away: "50%" };
1752
+ const homePct = Math.round(homeNum / total * 1e3) / 10;
1753
+ return { home: `${homePct}%`, away: `${Math.round((100 - homePct) * 10) / 10}%` };
1754
+ }
1690
1755
  function PostMatchStatsCard({ card, onTap }) {
1756
+ const { data } = card;
1691
1757
  return /* @__PURE__ */ jsxs23("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-postmatch-stats", onClick: () => onTap(card), children: [
1692
1758
  card.title ? /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1693
- /* @__PURE__ */ jsx27("div", { className: "ss-w-fandash-postmatch-stat-rows", children: card.data.rows.map((r, i) => {
1694
- const leftValue = card.data.clubIsHome ? r.clubValue : r.oppValue;
1695
- const rightValue = card.data.clubIsHome ? r.oppValue : r.clubValue;
1696
- return /* @__PURE__ */ jsxs23("div", { className: "ss-w-fandash-postmatch-stat-row", children: [
1697
- /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-value", children: leftValue }),
1698
- /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-label", children: r.label }),
1699
- /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-value", children: rightValue })
1759
+ /* @__PURE__ */ jsx27("div", { className: "ss-w-fandash-postmatch-stat-rows", children: data.rows.map((r, i) => {
1760
+ const leftValue = data.clubIsHome ? r.clubValue : r.oppValue;
1761
+ const rightValue = data.clubIsHome ? r.oppValue : r.clubValue;
1762
+ const homeNum = data.clubIsHome ? r.clubNum : r.oppNum;
1763
+ const awayNum = data.clubIsHome ? r.oppNum : r.clubNum;
1764
+ const { home, away } = splitWidths(homeNum, awayNum);
1765
+ return /* @__PURE__ */ jsxs23("div", { className: "ss-w-fandash-postmatch-stat", children: [
1766
+ /* @__PURE__ */ jsxs23("div", { className: "ss-w-fandash-postmatch-stat-row", children: [
1767
+ /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-value ss-w-fandash-postmatch-stat-value--home", children: leftValue }),
1768
+ /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-label", children: r.label }),
1769
+ /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-value ss-w-fandash-postmatch-stat-value--away", children: rightValue })
1770
+ ] }),
1771
+ /* @__PURE__ */ jsxs23("div", { className: "ss-w-fandash-postmatch-stat-bar", children: [
1772
+ /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-bar-home", style: { width: home } }),
1773
+ /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-postmatch-stat-bar-away", style: { width: away } })
1774
+ ] })
1700
1775
  ] }, `${r.label}-${i}`);
1701
1776
  }) }),
1777
+ data.rows.length > 0 ? /* @__PURE__ */ jsxs23("div", { className: "ss-w-fandash-postmatch-legend", children: [
1778
+ /* @__PURE__ */ jsxs23("span", { className: "ss-w-fandash-postmatch-legend-item", children: [
1779
+ /* @__PURE__ */ jsx27("i", { className: "ss-w-fandash-postmatch-legend-swatch ss-w-fandash-postmatch-legend-swatch--home" }),
1780
+ data.homeName
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs23("span", { className: "ss-w-fandash-postmatch-legend-item", children: [
1783
+ /* @__PURE__ */ jsx27("i", { className: "ss-w-fandash-postmatch-legend-swatch ss-w-fandash-postmatch-legend-swatch--away" }),
1784
+ data.awayName
1785
+ ] })
1786
+ ] }) : null,
1702
1787
  /* @__PURE__ */ jsx27("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1703
1788
  ] });
1704
1789
  }
1705
1790
 
1706
1791
  // src/components/dashboard/PostMatchTopPerformerCard.tsx
1707
1792
  import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
1793
+ function initials4(playerName) {
1794
+ const words = playerName.trim().split(/\s+/).filter(Boolean);
1795
+ if (words.length === 0) return "?";
1796
+ const first = words[0].charAt(0);
1797
+ const last = words[words.length - 1].charAt(0);
1798
+ return (words.length > 1 ? `${first}${last}` : first).toUpperCase();
1799
+ }
1708
1800
  function PostMatchTopPerformerCard({ card, onTap }) {
1709
1801
  const { data } = card;
1710
1802
  return /* @__PURE__ */ jsxs24("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-postmatch-performer", onClick: () => onTap(card), children: [
1711
1803
  card.title ? /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1712
- /* @__PURE__ */ jsxs24("div", { className: "ss-w-fandash-postmatch-performer-row", children: [
1713
- /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-postmatch-performer-name", children: data.playerName }),
1714
- /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-postmatch-performer-rating", children: data.ratingLabel })
1804
+ /* @__PURE__ */ jsxs24("div", { className: "ss-w-fandash-postmatch-perf-row", children: [
1805
+ /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-postmatch-perf-avatar", "aria-hidden": "true", children: initials4(data.playerName) }),
1806
+ /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-postmatch-perf-name", children: data.playerName }),
1807
+ /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-postmatch-perf-rating", children: data.ratingLabel })
1715
1808
  ] }),
1716
1809
  /* @__PURE__ */ jsx28("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1717
1810
  ] });
@@ -1787,10 +1880,10 @@ function RestingFormCard({ card }) {
1787
1880
  }
1788
1881
 
1789
1882
  // src/components/dashboard/LiveScoreCard.tsx
1790
- import { useRef as useRef3, useState as useState8 } from "react";
1883
+ import { useRef as useRef3, useState as useState10 } from "react";
1791
1884
 
1792
1885
  // src/useLiveClock.ts
1793
- import { useEffect as useEffect5, useState as useState7 } from "react";
1886
+ import { useEffect as useEffect5, useState as useState9 } from "react";
1794
1887
  var TICK_MS = 6e4;
1795
1888
  var MAX_LIVE_TICK_MINUTES = 15;
1796
1889
  function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
@@ -1799,7 +1892,7 @@ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
1799
1892
  return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
1800
1893
  }
1801
1894
  function useLiveClock(minute, clockRunning, receivedAtMs) {
1802
- const [displayMinute, setDisplayMinute] = useState7(
1895
+ const [displayMinute, setDisplayMinute] = useState9(
1803
1896
  () => computeDisplayMinute(minute, clockRunning, receivedAtMs)
1804
1897
  );
1805
1898
  useEffect5(() => {
@@ -1815,16 +1908,16 @@ function useLiveClock(minute, clockRunning, receivedAtMs) {
1815
1908
 
1816
1909
  // src/components/dashboard/LiveScoreCard.tsx
1817
1910
  import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
1818
- function initials3(name) {
1911
+ function initials5(name) {
1819
1912
  return name.trim().slice(0, 3).toUpperCase();
1820
1913
  }
1821
1914
  function LiveCrest({ url, name, side }) {
1822
- const [failed, setFailed] = useState8(false);
1915
+ const [failed, setFailed] = useState10(false);
1823
1916
  const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
1824
1917
  if (url && !failed) {
1825
1918
  return /* @__PURE__ */ jsx34("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
1826
1919
  }
1827
- return /* @__PURE__ */ jsx34("span", { className: `${cls} ss-w-fandash-live-crest--fallback`, "aria-hidden": "true", children: initials3(name) });
1920
+ return /* @__PURE__ */ jsx34("span", { className: `${cls} ss-w-fandash-live-crest--fallback`, "aria-hidden": "true", children: initials5(name) });
1828
1921
  }
1829
1922
  function scorersLine(goalEvents) {
1830
1923
  if (!goalEvents || goalEvents.length === 0) return null;
@@ -1878,9 +1971,9 @@ function LiveScoreCard({
1878
1971
  }
1879
1972
 
1880
1973
  // src/components/dashboard/LiveTimelineCard.tsx
1881
- import { useState as useState9 } from "react";
1974
+ import { useState as useState11 } from "react";
1882
1975
  import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
1883
- var SHOWN = 3;
1976
+ var SHOWN2 = 3;
1884
1977
  function EventIcon({ type }) {
1885
1978
  if (type === "goal") {
1886
1979
  return /* @__PURE__ */ jsx35("span", { className: "ss-w-fandash-live-tl-ico ss-w-fandash-live-tl-ico--goal", "aria-hidden": "true", children: /* @__PURE__ */ jsx35("span", { className: "ss-w-fandash-live-tl-ball" }) });
@@ -1920,12 +2013,12 @@ function EventRow({ e }) {
1920
2013
  ] });
1921
2014
  }
1922
2015
  function LiveTimelineCard({ card, ui }) {
1923
- const [expanded, setExpanded] = useState9(false);
2016
+ const [expanded, setExpanded] = useState11(false);
1924
2017
  const events = card.data.events;
1925
2018
  if (!events || events.length === 0) return null;
1926
2019
  const sorted = [...events].sort((a, b) => b.minute - a.minute);
1927
- const rest = sorted.length - SHOWN;
1928
- const shown = expanded ? sorted : sorted.slice(0, SHOWN);
2020
+ const rest = sorted.length - SHOWN2;
2021
+ const shown = expanded ? sorted : sorted.slice(0, SHOWN2);
1929
2022
  const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
1930
2023
  const showLess = ui?.showLess ?? "Show less";
1931
2024
  return /* @__PURE__ */ jsxs31("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
@@ -2001,7 +2094,7 @@ function MomentumWave({
2001
2094
 
2002
2095
  // src/components/dashboard/LiveStatsCard.tsx
2003
2096
  import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
2004
- function splitWidths(homeNum, awayNum) {
2097
+ function splitWidths2(homeNum, awayNum) {
2005
2098
  const total = homeNum + awayNum;
2006
2099
  if (total <= 0) return { home: "50%", away: "50%" };
2007
2100
  const homePct = Math.round(homeNum / total * 1e3) / 10;
@@ -2016,7 +2109,7 @@ function LiveStatsCard({ card }) {
2016
2109
  const awayValue = data.clubIsHome ? r.oppValue : r.clubValue;
2017
2110
  const homeNum = data.clubIsHome ? r.clubNum : r.oppNum;
2018
2111
  const awayNum = data.clubIsHome ? r.oppNum : r.clubNum;
2019
- const { home, away } = splitWidths(homeNum, awayNum);
2112
+ const { home, away } = splitWidths2(homeNum, awayNum);
2020
2113
  return /* @__PURE__ */ jsxs33("div", { className: "ss-w-fandash-live-stat", children: [
2021
2114
  /* @__PURE__ */ jsxs33("div", { className: "ss-w-fandash-live-stat-row", children: [
2022
2115
  /* @__PURE__ */ jsx37("span", { className: "ss-w-fandash-live-stat-value ss-w-fandash-live-stat-value--home", children: homeValue }),
@@ -2062,11 +2155,11 @@ function ClubFanDashboard({ dashboard, onTap, ui }) {
2062
2155
  function FanDashboardCard({ card, onTap, ui, goalEvents }) {
2063
2156
  switch (card.cardType) {
2064
2157
  case "fixture":
2065
- return /* @__PURE__ */ jsx38(FixtureCard, { card, onTap });
2158
+ return /* @__PURE__ */ jsx38(FixtureCard, { card });
2066
2159
  case "countdown":
2067
2160
  return /* @__PURE__ */ jsx38(CountdownCard, { card });
2068
2161
  case "injuries":
2069
- return /* @__PURE__ */ jsx38(InjuriesCard, { card });
2162
+ return /* @__PURE__ */ jsx38(InjuriesCard, { card, ui });
2070
2163
  case "reasonToBet":
2071
2164
  return /* @__PURE__ */ jsx38(ReasonToBetCard, { card });
2072
2165
  case "whereToWatch":
@@ -2209,10 +2302,10 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
2209
2302
  }
2210
2303
 
2211
2304
  // src/components/ChatInput.tsx
2212
- import { useState as useState10 } from "react";
2305
+ import { useState as useState12 } from "react";
2213
2306
  import { jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
2214
2307
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
2215
- const [value, setValue] = useState10("");
2308
+ const [value, setValue] = useState12("");
2216
2309
  const submit = () => {
2217
2310
  const t = value.trim();
2218
2311
  if (!t) return;
@@ -2235,7 +2328,7 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
2235
2328
  import { jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
2236
2329
  var FULLSCREEN_QUERY = "(max-width: 639px)";
2237
2330
  function useIsModal() {
2238
- const [modal, setModal] = useState11(false);
2331
+ const [modal, setModal] = useState13(false);
2239
2332
  useEffect7(() => {
2240
2333
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
2241
2334
  const mql = window.matchMedia(FULLSCREEN_QUERY);
@@ -2662,10 +2755,9 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2662
2755
  .ss-w-fandash-fixture-team--away .ss-w-fandash-fixture-name { text-align: right; }
2663
2756
  .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); }
2664
2757
  /* date + venue as labelled rows, so a long stadium name gets its own line to ellipsis in */
2665
- .ss-w-fandash-fixture-meta { display: flex; flex-direction: column; gap: 1px; border-top: 1px solid var(--ss-w-line); padding-top: 8px; }
2666
- .ss-w-fandash-fixture-metarow { display: flex; align-items: baseline; gap: 9px; }
2667
- .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); }
2668
- .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); }
2758
+ .ss-w-fandash-fixture-meta { border-top: 1px solid var(--ss-w-line); padding-top: 10px; text-align: center; font-size: 12.5px; color: var(--ss-w-faint); }
2759
+ .ss-w-fandash-fixture-kickoff { font-weight: 700; color: var(--ss-w-ink); }
2760
+ .ss-w-fandash-fixture-venue { font-weight: 600; }
2669
2761
  .ss-w-fandash-fact { font-size: 12px; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 8px; padding: 6px 8px; }
2670
2762
  /* each head-to-head figure sits on its own shaded tile, so the grid gap separates wins/draws/losses */
2671
2763
  .ss-w-fandash-h2h .ss-w-fandash-split-cell { background: var(--ss-w-bubble); border-radius: 10px; padding: 12px 6px; }
@@ -2675,7 +2767,12 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2675
2767
  /* last-five: one row per side, team name left, form pills right */
2676
2768
  .ss-w-fandash-fixture-form { display: flex; flex-direction: column; gap: 5px; }
2677
2769
  .ss-w-fandash-formrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
2770
+ .ss-w-fandash-formrow-team { display: flex; align-items: center; gap: 8px; min-width: 0; }
2771
+ .ss-w-fandash-formrow-team .ss-w-bi-crest { width: 20px; height: 20px; }
2772
+ .ss-w-fandash-formrow-team .ss-w-bi-crest-mono { font-size: 9px; }
2678
2773
  .ss-w-fandash-formrow-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; }
2774
+ .ss-w-fandash-injuries-more { margin-top: 10px; 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; }
2775
+ .ss-w-fandash-injuries-more:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
2679
2776
  .ss-w-fandash-form { display: inline-flex; gap: 3px; flex: none; }
2680
2777
  .ss-w-fandash-form-pill {
2681
2778
  display: inline-grid; place-items: center; width: 16px; height: 16px;
@@ -2739,35 +2836,76 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2739
2836
  .ss-w-fandash-link-label { font-size: 12.5px; font-weight: 650; overflow-wrap: anywhere; }
2740
2837
  .ss-w-fandash-link-chevron { flex: none; width: 12px; height: 12px; color: var(--ss-w-accent); }
2741
2838
 
2742
- /* post-match result: full-time scoreboard -- team names flank the score, meta line underneath */
2743
- .ss-w-fandash-postmatch-scoreboard { display: flex; align-items: center; gap: 8px; }
2744
- .ss-w-fandash-postmatch-team { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 650; }
2745
- .ss-w-fandash-postmatch-team--away { text-align: right; }
2746
- .ss-w-fandash-postmatch-score { flex: none; font-size: 20px; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2747
- .ss-w-fandash-postmatch-sep { flex: none; font-size: 14px; font-weight: 700; color: var(--ss-w-faint); }
2748
- .ss-w-fandash-postmatch-meta { font-size: 11.5px; color: var(--ss-w-faint); }
2749
-
2750
- /* post-match goals: a plain list, the club's own goals picked out in the accent color */
2751
- .ss-w-fandash-postmatch-goal-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
2752
- .ss-w-fandash-postmatch-goal { font-size: 12.5px; color: var(--ss-w-faint); }
2839
+ /* post-match result: full-time scoreboard, mirroring the live card's crest + big-score treatment.
2840
+ A neutral "Full time" pill sits left of a coloured Win/Draw/Loss tag; the 3-column main row
2841
+ (home crest+name | score | away crest+name, away reversed) puts the winner's score in full ink
2842
+ and the loser's dimmed; a centered meta line sits under a divider. */
2843
+ .ss-w-fandash-postmatch-top { display: flex; align-items: center; gap: 8px; margin-bottom: 15px; }
2844
+ .ss-w-fandash-postmatch-pill { display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 6px; padding: 3px 8px; }
2845
+ .ss-w-fandash-postmatch-tag { margin-left: auto; flex: none; font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; }
2846
+ .ss-w-fandash-postmatch-tag--win { color: var(--ss-w-pos); }
2847
+ .ss-w-fandash-postmatch-tag--loss { color: var(--ss-w-neg); }
2848
+ .ss-w-fandash-postmatch-tag--draw { color: var(--ss-w-faint); }
2849
+ .ss-w-fandash-postmatch-main { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; }
2850
+ .ss-w-fandash-postmatch-side { display: flex; align-items: center; gap: 10px; min-width: 0; }
2851
+ .ss-w-fandash-postmatch-side--away { flex-direction: row-reverse; text-align: right; }
2852
+ .ss-w-fandash-postmatch-crest { width: 38px; height: 38px; border-radius: 10px; flex: none; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 12.5px; object-fit: cover; box-shadow: 0 2px 6px rgba(0, 0, 0, .18); }
2853
+ .ss-w-fandash-postmatch-crest--fallback.ss-w-fandash-postmatch-crest--home { background: var(--ss-w-home); }
2854
+ .ss-w-fandash-postmatch-crest--fallback.ss-w-fandash-postmatch-crest--away { background: var(--ss-w-away); }
2855
+ .ss-w-fandash-postmatch-team-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; font-weight: 700; line-height: 1.15; color: var(--ss-w-ink); }
2856
+ .ss-w-fandash-postmatch-score-main { display: flex; align-items: baseline; justify-content: center; font-size: 36px; font-weight: 850; font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; }
2857
+ .ss-w-fandash-postmatch-score-value--win { color: var(--ss-w-ink); }
2858
+ .ss-w-fandash-postmatch-score-value--lose { color: var(--ss-w-faint); }
2859
+ .ss-w-fandash-postmatch-score-sep { color: var(--ss-w-faint); font-weight: 500; margin: 0 5px; }
2860
+ .ss-w-fandash-postmatch-meta { margin-top: 13px; padding-top: 12px; border-top: 1px solid var(--ss-w-line); font-size: 12px; color: var(--ss-w-faint); text-align: center; }
2861
+
2862
+ /* post-match goals: a football-ball icon per goal (mirrors the live timeline ball, duplicated here
2863
+ under the postmatch prefix), text dimmed for opponent goals and picked out in full ink + bold
2864
+ for the club's own goals */
2865
+ .ss-w-fandash-postmatch-goal-list { list-style: none; display: flex; flex-direction: column; gap: 6px; }
2866
+ .ss-w-fandash-postmatch-goal { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
2867
+ .ss-w-fandash-postmatch-goal-ball { width: 11px; height: 11px; flex: none; border-radius: 50%; background: radial-gradient(circle at 50% 42%, #fff 0 34%, transparent 36%), var(--ss-w-ink); border: 1px solid var(--ss-w-ink); }
2753
2868
  .ss-w-fandash-postmatch-goal--club { color: var(--ss-w-ink); font-weight: 650; }
2754
2869
 
2755
- /* post-match stats: club value / stat label / opponent value, three columns per row */
2756
- .ss-w-fandash-postmatch-stat-rows { display: flex; flex-direction: column; gap: 5px; }
2757
- .ss-w-fandash-postmatch-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
2870
+ /* post-match stats: home value / stat label / away value, oriented by venue (u316), each row now
2871
+ backed by a home/away split bar -- the big upgrade from plain text -- plus a legend. Mirrors
2872
+ LiveStatsCard's split-bar treatment under the postmatch-prefixed classes; the shared
2873
+ --ss-w-home/--ss-w-away/--ss-w-track tokens are defined above. */
2874
+ .ss-w-fandash-postmatch-stat-rows { display: flex; flex-direction: column; gap: 15px; }
2875
+ .ss-w-fandash-postmatch-stat { display: flex; flex-direction: column; gap: 7px; }
2876
+ .ss-w-fandash-postmatch-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 8px; }
2758
2877
  .ss-w-fandash-postmatch-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2759
- .ss-w-fandash-postmatch-stat-row .ss-w-fandash-postmatch-stat-value:last-child { text-align: right; }
2878
+ .ss-w-fandash-postmatch-stat-value--home { color: var(--ss-w-home); }
2879
+ .ss-w-fandash-postmatch-stat-value--away { color: var(--ss-w-away); text-align: right; }
2760
2880
  .ss-w-fandash-postmatch-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
2761
-
2762
- /* post-match top performer: player name left, rating pill right */
2763
- .ss-w-fandash-postmatch-performer-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
2764
- .ss-w-fandash-postmatch-performer-name { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
2765
- .ss-w-fandash-postmatch-performer-rating { flex: none; font-size: 12px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 13%, var(--ss-w-panel)); border-radius: 999px; padding: 2px 9px; }
2766
-
2767
- /* post-match next: the upcoming fixture teaser -- opponent left, when-label right */
2768
- .ss-w-fandash-postmatch-next-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
2769
- .ss-w-fandash-postmatch-next-opponent { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
2770
- .ss-w-fandash-postmatch-next-when { flex: none; font-size: 11.5px; color: var(--ss-w-faint); }
2881
+ .ss-w-fandash-postmatch-stat-bar { position: relative; height: 8px; border-radius: 999px; background: var(--ss-w-track); overflow: hidden; display: flex; }
2882
+ .ss-w-fandash-postmatch-stat-bar-home { height: 100%; background: var(--ss-w-home); }
2883
+ .ss-w-fandash-postmatch-stat-bar-away { height: 100%; background: var(--ss-w-away); margin-left: auto; }
2884
+ .ss-w-fandash-postmatch-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); }
2885
+ .ss-w-fandash-postmatch-legend-item { display: inline-flex; align-items: center; gap: 6px; }
2886
+ .ss-w-fandash-postmatch-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
2887
+ .ss-w-fandash-postmatch-legend-swatch--home { background: var(--ss-w-home); }
2888
+ .ss-w-fandash-postmatch-legend-swatch--away { background: var(--ss-w-away); }
2889
+
2890
+ /* post-match top performer (full mockup treatment): gradient initials avatar + name + a
2891
+ prominent rating chip. Avatar gradient reuses the --ss-w-away token, same pairing as the live
2892
+ crest fallback; "?" renders through the same avatar span when the name is blank. */
2893
+ .ss-w-fandash-postmatch-perf-row { display: flex; align-items: center; gap: 12px; }
2894
+ .ss-w-fandash-postmatch-perf-avatar {
2895
+ flex: none; width: 42px; height: 42px; border-radius: 50%;
2896
+ display: grid; place-items: center;
2897
+ font-size: 14px; font-weight: 800; color: #fff;
2898
+ background: linear-gradient(135deg, var(--ss-w-away), color-mix(in srgb, var(--ss-w-away) 55%, #000));
2899
+ }
2900
+ .ss-w-fandash-postmatch-perf-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; font-weight: 700; }
2901
+ .ss-w-fandash-postmatch-perf-rating { margin-left: auto; flex: none; font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border-radius: 999px; padding: 3px 11px; }
2902
+
2903
+ /* post-match next (v1, modest): whenLabel is already a complete localized phrase (opponent +
2904
+ venue + kickoff) composed server-side, so this card renders just that one line -- dressed up
2905
+ with an accent rule rather than a duplicated opponent line. The richer opponent-headline/venue-
2906
+ chip/crest layout from the mockup needs a server field split and is deferred. */
2907
+ .ss-w-fandash-postmatch-next-row { display: flex; align-items: center; gap: 10px; border-left: 3px solid var(--ss-w-accent); padding-left: 10px; }
2908
+ .ss-w-fandash-postmatch-next-when { font-size: 13.5px; font-weight: 650; color: var(--ss-w-ink); line-height: 1.3; }
2771
2909
 
2772
2910
  /* resting next: the between-matches fixture teaser -- when-label left, competition badge right */
2773
2911
  .ss-w-fandash-resting-next-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }