@energy8platform/platform-core 0.23.0 → 0.23.2
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/README.md +21 -0
- package/dist/index.cjs.js +60 -29
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.esm.js +60 -29
- package/dist/index.esm.js.map +1 -1
- package/dist/shell.cjs.js +60 -29
- package/dist/shell.cjs.js.map +1 -1
- package/dist/shell.d.ts +27 -1
- package/dist/shell.esm.js +60 -29
- package/dist/shell.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/shell/GameShell.ts +1 -0
- package/src/shell/components/BottomBar.ts +1 -1
- package/src/shell/components/BuyBonus.ts +24 -4
- package/src/shell/components/GameInfo.ts +7 -7
- package/src/shell/i18n.ts +1 -0
- package/src/shell/shell.css.ts +25 -18
- package/src/shell/types.ts +27 -0
|
@@ -28,7 +28,7 @@ export function openGameInfoModal(shell: GameShell): HTMLElement {
|
|
|
28
28
|
|
|
29
29
|
function renderSection(shell: GameShell, s: GameInfoSection): HTMLElement {
|
|
30
30
|
switch (s.type) {
|
|
31
|
-
case 'modes': return sectionModes(s.modes, sec('info-modes', s.title, shell.t('Modes')));
|
|
31
|
+
case 'modes': return sectionModes(shell, s.modes, sec('info-modes', s.title, shell.t('Modes')));
|
|
32
32
|
case 'controls': return sectionControls(shell, sec('info-controls', s.title, shell.t('Controls')));
|
|
33
33
|
case 'paytable': return sectionPaytable(s.rows, sec('info-paytable', s.title, shell.t('Paytable')));
|
|
34
34
|
case 'wins': return sectionWins(s, sec('info-wins', s.title, shell.t(winFallbackTitle(s.kind))));
|
|
@@ -46,20 +46,20 @@ function sec(ge: string, title: string | undefined, fallback: string): HTMLEleme
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// ── modes (rows — varying description lengths read better than fixed cards) ────
|
|
49
|
-
function sectionModes(modes: GameMode[], el: HTMLElement): HTMLElement {
|
|
49
|
+
function sectionModes(shell: GameShell, modes: GameMode[], el: HTMLElement): HTMLElement {
|
|
50
50
|
const list = document.createElement('div'); list.className = 'ge-gi-modes';
|
|
51
|
-
for (const m of modes) list.appendChild(modeRow(m));
|
|
51
|
+
for (const m of modes) list.appendChild(modeRow(shell, m));
|
|
52
52
|
el.appendChild(list);
|
|
53
53
|
return el;
|
|
54
54
|
}
|
|
55
|
-
function modeRow(m: GameMode): HTMLElement {
|
|
55
|
+
function modeRow(shell: GameShell, m: GameMode): HTMLElement {
|
|
56
56
|
const row = document.createElement('div'); row.className = 'ge-gi-mode';
|
|
57
57
|
const stat = (label: string, val: string) =>
|
|
58
58
|
`<span class="ge-gi-mode-st"><span>${label}</span><b>${val}</b></span>`;
|
|
59
59
|
let stats = '';
|
|
60
|
-
if (m.price != null) stats += stat('Price', m.price);
|
|
61
|
-
if (typeof m.rtp === 'number') stats += stat('RTP', `${m.rtp}%`);
|
|
62
|
-
if (m.maxWin != null) stats += stat('Max win', m.maxWin);
|
|
60
|
+
if (m.price != null) stats += stat(shell.t('Price'), m.price);
|
|
61
|
+
if (typeof m.rtp === 'number') stats += stat(shell.t('RTP'), `${m.rtp}%`);
|
|
62
|
+
if (m.maxWin != null) stats += stat(shell.t('Max win'), m.maxWin);
|
|
63
63
|
row.innerHTML =
|
|
64
64
|
`<div class="ge-gi-mode-top"><span class="ge-gi-mode-h">${m.title}</span>` +
|
|
65
65
|
(stats ? `<span class="ge-gi-mode-stats">${stats}</span>` : '') + '</div>' +
|
package/src/shell/i18n.ts
CHANGED
package/src/shell/shell.css.ts
CHANGED
|
@@ -7,6 +7,7 @@ export const SHELL_ROOT_ID = '__ge-game-shell__';
|
|
|
7
7
|
export const SHELL_CSS = SHELL_FONT_CSS + `
|
|
8
8
|
#${SHELL_ROOT_ID} {
|
|
9
9
|
position: absolute; inset: 0;
|
|
10
|
+
container-type: size; /* query container → centred modals size in cq units (responsive on every screen) */
|
|
10
11
|
pointer-events: none; z-index: 9000;
|
|
11
12
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
12
13
|
color: var(--shell-fg);
|
|
@@ -225,16 +226,18 @@ export const SHELL_CSS = SHELL_FONT_CSS + `
|
|
|
225
226
|
scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }
|
|
226
227
|
/* the one knob that scales the whole card — cqh measures the overlay (popout frame), not the
|
|
227
228
|
browser window, so cards shrink to fit the real container height. */
|
|
228
|
-
#${SHELL_ROOT_ID} .ge-bb-grid .ge-bonus-card { flex:0 0
|
|
229
|
-
font-size:clamp(7px,
|
|
229
|
+
#${SHELL_ROOT_ID} .ge-bb-grid .ge-bonus-card { flex:0 0 18em; scroll-snap-align:start;
|
|
230
|
+
font-size:clamp(7px, 3.6cqh, 12px); }
|
|
230
231
|
/* mobile: vertical stack at a fixed, readable size — scroll the list, don't shrink the cards */
|
|
231
232
|
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid { display:flex; flex-direction:column; gap:14px; overflow:visible; }
|
|
232
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid .ge-bonus-card { flex:0 0 auto; font-size:
|
|
233
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid .ge-bonus-card { flex:0 0 auto; font-size:12px; }
|
|
233
234
|
#${SHELL_ROOT_ID} .ge-bonus-card { display:flex; flex-direction:column; border-radius:1.4em; overflow:hidden;
|
|
234
235
|
background:var(--shell-plaque-glass); border:1px solid var(--shell-plaque-line); color:#fff; text-align:center;
|
|
235
236
|
pointer-events:auto; cursor:pointer; transition:box-shadow .12s ease, background .12s ease; }
|
|
236
237
|
#${SHELL_ROOT_ID} .ge-bonus-card:hover:not(.ge-bonus-off) {
|
|
237
238
|
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
239
|
+
/* custom card (BonusOption.custom): keep grid sizing + accent vars, drop the default chrome so the game owns the UI */
|
|
240
|
+
#${SHELL_ROOT_ID} .ge-bonus-card--custom { background:none; border:none; cursor:default; }
|
|
238
241
|
#${SHELL_ROOT_ID} .ge-bonus-body { display:flex; flex-direction:column; align-items:center; flex:1; padding:1.25em 1.1em .9em; }
|
|
239
242
|
#${SHELL_ROOT_ID} .ge-bonus-title { font-size:1.3em; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
|
|
240
243
|
color:var(--card-acc); margin-bottom:.75em; }
|
|
@@ -336,40 +339,44 @@ export const SHELL_CSS = SHELL_FONT_CSS + `
|
|
|
336
339
|
align-items:center; justify-content:center; padding:clamp(10px,4vh,24px); box-sizing:border-box;
|
|
337
340
|
background:rgba(12,17,28,.5); backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%);
|
|
338
341
|
-webkit-backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%); animation:ge-ov-in .16s ease-out; }
|
|
339
|
-
/*
|
|
340
|
-
|
|
341
|
-
|
|
342
|
+
/* Card sizes in cq units of the shell root → responsive on EVERY screen, not just popouts. The
|
|
343
|
+
card's font-size is the one knob (clamped for readability); everything inside is em-relative so
|
|
344
|
+
the whole card scales as a unit. GameShell.fitModal() still transform-scales it down as a
|
|
345
|
+
backstop for very short popouts. */
|
|
346
|
+
#${SHELL_ROOT_ID} .ge-modal-card { font-size:clamp(11px, 2cqmin, 15px); width:100%; max-width:28em; box-sizing:border-box;
|
|
347
|
+
overflow:hidden; transform-origin:center center; background:var(--shell-plaque-solid); border-radius:1.3em;
|
|
348
|
+
display:flex; flex-direction:column; }
|
|
342
349
|
/* ✕ pinned to the overlay corner (the screen), not the card */
|
|
343
350
|
#${SHELL_ROOT_ID} .ge-modal-close { position:absolute; top:12px; right:12px; z-index:2; width:36px; height:36px;
|
|
344
351
|
border:none; border-radius:50%; cursor:pointer; pointer-events:auto; background:var(--shell-plaque-dark); color:#fff;
|
|
345
352
|
display:flex; align-items:center; justify-content:center; font-size:20px; transition:background .12s ease, color .12s ease; }
|
|
346
353
|
#${SHELL_ROOT_ID} .ge-modal-close:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
347
|
-
#${SHELL_ROOT_ID} .ge-modal-body { padding:
|
|
354
|
+
#${SHELL_ROOT_ID} .ge-modal-body { padding:1.2em; display:flex; flex-direction:column; gap:1.05em; }
|
|
348
355
|
#${SHELL_ROOT_ID} .ge-modal-title { margin:0; text-align:center; color:var(--card-acc, var(--shell-accent));
|
|
349
|
-
font-weight:800; letter-spacing:.04em; text-transform:uppercase; font-size:
|
|
350
|
-
#${SHELL_ROOT_ID} .ge-modal-text { margin:0; text-align:center; color:rgba(255,255,255,.85); font-size
|
|
351
|
-
#${SHELL_ROOT_ID} .ge-sheet-grid { display:grid; gap
|
|
356
|
+
font-weight:800; letter-spacing:.04em; text-transform:uppercase; font-size:1.2em; }
|
|
357
|
+
#${SHELL_ROOT_ID} .ge-modal-text { margin:0; text-align:center; color:rgba(255,255,255,.85); font-size:.93em; line-height:1.5; }
|
|
358
|
+
#${SHELL_ROOT_ID} .ge-sheet-grid { display:grid; gap:.65em; }
|
|
352
359
|
#${SHELL_ROOT_ID} .ge-chip { pointer-events:auto; cursor:pointer; border:1px solid var(--shell-plaque-line);
|
|
353
|
-
border-radius
|
|
354
|
-
font-variant-numeric:tabular-nums; padding
|
|
360
|
+
border-radius:.8em; background:rgba(255,255,255,.04); color:#fff; font-size:1em; font-weight:700;
|
|
361
|
+
font-variant-numeric:tabular-nums; padding:.8em .55em; transition:background .12s ease, border-color .12s ease; }
|
|
355
362
|
#${SHELL_ROOT_ID} .ge-chip:hover { background:var(--shell-plaque-glass-hover); }
|
|
356
363
|
#${SHELL_ROOT_ID} .ge-chip.ge-on { border-color:var(--shell-accent); background:var(--shell-accent); color:#fff; }
|
|
357
364
|
/* full-bleed footer button(s), flush to the card's bottom edge (card clips the corners) */
|
|
358
365
|
#${SHELL_ROOT_ID} .ge-modal-actions { display:flex; }
|
|
359
366
|
#${SHELL_ROOT_ID} .ge-modal-actions > * { flex:1; }
|
|
360
|
-
#${SHELL_ROOT_ID} .ge-modal-btn { width:100%; border:none; padding:
|
|
367
|
+
#${SHELL_ROOT_ID} .ge-modal-btn { width:100%; border:none; padding:1.05em; font-size:1em; font-weight:800;
|
|
361
368
|
letter-spacing:.04em; text-transform:uppercase; cursor:pointer; pointer-events:auto; transition:filter .12s ease; }
|
|
362
369
|
#${SHELL_ROOT_ID} .ge-modal-btn:hover:not([disabled]) { filter:brightness(1.08); }
|
|
363
370
|
#${SHELL_ROOT_ID} .ge-modal-btn--accent { background:var(--card-acc, var(--shell-accent)); color:#fff; }
|
|
364
371
|
#${SHELL_ROOT_ID} .ge-modal-btn--ghost { background:var(--shell-plaque-glass-hover); color:#fff; }
|
|
365
372
|
/* replay summary — label/value rows, accented total-win row */
|
|
366
373
|
#${SHELL_ROOT_ID} .ge-replay-rows { display:flex; flex-direction:column; }
|
|
367
|
-
#${SHELL_ROOT_ID} .ge-replay-row { display:flex; justify-content:space-between; align-items:baseline; gap:
|
|
374
|
+
#${SHELL_ROOT_ID} .ge-replay-row { display:flex; justify-content:space-between; align-items:baseline; gap:1.05em; padding:.73em .13em; }
|
|
368
375
|
#${SHELL_ROOT_ID} .ge-replay-row + .ge-replay-row { border-top:1px solid var(--shell-plaque-line); }
|
|
369
|
-
#${SHELL_ROOT_ID} .ge-replay-row span { color:var(--shell-plaque-label); text-transform:uppercase; letter-spacing:.07em; font-size
|
|
370
|
-
#${SHELL_ROOT_ID} .ge-replay-row b { color:#fff; font-weight:800; font-size:
|
|
371
|
-
#${SHELL_ROOT_ID} .ge-replay-total span { color:#fff; font-size
|
|
372
|
-
#${SHELL_ROOT_ID} .ge-replay-total b { color:var(--shell-accent); font-size:
|
|
376
|
+
#${SHELL_ROOT_ID} .ge-replay-row span { color:var(--shell-plaque-label); text-transform:uppercase; letter-spacing:.07em; font-size:.73em; font-weight:700; }
|
|
377
|
+
#${SHELL_ROOT_ID} .ge-replay-row b { color:#fff; font-weight:800; font-size:1em; font-variant-numeric:tabular-nums; }
|
|
378
|
+
#${SHELL_ROOT_ID} .ge-replay-total span { color:#fff; font-size:.8em; }
|
|
379
|
+
#${SHELL_ROOT_ID} .ge-replay-total b { color:var(--shell-accent); font-size:1.27em; }
|
|
373
380
|
|
|
374
381
|
#${SHELL_ROOT_ID}.ge-shell-hidden { opacity:0; pointer-events:none; transition:opacity .25s ease; }
|
|
375
382
|
`;
|
package/src/shell/types.ts
CHANGED
|
@@ -26,6 +26,29 @@ export interface BonusOption {
|
|
|
26
26
|
priceMultiplier: number;
|
|
27
27
|
/** Per-option accent override. Falls back to the type default (bonus → purple, feature → gold). */
|
|
28
28
|
accentColor?: string;
|
|
29
|
+
/** Override the card UI. Return the card's inner content; the shell keeps the grid wrapper,
|
|
30
|
+
* accent vars and live re-pricing, and runs the normal buy flow when you call `ctx.select()`. */
|
|
31
|
+
custom?: (ctx: BonusCardContext) => HTMLElement;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Context passed to a `BonusOption.custom` renderer. Render the card however you like and wire
|
|
35
|
+
* your own control to `select()` — the buy/confirm flow stays internal to the shell. */
|
|
36
|
+
export interface BonusCardContext {
|
|
37
|
+
bonus: BonusOption;
|
|
38
|
+
/** Current bet. */
|
|
39
|
+
bet: number;
|
|
40
|
+
/** Card price = `bonus.priceMultiplier × bet`. */
|
|
41
|
+
price: number;
|
|
42
|
+
/** `price` formatted in the shell currency. */
|
|
43
|
+
priceText: string;
|
|
44
|
+
/** True when the option can't be bought right now (unaffordable / busy / buy-bonus disabled);
|
|
45
|
+
* reflect it in your UI. `select()` is a no-op while disabled. */
|
|
46
|
+
disabled: boolean;
|
|
47
|
+
/** Card accent (per-option override or the type default); also set as the `--card-acc` CSS var. */
|
|
48
|
+
accent: string;
|
|
49
|
+
/** Proceed through the shell's normal flow: opens the confirm modal, then emits `buyBonusSelect`
|
|
50
|
+
* / activates the feature. No-op while `disabled`. */
|
|
51
|
+
select: () => void;
|
|
29
52
|
}
|
|
30
53
|
|
|
31
54
|
export interface ThemeConfig {
|
|
@@ -168,6 +191,10 @@ export interface ShellConfig {
|
|
|
168
191
|
win: number;
|
|
169
192
|
mode: ShellMode;
|
|
170
193
|
features: ShellFeatures;
|
|
194
|
+
/** Override the BUY BONUS bar button's action: when set, tapping it calls this instead of
|
|
195
|
+
* opening the built-in buy-bonus overlay (e.g. the game shows its own bonus UI). The button
|
|
196
|
+
* is shown whenever this OR `features.buyBonus` is set. */
|
|
197
|
+
onBonusBuy?: () => void;
|
|
171
198
|
}
|
|
172
199
|
|
|
173
200
|
export interface ShellState {
|