@enigma-lake/mines-play-controller-sdk 3.2.0 → 4.0.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/dist/components/base/AutoPlayController/AutoPlayController.d.ts +1 -1
- package/dist/components/base/Button/Button.d.ts +1 -1
- package/dist/components/base/ChevronIcon/ChevronIcon.d.ts +1 -1
- package/dist/components/base/DifficultySelector/DifficultySelector.d.ts +1 -1
- package/dist/components/base/DifficultySelector/Selector.d.ts +1 -1
- package/dist/components/base/FreeRoundsIntroModal/FreeRoundsIntroModal.d.ts +2 -1
- package/dist/components/base/FreeRoundsSummaryModal/FreeRoundsSummaryModal.d.ts +1 -1
- package/dist/components/base/GroupRow/GroupRow.d.ts +1 -1
- package/dist/components/base/Input/Input.d.ts +1 -1
- package/dist/components/base/InputWithIcon/InputWithIcon.d.ts +1 -1
- package/dist/components/base/InputWithSwitch/InputWithSwitch.d.ts +1 -1
- package/dist/components/base/ManualPlayController/ManualPlayController.d.ts +1 -1
- package/dist/components/base/PlayController/PlayController.d.ts +1 -1
- package/dist/components/base/PlayValueInput/PlayValueInput.d.ts +1 -1
- package/dist/components/base/PlayValueList/PlayValueList.d.ts +1 -1
- package/dist/components/base/SelectMenu/GoldIcon.d.ts +1 -1
- package/dist/components/base/SelectMenu/SelectMenu.d.ts +1 -1
- package/dist/components/base/SelectMenu/SweepsIcon.d.ts +1 -1
- package/dist/components/base/Switch/Switch.d.ts +2 -1
- package/dist/i18n/I18nContext.d.ts +13 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/locales/en.d.ts +2 -0
- package/dist/i18n/locales/zh-CN.d.ts +2 -0
- package/dist/i18n/translator.d.ts +6 -0
- package/dist/i18n/types.d.ts +30 -0
- package/dist/index.d.ts +67 -13
- package/dist/index.mjs +234 -64
- package/dist/index.mjs.map +1 -1
- package/dist/types/playController.d.ts +7 -0
- package/package.json +9 -2
package/dist/index.mjs
CHANGED
|
@@ -346,8 +346,135 @@ const SelectMenu = ({ disabled = false }) => {
|
|
|
346
346
|
}) })] })) }));
|
|
347
347
|
};
|
|
348
348
|
|
|
349
|
+
// Supported play-controller languages. Codes are BCP-47 style tags and line up
|
|
350
|
+
// 1:1 with the `code` column seeded in el-auth's `languages` catalog
|
|
351
|
+
// (en, zh-CN). Adding a language = add a code here + a locale file.
|
|
352
|
+
const SUPPORTED_LANGUAGE_CODES = ["en", "zh-CN"];
|
|
353
|
+
|
|
354
|
+
// English — the fallback language. Any key missing from another locale falls
|
|
355
|
+
// back to the value here.
|
|
356
|
+
const en = {
|
|
357
|
+
playNow: "PLAY NOW",
|
|
358
|
+
selectPlayAmount: "SELECT PLAY AMOUNT",
|
|
359
|
+
cashout: "CASHOUT",
|
|
360
|
+
collect: "COLLECT",
|
|
361
|
+
doubleTitle: "DOUBLE",
|
|
362
|
+
doubleSubtitle: "OR NOTHING",
|
|
363
|
+
playFreeRound: "PLAY FREE ROUND",
|
|
364
|
+
startAutoplay: "START AUTOPLAY",
|
|
365
|
+
stopAutoplay: "STOP AUTOPLAY",
|
|
366
|
+
startFreeAutoplay: "START FREE AUTOPLAY",
|
|
367
|
+
numberOfPlays: "Number of Plays",
|
|
368
|
+
selectTileToast: "Please select at least one tile to start autoplay.",
|
|
369
|
+
minesLabel: "Mines",
|
|
370
|
+
selectPlayAmountAria: "Select play amount",
|
|
371
|
+
freeRoundsModalTitle: "Free Game Rounds",
|
|
372
|
+
freeRoundsHave: "You have",
|
|
373
|
+
freeRoundsUnitOne: "FREE GAME ROUND",
|
|
374
|
+
freeRoundsUnitOther: "FREE GAME ROUNDS",
|
|
375
|
+
freeRoundsPerRound: "{amount} per round",
|
|
376
|
+
freeRoundsExpiresIn: "Expires in {days} {unit}",
|
|
377
|
+
dayUnitOne: "day",
|
|
378
|
+
dayUnitOther: "days",
|
|
379
|
+
freeRoundsModalPlayNow: "Play now",
|
|
380
|
+
freeRoundsModalPlayLater: "Maybe later",
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// Simplified Chinese.
|
|
384
|
+
const zhCN = {
|
|
385
|
+
playNow: "立即游戏",
|
|
386
|
+
selectPlayAmount: "选择下注金额",
|
|
387
|
+
cashout: "提现",
|
|
388
|
+
collect: "收取",
|
|
389
|
+
doubleTitle: "双倍",
|
|
390
|
+
doubleSubtitle: "或全无",
|
|
391
|
+
playFreeRound: "免费游戏",
|
|
392
|
+
startAutoplay: "开始自动游戏",
|
|
393
|
+
stopAutoplay: "停止自动游戏",
|
|
394
|
+
startFreeAutoplay: "开始免费自动游戏",
|
|
395
|
+
numberOfPlays: "游戏次数",
|
|
396
|
+
selectTileToast: "请至少选择一个方块以开始自动游戏。",
|
|
397
|
+
minesLabel: "地雷",
|
|
398
|
+
selectPlayAmountAria: "选择下注金额",
|
|
399
|
+
freeRoundsModalTitle: "免费游戏回合",
|
|
400
|
+
freeRoundsHave: "您有",
|
|
401
|
+
freeRoundsUnitOne: "个免费游戏回合",
|
|
402
|
+
freeRoundsUnitOther: "个免费游戏回合",
|
|
403
|
+
freeRoundsPerRound: "每回合 {amount}",
|
|
404
|
+
freeRoundsExpiresIn: "{days} {unit}后到期",
|
|
405
|
+
dayUnitOne: "天",
|
|
406
|
+
dayUnitOther: "天",
|
|
407
|
+
freeRoundsModalPlayNow: "立即游戏",
|
|
408
|
+
freeRoundsModalPlayLater: "稍后再玩",
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const DEFAULT_LANGUAGE = "en";
|
|
412
|
+
const SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGE_CODES;
|
|
413
|
+
// Central registry of locale bundles. Adding a language is: add its code to
|
|
414
|
+
// SUPPORTED_LANGUAGE_CODES, create a locale file, and register it here.
|
|
415
|
+
const dictionaries = {
|
|
416
|
+
en,
|
|
417
|
+
"zh-CN": zhCN,
|
|
418
|
+
};
|
|
419
|
+
// Normalize an arbitrary language input (e.g. "fr-FR", "ZH", "en-US") to one of
|
|
420
|
+
// the supported codes, falling back to English. Matches exact codes first, then
|
|
421
|
+
// by primary subtag ("fr-FR" -> "fr", "zh" -> "zh-CN").
|
|
422
|
+
const resolveLanguage = (input) => {
|
|
423
|
+
if (!input) {
|
|
424
|
+
return DEFAULT_LANGUAGE;
|
|
425
|
+
}
|
|
426
|
+
const normalized = input.trim().toLowerCase();
|
|
427
|
+
if (!normalized) {
|
|
428
|
+
return DEFAULT_LANGUAGE;
|
|
429
|
+
}
|
|
430
|
+
const exact = SUPPORTED_LANGUAGES.find((code) => code.toLowerCase() === normalized);
|
|
431
|
+
if (exact) {
|
|
432
|
+
return exact;
|
|
433
|
+
}
|
|
434
|
+
const primary = normalized.split("-")[0];
|
|
435
|
+
const byPrimary = SUPPORTED_LANGUAGES.find((code) => code.toLowerCase().split("-")[0] === primary);
|
|
436
|
+
return byPrimary ?? DEFAULT_LANGUAGE;
|
|
437
|
+
};
|
|
438
|
+
const interpolate = (template, params) => {
|
|
439
|
+
if (!params) {
|
|
440
|
+
return template;
|
|
441
|
+
}
|
|
442
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => key in params ? String(params[key]) : match);
|
|
443
|
+
};
|
|
444
|
+
// Build a translator bound to a language. Per-key fallback to English ensures a
|
|
445
|
+
// partially-translated locale never renders a blank string.
|
|
446
|
+
const createTranslator = (language) => {
|
|
447
|
+
const dictionary = dictionaries[language] ?? dictionaries[DEFAULT_LANGUAGE];
|
|
448
|
+
const fallback = dictionaries[DEFAULT_LANGUAGE];
|
|
449
|
+
return (key, params) => {
|
|
450
|
+
const template = dictionary[key] ?? fallback[key] ?? key;
|
|
451
|
+
return interpolate(template, params);
|
|
452
|
+
};
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const I18nContext = createContext(undefined);
|
|
456
|
+
// Provider placed at the root of the play-controller tree. `language` is the
|
|
457
|
+
// raw code coming from the host (UserInformation.language); it is normalized to
|
|
458
|
+
// a supported code here.
|
|
459
|
+
const I18nProvider = ({ language, children, }) => {
|
|
460
|
+
const value = useMemo(() => {
|
|
461
|
+
const resolved = resolveLanguage(language);
|
|
462
|
+
return { language: resolved, t: createTranslator(resolved) };
|
|
463
|
+
}, [language]);
|
|
464
|
+
return jsx(I18nContext.Provider, { value: value, children: children });
|
|
465
|
+
};
|
|
466
|
+
// Fallback value used when a component renders outside an I18nProvider (e.g. the
|
|
467
|
+
// standalone FreeRoundsIntroModal export). Defaults to English so nothing ever
|
|
468
|
+
// crashes or renders an empty label.
|
|
469
|
+
const DEFAULT_VALUE = {
|
|
470
|
+
language: DEFAULT_LANGUAGE,
|
|
471
|
+
t: createTranslator(DEFAULT_LANGUAGE),
|
|
472
|
+
};
|
|
473
|
+
const useTranslation = () => useContext(I18nContext) ?? DEFAULT_VALUE;
|
|
474
|
+
|
|
349
475
|
const usePlayController = () => {
|
|
350
476
|
const { config, autoPlay: { setNumberOfPlays, numberOfPlays, setPlayedRounds, playedRounds, selection, setState, state, }, playValues: { displayPlayAmountView, setDisplayPlayAmountView, togglePlayAmountView, }, } = useAutoManualPlayState();
|
|
477
|
+
const { t } = useTranslation();
|
|
351
478
|
const { current } = config.currencyOptions;
|
|
352
479
|
const { isPlaying, canCashout, disabledController, showAutoPlayToast, autoPlayDelay = 1500, playHook, } = config.playOptions;
|
|
353
480
|
const { playAmount: rawPlayAmount, playLimits, setPlayAmount: rawSetPlayAmount, } = playHook?.() ?? {};
|
|
@@ -380,6 +507,17 @@ const usePlayController = () => {
|
|
|
380
507
|
engaged: Boolean(freeRoundsOptions?.engaged),
|
|
381
508
|
remaining: freeRoundsOptions?.roundsRemaining ?? 0,
|
|
382
509
|
};
|
|
510
|
+
// Per-session accounting for engaged autoplay. roundsRemaining is game-driven
|
|
511
|
+
// and only decrements via an async refetch AFTER each settlement, so the live
|
|
512
|
+
// ref can still read stale-high when the loop schedules the next round. We
|
|
513
|
+
// therefore also count submissions locally: a session that started engaged
|
|
514
|
+
// may never submit more rounds than the grants available when it started,
|
|
515
|
+
// regardless of refetch timing.
|
|
516
|
+
const autoplaySessionRef = useRef({
|
|
517
|
+
startedEngaged: false,
|
|
518
|
+
grantsAtStart: 0,
|
|
519
|
+
roundsSubmitted: 0,
|
|
520
|
+
});
|
|
383
521
|
const stopAutoplay = () => {
|
|
384
522
|
isAutoplayActiveRef.current = false;
|
|
385
523
|
if (playIntervalRef.current) {
|
|
@@ -397,11 +535,23 @@ const usePlayController = () => {
|
|
|
397
535
|
if (!isAutoplayActiveRef.current) {
|
|
398
536
|
return;
|
|
399
537
|
}
|
|
400
|
-
// Hard-stop:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
538
|
+
// Hard-stop: an autoplay session that started on free rounds ends the
|
|
539
|
+
// moment the grants are exhausted or engagement ends mid-session — it must
|
|
540
|
+
// never submit a paid round. Checked BEFORE each submission so the round
|
|
541
|
+
// that consumes the last grant completes normally, then the loop halts.
|
|
542
|
+
// Reads live values via refs (closures here are stale across rounds) plus
|
|
543
|
+
// the local per-session submission count (the game's roundsRemaining
|
|
544
|
+
// refetch can lag behind the next scheduled round).
|
|
545
|
+
const session = autoplaySessionRef.current;
|
|
546
|
+
if (session.startedEngaged) {
|
|
547
|
+
const live = freeRoundsRef.current;
|
|
548
|
+
const grantsExhausted = session.roundsSubmitted >= session.grantsAtStart ||
|
|
549
|
+
live.remaining <= 0;
|
|
550
|
+
if (grantsExhausted || !live.engaged) {
|
|
551
|
+
setNumberOfPlays(AUTOPLAY_DEFAULT_PLAY_ROUNDS_COUNT);
|
|
552
|
+
stopAutoplay();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
405
555
|
}
|
|
406
556
|
if (remainingPlays < 1 || numberOfPlays === 0) {
|
|
407
557
|
setNumberOfPlays(AUTOPLAY_DEFAULT_PLAY_ROUNDS_COUNT);
|
|
@@ -410,6 +560,7 @@ const usePlayController = () => {
|
|
|
410
560
|
}
|
|
411
561
|
setPlayedRounds(currentPlayedRounds + 1);
|
|
412
562
|
setNumberOfPlays((prev) => Math.max(prev - 1, 0));
|
|
563
|
+
session.roundsSubmitted += 1;
|
|
413
564
|
config.onAutoPlay(selection, () => {
|
|
414
565
|
if (!isAutoplayActiveRef.current) {
|
|
415
566
|
return;
|
|
@@ -424,7 +575,7 @@ const usePlayController = () => {
|
|
|
424
575
|
if (selection.length === 0) {
|
|
425
576
|
showAutoPlayToast?.({
|
|
426
577
|
type: "info",
|
|
427
|
-
message: "
|
|
578
|
+
message: t("selectTileToast"),
|
|
428
579
|
});
|
|
429
580
|
return;
|
|
430
581
|
}
|
|
@@ -438,6 +589,15 @@ const usePlayController = () => {
|
|
|
438
589
|
if (isFreeRoundsEngaged && effectiveNumberOfPlays !== numberOfPlays) {
|
|
439
590
|
setNumberOfPlays(effectiveNumberOfPlays);
|
|
440
591
|
}
|
|
592
|
+
// Snapshot the free-rounds state for this session. Uses the RAW engaged
|
|
593
|
+
// flag (not isFreeRoundsEngaged, which also requires remaining > 0) so a
|
|
594
|
+
// session started while engaged with 0 grants fails closed on the first
|
|
595
|
+
// loop iteration instead of silently becoming a paid session.
|
|
596
|
+
autoplaySessionRef.current = {
|
|
597
|
+
startedEngaged: Boolean(freeRoundsOptions?.engaged),
|
|
598
|
+
grantsAtStart: freeRoundsOptions?.roundsRemaining ?? 0,
|
|
599
|
+
roundsSubmitted: 0,
|
|
600
|
+
};
|
|
441
601
|
isAutoplayActiveRef.current = true;
|
|
442
602
|
setState(AUTO_PLAY_STATE.PLAYING);
|
|
443
603
|
loopRounds(playedRounds, effectiveNumberOfPlays);
|
|
@@ -571,12 +731,6 @@ const PlayAmountControl = ({ isDisabled }) => {
|
|
|
571
731
|
state === AUTO_PLAY_STATE.PLAYING, onClick: handleTogglePlayAmount, isClassicGame: isClassicGame, children: jsx(SelectMenu, { disabled: isDisabled() }) }), jsx(Button, { className: styles_group.groupItem, onClick: () => adjustPlayAmount({ direction: -1 }), theme: "ghost", disabled: isDisabled(), children: jsx("span", { className: styles_group.x2, children: "-" }) }), jsx(Button, { className: styles_group.groupItem, onClick: () => adjustPlayAmount({ direction: 1 }), theme: "ghost", disabled: isDisabled(), children: jsx("span", { className: cx$1(styles_group.x2, styles_group.last), children: "+" }) })] }));
|
|
572
732
|
};
|
|
573
733
|
|
|
574
|
-
var AUTOPLAY_LABEL;
|
|
575
|
-
(function (AUTOPLAY_LABEL) {
|
|
576
|
-
AUTOPLAY_LABEL["START"] = "START AUTOPLAY";
|
|
577
|
-
AUTOPLAY_LABEL["STOP"] = "STOP AUTOPLAY";
|
|
578
|
-
})(AUTOPLAY_LABEL || (AUTOPLAY_LABEL = {}));
|
|
579
|
-
|
|
580
734
|
function clamp01(value) {
|
|
581
735
|
if (value < 0) {
|
|
582
736
|
return 0;
|
|
@@ -721,6 +875,7 @@ function usePlayControllerButtons(params) {
|
|
|
721
875
|
|
|
722
876
|
const AutoPlayController = () => {
|
|
723
877
|
const { config } = useAutoManualPlayState();
|
|
878
|
+
const { t } = useTranslation();
|
|
724
879
|
const { isValidPlayAmount, freeRounds, playValues: { displayPlayAmountView, togglePlayAmountView }, autoPlay: { isDisabled, state, onPlay, onStopPlay }, } = usePlayController();
|
|
725
880
|
const { current } = config.currencyOptions;
|
|
726
881
|
const roleButton = GAME_MODE.AUTOPLAY;
|
|
@@ -770,10 +925,10 @@ const AutoPlayController = () => {
|
|
|
770
925
|
}, [state]);
|
|
771
926
|
const buttonFallbackLabel = useMemo(() => {
|
|
772
927
|
if (state === AUTO_PLAY_STATE.PLAYING) {
|
|
773
|
-
return
|
|
928
|
+
return t("stopAutoplay");
|
|
774
929
|
}
|
|
775
|
-
return
|
|
776
|
-
}, [state]);
|
|
930
|
+
return t("startAutoplay");
|
|
931
|
+
}, [state, t]);
|
|
777
932
|
const handleKeyPress = useCallback((event) => {
|
|
778
933
|
if (event.code !== "Space") {
|
|
779
934
|
return;
|
|
@@ -856,7 +1011,7 @@ const AutoPlayController = () => {
|
|
|
856
1011
|
const renderButton = useMemo(() => {
|
|
857
1012
|
if (displayPlayAmountView) {
|
|
858
1013
|
const id = "selectPlayAmount";
|
|
859
|
-
const label = buttons.resolveLabel(id, "
|
|
1014
|
+
const label = buttons.resolveLabel(id, t("selectPlayAmount"));
|
|
860
1015
|
const style = buttons.resolveStyle(id, {
|
|
861
1016
|
pressed: buttons.pressedId === id,
|
|
862
1017
|
disabled: false,
|
|
@@ -874,7 +1029,7 @@ const AutoPlayController = () => {
|
|
|
874
1029
|
// STOP button keeps its cashout styling and gains a remaining-rounds
|
|
875
1030
|
// count: "STOP AUTOPLAY (N)".
|
|
876
1031
|
if (freeRounds.isEngaged && isStart) {
|
|
877
|
-
return (jsx(Button, { disabled: isButtonDisabled, className: styles_button.buttonFree, style: style, ...buttons.getPressHandlers(buttonId), onClick: buttonAction, roleType: roleButton, children: "
|
|
1032
|
+
return (jsx(Button, { disabled: isButtonDisabled, className: styles_button.buttonFree, style: style, ...buttons.getPressHandlers(buttonId), onClick: buttonAction, roleType: roleButton, children: t("startFreeAutoplay") }));
|
|
878
1033
|
}
|
|
879
1034
|
let label = buttons.resolveLabel(buttonId, buttonFallbackLabel);
|
|
880
1035
|
// Free-rounds engaged: show the remaining grant count on the STOP button
|
|
@@ -900,6 +1055,7 @@ const AutoPlayController = () => {
|
|
|
900
1055
|
handleTogglePlayAmount,
|
|
901
1056
|
isButtonDisabled,
|
|
902
1057
|
roleButton,
|
|
1058
|
+
t,
|
|
903
1059
|
]);
|
|
904
1060
|
return (jsxs(Fragment$1, { children: [jsx(PlayAmountControl, { isDisabled: () => isDisabled() || freeRounds.isEngaged }), renderButton] }));
|
|
905
1061
|
};
|
|
@@ -929,6 +1085,7 @@ const useIsRunningExternal = () => {
|
|
|
929
1085
|
const ManualPlayController = () => {
|
|
930
1086
|
const { config, doubleOrNothing } = useAutoManualPlayState();
|
|
931
1087
|
const { isRunningExternal } = useIsRunningExternal();
|
|
1088
|
+
const { t } = useTranslation();
|
|
932
1089
|
const { isValidPlayAmount, freeRounds, playValues: { displayPlayAmountView, togglePlayAmountView }, manualPlay: { isDisabled, onPlay, canCashout }, } = usePlayController();
|
|
933
1090
|
const { current } = config.currencyOptions;
|
|
934
1091
|
const roleButton = GAME_MODE.MANUAL;
|
|
@@ -1079,7 +1236,7 @@ const ManualPlayController = () => {
|
|
|
1079
1236
|
const renderButton = useMemo(() => {
|
|
1080
1237
|
if (displayPlayAmountView) {
|
|
1081
1238
|
const id = "selectPlayAmount";
|
|
1082
|
-
const label = buttons.resolveLabel(id, "
|
|
1239
|
+
const label = buttons.resolveLabel(id, t("selectPlayAmount"));
|
|
1083
1240
|
const style = buttons.resolveStyle(id, {
|
|
1084
1241
|
pressed: buttons.pressedId === id,
|
|
1085
1242
|
disabled: false,
|
|
@@ -1095,7 +1252,7 @@ const ManualPlayController = () => {
|
|
|
1095
1252
|
const isDoubleOrNothingDisabled = isCashoutDisabled ||
|
|
1096
1253
|
!!config.doubleOrNothing?.disabled ||
|
|
1097
1254
|
doubleOrNothing.state === DOUBLE_OR_NOTHING_STATE.PROGRESS;
|
|
1098
|
-
const label = buttons.resolveLabel(id, isRunningExternal ? "
|
|
1255
|
+
const label = buttons.resolveLabel(id, isRunningExternal ? t("collect") : t("cashout"));
|
|
1099
1256
|
const style = buttons.resolveStyle(id, {
|
|
1100
1257
|
pressed: buttons.pressedId === id,
|
|
1101
1258
|
disabled: isCashoutDisabled,
|
|
@@ -1104,7 +1261,7 @@ const ManualPlayController = () => {
|
|
|
1104
1261
|
[styles_button.buttonRow__cashout]: showDoubleOrNothing,
|
|
1105
1262
|
}), style: style, ...buttons.getPressHandlers(id), onClick: handleCashoutClick, roleType: roleButton, children: [label, " ", format(current.possibleWin ?? 0, current.decimals), " ", current.abbr] }), showDoubleOrNothing ? (jsx(Button, { disabled: isDoubleOrNothingDisabled, className: cx$1(styles_button.buttonDON, styles_button.buttonRow__don, {
|
|
1106
1263
|
[styles_button.buttonDON__disabled]: isDoubleOrNothingDisabled,
|
|
1107
|
-
}), onClick: handleDoubleOrNothingClick, roleType: roleButton, children: jsxs("div", { className: styles_button.buttonDON__content, children: [jsx("p", { className: styles_button.buttonDON__title, children: "
|
|
1264
|
+
}), onClick: handleDoubleOrNothingClick, roleType: roleButton, children: jsxs("div", { className: styles_button.buttonDON__content, children: [jsx("p", { className: styles_button.buttonDON__title, children: t("doubleTitle") }), jsx("p", { className: styles_button.buttonDON__subtitle, children: t("doubleSubtitle") })] }) })) : null] }));
|
|
1108
1265
|
}
|
|
1109
1266
|
{
|
|
1110
1267
|
// Game-driven autoplay loop running: the stop-autoplay control owns the
|
|
@@ -1121,9 +1278,9 @@ const ManualPlayController = () => {
|
|
|
1121
1278
|
// free round. No count on the button — the remaining count lives only
|
|
1122
1279
|
// in the footer status line.
|
|
1123
1280
|
if (freeRounds.isEngaged) {
|
|
1124
|
-
return (jsx(Button, { disabled: isButtonDisabled, className: styles_button.buttonFree, style: style, ...buttons.getPressHandlers(id), onClick: onPlay, roleType: roleButton, children: "
|
|
1281
|
+
return (jsx(Button, { disabled: isButtonDisabled, className: styles_button.buttonFree, style: style, ...buttons.getPressHandlers(id), onClick: onPlay, roleType: roleButton, children: t("playFreeRound") }));
|
|
1125
1282
|
}
|
|
1126
|
-
const label = buttons.resolveLabel(id, "
|
|
1283
|
+
const label = buttons.resolveLabel(id, t("playNow"), {
|
|
1127
1284
|
disabled: isButtonDisabled,
|
|
1128
1285
|
isPlaying: config.playOptions.isPlaying,
|
|
1129
1286
|
});
|
|
@@ -1152,6 +1309,7 @@ const ManualPlayController = () => {
|
|
|
1152
1309
|
isRunningExternal,
|
|
1153
1310
|
onPlay,
|
|
1154
1311
|
roleButton,
|
|
1312
|
+
t,
|
|
1155
1313
|
]);
|
|
1156
1314
|
return (jsxs(Fragment$1, { children: [jsx(PlayAmountControl, { isDisabled: () => isDisabled() || displayPlayAmountView || freeRounds.isEngaged }), renderButton] }));
|
|
1157
1315
|
};
|
|
@@ -1162,8 +1320,9 @@ var styles$3 = {"backdrop":"FreeRoundsIntroModal-module__backdrop___qIgNx","cont
|
|
|
1162
1320
|
// callbacks only; the SDK does no fetching. `onPlayNow` should engage
|
|
1163
1321
|
// free-rounds mode + close, `onPlayLater` should close and render the
|
|
1164
1322
|
// controller exactly as if the user had no free rounds for this session.
|
|
1165
|
-
const FreeRoundsIntroModal = ({ open, roundsRemaining, stakeCents, coinType, currency, decimals = 2, expiresAt, onPlayNow, onPlayLater, }) => {
|
|
1323
|
+
const FreeRoundsIntroModal = ({ open, roundsRemaining, stakeCents, coinType, currency, decimals = 2, expiresAt, language, onPlayNow, onPlayLater, }) => {
|
|
1166
1324
|
const handleClose = () => onPlayLater?.();
|
|
1325
|
+
const t = useMemo(() => createTranslator(resolveLanguage(language)), [language]);
|
|
1167
1326
|
const coinLabel = resolveFreeRoundsCoinLabel({ coinType, currency });
|
|
1168
1327
|
const valueDecimals = resolveFreeRoundsDecimals({
|
|
1169
1328
|
coinType,
|
|
@@ -1171,8 +1330,13 @@ const FreeRoundsIntroModal = ({ open, roundsRemaining, stakeCents, coinType, cur
|
|
|
1171
1330
|
fallback: decimals,
|
|
1172
1331
|
});
|
|
1173
1332
|
const valueDisplay = format(stakeCents / 100, valueDecimals);
|
|
1333
|
+
const valueAmount = `${valueDisplay}${coinLabel ? ` ${coinLabel}` : ""}`;
|
|
1174
1334
|
const expiresInDays = daysUntil(expiresAt);
|
|
1175
|
-
|
|
1335
|
+
const roundsUnit = roundsRemaining === 1 ? t("freeRoundsUnitOne") : t("freeRoundsUnitOther");
|
|
1336
|
+
return (jsx(Dialog, { open: open, onClose: handleClose, className: styles$3.backdrop, children: jsx("div", { className: styles$3.content, children: jsxs(DialogPanel, { className: styles$3.panel, children: [jsx(DialogTitle, { className: styles$3.header, children: t("freeRoundsModalTitle") }), jsxs("div", { className: styles$3.summary, children: [t("freeRoundsHave"), " ", jsx("span", { className: styles$3.accent, children: roundsRemaining }), " ", roundsUnit] }), jsx("div", { className: styles$3.perRound, children: t("freeRoundsPerRound", { amount: valueAmount }) }), expiresInDays !== null ? (jsx("div", { className: styles$3.expiry, children: t("freeRoundsExpiresIn", {
|
|
1337
|
+
days: expiresInDays,
|
|
1338
|
+
unit: expiresInDays === 1 ? t("dayUnitOne") : t("dayUnitOther"),
|
|
1339
|
+
}) })) : null, jsxs("div", { className: styles$3.actions, children: [jsx("button", { type: "button", className: `${styles$3.button} ${styles$3.playNow}`, onClick: onPlayNow, children: t("freeRoundsModalPlayNow") }), jsx("button", { type: "button", className: `${styles$3.button} ${styles$3.playLater}`, onClick: onPlayLater, children: t("freeRoundsModalPlayLater") })] })] }) }) }));
|
|
1176
1340
|
};
|
|
1177
1341
|
|
|
1178
1342
|
var styles_ui = {"base":"UI-module__base___wThyQ","container":"UI-module__container___gMSiK","betForm":"UI-module__betForm___hQkYd","isClassicGame":"UI-module__isClassicGame___zV1we","containerSpread":"UI-module__containerSpread___Axb7e","baseForceLeft":"UI-module__baseForceLeft___xfc-h","disabled":"UI-module__disabled___dnZJX","auto":"UI-module__auto___5peb8","freeRoundsFooter":"UI-module__freeRoundsFooter___AOprS","freeRoundsFooter__count":"UI-module__freeRoundsFooter__count___eHp9g","controllerWidgets":"UI-module__controllerWidgets___x2lBy","sideWidget":"UI-module__sideWidget___WMGRy","centerWidget":"UI-module__centerWidget___sLLUi","left":"UI-module__left___1wtUp","center":"UI-module__center___E0ILH","right":"UI-module__right___yLHvk","controls":"UI-module__controls___Z6L-V"};
|
|
@@ -1282,6 +1446,7 @@ var styles$2 = {"base":"PlayValueList-module__base___5duc6","playValuesWrapper":
|
|
|
1282
1446
|
|
|
1283
1447
|
const PlayValueList = () => {
|
|
1284
1448
|
const { config } = useAutoManualPlayState();
|
|
1449
|
+
const { t } = useTranslation();
|
|
1285
1450
|
const isClassicGame = config?.isClassicGame ?? false;
|
|
1286
1451
|
const { onChangeAmount, playAmount, totalBalance, playValues: { displayPlayAmountView, togglePlayAmountView }, } = usePlayController();
|
|
1287
1452
|
const { playLimits } = config.playOptions.playHook();
|
|
@@ -1316,7 +1481,7 @@ const PlayValueList = () => {
|
|
|
1316
1481
|
applyValue(value);
|
|
1317
1482
|
}
|
|
1318
1483
|
};
|
|
1319
|
-
return (jsx("div", { className: cx$1(styles$2.base, { [styles$2.isClassicGame]: isClassicGame }), children: jsx("div", { className: cx$1(styles$2.playValuesWrapper), onClick: handleClick, role: "listbox", "aria-label": "
|
|
1484
|
+
return (jsx("div", { className: cx$1(styles$2.base, { [styles$2.isClassicGame]: isClassicGame }), children: jsx("div", { className: cx$1(styles$2.playValuesWrapper), onClick: handleClick, role: "listbox", "aria-label": t("selectPlayAmountAria"), "data-testid": "play-value-list", children: playLimits?.[config.currencyOptions.current.code].defaultBetOptions.map((playValue) => (jsx("div", { className: cx$1(styles$2.playValue, {
|
|
1320
1485
|
[styles$2.selectedPlayValue]: playValue === playAmount,
|
|
1321
1486
|
[styles$2.disabledPlayValue]: playValue > totalBalance,
|
|
1322
1487
|
}), "data-value": playValue, role: "option", tabIndex: 0, "aria-selected": false, children: format(playValue, config.currencyOptions.current.decimals) }, `${config.currencyOptions.current.code}-${playValue}`))) }) }));
|
|
@@ -1379,6 +1544,7 @@ const generateMines = (numberOfMines) => {
|
|
|
1379
1544
|
};
|
|
1380
1545
|
|
|
1381
1546
|
const DifficultySelector = ({ playOptions, }) => {
|
|
1547
|
+
const { t } = useTranslation();
|
|
1382
1548
|
const classicGame = playOptions?.classicGame;
|
|
1383
1549
|
const disabledController = playOptions.disabledController;
|
|
1384
1550
|
const disabledMenu = classicGame?.disabledMenu ?? false;
|
|
@@ -1393,7 +1559,7 @@ const DifficultySelector = ({ playOptions, }) => {
|
|
|
1393
1559
|
}
|
|
1394
1560
|
onMinesChange(value);
|
|
1395
1561
|
}
|
|
1396
|
-
return (jsx("div", { className: style_difficulty.base, children: jsx(Selector, { currentValue: currentMines, label:
|
|
1562
|
+
return (jsx("div", { className: style_difficulty.base, children: jsx(Selector, { currentValue: currentMines, label: t("minesLabel"), values: mines, onSelect: onMinesSelected, disabled: disabledMenu || disabledController }) }));
|
|
1397
1563
|
};
|
|
1398
1564
|
|
|
1399
1565
|
const AutoManualPlayProvider = ({ children, config, }) => {
|
|
@@ -1420,6 +1586,10 @@ const AutoManualPlayProvider = ({ children, config, }) => {
|
|
|
1420
1586
|
if (isFreeRoundsEngaged) {
|
|
1421
1587
|
displayedNumberOfPlays = Math.min(displayedNumberOfPlays, freeRoundsRemaining);
|
|
1422
1588
|
}
|
|
1589
|
+
// Local translator for strings rendered by the provider itself. The provider
|
|
1590
|
+
// sits above the <I18nProvider> it renders, so it cannot use useTranslation();
|
|
1591
|
+
// descendant components (controllers, modal) use the context hook instead.
|
|
1592
|
+
const t = useMemo(() => createTranslator(resolveLanguage(config.language)), [config.language]);
|
|
1423
1593
|
const togglePlayAmountView = useCallback(() => {
|
|
1424
1594
|
setDisplayPlayAmountView((v) => !v);
|
|
1425
1595
|
}, []);
|
|
@@ -1539,43 +1709,43 @@ const AutoManualPlayProvider = ({ children, config, }) => {
|
|
|
1539
1709
|
displayPlayAmountView,
|
|
1540
1710
|
togglePlayAmountView,
|
|
1541
1711
|
]);
|
|
1542
|
-
return (jsxs(AutoManualPlayStateContext.Provider, { value: contextValue, children: [typeof children === "function" ? children(contextValue) : children, config.playOptions.displayController && (jsx("div", { className: cx$1(styles_ui.base, styles_ui.betForm, {
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1712
|
+
return (jsx(I18nProvider, { language: config.language, children: jsxs(AutoManualPlayStateContext.Provider, { value: contextValue, children: [typeof children === "function" ? children(contextValue) : children, config.playOptions.displayController && (jsx("div", { className: cx$1(styles_ui.base, styles_ui.betForm, {
|
|
1713
|
+
[styles_ui.baseForceLeft]: config?.classicGame?.forceLeftStyle,
|
|
1714
|
+
[styles_ui.isClassicGame]: config?.isClassicGame,
|
|
1715
|
+
}), style: {
|
|
1716
|
+
"--play-bottom": config.panel?.bottom ?? 0,
|
|
1717
|
+
"--play-panel-bg": hexToRgb(config.panel.bgColorHex ?? "#01243A"),
|
|
1718
|
+
"--play-panel-bg-opacity": config.panel.opacity ?? 0.5,
|
|
1719
|
+
"--play-dropdown-bg": hexToRgb(config?.classicGame?.dropdown?.bgColorHex ?? "#01243A"),
|
|
1720
|
+
}, children: jsxs("div", { className: cx$1(styles_ui.container, {
|
|
1721
|
+
[styles_ui.containerSpread]: config?.classicGame?.forceLeftStyle,
|
|
1722
|
+
}), children: [jsxs("div", { className: cx$1(styles_ui.controls), children: [jsx(PlayValueList, {}), jsxs("div", { className: cx$1(styles_ui.auto), children: [config.isClassicGame ? (jsx(DifficultySelector, { playOptions: {
|
|
1723
|
+
...config.playOptions,
|
|
1724
|
+
} })) : null, jsx(InputWithSwitch, { value: displayedNumberOfPlays, type: "number", onChange: (e) => {
|
|
1725
|
+
const next = Number(e.currentTarget.value);
|
|
1726
|
+
setNumberOfPlays(isFreeRoundsEngaged
|
|
1727
|
+
? Math.min(next, freeRoundsRemaining)
|
|
1728
|
+
: next);
|
|
1729
|
+
}, placeholder: t("numberOfPlays"), min: 0, max: isFreeRoundsEngaged ? freeRoundsRemaining : 99, disabled: config.playOptions.disabledController ||
|
|
1730
|
+
mode === GAME_MODE.MANUAL, switcherConfig: {
|
|
1731
|
+
onSwitch: toggleMode,
|
|
1732
|
+
isPlaying: isAutoPlaying || config.playOptions.isPlaying,
|
|
1733
|
+
enabled: mode !== GAME_MODE.MANUAL,
|
|
1734
|
+
disabled: config.playOptions.disabledController ||
|
|
1735
|
+
autoplayState === AUTO_PLAY_STATE.PLAYING,
|
|
1736
|
+
}, isClassicGame: config.isClassicGame, children: jsx("span", { className: cx$1({
|
|
1737
|
+
[styles_ui.disabled]: mode !== GAME_MODE.AUTOPLAY ||
|
|
1738
|
+
numberOfPlays !== Infinity ||
|
|
1739
|
+
autoplayState === AUTO_PLAY_STATE.PLAYING ||
|
|
1740
|
+
// Free-rounds autoplay is exactly grant-count: no infinite.
|
|
1741
|
+
isFreeRoundsEngaged,
|
|
1742
|
+
}), children: "\u221E" }) })] }), mode === GAME_MODE.MANUAL ? (jsx(ManualPlayController, {})) : (jsx(AutoPlayController, {})), isFreeRoundsEngaged ? (jsxs("div", { className: styles_ui.freeRoundsFooter, children: [jsx("span", { className: styles_ui.freeRoundsFooter__count, children: freeRoundsRemaining }), " ", freeRoundsRemaining === 1
|
|
1743
|
+
? "FREE ROUND LEFT"
|
|
1744
|
+
: "FREE ROUNDS LEFT"] })) : null] }), freeRoundsOptions ? (jsx(FreeRoundsIntroModal, { open: showFreeRoundsIntroModal, roundsRemaining: freeRoundsRemaining, totalRounds: freeRoundsOptions.totalRounds, stakeCents: freeRoundsOptions.stakeCents, coinType: freeRoundsOptions.coinType, currency: freeRoundsOptions.currency, decimals: config.currencyOptions.current.decimals, expiresAt: freeRoundsOptions.expiresAt, onPlayNow: freeRoundsOptions.onPlayNow, onPlayLater: freeRoundsOptions.onPlayLater })) : null, jsx(WidgetContainer, { state: autoplayState, displayPlayAmountView: displayPlayAmountView, widgets: {
|
|
1745
|
+
left: config.leftWidgets,
|
|
1746
|
+
center: config.centerWidgets,
|
|
1747
|
+
right: config.rightWidgets,
|
|
1748
|
+
} })] }) }))] }) }));
|
|
1579
1749
|
};
|
|
1580
1750
|
|
|
1581
1751
|
var styles = {"backdrop":"FreeRoundsSummaryModal-module__backdrop___tNi3I","panel":"FreeRoundsSummaryModal-module__panel___2MIEp","header":"FreeRoundsSummaryModal-module__header___-BkpP","summary":"FreeRoundsSummaryModal-module__summary___YExX9","accent":"FreeRoundsSummaryModal-module__accent___z9aBF","hint":"FreeRoundsSummaryModal-module__hint___CYWqA","close":"FreeRoundsSummaryModal-module__close___4-irR"};
|
|
@@ -1593,5 +1763,5 @@ const FreeRoundsSummaryModal = ({ totalWinAmount, coin, roundsPlayed, decimals,
|
|
|
1593
1763
|
return (jsx(Dialog, { open: true, onClose: onClose, className: styles.backdrop, children: jsxs(DialogPanel, { className: styles.panel, children: [jsx(DialogTitle, { className: styles.header, children: "Free Game Rounds" }), jsxs("div", { className: styles.summary, children: ["You won", " ", jsxs("span", { className: styles.accent, children: [amountDisplay, " ", coin] }), " ", "in ", roundsPlayed, " ", roundsPlayed === 1 ? "free game round" : "free game rounds"] }), jsx("div", { className: styles.hint, children: "Winnings have been credited to your bonus balance." }), jsx("button", { type: "button", className: styles.close, onClick: onClose, children: "Close" })] }) }));
|
|
1594
1764
|
};
|
|
1595
1765
|
|
|
1596
|
-
export { AUTO_PLAY_STATE, AutoManualPlayProvider, DOUBLE_OR_NOTHING_STATE, FreeRoundsIntroModal, FreeRoundsSummaryModal, GAME_MODE, WIDGET, format };
|
|
1766
|
+
export { AUTO_PLAY_STATE, AutoManualPlayProvider, DEFAULT_LANGUAGE, DOUBLE_OR_NOTHING_STATE, FreeRoundsIntroModal, FreeRoundsSummaryModal, GAME_MODE, I18nProvider, SUPPORTED_LANGUAGES, SUPPORTED_LANGUAGE_CODES, WIDGET, createTranslator, format, resolveLanguage, useTranslation };
|
|
1597
1767
|
//# sourceMappingURL=index.mjs.map
|