@sensiblestats/widget-react 0.56.0 → 0.58.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 +22 -0
- package/dist/index.cjs +77 -15
- 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 +77 -15
- package/dist/index.js.map +1 -1
- package/dist/styles.css +11 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1687,18 +1687,19 @@ function computeGameDay(timezone) {
|
|
|
1687
1687
|
return new Intl.DateTimeFormat("en-CA", { timeZone: timezone }).format(/* @__PURE__ */ new Date());
|
|
1688
1688
|
}
|
|
1689
1689
|
function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
1690
|
-
const [enabled, setEnabled] = useState4(
|
|
1690
|
+
const [enabled, setEnabled] = useState4(false);
|
|
1691
1691
|
const [games, setGames] = useState4([]);
|
|
1692
1692
|
const [loading, setLoading] = useState4(false);
|
|
1693
1693
|
const [refreshCount, setRefreshCount] = useState4(0);
|
|
1694
1694
|
const inFlightRef = useRef5(false);
|
|
1695
|
-
const
|
|
1695
|
+
const hardOffRef = useRef5(false);
|
|
1696
1696
|
const conversationRef = useRef5(conversation);
|
|
1697
1697
|
conversationRef.current = conversation;
|
|
1698
1698
|
const tzRef = useRef5(operatorTimezone ?? DEFAULT_TZ);
|
|
1699
1699
|
tzRef.current = operatorTimezone ?? DEFAULT_TZ;
|
|
1700
|
+
const ready = conversation != null;
|
|
1700
1701
|
useEffect5(() => {
|
|
1701
|
-
if (
|
|
1702
|
+
if (hardOffRef.current) return;
|
|
1702
1703
|
let active = true;
|
|
1703
1704
|
const doFetch = async () => {
|
|
1704
1705
|
const conv = conversationRef.current;
|
|
@@ -1710,10 +1711,12 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
|
1710
1711
|
const result = await conv.getGames(gameDay);
|
|
1711
1712
|
if (!active) return;
|
|
1712
1713
|
if (result === null) {
|
|
1713
|
-
|
|
1714
|
+
hardOffRef.current = true;
|
|
1714
1715
|
setEnabled(false);
|
|
1716
|
+
setGames([]);
|
|
1715
1717
|
} else {
|
|
1716
1718
|
setGames(result.games);
|
|
1719
|
+
setEnabled(result.games.length > 0);
|
|
1717
1720
|
}
|
|
1718
1721
|
} catch {
|
|
1719
1722
|
} finally {
|
|
@@ -1725,7 +1728,7 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
|
1725
1728
|
let intervalId;
|
|
1726
1729
|
if (activeTab === "games") {
|
|
1727
1730
|
intervalId = window.setInterval(() => {
|
|
1728
|
-
if (
|
|
1731
|
+
if (hardOffRef.current) {
|
|
1729
1732
|
window.clearInterval(intervalId);
|
|
1730
1733
|
return;
|
|
1731
1734
|
}
|
|
@@ -1734,9 +1737,10 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
|
1734
1737
|
}
|
|
1735
1738
|
return () => {
|
|
1736
1739
|
active = false;
|
|
1740
|
+
inFlightRef.current = false;
|
|
1737
1741
|
if (intervalId !== void 0) window.clearInterval(intervalId);
|
|
1738
1742
|
};
|
|
1739
|
-
}, [activeTab, refreshCount]);
|
|
1743
|
+
}, [activeTab, refreshCount, ready]);
|
|
1740
1744
|
const refresh = useCallback4(() => {
|
|
1741
1745
|
setRefreshCount((c) => c + 1);
|
|
1742
1746
|
}, []);
|
|
@@ -4933,6 +4937,47 @@ function toRows(positions) {
|
|
|
4933
4937
|
});
|
|
4934
4938
|
return [...byRow.keys()].sort((a, b) => b - a).map((k) => byRow.get(k));
|
|
4935
4939
|
}
|
|
4940
|
+
function formationLabel(rows) {
|
|
4941
|
+
if (rows.length < 2) return "";
|
|
4942
|
+
const gkFirst = [...rows].reverse();
|
|
4943
|
+
return gkFirst.slice(1).map((r) => r.length).join("-");
|
|
4944
|
+
}
|
|
4945
|
+
var FORMATION_POSITIONS = {
|
|
4946
|
+
"4-4-2": [["GK"], ["LB", "CB", "CB", "RB"], ["LM", "CM", "CM", "RM"], ["ST", "ST"]],
|
|
4947
|
+
"4-3-3": [["GK"], ["LB", "CB", "CB", "RB"], ["CM", "CM", "CM"], ["LW", "ST", "RW"]],
|
|
4948
|
+
"4-2-3-1": [["GK"], ["LB", "CB", "CB", "RB"], ["CDM", "CDM"], ["LW", "CAM", "RW"], ["ST"]],
|
|
4949
|
+
"4-1-4-1": [["GK"], ["LB", "CB", "CB", "RB"], ["CDM"], ["LM", "CM", "CM", "RM"], ["ST"]],
|
|
4950
|
+
"4-5-1": [["GK"], ["LB", "CB", "CB", "RB"], ["LM", "CM", "CM", "CM", "RM"], ["ST"]],
|
|
4951
|
+
"4-4-1-1": [["GK"], ["LB", "CB", "CB", "RB"], ["LM", "CM", "CM", "RM"], ["CAM"], ["ST"]],
|
|
4952
|
+
"4-1-2-1-2": [["GK"], ["LB", "CB", "CB", "RB"], ["CDM"], ["LM", "RM"], ["CAM"], ["ST", "ST"]],
|
|
4953
|
+
"3-5-2": [["GK"], ["CB", "CB", "CB"], ["LWB", "CM", "CM", "CM", "RWB"], ["ST", "ST"]],
|
|
4954
|
+
"3-4-3": [["GK"], ["CB", "CB", "CB"], ["LM", "CM", "CM", "RM"], ["LW", "ST", "RW"]],
|
|
4955
|
+
"5-3-2": [["GK"], ["LWB", "CB", "CB", "CB", "RWB"], ["CM", "CM", "CM"], ["ST", "ST"]],
|
|
4956
|
+
"5-4-1": [["GK"], ["LWB", "CB", "CB", "CB", "RWB"], ["LM", "CM", "CM", "RM"], ["ST"]]
|
|
4957
|
+
};
|
|
4958
|
+
function lineFallback(lineIdx, totalLines) {
|
|
4959
|
+
if (lineIdx === 0) return "GK";
|
|
4960
|
+
if (lineIdx === totalLines - 1) return "FWD";
|
|
4961
|
+
if (lineIdx === 1) return "DEF";
|
|
4962
|
+
return "MID";
|
|
4963
|
+
}
|
|
4964
|
+
function positionsBySlot(formation, rowsAttackFirst) {
|
|
4965
|
+
const gkFirst = [...rowsAttackFirst].reverse();
|
|
4966
|
+
const template = FORMATION_POSITIONS[formation];
|
|
4967
|
+
const map = /* @__PURE__ */ new Map();
|
|
4968
|
+
gkFirst.forEach((line, li) => {
|
|
4969
|
+
line.forEach((entry, ci) => {
|
|
4970
|
+
if (!entry.slot) return;
|
|
4971
|
+
const label = template?.[li]?.[ci] ?? lineFallback(li, gkFirst.length);
|
|
4972
|
+
map.set(entry.slot, label);
|
|
4973
|
+
});
|
|
4974
|
+
});
|
|
4975
|
+
return map;
|
|
4976
|
+
}
|
|
4977
|
+
function surname(name) {
|
|
4978
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
4979
|
+
return parts.length ? parts[parts.length - 1] : name;
|
|
4980
|
+
}
|
|
4936
4981
|
function GuessTheXiGame({
|
|
4937
4982
|
config,
|
|
4938
4983
|
result,
|
|
@@ -4946,9 +4991,11 @@ function GuessTheXiGame({
|
|
|
4946
4991
|
const solved = clientState?.status === "solved";
|
|
4947
4992
|
const guessedTeamIds = new Set(clientState?.guessHistory.map((g) => g.teamId) ?? []);
|
|
4948
4993
|
const rows = config ? toRows(config.positions) : [];
|
|
4949
|
-
const
|
|
4994
|
+
const formation = config?.hasRealFormation ? formationLabel(rows) : "";
|
|
4995
|
+
const posBySlot = config?.hasRealFormation ? positionsBySlot(formation, rows) : /* @__PURE__ */ new Map();
|
|
4996
|
+
const nameBySlot = /* @__PURE__ */ new Map();
|
|
4950
4997
|
(clientState?.earnedReveals ?? []).forEach((r) => {
|
|
4951
|
-
if (r.slot)
|
|
4998
|
+
if (r.slot && r.type !== "position") nameBySlot.set(r.slot, r.value);
|
|
4952
4999
|
});
|
|
4953
5000
|
const filtered = config && !isOver ? config.teamPool.filter(
|
|
4954
5001
|
(t) => !guessedTeamIds.has(t.teamId) && t.teamName.toLowerCase().includes(query.toLowerCase())
|
|
@@ -4961,13 +5008,16 @@ function GuessTheXiGame({
|
|
|
4961
5008
|
onSubmit(JSON.stringify({ guesses: newGuesses }));
|
|
4962
5009
|
}
|
|
4963
5010
|
const wrongGuesses = (clientState?.guessHistory ?? []).filter((g) => !g.wasCorrect);
|
|
4964
|
-
const revealCount =
|
|
5011
|
+
const revealCount = nameBySlot.size;
|
|
4965
5012
|
return /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi", "data-testid": "game-screen-guess-the-xi", children: [
|
|
4966
5013
|
!config && /* @__PURE__ */ jsx59("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
|
|
4967
5014
|
config && /* @__PURE__ */ jsxs53(Fragment14, { children: [
|
|
4968
5015
|
/* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-head", children: [
|
|
4969
5016
|
/* @__PURE__ */ jsxs53("div", { children: [
|
|
4970
|
-
/* @__PURE__ */
|
|
5017
|
+
/* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-title", children: [
|
|
5018
|
+
"Guess the XI",
|
|
5019
|
+
formation && /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-form", "aria-label": `Formation ${formation}`, children: formation })
|
|
5020
|
+
] }),
|
|
4971
5021
|
/* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-sub", children: [
|
|
4972
5022
|
"Name the team \xB7 flags are each player's nationality \xB7 ",
|
|
4973
5023
|
config.poolLabel
|
|
@@ -4980,18 +5030,20 @@ function GuessTheXiGame({
|
|
|
4980
5030
|
] }),
|
|
4981
5031
|
/* @__PURE__ */ jsxs53("div", { className: "ss-w-gscroll", children: [
|
|
4982
5032
|
rows.length > 0 && /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-pitch", children: rows.map((row, r) => /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-prow", children: row.map((pos, c) => {
|
|
4983
|
-
const
|
|
5033
|
+
const posLabel = pos.slot ? posBySlot.get(pos.slot) ?? "" : "";
|
|
5034
|
+
const name = pos.slot ? nameBySlot.get(pos.slot) : void 0;
|
|
4984
5035
|
return /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-slot", children: [
|
|
4985
5036
|
/* @__PURE__ */ jsx59(
|
|
4986
5037
|
"div",
|
|
4987
5038
|
{
|
|
4988
|
-
className: `ss-w-gxi-flag${
|
|
5039
|
+
className: `ss-w-gxi-flag${name ? " ss-w-gxi-flag--revealed" : ""}`,
|
|
4989
5040
|
title: pos.nationality,
|
|
4990
|
-
"aria-label":
|
|
5041
|
+
"aria-label": `${posLabel ? posLabel + " \u2014 " : ""}${pos.nationality}${name ? " \u2014 " + name : ""}`,
|
|
4991
5042
|
children: nationalityFlag(pos.nationality)
|
|
4992
5043
|
}
|
|
4993
5044
|
),
|
|
4994
|
-
|
|
5045
|
+
posLabel && /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-pos", children: posLabel }),
|
|
5046
|
+
name && /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-flag-lab", children: surname(name) })
|
|
4995
5047
|
] }, `${pos.slot}-${c}`);
|
|
4996
5048
|
}) }, r)) }),
|
|
4997
5049
|
revealCount > 0 && !isOver && /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-revnote", children: [
|
|
@@ -6935,6 +6987,10 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
6935
6987
|
.ss-w-gscreen { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 0; }
|
|
6936
6988
|
.ss-w-gscreen > .ss-w-gxi, .ss-w-gscreen > .ss-w-gt, .ss-w-gscreen > .ss-w-cpath,
|
|
6937
6989
|
.ss-w-gscreen > .ss-w-btg, .ss-w-gscreen > .ss-w-hl { flex: 1; min-height: 0; }
|
|
6990
|
+
/* Daily Quiz has no inner .ss-w-gscroll region \u2014 its whole body (score, questions, submit) is the
|
|
6991
|
+
scroller, so it takes the fill AND the overflow here. Without this it was clipped by the
|
|
6992
|
+
overflow:hidden frame and the lower questions + Submit could not be reached. */
|
|
6993
|
+
.ss-w-gscreen > .ss-w-dq { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
|
|
6938
6994
|
/* Scrollable board + guess-history region. */
|
|
6939
6995
|
.ss-w-gscroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 10px; padding-bottom: 2px; }
|
|
6940
6996
|
/* Bottom-pinned bar (search input / action). */
|
|
@@ -7152,7 +7208,9 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
7152
7208
|
.ss-w-gxi { display: flex; flex-direction: column; gap: 12px; }
|
|
7153
7209
|
/* Header */
|
|
7154
7210
|
.ss-w-gxi-head { display: flex; align-items: flex-start; gap: 8px; }
|
|
7155
|
-
.ss-w-gxi-title { font-size: 18px; font-weight: 800; text-transform: uppercase; letter-spacing: .02em; color: var(--ss-w-ink); line-height: 1; }
|
|
7211
|
+
.ss-w-gxi-title { font-size: 18px; font-weight: 800; text-transform: uppercase; letter-spacing: .02em; color: var(--ss-w-ink); line-height: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
7212
|
+
/* Formation pill next to the title, e.g. "4-2-3-1". */
|
|
7213
|
+
.ss-w-gxi-form { font-size: 12px; font-weight: 800; letter-spacing: .06em; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 26%, var(--ss-w-line)); border-radius: 999px; padding: 2px 9px; font-variant-numeric: tabular-nums; }
|
|
7156
7214
|
.ss-w-gxi-sub { font-size: 11px; color: var(--ss-w-faint); margin-top: 4px; }
|
|
7157
7215
|
.ss-w-gxi-left { margin-left: auto; flex: none; font-size: 11px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 11%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 22%, var(--ss-w-line)); border-radius: 999px; padding: 4px 10px; white-space: nowrap; }
|
|
7158
7216
|
/* Pitch: a flag per slot laid out in formation rows on a green field. A real min-height plus
|
|
@@ -7171,6 +7229,10 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
7171
7229
|
.ss-w-gxi-flag { width: 32px; height: 32px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 17px; line-height: 1; background: rgba(9, 12, 10, .55); border: 1px solid rgba(255,255,255,.16); box-shadow: 0 1px 3px rgba(0,0,0,.3); overflow: hidden; }
|
|
7172
7230
|
/* A slot whose player has been revealed by a wrong guess. */
|
|
7173
7231
|
.ss-w-gxi-flag--revealed { border-color: var(--ss-w-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ss-w-accent) 45%, transparent); }
|
|
7232
|
+
/* Position label shown under every flag (GK, CB, CM, ST\u2026) \u2014 reads the formation and marks the
|
|
7233
|
+
keeper's end. Muted so it sits behind the flag, not competing with it. */
|
|
7234
|
+
.ss-w-gxi-pos { font-size: 8.5px; line-height: 1.05; font-weight: 800; letter-spacing: .04em; text-align: center; color: rgba(255,255,255,.82); text-shadow: 0 1px 2px rgba(0,0,0,.5); }
|
|
7235
|
+
/* Player surname, shown under the position once a wrong guess reveals it \u2014 in the accent colour. */
|
|
7174
7236
|
.ss-w-gxi-flag-lab { font-size: 8px; line-height: 1.05; font-weight: 700; text-align: center; color: var(--ss-w-accent); max-width: 46px; word-break: break-word; }
|
|
7175
7237
|
.ss-w-gxi-revnote { font-size: 11px; color: var(--ss-w-faint); }
|
|
7176
7238
|
/* Wrong-guess trail */
|