@sensiblestats/widget-react 0.73.0 → 0.74.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.74.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Set the numbers: a fifth row, "BTTS picks today", with a market dropdown (BTTS, Over 2.5, Under 2.5, Double chance, Match winner), an "N ready" pill and the round send. It shows only when the server's preset grid has a `markets` section.
8
+
9
+ ## 0.73.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Goal dashboard rows are compact cards: 60px tall with 8px corners, and a tile's badge shows as a price column on the right instead of a pill under the subtitle.
14
+
3
15
  ## 0.73.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -187,7 +187,11 @@ var STRINGS = {
187
187
  maxPriceName: "Max price",
188
188
  minPriceName: "Lowest price",
189
189
  notToday: " \xB7 not today",
190
- changeCombo: "Change total or matches"
190
+ changeCombo: "Change total or matches",
191
+ marketRow: "{market} picks today",
192
+ marketName: "Market",
193
+ marketShort: (dev, market, sel) => ({ BOTH_TEAMS_TO_SCORE: "BTTS", DOUBLE_CHANCE: "Double chance", FULLTIME_RESULT: "Match winner" })[dev] ?? (dev === "GOALS_OVER_UNDER" && sel ? sel : sel ? `${market} ${sel}` : market),
194
+ marketMessage: (market, sel, count) => `Show me ${count} ${sel ? `${market} \u2013 ${sel}` : market} picks.`
191
195
  },
192
196
  games: {
193
197
  lang: "en",
@@ -521,7 +525,11 @@ var STRINGS = {
521
525
  maxPriceName: "En y\xFCksek oran",
522
526
  minPriceName: "En d\xFC\u015F\xFCk oran",
523
527
  notToday: " \xB7 bug\xFCn yok",
524
- changeCombo: "Toplam\u0131 veya ma\xE7lar\u0131 de\u011Fi\u015Ftir"
528
+ changeCombo: "Toplam\u0131 veya ma\xE7lar\u0131 de\u011Fi\u015Ftir",
529
+ marketRow: "Bug\xFCnk\xFC {market} se\xE7imleri",
530
+ marketName: "Bahis t\xFCr\xFC",
531
+ marketShort: (dev, market, sel) => ({ BOTH_TEAMS_TO_SCORE: "KG Var", DOUBLE_CHANCE: "\xC7ifte \u015Fans", FULLTIME_RESULT: "Ma\xE7 sonucu" })[dev] ?? (dev === "GOALS_OVER_UNDER" && sel ? sel : sel ? `${market} ${sel}` : market),
532
+ marketMessage: (market, sel, count) => `${count} ${sel ? `${market} \u2013 ${sel}` : market} se\xE7imi g\xF6ster.`
525
533
  },
526
534
  games: {
527
535
  lang: "tr",
@@ -2977,6 +2985,16 @@ function comboAsk(cell, s) {
2977
2985
  preset: { kind: "combo", total: cell.total, legs: cell.legs, ...idsOf(cell.combo.legs) }
2978
2986
  };
2979
2987
  }
2988
+ function marketAsk(cell, s) {
2989
+ return {
2990
+ message: s.marketMessage(cell.marketLabel, cell.selectionLabel ?? null, cell.count),
2991
+ displayText: marketDisplay(cell, s),
2992
+ preset: { kind: "market", marketDeveloperName: cell.marketDeveloperName, ...idsOf(cell.picks) }
2993
+ };
2994
+ }
2995
+ function marketDisplay(cell, s) {
2996
+ return s.marketRow.replace("{market}", s.marketShort(cell.marketDeveloperName, cell.marketLabel, cell.selectionLabel ?? null));
2997
+ }
2980
2998
 
2981
2999
  // src/usePresetGrid.ts
2982
3000
  var GRID_MAX_AGE_MS = 6e5;
@@ -3024,7 +3042,7 @@ function usePresetGrid({ getPricePresets, isOpen, locale, now = Date.now }) {
3024
3042
  return grid;
3025
3043
  }
3026
3044
  function hasAnyCell(grid) {
3027
- return grid.singles.length > 0 || grid.singlesFrom.length > 0 || grid.combos.length > 0;
3045
+ return grid.singles.length > 0 || grid.singlesFrom.length > 0 || grid.combos.length > 0 || grid.markets !== void 0 && grid.markets.length > 0;
3028
3046
  }
3029
3047
 
3030
3048
  // src/liveClock.ts
@@ -4200,11 +4218,13 @@ function HeroTile({ tile, onTap, oddsLabel }) {
4200
4218
  ] })
4201
4219
  ] });
4202
4220
  }
4203
- function DashboardTile({ tile, onTap, oddsLabel = "Odds" }) {
4221
+ function DashboardTile({ tile, onTap, oddsLabel = "Odds", badgePlacement = "meta" }) {
4204
4222
  if (tile.emphasis === "hero") return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HeroTile, { tile, onTap, oddsLabel });
4205
4223
  const spanClass = tile.span === "full" ? "ss-w-dash-tile--full" : "ss-w-dash-tile--half";
4206
4224
  const crests = (tile.imageUrls ?? []).slice(0, MAX_CRESTS);
4207
- const hasMeta = Boolean(tile.badgeText) || crests.length > 0;
4225
+ const endBadge = badgePlacement === "end" && tile.badgeText ? tile.badgeText : null;
4226
+ const metaBadge = endBadge ? null : tile.badgeText;
4227
+ const hasMeta = Boolean(metaBadge) || crests.length > 0;
4208
4228
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { type: "button", className: `ss-w-dash-tile ${spanClass}`, onClick: onTap, children: [
4209
4229
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-icon", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DashboardIcon, { name: tile.icon }) }),
4210
4230
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ss-w-dash-tile-body", children: [
@@ -4215,12 +4235,12 @@ function DashboardTile({ tile, onTap, oddsLabel = "Odds" }) {
4215
4235
  // icon/chevron row -- at the half-span tile's ~180px width a nowrap badge alone
4216
4236
  // (~70px) left the title column at ~22px and forced character-by-character wrapping.
4217
4237
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ss-w-dash-tile-meta", children: [
4218
- tile.badgeText ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-badge", children: tile.badgeText }) : null,
4238
+ metaBadge ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-badge", children: metaBadge }) : null,
4219
4239
  crests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-crests", children: crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-crest", style: { backgroundImage: `url(${url})` }, "aria-hidden": "true" }, `${url}-${i}`)) }) : null
4220
4240
  ] })
4221
4241
  ) : null
4222
4242
  ] }),
4223
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-chevron", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChevronIcon, {}) })
4243
+ endBadge ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-value", children: endBadge }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-dash-tile-chevron", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChevronIcon, {}) })
4224
4244
  ] });
4225
4245
  }
4226
4246
 
@@ -4235,12 +4255,14 @@ function layoutClass(layout) {
4235
4255
  return Object.prototype.hasOwnProperty.call(LAYOUT_CLASSES, layout) ? LAYOUT_CLASSES[layout] : LAYOUT_CLASSES.goal;
4236
4256
  }
4237
4257
  function GreetingDashboard({ dashboard, onTap, oddsLabel }) {
4238
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `ss-w-dash ${layoutClass(dashboard.layout)}`, children: [
4258
+ const layout = layoutClass(dashboard.layout);
4259
+ const badgePlacement = layout === LAYOUT_CLASSES.goal ? "end" : "meta";
4260
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `ss-w-dash ${layout}`, children: [
4239
4261
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "ss-w-dash-head", children: [
4240
4262
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-dash-title", children: dashboard.title }),
4241
4263
  dashboard.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-dash-subtitle", children: dashboard.subtitle }) : null
4242
4264
  ] }),
4243
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ss-w-dash-grid", children: dashboard.tiles.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DashboardTile, { tile: t, onTap: () => onTap(t, i), oddsLabel }, `${t.title}-${i}`)) }),
4265
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ss-w-dash-grid", children: dashboard.tiles.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DashboardTile, { tile: t, onTap: () => onTap(t, i), oddsLabel, badgePlacement }, `${t.title}-${i}`)) }),
4244
4266
  dashboard.footerTip ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-dash-foot", children: dashboard.footerTip }) : null
4245
4267
  ] });
4246
4268
  }
@@ -8730,6 +8752,13 @@ function NumberSelect({ current, choices: choices2, label, notToday, lockEmpty =
8730
8752
  }, children: choices2.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("option", { value: String(i), disabled: lockEmpty && !c.available && i !== selected, children: c.available ? c.text : `${c.text}${notToday}` }, i)) })
8731
8753
  ] });
8732
8754
  }
8755
+ function ChoiceSelect({ currentIdx, sizeLabels, optionLabels, label, onChoose }) {
8756
+ const sizeText = sizeLabels[currentIdx] ?? "";
8757
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { className: "ss-w-tool-num", children: [
8758
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { className: "ss-w-tool-num-size", "aria-hidden": "true", children: sizeText }),
8759
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("select", { className: "ss-w-tool-num-select", "aria-label": label, value: String(currentIdx), onChange: (e) => onChoose(Number(e.target.value)), children: optionLabels.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("option", { value: String(i), children: opt }, i)) })
8760
+ ] });
8761
+ }
8733
8762
  function SendButton({ label, display, disabled, onClick }) {
8734
8763
  return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("button", { type: "button", className: "ss-w-tool-send", "aria-label": `${label}: ${display}`, disabled, onClick, children: label });
8735
8764
  }
@@ -8793,6 +8822,11 @@ function SetNumbersTool({ grid, strings, streaming, onSend, onClose }) {
8793
8822
  const comboCell = findCombo(grid, total, legs);
8794
8823
  const bigCell = findCombo(grid, bigTotal, bigLegs);
8795
8824
  const announce = (display, available) => setAnnouncement(available ? display : `${display}. ${strings.unavailable}`);
8825
+ const chooseMarket = (idx) => {
8826
+ setMarketIdx(idx);
8827
+ const cell = markets[idx];
8828
+ if (cell) announce(marketDisplay(cell, strings), cell.count > 0);
8829
+ };
8796
8830
  const chooseMax = (v) => {
8797
8831
  setMaxPrice(v);
8798
8832
  announce(strings.maxPriceDisplay(count, v), !!findSingles(grid.singles, v, count));
@@ -8825,6 +8859,10 @@ function SetNumbersTool({ grid, strings, streaming, onSend, onClose }) {
8825
8859
  const minChoices = floors.map((v) => ({ value: v, text: v.toFixed(2), available: !!findSingles(grid.singlesFrom, v, count) }));
8826
8860
  const totalChoices = totals.map((v) => ({ value: v, text: v.toFixed(1), available: legChoices.some((n) => !!findCombo(grid, v, n)) }));
8827
8861
  const legChoicesFor = (t) => legChoices.map((n) => ({ value: n, text: String(n), available: !!findCombo(grid, t, n) }));
8862
+ const markets = grid.markets ?? [];
8863
+ const [marketIdx, setMarketIdx] = (0, import_react51.useState)(0);
8864
+ const marketCell = markets[marketIdx] ?? null;
8865
+ const marketLabels = markets.map((c) => strings.marketShort(c.marketDeveloperName, c.marketLabel, c.selectionLabel ?? null));
8828
8866
  const singlesReady = (cell) => cell ? strings.readyCount(cell.picks.length) : strings.unavailable;
8829
8867
  const comboReady = (cell) => cell ? strings.readyAt(cell.combo.total) : strings.unavailable;
8830
8868
  return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(PresetToolModal, { title: strings.setNumbers, closeLabel: strings.close, onClose, children: [
@@ -8884,6 +8922,19 @@ function SetNumbersTool({ grid, strings, streaming, onSend, onClose }) {
8884
8922
  } })
8885
8923
  }
8886
8924
  ),
8925
+ markets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
8926
+ AskRow,
8927
+ {
8928
+ icon: "\u26BD",
8929
+ ask: fillTemplate(strings.marketRow, {
8930
+ market: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(ChoiceSelect, { currentIdx: marketIdx, sizeLabels: marketLabels, optionLabels: marketLabels, label: strings.marketName, onChoose: chooseMarket })
8931
+ }),
8932
+ ready: marketCell ? strings.readyCount(marketCell.count) : null,
8933
+ send: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(RoundSend, { label: strings.send, display: marketCell ? marketDisplay(marketCell, strings) : "", disabled: !marketCell || streaming, onClick: () => {
8934
+ if (marketCell) onSend(marketAsk(marketCell, strings));
8935
+ } })
8936
+ }
8937
+ ),
8887
8938
  /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
8888
8939
  ] });
8889
8940
  }
@@ -9873,9 +9924,21 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
9873
9924
  part of that visual language rather than a plain unstyled button. */
9874
9925
  .ss-w-dash--market .ss-w-dash-tile { border-left: 3px solid var(--ss-w-accent); }
9875
9926
 
9876
- /* Goal: one column of pill rows. */
9927
+ /* Goal: one column of compact cards, 60px tall with the badge as a price column on the right.
9928
+ The value ink is accent mixed toward ink (as on the hero price): plain light accent on the
9929
+ panel is 3.9:1, under the 4.5:1 small text needs. */
9877
9930
  .ss-w-dash--goal .ss-w-dash-grid { grid-template-columns: 1fr; }
9878
- .ss-w-dash--goal .ss-w-dash-tile { border-radius: 999px; padding: 10px 14px; }
9931
+ .ss-w-dash--goal .ss-w-dash-tile { gap: 12px; min-height: 60px; padding: 8px 12px 8px 14px; border-radius: 8px; }
9932
+ .ss-w-dash--goal .ss-w-dash-tile:hover { background: var(--ss-w-bubble); }
9933
+ .ss-w-dash--goal .ss-w-dash-tile-icon { width: 24px; height: 24px; font-size: 20px; }
9934
+ .ss-w-dash--goal .ss-w-dash-tile-body { gap: 1px; }
9935
+ .ss-w-dash--goal .ss-w-dash-tile-title { font-size: 15px; line-height: 1.25; }
9936
+ .ss-w-dash--goal .ss-w-dash-tile-subtitle { font-size: 13px; line-height: 1.3; overflow-wrap: break-word; }
9937
+ .ss-w-dash-tile-value {
9938
+ flex: none; margin-left: auto; max-width: 40%; text-align: right;
9939
+ font-size: 16px; font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums;
9940
+ color: color-mix(in srgb, var(--ss-w-accent) 70%, var(--ss-w-ink));
9941
+ }
9879
9942
 
9880
9943
  /* Discovery: tinted bento cards. Stacked (icon over text) so a two- or three-word title gets
9881
9944
  the tile's full width instead of the ~100px left of a horizontal icon+chevron -- that narrow