fansunited-frontend-components 0.0.49 → 0.0.52
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/Betslip.d.ts +5 -0
- package/Betslip.d.ts.map +1 -0
- package/Betslip.js +5 -0
- package/ChanceGame.js +87 -86
- package/ClassicQuizPlay.js +174 -173
- package/CollectLead.js +33 -32
- package/Discussion.js +1 -1
- package/EitherOrPlay.js +171 -170
- package/EventGamePlay.js +25 -24
- package/Leaderboard.js +88 -87
- package/MatchQuizPlay.js +28 -27
- package/PersonalityQuizPlay.js +21 -20
- package/PickThePair.js +87 -86
- package/PollVote.js +19 -18
- package/Predictor.js +4756 -4693
- package/README.md +523 -1
- package/chunks/{AlertMessage-Ds45MBHB.js → AlertMessage-CQ9WJHSb.js} +25 -24
- package/chunks/{ArrowRightAlt-DKADQN2o.js → ArrowRightAlt-C1U8SNu-.js} +5 -5
- package/chunks/{Avatar-K1EWhhkA.js → Avatar-COVJnfGS.js} +79 -78
- package/chunks/Button-COvmPv4P.js +2270 -0
- package/chunks/{ChanceGameEmbedVariant-DqFg4YKU.js → ChanceGameEmbedVariant-lfZlSATv.js} +44 -43
- package/chunks/{CheckCircle-CV9iMpkZ.js → CheckCircle-h7-xLUq-.js} +27 -26
- package/chunks/{Close-C6Cw96SJ.js → Close-BTC6xyyj.js} +1 -1
- package/chunks/{Close-BHp4N1-6.js → Close-DBaorsP1.js} +1 -1
- package/chunks/{CollectLeadForm-DnLKA8qz.js → CollectLeadForm-O7Cpe0ix.js} +128 -127
- package/chunks/{Divider-BvUimRFw.js → Divider-DBCeQt9x.js} +15 -14
- package/chunks/{Edit-CZ3ni-Ur.js → Edit-BwwxJdNv.js} +35 -34
- package/chunks/{EmojiEvents-DpmLq98V.js → EmojiEvents-DVPGlTU_.js} +3 -3
- package/chunks/{FavoriteBorder-DUK_DrY4.js → FavoriteBorder-BnhfbKy1.js} +1 -1
- package/chunks/{FormControl-BUItlVIg.js → FormControl-COjw1gQd.js} +38 -37
- package/chunks/{FormLabel-CeTNPUn7.js → FormLabel-e5_v5cjP.js} +18 -17
- package/chunks/{Input-MJCQcYzo.js → Input-er-Yyqtt.js} +17 -16
- package/chunks/{KeyboardArrowDown-SZpn2GhJ.js → KeyboardArrowDown-BeaYH92n.js} +1 -1
- package/chunks/{ModalDialog-Cfgv9UAA.js → ModalDialog-XkhZtgYm.js} +58 -57
- package/chunks/{NotFoundSkeleton-Cs6DtD9u.js → NotFoundSkeleton-C71TRYl5.js} +95 -94
- package/chunks/{Notification-pcKtlRms.js → Notification-CkPupg9q.js} +57 -56
- package/chunks/{OverlayApiErrorSkeleton-Dl3s5Oaf.js → OverlayApiErrorSkeleton-CkuDu9uV.js} +20 -19
- package/chunks/{OverlayLeadAfterCollection-F2TAV9wK.js → OverlayLeadAfterCollection-Dkxl4cKn.js} +28 -27
- package/chunks/{OverlayScoreStateSkeleton-LQ0svzeX.js → OverlayScoreStateSkeleton-RauDjIg5.js} +16 -15
- package/chunks/{Person-BGdniS_p.js → Person-WCt2MN5j.js} +32 -31
- package/chunks/{PickOneOfX-Ci4ZJf7G.js → PickOneOfX-DabfTR2W.js} +8 -7
- package/chunks/{Popper-DOVSI4rJ.js → Popper-OPgbZzY7.js} +127 -126
- package/chunks/{Portal-Cs0dgwmH.js → Portal-BQilulgu.js} +5 -5
- package/chunks/{PrizeCard-DF3QxwQJ.js → PrizeCard-DgHyperX.js} +5238 -6653
- package/chunks/{Select-BHeTdOYr.js → Select-9gmhEo71.js} +133 -132
- package/chunks/{Spinner-DbUB64Gj.js → Spinner-BoysVPn7.js} +12 -11
- package/chunks/{SplitLeadAfterCollection-DSw6X5fx.js → SplitLeadAfterCollection-C5oCDYT1.js} +15 -14
- package/chunks/{Stack-BpHk5eb0.js → Stack-INH9bhkC.js} +30 -29
- package/chunks/{StandardContentSkeleton-Bae1LLQ_.js → StandardContentSkeleton-DsAhzMfU.js} +1 -1
- package/chunks/{Tabs-hwJ7jsUl.js → Tabs-DaVatjCJ.js} +60 -59
- package/chunks/{Textarea-zOtMdgc5.js → Textarea-DeMxp3Zn.js} +48 -47
- package/chunks/{TimerOutlined-DWE7f0YX.js → TimerOutlined-DJh2vL3s.js} +3 -3
- package/chunks/{helpers-NuC1SzL3.js → helpers-Bd9FT9hj.js} +1 -1
- package/chunks/{index-D1K1OB4p.js → index-5MPtbA_r.js} +255 -239
- package/chunks/index-BjfKDjAM.js +4 -0
- package/chunks/main-DDb9jezz.js +1758 -0
- package/chunks/{main-hB01WcoP.js → main-DWw1vqVY.js} +38 -37
- package/chunks/{useEventCallback-CizlfWBo.js → useEventCallback-vbTEFVPa.js} +8 -7
- package/chunks/{useForwardedInput-RWDntqBU.js → useForwardedInput-DumYVfgj.js} +26 -25
- package/chunks/{useImageVariant-Cex1eIqq.js → useImageVariant-B1ObJh_B.js} +1 -1
- package/components.d.ts +2 -1
- package/components.d.ts.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +5 -3
- package/package.json +5 -1
- package/chunks/index-BaypiKlO.js +0 -4
package/README.md
CHANGED
|
@@ -3072,6 +3072,7 @@ Full-featured football score predictor component with a multi-tab interface for
|
|
|
3072
3072
|
- Consent management with required/optional consent gates before predicting
|
|
3073
3073
|
- Customisable banners in 6 positions within the Play tab
|
|
3074
3074
|
- Authentication-aware UI with configurable sign-in CTA
|
|
3075
|
+
- Built-in Betslip integration — renders the Betslip widget automatically via the `betslip` prop with two trigger modes
|
|
3075
3076
|
- Custom theming and multi-language support
|
|
3076
3077
|
|
|
3077
3078
|
#### Required Props
|
|
@@ -3093,7 +3094,8 @@ Full-featured football score predictor component with a multi-tab interface for
|
|
|
3093
3094
|
| `defaultImagePlaceholderUrl` | `string` | Fallback image URL for the hero header |
|
|
3094
3095
|
| `playTabBanners` | `PlayTabBanner[]` | Custom banners rendered in the Play tab |
|
|
3095
3096
|
| `consents` | `ConsentDef[]` | Consent definitions required before predicting |
|
|
3096
|
-
| `matchCardBgImageUrl` | `string`
|
|
3097
|
+
| `matchCardBgImageUrl` | `string` | Background image URL for match prediction cards |
|
|
3098
|
+
| `betslip` | `PredictorBetslipConfig` | Betslip integration config. Omit to disable. See [Betslip Integration](#betslip-integration) |
|
|
3097
3099
|
|
|
3098
3100
|
#### Tabs
|
|
3099
3101
|
|
|
@@ -3229,6 +3231,67 @@ const consents: ConsentDef[] = [
|
|
|
3229
3231
|
- Optional consents are shown in the same modal but do not block progression
|
|
3230
3232
|
- Once accepted, consents are stored and the modal does not reappear
|
|
3231
3233
|
|
|
3234
|
+
#### Betslip Integration
|
|
3235
|
+
|
|
3236
|
+
When the `betslip` prop is provided, the Predictor automatically renders a `Betslip` widget as a sibling alongside its own UI — you do not need to add a separate `<Betslip />` component to the page. The widget is configured entirely through `PredictorBetslipConfig`.
|
|
3237
|
+
|
|
3238
|
+
```tsx
|
|
3239
|
+
import { PredictorBetslipConfig } from "fansunited-frontend-core";
|
|
3240
|
+
|
|
3241
|
+
const betslipConfig: PredictorBetslipConfig = {
|
|
3242
|
+
trigger: "predictions-only",
|
|
3243
|
+
position: "side-right",
|
|
3244
|
+
currency: "£",
|
|
3245
|
+
stakePresets: [5, 10, 25, 50],
|
|
3246
|
+
ctaUrlTemplate:
|
|
3247
|
+
"https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}&ref={currentUrl}",
|
|
3248
|
+
};
|
|
3249
|
+
|
|
3250
|
+
<Predictor {...otherProps} betslip={betslipConfig} />;
|
|
3251
|
+
```
|
|
3252
|
+
|
|
3253
|
+
**`PredictorBetslipConfig` fields:**
|
|
3254
|
+
|
|
3255
|
+
| Field | Type | Default | Description |
|
|
3256
|
+
| --------------------- | ------------------------- | -------------------- | ----------------------------------------------------------------------------------------- |
|
|
3257
|
+
| `trigger` | `PredictorBetslipTrigger` | `"predictions-only"` | When to send selections to the betslip |
|
|
3258
|
+
| `position` | `BetslipPosition` | `"bottom-right"` | Viewport position of the Betslip widget |
|
|
3259
|
+
| `maxSelections` | `number` | `20` | Hard cap on simultaneous selections |
|
|
3260
|
+
| `stakePresets` | `number[]` | `[5, 10, 20]` | Quick-select stake buttons |
|
|
3261
|
+
| `oddsPollingInterval` | `number` | `30000` | Odds refresh interval in milliseconds |
|
|
3262
|
+
| `currency` | `string` | `"€"` | Currency symbol |
|
|
3263
|
+
| `ctaUrlTemplate` | `string` | — | Bookmaker deep-link URL with `{placeholder}` substitution |
|
|
3264
|
+
| `brandingLogoUrl` | `string` | — | Operator logo URL |
|
|
3265
|
+
| `labels` | `BetslipLabels` | — | UI text overrides |
|
|
3266
|
+
| `themeOptions` | `CustomThemeOptions` | — | Theme for the Betslip widget. Falls back to the Predictor's `themeOptions` when omitted |
|
|
3267
|
+
|
|
3268
|
+
**Trigger modes (`PredictorBetslipTrigger`):**
|
|
3269
|
+
|
|
3270
|
+
```tsx
|
|
3271
|
+
type PredictorBetslipTrigger =
|
|
3272
|
+
| "predictions-only" // score changes feed betslip (before submit)
|
|
3273
|
+
| "odds-only"; // odds button click feeds betslip (after submit)
|
|
3274
|
+
```
|
|
3275
|
+
|
|
3276
|
+
| Mode | When selections are sent |
|
|
3277
|
+
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3278
|
+
| `"predictions-only"` | Every time the user edits a score (home/away increment or decrement). If the user submits without editing (e.g. a default 0:0), the selection is sent at submit time as a fallback. Derived outcome: `"1"` (home win), `"X"` (draw), `"2"` (away win). |
|
|
3279
|
+
| `"odds-only"` | Only when the user clicks an odds button on the post-submit odds display. The odds button becomes a command-bus trigger instead of a direct bookmaker link. |
|
|
3280
|
+
|
|
3281
|
+
**Selection format sent by Predictor:**
|
|
3282
|
+
|
|
3283
|
+
```
|
|
3284
|
+
"{matchId}:FT_1X2:{outcomeKey}"
|
|
3285
|
+
// Examples:
|
|
3286
|
+
// "fb:m:451634:FT_1X2:1" → home win
|
|
3287
|
+
// "fb:m:451634:FT_1X2:X" → draw
|
|
3288
|
+
// "fb:m:451634:FT_1X2:2" → away win
|
|
3289
|
+
```
|
|
3290
|
+
|
|
3291
|
+
**Automatic removal:** When a user deletes a submitted prediction, the corresponding selection is automatically removed from the betslip via `betslipApi.removeSelection()`. When a score is edited after submission (in `"predictions-only"` mode), the new outcome replaces the old one in the betslip (upsert — no explicit removal needed).
|
|
3292
|
+
|
|
3293
|
+
**Theme fallback:** If `betslip.themeOptions` is not provided, the Betslip widget inherits the Predictor's own `themeOptions`. A single `themeOptions` on the Predictor is enough to theme both components consistently.
|
|
3294
|
+
|
|
3232
3295
|
#### TypeScript Support
|
|
3233
3296
|
|
|
3234
3297
|
Import types from `fansunited-frontend-core`:
|
|
@@ -3237,6 +3300,8 @@ Import types from `fansunited-frontend-core`:
|
|
|
3237
3300
|
import {
|
|
3238
3301
|
PredictorProps,
|
|
3239
3302
|
PredictorTab,
|
|
3303
|
+
PredictorBetslipConfig,
|
|
3304
|
+
PredictorBetslipTrigger,
|
|
3240
3305
|
PlayTabBanner,
|
|
3241
3306
|
PlayTabBannerPosition,
|
|
3242
3307
|
ConsentDef,
|
|
@@ -3328,6 +3393,462 @@ import { Predictor } from "fansunited-frontend-components";
|
|
|
3328
3393
|
/>
|
|
3329
3394
|
```
|
|
3330
3395
|
|
|
3396
|
+
##### With Betslip — Predictions Trigger
|
|
3397
|
+
|
|
3398
|
+
Selections are sent to the Betslip automatically as the user edits scores, before they submit.
|
|
3399
|
+
|
|
3400
|
+
```tsx
|
|
3401
|
+
<Predictor
|
|
3402
|
+
entityId="predictor-template-123"
|
|
3403
|
+
sdk={sdkInstance}
|
|
3404
|
+
language="en"
|
|
3405
|
+
userIsLoggedIn={true}
|
|
3406
|
+
tabs={["play", "leaderboard"]}
|
|
3407
|
+
betslip={{
|
|
3408
|
+
trigger: "predictions-only",
|
|
3409
|
+
position: "side-right",
|
|
3410
|
+
currency: "£",
|
|
3411
|
+
stakePresets: [5, 10, 25, 50],
|
|
3412
|
+
ctaUrlTemplate:
|
|
3413
|
+
"https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}&ref={currentUrl}",
|
|
3414
|
+
labels: {
|
|
3415
|
+
disclaimer: "18+ | Please gamble responsibly",
|
|
3416
|
+
},
|
|
3417
|
+
}}
|
|
3418
|
+
themeOptions={{ mode: "dark" }}
|
|
3419
|
+
/>
|
|
3420
|
+
```
|
|
3421
|
+
|
|
3422
|
+
##### With Betslip — Odds Trigger
|
|
3423
|
+
|
|
3424
|
+
Selections are sent only when the user explicitly clicks an odds button on the post-submit view.
|
|
3425
|
+
|
|
3426
|
+
```tsx
|
|
3427
|
+
<Predictor
|
|
3428
|
+
entityId="predictor-template-123"
|
|
3429
|
+
sdk={sdkInstance}
|
|
3430
|
+
language="en"
|
|
3431
|
+
userIsLoggedIn={true}
|
|
3432
|
+
betslip={{
|
|
3433
|
+
trigger: "odds-only",
|
|
3434
|
+
position: "bottom-right",
|
|
3435
|
+
currency: "€",
|
|
3436
|
+
ctaUrlTemplate:
|
|
3437
|
+
"https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}",
|
|
3438
|
+
}}
|
|
3439
|
+
/>
|
|
3440
|
+
```
|
|
3441
|
+
|
|
3442
|
+
---
|
|
3443
|
+
|
|
3444
|
+
### Betslip
|
|
3445
|
+
|
|
3446
|
+
A floating betslip component that collects selections from any external source and routes users to a bookmaker via a configurable deep-link URL. The component communicates with the rest of the page through a singleton command bus (`betslipApi`), so it decouples cleanly from host-page code.
|
|
3447
|
+
|
|
3448
|
+
**Key Features:**
|
|
3449
|
+
|
|
3450
|
+
- Six viewport positions: four corner anchors and two full-height side panels
|
|
3451
|
+
- Singleton command bus with pre-mount queuing — selections sent before the widget mounts are replayed on load
|
|
3452
|
+
- Automatic odds polling from the SDK with configurable interval
|
|
3453
|
+
- Bookmaker branding color automatically used as the primary accent (overridable via `themeOptions`)
|
|
3454
|
+
- Configurable CTA deep-link URL template with dynamic placeholder substitution
|
|
3455
|
+
- Shadow DOM style isolation — no CSS conflicts with the host application
|
|
3456
|
+
- Multi-language support and fully overridable UI text labels
|
|
3457
|
+
|
|
3458
|
+
#### Quick Start
|
|
3459
|
+
|
|
3460
|
+
```tsx
|
|
3461
|
+
import React from "react";
|
|
3462
|
+
import { Betslip } from "fansunited-frontend-components";
|
|
3463
|
+
import { betslipApi } from "fansunited-frontend-core";
|
|
3464
|
+
import { FansUnitedSDK } from "fansunited-sdk-esm";
|
|
3465
|
+
|
|
3466
|
+
const sdk = FansUnitedSDK({ /* your config */ });
|
|
3467
|
+
|
|
3468
|
+
const App: React.FC = () => (
|
|
3469
|
+
<>
|
|
3470
|
+
{/* Place once anywhere in the tree */}
|
|
3471
|
+
<Betslip
|
|
3472
|
+
sdk={sdk}
|
|
3473
|
+
language="en"
|
|
3474
|
+
position="bottom-right"
|
|
3475
|
+
ctaUrlTemplate="https://your-bookmaker.com/betslip?selections={selectionIds}&stake={stake}"
|
|
3476
|
+
/>
|
|
3477
|
+
|
|
3478
|
+
{/* From anywhere in your app, add or remove selections */}
|
|
3479
|
+
<button onClick={() => betslipApi.setSelection("fb:m:451678:FT_1X2:1")}>
|
|
3480
|
+
Back Home Win
|
|
3481
|
+
</button>
|
|
3482
|
+
</>
|
|
3483
|
+
);
|
|
3484
|
+
```
|
|
3485
|
+
|
|
3486
|
+
#### Required Props
|
|
3487
|
+
|
|
3488
|
+
| Prop | Type | Description |
|
|
3489
|
+
| ---------- | -------------------- | ---------------- |
|
|
3490
|
+
| `sdk` | `FansUnitedSDKModel` | SDK instance |
|
|
3491
|
+
| `language` | `LanguageType` | Display language |
|
|
3492
|
+
|
|
3493
|
+
#### Optional Props
|
|
3494
|
+
|
|
3495
|
+
| Prop | Type | Default | Description |
|
|
3496
|
+
| ---------------------- | -------------------- | ---------------- | --------------------------------------------------------------- |
|
|
3497
|
+
| `position` | `BetslipPosition` | `"bottom-right"` | Viewport anchor. See [Positions](#positions) below |
|
|
3498
|
+
| `maxSelections` | `number` | `20` | Hard cap on simultaneous selections |
|
|
3499
|
+
| `stakePresets` | `number[]` | `[5, 10, 20]` | Quick-select stake buttons shown in the footer |
|
|
3500
|
+
| `oddsPollingInterval` | `number` | `30000` | Odds refresh interval in milliseconds |
|
|
3501
|
+
| `currency` | `string` | `"€"` | Currency symbol shown next to stake and potential win |
|
|
3502
|
+
| `ctaUrlTemplate` | `string` | — | Deep-link URL template with `{placeholder}` substitution |
|
|
3503
|
+
| `brandingLogoUrl` | `string` | — | Operator logo URL that overrides the bookmaker logo |
|
|
3504
|
+
| `labels` | `BetslipLabels` | — | UI text overrides. See [Labels](#labels) below |
|
|
3505
|
+
| `themeOptions` | `CustomThemeOptions` | — | Theme tokens. See [Theming](#theming-1) below |
|
|
3506
|
+
|
|
3507
|
+
#### Positions
|
|
3508
|
+
|
|
3509
|
+
```tsx
|
|
3510
|
+
import { BetslipPosition } from "fansunited-frontend-core";
|
|
3511
|
+
|
|
3512
|
+
type BetslipPosition =
|
|
3513
|
+
| "bottom-right" // Fixed corner anchor, slides up on open (default)
|
|
3514
|
+
| "bottom-left" // Fixed corner anchor, slides up on open
|
|
3515
|
+
| "top-right" // Fixed corner anchor, slides down on open
|
|
3516
|
+
| "top-left" // Fixed corner anchor, slides down on open
|
|
3517
|
+
| "side-right" // Full-height panel, slides in from the right
|
|
3518
|
+
| "side-left"; // Full-height panel, slides in from the left
|
|
3519
|
+
```
|
|
3520
|
+
|
|
3521
|
+
**Corner positions** (`bottom-*`, `top-*`):
|
|
3522
|
+
- 320 px wide, fixed 16 px from the viewport edges
|
|
3523
|
+
- Header shows an expand/collapse chevron
|
|
3524
|
+
- Collapsed state renders a compact summary (combined odds, stake, potential win)
|
|
3525
|
+
- Expanded state shows the full selection list (scrollable at 300 px max height) plus the stake/CTA footer
|
|
3526
|
+
|
|
3527
|
+
**Side positions** (`side-*`):
|
|
3528
|
+
- 340 px wide panel covering full viewport height (100 vh)
|
|
3529
|
+
- A 32 × 64 px tab handle peeks out at 33 % from the top when closed — it shows the bookmaker/branding logo rotated 90°, or three grip lines when no logo is available
|
|
3530
|
+
- A selection count badge appears on the closed tab when `maxSelections` is set
|
|
3531
|
+
- Smooth 0.3 s slide-in/out transition
|
|
3532
|
+
|
|
3533
|
+
#### Command Bus (`betslipApi`)
|
|
3534
|
+
|
|
3535
|
+
The betslip exposes a singleton command bus for external code to manage selections without needing a direct reference to the React component.
|
|
3536
|
+
|
|
3537
|
+
```tsx
|
|
3538
|
+
import { betslipApi } from "fansunited-frontend-core";
|
|
3539
|
+
|
|
3540
|
+
// Add or replace a selection (same eventId + market = outcome replaced)
|
|
3541
|
+
betslipApi.setSelection("fb:m:451678:FT_1X2:1");
|
|
3542
|
+
|
|
3543
|
+
// Remove a selection by its exact id
|
|
3544
|
+
betslipApi.removeSelection("fb:m:451678:FT_1X2:1");
|
|
3545
|
+
```
|
|
3546
|
+
|
|
3547
|
+
**Selection ID format:**
|
|
3548
|
+
|
|
3549
|
+
```
|
|
3550
|
+
"{eventId}:{market}:{outcome}"
|
|
3551
|
+
```
|
|
3552
|
+
|
|
3553
|
+
| Segment | Examples |
|
|
3554
|
+
| ---------- | ---------------------------------------------- |
|
|
3555
|
+
| `eventId` | `fb:m:451678` |
|
|
3556
|
+
| `market` | `FT_1X2`, `DOUBLE_CHANCE`, `OVER_GOALS_2_5`, `CORRECT_SCORE`, `PLAYER_SCORE_FIRST_GOAL` |
|
|
3557
|
+
| `outcome` | `1` (home), `x` (draw), `2` (away), `1x`, `yes`, `no`, `1-2` (correct score) |
|
|
3558
|
+
|
|
3559
|
+
**Pre-mount queuing:**
|
|
3560
|
+
|
|
3561
|
+
Calls to `betslipApi.setSelection` made before the `<Betslip />` component has mounted are queued and automatically replayed once the widget finishes initializing. There is no need to delay external code to wait for the widget.
|
|
3562
|
+
|
|
3563
|
+
```tsx
|
|
3564
|
+
// Safe to call before <Betslip /> is rendered
|
|
3565
|
+
betslipApi.setSelection("fb:m:451678:FT_1X2:1");
|
|
3566
|
+
|
|
3567
|
+
// Later, when <Betslip /> mounts, it will receive the queued selection
|
|
3568
|
+
ReactDOM.render(<Betslip sdk={sdk} language="en" />, root);
|
|
3569
|
+
```
|
|
3570
|
+
|
|
3571
|
+
#### Predictor Integration
|
|
3572
|
+
|
|
3573
|
+
The `Predictor` component has first-class Betslip support via its `betslip` prop. When configured, the Predictor renders the `Betslip` widget internally — no separate `<Betslip />` component is needed. See the [Predictor — Betslip Integration](#betslip-integration) section for full details.
|
|
3574
|
+
|
|
3575
|
+
```tsx
|
|
3576
|
+
import { Predictor } from "fansunited-frontend-components";
|
|
3577
|
+
|
|
3578
|
+
<Predictor
|
|
3579
|
+
entityId="predictor-123"
|
|
3580
|
+
sdk={sdk}
|
|
3581
|
+
language="en"
|
|
3582
|
+
betslip={{
|
|
3583
|
+
trigger: "predictions-only", // or "odds-only"
|
|
3584
|
+
position: "side-right",
|
|
3585
|
+
currency: "£",
|
|
3586
|
+
stakePresets: [5, 10, 25, 50],
|
|
3587
|
+
ctaUrlTemplate: "https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}",
|
|
3588
|
+
}}
|
|
3589
|
+
/>
|
|
3590
|
+
```
|
|
3591
|
+
|
|
3592
|
+
**Trigger modes:**
|
|
3593
|
+
|
|
3594
|
+
| Mode | When selections are sent to Betslip |
|
|
3595
|
+
| --------------------- | --------------------------------------------------------------------------------- |
|
|
3596
|
+
| `"predictions-only"` | Each time the user changes a score prediction (before submitting) |
|
|
3597
|
+
| `"odds-only"` | When the user clicks an odds button on the post-submit odds view |
|
|
3598
|
+
|
|
3599
|
+
#### CTA URL Template
|
|
3600
|
+
|
|
3601
|
+
The `ctaUrlTemplate` prop defines the bookmaker deep-link. Placeholders are replaced at click time with live values from the current betslip state.
|
|
3602
|
+
|
|
3603
|
+
```tsx
|
|
3604
|
+
ctaUrlTemplate="https://betano.bg/mybet/?selection_ids={selectionIds}&stake={stake}&return_url={currentUrl}"
|
|
3605
|
+
```
|
|
3606
|
+
|
|
3607
|
+
**Available placeholders:**
|
|
3608
|
+
|
|
3609
|
+
| Placeholder | Value |
|
|
3610
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
3611
|
+
| `{selectionIds}` | Comma-separated SDK `selection.id` values (up to the first 10) |
|
|
3612
|
+
| `{stake}` | Current stake input value (float) |
|
|
3613
|
+
| `{currentUrl}` | `window.location.href`, URI-encoded |
|
|
3614
|
+
| `{eventId}` | `eventId` of the first selection |
|
|
3615
|
+
| `{oddsDecimal}` | Decimal odds of the first selection |
|
|
3616
|
+
| `{market}` | Market code of the first selection |
|
|
3617
|
+
| `{outcome}` | Outcome label of the first selection |
|
|
3618
|
+
|
|
3619
|
+
#### Theming
|
|
3620
|
+
|
|
3621
|
+
The Betslip derives its primary accent color from a three-level priority chain:
|
|
3622
|
+
|
|
3623
|
+
1. **Custom primary** — `themeOptions.colorSchemes[mode].palette.primary.plainColor`
|
|
3624
|
+
2. **Bookmaker color** — `bookmaker.branding.backgroundColor` (or `bookmaker.assets[0].backgroundColor` as fallback) extracted live from the SDK odds response
|
|
3625
|
+
3. **Default theme primary**
|
|
3626
|
+
|
|
3627
|
+
This means you get bookmaker brand colors for free when odds are available, and can override them at any time via `themeOptions`.
|
|
3628
|
+
|
|
3629
|
+
```tsx
|
|
3630
|
+
import { CustomThemeOptions } from "fansunited-frontend-core";
|
|
3631
|
+
|
|
3632
|
+
// Override primary accent — disables the bookmaker color fallback
|
|
3633
|
+
const themeOptions: CustomThemeOptions = {
|
|
3634
|
+
mode: "dark",
|
|
3635
|
+
colorSchemes: {
|
|
3636
|
+
dark: {
|
|
3637
|
+
palette: {
|
|
3638
|
+
primary: {
|
|
3639
|
+
plainColor: "#FF5722", // your brand color
|
|
3640
|
+
primaryContainer: "#FF7043",
|
|
3641
|
+
onPrimary: "#FFFFFF",
|
|
3642
|
+
},
|
|
3643
|
+
success: {
|
|
3644
|
+
plainColor: "#4CAF50",
|
|
3645
|
+
softBg: "#042F04",
|
|
3646
|
+
},
|
|
3647
|
+
danger: {
|
|
3648
|
+
plainColor: "#F44336",
|
|
3649
|
+
softBg: "#430A0A",
|
|
3650
|
+
},
|
|
3651
|
+
},
|
|
3652
|
+
surface: "#212121",
|
|
3653
|
+
onSurface: "#121212",
|
|
3654
|
+
surfaceVariant: "#333333",
|
|
3655
|
+
textPrimary: "#FAFAFA",
|
|
3656
|
+
textSecondary: "#BDBDBD",
|
|
3657
|
+
outlineEnabledBorder: "#555555",
|
|
3658
|
+
},
|
|
3659
|
+
},
|
|
3660
|
+
customFontFamily: {
|
|
3661
|
+
dark: {
|
|
3662
|
+
primary: "Inter, sans-serif",
|
|
3663
|
+
secondary: "Roboto, sans-serif",
|
|
3664
|
+
},
|
|
3665
|
+
},
|
|
3666
|
+
};
|
|
3667
|
+
|
|
3668
|
+
<Betslip sdk={sdk} language="en" themeOptions={themeOptions} />;
|
|
3669
|
+
```
|
|
3670
|
+
|
|
3671
|
+
> **Note:** When neither a custom primary nor bookmaker odds are present, the widget falls back to the default theme primary (`#1A77D2`).
|
|
3672
|
+
|
|
3673
|
+
#### Labels
|
|
3674
|
+
|
|
3675
|
+
Override any piece of UI text without switching languages:
|
|
3676
|
+
|
|
3677
|
+
```tsx
|
|
3678
|
+
import { BetslipLabels } from "fansunited-frontend-core";
|
|
3679
|
+
|
|
3680
|
+
const labels: BetslipLabels = {
|
|
3681
|
+
title: "My Betslip",
|
|
3682
|
+
placeBetLabel: "Place Bet",
|
|
3683
|
+
emptyTitle: "Your betslip is empty",
|
|
3684
|
+
emptyDescription: "Add selections to get started",
|
|
3685
|
+
continueLabel: "Continue",
|
|
3686
|
+
disclaimer: "18+ | Gamble responsibly",
|
|
3687
|
+
combinedOddsLabel: "Combined Odds",
|
|
3688
|
+
stakeLabel: "Stake",
|
|
3689
|
+
totalStakeLabel: "Total Stake",
|
|
3690
|
+
potentialWinLabel: "Potential Win",
|
|
3691
|
+
oddsUnavailableLabel: "Odds N/A",
|
|
3692
|
+
suspendedLabel: "Suspended",
|
|
3693
|
+
|
|
3694
|
+
// Override individual outcome labels
|
|
3695
|
+
outcomeLabels: {
|
|
3696
|
+
yes: "Over",
|
|
3697
|
+
no: "Under",
|
|
3698
|
+
draw: "Draw",
|
|
3699
|
+
or: "/",
|
|
3700
|
+
nobody: "No Scorer",
|
|
3701
|
+
noGoal: "No Goal",
|
|
3702
|
+
},
|
|
3703
|
+
|
|
3704
|
+
// Override market display names
|
|
3705
|
+
marketLabels: {
|
|
3706
|
+
FT_1X2: "Full Time",
|
|
3707
|
+
DOUBLE_CHANCE: "Double Chance",
|
|
3708
|
+
OVER_GOALS_2_5: "Over/Under 2.5 Goals",
|
|
3709
|
+
},
|
|
3710
|
+
};
|
|
3711
|
+
|
|
3712
|
+
<Betslip sdk={sdk} language="en" labels={labels} />;
|
|
3713
|
+
```
|
|
3714
|
+
|
|
3715
|
+
#### Conflict Detection
|
|
3716
|
+
|
|
3717
|
+
The betslip automatically detects selections that are mutually exclusive — i.e., two selections with the same `eventId` and market. When conflicts are detected:
|
|
3718
|
+
|
|
3719
|
+
- A warning banner appears at the top of the expanded selection list
|
|
3720
|
+
- Conflicting selection cards are highlighted with danger styling
|
|
3721
|
+
- The CTA button is disabled until the conflict is resolved
|
|
3722
|
+
|
|
3723
|
+
Conflicts resolve automatically when the user removes one of the conflicting selections. No extra configuration is required.
|
|
3724
|
+
|
|
3725
|
+
#### Odds Behavior
|
|
3726
|
+
|
|
3727
|
+
- **Polling**: Odds are fetched from `sdk.odds.getByMatchIds()` on mount and refreshed every `oddsPollingInterval` ms (default 30 s)
|
|
3728
|
+
- **Movement indicator**: A green ▲ or red ▼ is shown when the bookmaker response includes a `movement` field (`"UP"` or `"DOWN"`) on the matched selection. No indicator is shown when `movement` is absent
|
|
3729
|
+
- **Unavailable**: When no odds are returned for a selection, the card shows `oddsUnavailableLabel` and the CTA is disabled
|
|
3730
|
+
- **Suspended**: Selections with status `"suspended"` are highlighted and block the CTA
|
|
3731
|
+
- **Settled states**: Cards display ✓ (won), ✗ (lost), or ≈ (void) once a selection is settled
|
|
3732
|
+
|
|
3733
|
+
**Over/Under markets:** The `outcome` code in the selection ID uses `"yes"` for Over and `"no"` for Under (not `"over"`/`"under"`). The widget handles the conversion and label display internally.
|
|
3734
|
+
|
|
3735
|
+
**Correct Score market:** Outcomes use hyphen notation, e.g. `"1-2"`. The widget parses both `-` and `:` as separators.
|
|
3736
|
+
|
|
3737
|
+
#### TypeScript Support
|
|
3738
|
+
|
|
3739
|
+
Import types from `fansunited-frontend-core`:
|
|
3740
|
+
|
|
3741
|
+
```tsx
|
|
3742
|
+
import {
|
|
3743
|
+
BetslipProps,
|
|
3744
|
+
BetslipLabels,
|
|
3745
|
+
BetslipPosition,
|
|
3746
|
+
BetslipSelectionInput,
|
|
3747
|
+
BetslipOutcomeLabels,
|
|
3748
|
+
SelectionStatus,
|
|
3749
|
+
CustomThemeOptions,
|
|
3750
|
+
LanguageType,
|
|
3751
|
+
betslipApi,
|
|
3752
|
+
} from "fansunited-frontend-core";
|
|
3753
|
+
```
|
|
3754
|
+
|
|
3755
|
+
#### Examples
|
|
3756
|
+
|
|
3757
|
+
##### Basic Betslip
|
|
3758
|
+
|
|
3759
|
+
```tsx
|
|
3760
|
+
import { Betslip } from "fansunited-frontend-components";
|
|
3761
|
+
import { FansUnitedSDK } from "fansunited-sdk-esm";
|
|
3762
|
+
|
|
3763
|
+
const sdk = FansUnitedSDK({ /* config */ });
|
|
3764
|
+
|
|
3765
|
+
<Betslip
|
|
3766
|
+
sdk={sdk}
|
|
3767
|
+
language="en"
|
|
3768
|
+
ctaUrlTemplate="https://your-bookmaker.com/bet?selections={selectionIds}"
|
|
3769
|
+
/>;
|
|
3770
|
+
```
|
|
3771
|
+
|
|
3772
|
+
##### Side Panel with Custom Branding
|
|
3773
|
+
|
|
3774
|
+
```tsx
|
|
3775
|
+
import { Betslip } from "fansunited-frontend-components";
|
|
3776
|
+
|
|
3777
|
+
<Betslip
|
|
3778
|
+
sdk={sdk}
|
|
3779
|
+
language="en"
|
|
3780
|
+
position="side-right"
|
|
3781
|
+
maxSelections={10}
|
|
3782
|
+
stakePresets={[5, 10, 25, 50]}
|
|
3783
|
+
currency="£"
|
|
3784
|
+
brandingLogoUrl="https://your-cdn.com/logo.png"
|
|
3785
|
+
ctaUrlTemplate="https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}&ref={currentUrl}"
|
|
3786
|
+
labels={{
|
|
3787
|
+
title: "Your Selections",
|
|
3788
|
+
placeBetLabel: "Bet Now",
|
|
3789
|
+
disclaimer: "18+ | Please gamble responsibly | BeGambleAware.org",
|
|
3790
|
+
}}
|
|
3791
|
+
themeOptions={{
|
|
3792
|
+
mode: "dark",
|
|
3793
|
+
colorSchemes: {
|
|
3794
|
+
dark: {
|
|
3795
|
+
palette: {
|
|
3796
|
+
primary: {
|
|
3797
|
+
plainColor: "#FF5722",
|
|
3798
|
+
primaryContainer: "#FF7043",
|
|
3799
|
+
onPrimary: "#FFFFFF",
|
|
3800
|
+
},
|
|
3801
|
+
},
|
|
3802
|
+
surface: "#1A1A1A",
|
|
3803
|
+
textPrimary: "#FAFAFA",
|
|
3804
|
+
},
|
|
3805
|
+
},
|
|
3806
|
+
}}
|
|
3807
|
+
/>
|
|
3808
|
+
```
|
|
3809
|
+
|
|
3810
|
+
##### Programmatic Selection Management
|
|
3811
|
+
|
|
3812
|
+
```tsx
|
|
3813
|
+
import { betslipApi } from "fansunited-frontend-core";
|
|
3814
|
+
|
|
3815
|
+
// Add selections (safe to call before the widget mounts)
|
|
3816
|
+
betslipApi.setSelection("fb:m:451678:FT_1X2:1");
|
|
3817
|
+
betslipApi.setSelection("fb:m:451679:DOUBLE_CHANCE:1x");
|
|
3818
|
+
|
|
3819
|
+
// Replace an outcome for the same event+market
|
|
3820
|
+
betslipApi.setSelection("fb:m:451678:FT_1X2:2"); // replaces the home-win
|
|
3821
|
+
|
|
3822
|
+
// Remove a specific selection
|
|
3823
|
+
betslipApi.removeSelection("fb:m:451679:DOUBLE_CHANCE:1x");
|
|
3824
|
+
```
|
|
3825
|
+
|
|
3826
|
+
##### Predictor + Betslip Integration
|
|
3827
|
+
|
|
3828
|
+
The `Predictor` renders the `Betslip` widget internally when `betslip` is provided — no separate `<Betslip />` is needed.
|
|
3829
|
+
|
|
3830
|
+
```tsx
|
|
3831
|
+
import { Predictor } from "fansunited-frontend-components";
|
|
3832
|
+
|
|
3833
|
+
<Predictor
|
|
3834
|
+
entityId="predictor-123"
|
|
3835
|
+
sdk={sdk}
|
|
3836
|
+
language="en"
|
|
3837
|
+
betslip={{
|
|
3838
|
+
trigger: "predictions-only",
|
|
3839
|
+
position: "side-right",
|
|
3840
|
+
currency: "€",
|
|
3841
|
+
stakePresets: [5, 10, 20, 50],
|
|
3842
|
+
ctaUrlTemplate:
|
|
3843
|
+
"https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}",
|
|
3844
|
+
labels: {
|
|
3845
|
+
disclaimer: "18+ | Gamble responsibly",
|
|
3846
|
+
},
|
|
3847
|
+
}}
|
|
3848
|
+
themeOptions={{ mode: "light" }}
|
|
3849
|
+
/>
|
|
3850
|
+
```
|
|
3851
|
+
|
|
3331
3852
|
---
|
|
3332
3853
|
|
|
3333
3854
|
## Available Components
|
|
@@ -3345,6 +3866,7 @@ This package exports the following components:
|
|
|
3345
3866
|
- **`Discussion`** - Threaded comments with reactions, replies, reporting, and authentication support
|
|
3346
3867
|
- **`Leaderboard`** - Ranked standings for Classic Quiz, Match Quiz, Top X, and Template entities with multi-entity support
|
|
3347
3868
|
- **`Predictor`** - Multi-tab football score predictor with leaderboard, private leagues, rules, and prizes
|
|
3869
|
+
- **`Betslip`** - Floating betslip with bookmaker deep-link integration, six viewport positions, and command bus API
|
|
3348
3870
|
|
|
3349
3871
|
## Related Packages
|
|
3350
3872
|
|