@sensiblestats/widget-react 0.38.0 → 0.39.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 +6 -0
- package/dist/index.cjs +149 -65
- 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 +365 -281
- package/dist/index.js.map +1 -1
- package/dist/styles.css +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sensiblestats/widget-react
|
|
2
2
|
|
|
3
|
+
## 0.39.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a5e97e2: Move the "New conversation" header action into a kebab (⋮) overflow menu, keeping Close as a one-tap control. The menu opens on click, closes on Escape, outside-click, or selection, and manages focus for keyboard users.
|
|
8
|
+
|
|
3
9
|
## 0.38.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
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_react24 = require("react");
|
|
40
40
|
var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
|
|
41
41
|
|
|
42
42
|
// src/kickoff.ts
|
|
@@ -58,6 +58,7 @@ var STRINGS = {
|
|
|
58
58
|
placeholder: "Ask about a team, player or match\u2026",
|
|
59
59
|
closeChat: "Close chat",
|
|
60
60
|
newConversation: "New conversation",
|
|
61
|
+
moreOptions: "More options",
|
|
61
62
|
chatDialogLabel: "SensibleStats chat",
|
|
62
63
|
stopResponse: "Stop response",
|
|
63
64
|
sendMessage: "Send message",
|
|
@@ -131,6 +132,7 @@ var STRINGS = {
|
|
|
131
132
|
placeholder: "Bir tak\u0131m, oyuncu veya ma\xE7 hakk\u0131nda sor\u2026",
|
|
132
133
|
closeChat: "Sohbeti kapat",
|
|
133
134
|
newConversation: "Yeni sohbet",
|
|
135
|
+
moreOptions: "Di\u011Fer se\xE7enekler",
|
|
134
136
|
chatDialogLabel: "SensibleStats sohbet",
|
|
135
137
|
stopResponse: "Yan\u0131t\u0131 durdur",
|
|
136
138
|
sendMessage: "Mesaj g\xF6nder",
|
|
@@ -227,6 +229,7 @@ function normalizeUi(cfg) {
|
|
|
227
229
|
injectStyles: cfg.injectStyles ?? true,
|
|
228
230
|
closeChat: strings.closeChat,
|
|
229
231
|
newConversation: strings.newConversation,
|
|
232
|
+
moreOptions: strings.moreOptions,
|
|
230
233
|
chatDialogLabel: strings.chatDialogLabel,
|
|
231
234
|
stopResponse: strings.stopResponse,
|
|
232
235
|
sendMessage: strings.sendMessage,
|
|
@@ -1431,6 +1434,13 @@ function ChevronIcon() {
|
|
|
1431
1434
|
function NewChatIcon() {
|
|
1432
1435
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", d: "M12 5v14M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) });
|
|
1433
1436
|
}
|
|
1437
|
+
function KebabIcon() {
|
|
1438
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "currentColor", children: [
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "5", r: "1.6" }),
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "19", r: "1.6" })
|
|
1442
|
+
] }) });
|
|
1443
|
+
}
|
|
1434
1444
|
var DASHBOARD_EMOJI = {
|
|
1435
1445
|
football: "\u26BD",
|
|
1436
1446
|
"goal-net": "\u{1F945}",
|
|
@@ -1495,21 +1505,89 @@ function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup,
|
|
|
1495
1505
|
}
|
|
1496
1506
|
|
|
1497
1507
|
// src/components/ChatPanel.tsx
|
|
1498
|
-
var
|
|
1508
|
+
var import_react23 = require("react");
|
|
1499
1509
|
|
|
1500
1510
|
// src/components/ChatHeader.tsx
|
|
1511
|
+
var import_react6 = require("react");
|
|
1501
1512
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1502
|
-
function ChatHeader({
|
|
1513
|
+
function ChatHeader({
|
|
1514
|
+
onClose,
|
|
1515
|
+
closeLabel = "Close chat",
|
|
1516
|
+
logoUrl,
|
|
1517
|
+
headerTitle,
|
|
1518
|
+
onNewConversation,
|
|
1519
|
+
canReset,
|
|
1520
|
+
newConversationLabel = "New conversation",
|
|
1521
|
+
moreOptionsLabel = "More options"
|
|
1522
|
+
}) {
|
|
1523
|
+
const [menuOpen, setMenuOpen] = (0, import_react6.useState)(false);
|
|
1524
|
+
const wrapRef = (0, import_react6.useRef)(null);
|
|
1525
|
+
const kebabRef = (0, import_react6.useRef)(null);
|
|
1526
|
+
const firstItemRef = (0, import_react6.useRef)(null);
|
|
1527
|
+
const hasMenu = !!(canReset && onNewConversation);
|
|
1528
|
+
(0, import_react6.useEffect)(() => {
|
|
1529
|
+
if (menuOpen) firstItemRef.current?.focus();
|
|
1530
|
+
}, [menuOpen]);
|
|
1531
|
+
(0, import_react6.useEffect)(() => {
|
|
1532
|
+
if (!menuOpen) return;
|
|
1533
|
+
const onPointer = (e) => {
|
|
1534
|
+
if (!wrapRef.current?.contains(e.target)) setMenuOpen(false);
|
|
1535
|
+
};
|
|
1536
|
+
const onKey = (e) => {
|
|
1537
|
+
if (e.key === "Escape") {
|
|
1538
|
+
setMenuOpen(false);
|
|
1539
|
+
kebabRef.current?.focus();
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
document.addEventListener("mousedown", onPointer);
|
|
1543
|
+
document.addEventListener("keydown", onKey);
|
|
1544
|
+
return () => {
|
|
1545
|
+
document.removeEventListener("mousedown", onPointer);
|
|
1546
|
+
document.removeEventListener("keydown", onKey);
|
|
1547
|
+
};
|
|
1548
|
+
}, [menuOpen]);
|
|
1549
|
+
const runNewConversation = () => {
|
|
1550
|
+
setMenuOpen(false);
|
|
1551
|
+
onNewConversation?.();
|
|
1552
|
+
};
|
|
1503
1553
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "ss-w-header", children: [
|
|
1504
1554
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BrandMark, { logoUrl, className: "ss-w-header-mark" }),
|
|
1505
1555
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-header-title", children: headerTitle ?? "SensibleStats" }),
|
|
1506
|
-
|
|
1556
|
+
hasMenu ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-menu-wrap", ref: wrapRef, children: [
|
|
1557
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1558
|
+
"button",
|
|
1559
|
+
{
|
|
1560
|
+
type: "button",
|
|
1561
|
+
className: "ss-w-kebab",
|
|
1562
|
+
ref: kebabRef,
|
|
1563
|
+
"aria-haspopup": "menu",
|
|
1564
|
+
"aria-expanded": menuOpen,
|
|
1565
|
+
"aria-label": moreOptionsLabel,
|
|
1566
|
+
onClick: () => setMenuOpen((open) => !open),
|
|
1567
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(KebabIcon, {})
|
|
1568
|
+
}
|
|
1569
|
+
),
|
|
1570
|
+
menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-menu", role: "menu", "aria-label": moreOptionsLabel, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1571
|
+
"button",
|
|
1572
|
+
{
|
|
1573
|
+
type: "button",
|
|
1574
|
+
className: "ss-w-menu-item",
|
|
1575
|
+
role: "menuitem",
|
|
1576
|
+
ref: firstItemRef,
|
|
1577
|
+
onClick: runNewConversation,
|
|
1578
|
+
children: [
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NewChatIcon, {}),
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: newConversationLabel })
|
|
1581
|
+
]
|
|
1582
|
+
}
|
|
1583
|
+
) }) : null
|
|
1584
|
+
] }) : null,
|
|
1507
1585
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ss-w-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {}) })
|
|
1508
1586
|
] });
|
|
1509
1587
|
}
|
|
1510
1588
|
|
|
1511
1589
|
// src/components/MessageList.tsx
|
|
1512
|
-
var
|
|
1590
|
+
var import_react21 = require("react");
|
|
1513
1591
|
|
|
1514
1592
|
// src/components/Markdown.tsx
|
|
1515
1593
|
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
@@ -1527,7 +1605,7 @@ function Markdown({ text }) {
|
|
|
1527
1605
|
}
|
|
1528
1606
|
|
|
1529
1607
|
// src/components/EntityCardList.tsx
|
|
1530
|
-
var
|
|
1608
|
+
var import_react7 = require("react");
|
|
1531
1609
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1532
1610
|
var MAX_VISIBLE = 3;
|
|
1533
1611
|
var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
|
|
@@ -1611,7 +1689,7 @@ function ScopeTabs({ scopes, active, onSelect }) {
|
|
|
1611
1689
|
)) });
|
|
1612
1690
|
}
|
|
1613
1691
|
function EntityCardItem({ c }) {
|
|
1614
|
-
const [active, setActive] = (0,
|
|
1692
|
+
const [active, setActive] = (0, import_react7.useState)(0);
|
|
1615
1693
|
const hasBody = c.scopes.length > 0;
|
|
1616
1694
|
if (!hasBody) {
|
|
1617
1695
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardHeader, { c, showScopeCrest: false }) });
|
|
@@ -1625,7 +1703,7 @@ function EntityCardItem({ c }) {
|
|
|
1625
1703
|
] });
|
|
1626
1704
|
}
|
|
1627
1705
|
function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
|
|
1628
|
-
const [expanded, setExpanded] = (0,
|
|
1706
|
+
const [expanded, setExpanded] = (0, import_react7.useState)(false);
|
|
1629
1707
|
if (cards.length === 0) return null;
|
|
1630
1708
|
const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
|
|
1631
1709
|
const overflow = cards.length - MAX_VISIBLE;
|
|
@@ -1636,18 +1714,18 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
|
|
|
1636
1714
|
}
|
|
1637
1715
|
|
|
1638
1716
|
// src/components/BettingInsightList.tsx
|
|
1639
|
-
var
|
|
1717
|
+
var import_react12 = require("react");
|
|
1640
1718
|
|
|
1641
1719
|
// src/components/AddToSlipButton.tsx
|
|
1642
|
-
var
|
|
1720
|
+
var import_react9 = require("react");
|
|
1643
1721
|
var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
|
|
1644
1722
|
|
|
1645
1723
|
// src/components/Toast.tsx
|
|
1646
|
-
var
|
|
1724
|
+
var import_react8 = require("react");
|
|
1647
1725
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1648
1726
|
var DISMISS_MS = 4e3;
|
|
1649
1727
|
function Toast({ toast, onDismiss }) {
|
|
1650
|
-
(0,
|
|
1728
|
+
(0, import_react8.useEffect)(() => {
|
|
1651
1729
|
if (!toast) return;
|
|
1652
1730
|
const timer = setTimeout(onDismiss, DISMISS_MS);
|
|
1653
1731
|
return () => clearTimeout(timer);
|
|
@@ -1665,10 +1743,10 @@ function withTimeout(value) {
|
|
|
1665
1743
|
]);
|
|
1666
1744
|
}
|
|
1667
1745
|
function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
|
|
1668
|
-
const [pending, setPending] = (0,
|
|
1669
|
-
const [settled, setSettled] = (0,
|
|
1670
|
-
const [toast, setToast] = (0,
|
|
1671
|
-
const dismiss = (0,
|
|
1746
|
+
const [pending, setPending] = (0, import_react9.useState)(false);
|
|
1747
|
+
const [settled, setSettled] = (0, import_react9.useState)(false);
|
|
1748
|
+
const [toast, setToast] = (0, import_react9.useState)(null);
|
|
1749
|
+
const dismiss = (0, import_react9.useCallback)(() => setToast(null), []);
|
|
1672
1750
|
const handler = slip.onAddToSlip;
|
|
1673
1751
|
if (!ui || !handler || !selection) return null;
|
|
1674
1752
|
const onClick = async () => {
|
|
@@ -1732,14 +1810,14 @@ function OddsMovementBadge({
|
|
|
1732
1810
|
}
|
|
1733
1811
|
|
|
1734
1812
|
// src/components/Crest.tsx
|
|
1735
|
-
var
|
|
1813
|
+
var import_react10 = require("react");
|
|
1736
1814
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1737
1815
|
function initials2(name) {
|
|
1738
1816
|
return (name ?? "").trim().slice(0, 1).toUpperCase() || "?";
|
|
1739
1817
|
}
|
|
1740
1818
|
function Crest2({ url, name, kind }) {
|
|
1741
1819
|
const cls = kind === "comp" ? "ss-w-bi-comp-logo" : "ss-w-bi-crest";
|
|
1742
|
-
const [failed, setFailed] = (0,
|
|
1820
|
+
const [failed, setFailed] = (0, import_react10.useState)(false);
|
|
1743
1821
|
if (url && !failed) {
|
|
1744
1822
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
1745
1823
|
}
|
|
@@ -1747,7 +1825,7 @@ function Crest2({ url, name, kind }) {
|
|
|
1747
1825
|
}
|
|
1748
1826
|
|
|
1749
1827
|
// src/components/PinToggle.tsx
|
|
1750
|
-
var
|
|
1828
|
+
var import_react11 = require("react");
|
|
1751
1829
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1752
1830
|
var PIN_GLYPHS = {
|
|
1753
1831
|
pin: { viewBox: "0 0 24 24", strokeWidth: 1.4, transform: "scale(1.5)", d: "M9.5 1.5 14.5 6.5 13 8l-.5-.5-3 3 .3 2.2-1.4 1.4L5.6 11.3l-3.3 3.3-1-1 3.3-3.3L1.9 7.5l1.4-1.4 2.2.3 3-3L8 2.9z" },
|
|
@@ -1755,8 +1833,8 @@ var PIN_GLYPHS = {
|
|
|
1755
1833
|
bookmark: { viewBox: "0 0 24 24", strokeWidth: 2, d: "M7 3h10a1.5 1.5 0 0 1 1.5 1.5V21l-6.5-3.6L5.5 21V4.5A1.5 1.5 0 0 1 7 3z" }
|
|
1756
1834
|
};
|
|
1757
1835
|
function PinToggle({ pin, monitor, ui }) {
|
|
1758
|
-
const [toast, setToast] = (0,
|
|
1759
|
-
const dismiss = (0,
|
|
1836
|
+
const [toast, setToast] = (0, import_react11.useState)(null);
|
|
1837
|
+
const dismiss = (0, import_react11.useCallback)(() => setToast(null), []);
|
|
1760
1838
|
if (!monitor || !monitor.enabled) return null;
|
|
1761
1839
|
const key = pinKey(pin);
|
|
1762
1840
|
const pinned = monitor.isPinned(key);
|
|
@@ -1808,7 +1886,7 @@ function StatRows({ stats }) {
|
|
|
1808
1886
|
] }, `${s.label}:${s.value}`)) });
|
|
1809
1887
|
}
|
|
1810
1888
|
function ScopedStats({ scopes }) {
|
|
1811
|
-
const [active, setActive] = (0,
|
|
1889
|
+
const [active, setActive] = (0, import_react12.useState)(0);
|
|
1812
1890
|
const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
|
|
1813
1891
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ss-w-bi-scoped", children: [
|
|
1814
1892
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
|
|
@@ -1819,7 +1897,7 @@ function hasStatsContent(it) {
|
|
|
1819
1897
|
return !!it.scopes && it.scopes.length >= 2 || it.stats.length > 0;
|
|
1820
1898
|
}
|
|
1821
1899
|
function ReasonStrip({ it, ui }) {
|
|
1822
|
-
const [open, setOpen] = (0,
|
|
1900
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
1823
1901
|
const hasScopes = !!it.scopes && it.scopes.length >= 2;
|
|
1824
1902
|
const hasStats = hasStatsContent(it);
|
|
1825
1903
|
const hasText = !!it.text;
|
|
@@ -1929,7 +2007,7 @@ function BettingInsightList({ insights, ui, slip, monitor }) {
|
|
|
1929
2007
|
}
|
|
1930
2008
|
|
|
1931
2009
|
// src/components/MarketOddsCardList.tsx
|
|
1932
|
-
var
|
|
2010
|
+
var import_react13 = require("react");
|
|
1933
2011
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1934
2012
|
function OddsRow({ vm, row, ui, slip, inSlipIds }) {
|
|
1935
2013
|
const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
@@ -1946,7 +2024,7 @@ function OddsRow({ vm, row, ui, slip, inSlipIds }) {
|
|
|
1946
2024
|
] });
|
|
1947
2025
|
}
|
|
1948
2026
|
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
1949
|
-
const [open, setOpen] = (0,
|
|
2027
|
+
const [open, setOpen] = (0, import_react13.useState)(false);
|
|
1950
2028
|
const kickoff = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
|
|
1951
2029
|
const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
|
|
1952
2030
|
const showPin = !!monitor && vm.pinEnabled;
|
|
@@ -2241,7 +2319,7 @@ function FixtureCard({ card }) {
|
|
|
2241
2319
|
}
|
|
2242
2320
|
|
|
2243
2321
|
// src/components/dashboard/CountdownCard.tsx
|
|
2244
|
-
var
|
|
2322
|
+
var import_react14 = require("react");
|
|
2245
2323
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
2246
2324
|
var DAY_MS = 864e5;
|
|
2247
2325
|
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
@@ -2260,12 +2338,12 @@ function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
|
2260
2338
|
function CountdownCard({ card }) {
|
|
2261
2339
|
const { data } = card;
|
|
2262
2340
|
const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
|
|
2263
|
-
const [nowMs, setNowMs] = (0,
|
|
2264
|
-
const [reduced] = (0,
|
|
2341
|
+
const [nowMs, setNowMs] = (0, import_react14.useState)(() => Date.now());
|
|
2342
|
+
const [reduced] = (0, import_react14.useState)(
|
|
2265
2343
|
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
2266
2344
|
);
|
|
2267
2345
|
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
2268
|
-
(0,
|
|
2346
|
+
(0, import_react14.useEffect)(() => {
|
|
2269
2347
|
if (reduced) return;
|
|
2270
2348
|
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
2271
2349
|
return () => window.clearInterval(id);
|
|
@@ -2273,7 +2351,7 @@ function CountdownCard({ card }) {
|
|
|
2273
2351
|
const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
|
|
2274
2352
|
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "ss-w-fandash-countdown-top", children: [
|
|
2275
2353
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
|
|
2276
|
-
segments ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2354
|
+
segments ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react14.Fragment, { children: [
|
|
2277
2355
|
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
|
|
2278
2356
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ss-w-fandash-countdown-seg", children: [
|
|
2279
2357
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
|
|
@@ -2284,7 +2362,7 @@ function CountdownCard({ card }) {
|
|
|
2284
2362
|
}
|
|
2285
2363
|
|
|
2286
2364
|
// src/components/dashboard/InjuriesCard.tsx
|
|
2287
|
-
var
|
|
2365
|
+
var import_react15 = require("react");
|
|
2288
2366
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2289
2367
|
var SHOWN = 3;
|
|
2290
2368
|
function ColHead({ name, crestUrl }) {
|
|
@@ -2307,7 +2385,7 @@ function InjuryList({ rows, emptyLabel, limit }) {
|
|
|
2307
2385
|
}
|
|
2308
2386
|
function InjuriesCard({ card, ui }) {
|
|
2309
2387
|
const { data } = card;
|
|
2310
|
-
const [expanded, setExpanded] = (0,
|
|
2388
|
+
const [expanded, setExpanded] = (0, import_react15.useState)(false);
|
|
2311
2389
|
const hidden = Math.max(0, data.ours.length - SHOWN) + Math.max(0, data.theirs.length - SHOWN);
|
|
2312
2390
|
const limit = expanded ? void 0 : SHOWN;
|
|
2313
2391
|
const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
|
|
@@ -2411,7 +2489,7 @@ function LinksCard({ card }) {
|
|
|
2411
2489
|
}
|
|
2412
2490
|
|
|
2413
2491
|
// src/components/dashboard/PostMatchResultCard.tsx
|
|
2414
|
-
var
|
|
2492
|
+
var import_react16 = require("react");
|
|
2415
2493
|
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
2416
2494
|
function initials3(name) {
|
|
2417
2495
|
return name.trim().slice(0, 3).toUpperCase();
|
|
@@ -2421,7 +2499,7 @@ function PostMatchCrest({
|
|
|
2421
2499
|
name,
|
|
2422
2500
|
side
|
|
2423
2501
|
}) {
|
|
2424
|
-
const [failed, setFailed] = (0,
|
|
2502
|
+
const [failed, setFailed] = (0, import_react16.useState)(false);
|
|
2425
2503
|
const cls = `ss-w-fandash-postmatch-crest ss-w-fandash-postmatch-crest--${side}`;
|
|
2426
2504
|
if (url && !failed) {
|
|
2427
2505
|
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
@@ -2620,10 +2698,10 @@ function RestingFormCard({ card }) {
|
|
|
2620
2698
|
}
|
|
2621
2699
|
|
|
2622
2700
|
// src/components/dashboard/LiveScoreCard.tsx
|
|
2623
|
-
var
|
|
2701
|
+
var import_react18 = require("react");
|
|
2624
2702
|
|
|
2625
2703
|
// src/useLiveClock.ts
|
|
2626
|
-
var
|
|
2704
|
+
var import_react17 = require("react");
|
|
2627
2705
|
var TICK_MS = 6e4;
|
|
2628
2706
|
var MAX_LIVE_TICK_MINUTES = 15;
|
|
2629
2707
|
function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
@@ -2632,10 +2710,10 @@ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
|
2632
2710
|
return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
|
|
2633
2711
|
}
|
|
2634
2712
|
function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
2635
|
-
const [displayMinute, setDisplayMinute] = (0,
|
|
2713
|
+
const [displayMinute, setDisplayMinute] = (0, import_react17.useState)(
|
|
2636
2714
|
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
2637
2715
|
);
|
|
2638
|
-
(0,
|
|
2716
|
+
(0, import_react17.useEffect)(() => {
|
|
2639
2717
|
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
2640
2718
|
if (!clockRunning) return;
|
|
2641
2719
|
const id = window.setInterval(() => {
|
|
@@ -2652,7 +2730,7 @@ function initials5(name) {
|
|
|
2652
2730
|
return name.trim().slice(0, 3).toUpperCase();
|
|
2653
2731
|
}
|
|
2654
2732
|
function LiveCrest({ url, name, side }) {
|
|
2655
|
-
const [failed, setFailed] = (0,
|
|
2733
|
+
const [failed, setFailed] = (0, import_react18.useState)(false);
|
|
2656
2734
|
const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
|
|
2657
2735
|
if (url && !failed) {
|
|
2658
2736
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
@@ -2671,7 +2749,7 @@ function LiveScoreCard({
|
|
|
2671
2749
|
}) {
|
|
2672
2750
|
const { data } = card;
|
|
2673
2751
|
const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
|
|
2674
|
-
const seedRef = (0,
|
|
2752
|
+
const seedRef = (0, import_react18.useRef)(null);
|
|
2675
2753
|
if (!seedRef.current || seedRef.current.key !== seedKey) {
|
|
2676
2754
|
seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
|
|
2677
2755
|
}
|
|
@@ -2711,7 +2789,7 @@ function LiveScoreCard({
|
|
|
2711
2789
|
}
|
|
2712
2790
|
|
|
2713
2791
|
// src/components/dashboard/LiveTimelineCard.tsx
|
|
2714
|
-
var
|
|
2792
|
+
var import_react19 = require("react");
|
|
2715
2793
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2716
2794
|
var SHOWN2 = 3;
|
|
2717
2795
|
function EventIcon({ type }) {
|
|
@@ -2753,7 +2831,7 @@ function EventRow({ e }) {
|
|
|
2753
2831
|
] });
|
|
2754
2832
|
}
|
|
2755
2833
|
function LiveTimelineCard({ card, ui }) {
|
|
2756
|
-
const [expanded, setExpanded] = (0,
|
|
2834
|
+
const [expanded, setExpanded] = (0, import_react19.useState)(false);
|
|
2757
2835
|
const events = card.data.events;
|
|
2758
2836
|
if (!events || events.length === 0) return null;
|
|
2759
2837
|
const sorted = [...events].sort((a, b) => b.minute - a.minute);
|
|
@@ -2778,7 +2856,7 @@ function LiveTimelineCard({ card, ui }) {
|
|
|
2778
2856
|
}
|
|
2779
2857
|
|
|
2780
2858
|
// src/components/dashboard/MomentumWave.tsx
|
|
2781
|
-
var
|
|
2859
|
+
var import_react20 = require("react");
|
|
2782
2860
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2783
2861
|
var WIDTH = 270;
|
|
2784
2862
|
var HEIGHT = 56;
|
|
@@ -2802,7 +2880,7 @@ function MomentumWave({
|
|
|
2802
2880
|
}) {
|
|
2803
2881
|
if (points.length === 0) return null;
|
|
2804
2882
|
const { area, line, lastLeftPct, lastTopPx } = buildPaths(points);
|
|
2805
|
-
const clipId = (0,
|
|
2883
|
+
const clipId = (0, import_react20.useId)();
|
|
2806
2884
|
const topClipId = `${clipId}-top`;
|
|
2807
2885
|
const bottomClipId = `${clipId}-bottom`;
|
|
2808
2886
|
const topClass = clubIsHome ? "ss-w-fandash-live-mom-home" : "ss-w-fandash-live-mom-away";
|
|
@@ -2993,11 +3071,11 @@ function turnAnchor(messages) {
|
|
|
2993
3071
|
}
|
|
2994
3072
|
function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
2995
3073
|
const { state, send } = controller;
|
|
2996
|
-
const listRef = (0,
|
|
2997
|
-
const endRef = (0,
|
|
2998
|
-
const pinnedRef = (0,
|
|
2999
|
-
const anchoredTurnRef = (0,
|
|
3000
|
-
(0,
|
|
3074
|
+
const listRef = (0, import_react21.useRef)(null);
|
|
3075
|
+
const endRef = (0, import_react21.useRef)(null);
|
|
3076
|
+
const pinnedRef = (0, import_react21.useRef)(true);
|
|
3077
|
+
const anchoredTurnRef = (0, import_react21.useRef)(null);
|
|
3078
|
+
(0, import_react21.useEffect)(() => {
|
|
3001
3079
|
const anchor = turnAnchor(state.messages);
|
|
3002
3080
|
const list = listRef.current;
|
|
3003
3081
|
if (anchor && anchor.turnId !== anchoredTurnRef.current) {
|
|
@@ -3053,10 +3131,10 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3053
3131
|
}
|
|
3054
3132
|
|
|
3055
3133
|
// src/components/ChatInput.tsx
|
|
3056
|
-
var
|
|
3134
|
+
var import_react22 = require("react");
|
|
3057
3135
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
3058
3136
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
3059
|
-
const [value, setValue] = (0,
|
|
3137
|
+
const [value, setValue] = (0, import_react22.useState)("");
|
|
3060
3138
|
const submit = () => {
|
|
3061
3139
|
const t = value.trim();
|
|
3062
3140
|
if (!t) return;
|
|
@@ -3363,8 +3441,8 @@ function OddsPanel({ monitor, ui, slip }) {
|
|
|
3363
3441
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3364
3442
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
3365
3443
|
function useIsModal() {
|
|
3366
|
-
const [modal, setModal] = (0,
|
|
3367
|
-
(0,
|
|
3444
|
+
const [modal, setModal] = (0, import_react23.useState)(false);
|
|
3445
|
+
(0, import_react23.useEffect)(() => {
|
|
3368
3446
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
3369
3447
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
3370
3448
|
const sync = () => setModal(mql.matches);
|
|
@@ -3377,7 +3455,7 @@ function useIsModal() {
|
|
|
3377
3455
|
function ChatPanel({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
3378
3456
|
const { state, close, stop, send, reset } = controller;
|
|
3379
3457
|
const isModal = useIsModal();
|
|
3380
|
-
(0,
|
|
3458
|
+
(0, import_react23.useEffect)(() => {
|
|
3381
3459
|
const onKey = (e) => {
|
|
3382
3460
|
if (e.key === "Escape") close();
|
|
3383
3461
|
};
|
|
@@ -3388,7 +3466,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3388
3466
|
monitor?.setActiveTab("chat");
|
|
3389
3467
|
send(text);
|
|
3390
3468
|
};
|
|
3391
|
-
(0,
|
|
3469
|
+
(0, import_react23.useEffect)(() => {
|
|
3392
3470
|
if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
|
|
3393
3471
|
}, [monitor, state.isStreaming, state.messages.length]);
|
|
3394
3472
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
@@ -3401,7 +3479,8 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3401
3479
|
headerTitle: ui.headerTitle,
|
|
3402
3480
|
onNewConversation: reset,
|
|
3403
3481
|
canReset: state.messages.length > 0,
|
|
3404
|
-
newConversationLabel: ui.newConversation
|
|
3482
|
+
newConversationLabel: ui.newConversation,
|
|
3483
|
+
moreOptionsLabel: ui.moreOptions
|
|
3405
3484
|
}
|
|
3406
3485
|
),
|
|
3407
3486
|
monitor && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
@@ -3519,8 +3598,13 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
3519
3598
|
.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }
|
|
3520
3599
|
.ss-w-header-mark { width: 18px; height: 18px; display: block; object-fit: contain; }
|
|
3521
3600
|
.ss-w-header-title { font-weight: 660; flex: 1; }
|
|
3522
|
-
.ss-w-newconv { background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
|
|
3523
3601
|
.ss-w-close { background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
|
|
3602
|
+
.ss-w-menu-wrap { position: relative; display: grid; place-items: center; }
|
|
3603
|
+
.ss-w-kebab { background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
|
|
3604
|
+
.ss-w-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 5; min-width: 184px; padding: 6px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,.18); }
|
|
3605
|
+
.ss-w-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; background: none; border: none; border-radius: 8px; color: var(--ss-w-ink); font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
|
|
3606
|
+
.ss-w-menu-item:hover, .ss-w-menu-item:focus-visible { background: var(--ss-w-bubble); }
|
|
3607
|
+
.ss-w-menu-item svg { color: var(--ss-w-faint); font-size: 15px; flex: none; }
|
|
3524
3608
|
|
|
3525
3609
|
/* messages */
|
|
3526
3610
|
/* .ss-w-mon-list (Matches/Odds tab scrollers) shares .ss-w-list's box/scroll rules by design --
|
|
@@ -4260,11 +4344,11 @@ function injectStyles(root) {
|
|
|
4260
4344
|
function StatsWidget(props) {
|
|
4261
4345
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
4262
4346
|
const cfg = props.config;
|
|
4263
|
-
const built = (0,
|
|
4264
|
-
const ui = (0,
|
|
4265
|
-
const rootRef = (0,
|
|
4266
|
-
const introDoneRef = (0,
|
|
4267
|
-
const markIntroDone = (0,
|
|
4347
|
+
const built = (0, import_react24.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
4348
|
+
const ui = (0, import_react24.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
4349
|
+
const rootRef = (0, import_react24.useRef)(null);
|
|
4350
|
+
const introDoneRef = (0, import_react24.useRef)(false);
|
|
4351
|
+
const markIntroDone = (0, import_react24.useCallback)(() => {
|
|
4268
4352
|
introDoneRef.current = true;
|
|
4269
4353
|
}, []);
|
|
4270
4354
|
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
@@ -4275,7 +4359,7 @@ function StatsWidget(props) {
|
|
|
4275
4359
|
storageKey: `ss-w-pins:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`,
|
|
4276
4360
|
isOpen: controller.state.isOpen
|
|
4277
4361
|
});
|
|
4278
|
-
const slip = (0,
|
|
4362
|
+
const slip = (0, import_react24.useMemo)(
|
|
4279
4363
|
() => {
|
|
4280
4364
|
const handler = props.config?.onAddToSlip;
|
|
4281
4365
|
return {
|
|
@@ -4298,11 +4382,11 @@ function StatsWidget(props) {
|
|
|
4298
4382
|
},
|
|
4299
4383
|
[props.config?.onAddToSlip, controller.state.conversationId, built, monitor.markInSlip]
|
|
4300
4384
|
);
|
|
4301
|
-
const onDashboardTap = (0,
|
|
4385
|
+
const onDashboardTap = (0, import_react24.useMemo)(
|
|
4302
4386
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
4303
4387
|
[built]
|
|
4304
4388
|
);
|
|
4305
|
-
(0,
|
|
4389
|
+
(0, import_react24.useEffect)(() => {
|
|
4306
4390
|
if (!built || !ui.injectStyles) return;
|
|
4307
4391
|
const node = rootRef.current?.getRootNode();
|
|
4308
4392
|
if (node) injectStyles(node);
|