@energy8platform/shell 0.9.0 → 0.11.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/html.cjs.js +276 -8
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +70 -4
- package/dist/html.esm.js +273 -9
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +78 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +70 -4
- package/dist/index.esm.js +75 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +302 -5
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +70 -4
- package/dist/pixi.esm.js +299 -6
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/index.ts +4 -0
- package/src/core/locales.ts +14 -0
- package/src/core/scrollHint.ts +91 -0
- package/src/core/version.ts +1 -1
- package/src/ui/html/components/BuyBonus.ts +11 -1
- package/src/ui/html/components/GameInfo.ts +3 -1
- package/src/ui/html/components/pickers.ts +4 -0
- package/src/ui/html/primitives.ts +16 -5
- package/src/ui/html/scroll-affordance.ts +144 -0
- package/src/ui/html/shell.css.ts +65 -1
- package/src/ui/pixi/components/BuyBonus.ts +20 -1
- package/src/ui/pixi/primitives/overlay.ts +1 -1
- package/src/ui/pixi/primitives/scroll-affordance.ts +244 -0
- package/src/ui/pixi/primitives/scroll.ts +21 -3
package/dist/pixi.cjs.js
CHANGED
|
@@ -1309,6 +1309,19 @@ const D = {
|
|
|
1309
1309
|
L4: 'The game display is not representative of any physical device and is for illustrative purposes only.',
|
|
1310
1310
|
L5: 'Winnings are settled according to the amount received from the Remote Game Server and not from events within the web browser.',
|
|
1311
1311
|
};
|
|
1312
|
+
/**
|
|
1313
|
+
* The disclaimer body every launch shows, in source English — and the SAME strings that key the
|
|
1314
|
+
* translations below. One constant for both on purpose: a host that kept its own copy would drift
|
|
1315
|
+
* from these keys by one character and the lookup would miss in silence, leaving a player on any
|
|
1316
|
+
* language with English legal text. Certification asks for the opposite ("if multiple languages are
|
|
1317
|
+
* supported, the disclaimer is translated and displayed in each language").
|
|
1318
|
+
*
|
|
1319
|
+
* Brand-free by construction. Stake's own template ends with a seventh line — "TM and © {year}
|
|
1320
|
+
* Stake Engine." — and that one belongs to a Stake launch alone: it arrives with the Stake bridge's
|
|
1321
|
+
* `INIT.config.disclaimerLines`, which outrank this default, and renders verbatim (see the host's
|
|
1322
|
+
* `isBrandLine`). Every other platform gets the same mandated wording without someone else's mark.
|
|
1323
|
+
*/
|
|
1324
|
+
const DISCLAIMER_LINES = [D.L1, D.L2, D.L3, D.L4, D.L5];
|
|
1312
1325
|
const DISCLAIMER_LOCALES = {
|
|
1313
1326
|
da: {
|
|
1314
1327
|
[D.L1]: 'Fejlfunktion annullerer alle gevinster og spil.',
|
|
@@ -1726,7 +1739,7 @@ function keyboardCapable(win = typeof window === 'undefined' ? undefined : windo
|
|
|
1726
1739
|
|
|
1727
1740
|
// AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
|
|
1728
1741
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
1729
|
-
const PACKAGE_VERSION = '0.
|
|
1742
|
+
const PACKAGE_VERSION = '0.11.0';
|
|
1730
1743
|
|
|
1731
1744
|
/** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
|
|
1732
1745
|
function resolveConfig(config) {
|
|
@@ -2212,6 +2225,66 @@ function placePopover(anchor, surface, size, pointer = null) {
|
|
|
2212
2225
|
return { x, y, maxH, arrowX, below };
|
|
2213
2226
|
}
|
|
2214
2227
|
|
|
2228
|
+
/**
|
|
2229
|
+
* What a scrollable region should ADVERTISE about itself.
|
|
2230
|
+
*
|
|
2231
|
+
* A slot in a Stake popout is 400×225. At that size the game-info overlay holds twelve screens of
|
|
2232
|
+
* content, the buy-bonus switches to a vertical card stack, and the menu popover hides its last
|
|
2233
|
+
* row — all of them scroll, and (before this module) none of them said so. macOS makes it worse:
|
|
2234
|
+
* overlay scrollbars stay invisible until something is already scrolling, so the very affordance a
|
|
2235
|
+
* player needs BEFORE they touch anything is the one the OS withholds. A certification reviewer
|
|
2236
|
+
* reads that as content the player can't reach.
|
|
2237
|
+
*
|
|
2238
|
+
* The maths lives here, apart from both renderers, for one reason: the DOM shell reads
|
|
2239
|
+
* `scrollTop`/`scrollHeight` while the Pixi shell tracks its own offset against a mask, and those
|
|
2240
|
+
* two must never disagree about whether a fade belongs at the bottom edge. Renderers decide how a
|
|
2241
|
+
* thumb LOOKS; this decides when there is one and where it sits.
|
|
2242
|
+
*/
|
|
2243
|
+
/** Fractions of a pixel are layout rounding, not reachable content. */
|
|
2244
|
+
const EPSILON = 1;
|
|
2245
|
+
/** Landing within half a pixel of an edge counts as arriving: a browser settles a flung scroll on
|
|
2246
|
+
* 199.5 of 200, and a fade left glowing over content the player has already reached reads as a
|
|
2247
|
+
* bug. */
|
|
2248
|
+
const EDGE_EPSILON = 0.5;
|
|
2249
|
+
/**
|
|
2250
|
+
* Shortest thumb we will draw, as a fraction of its track.
|
|
2251
|
+
*
|
|
2252
|
+
* The honest ratio for game info at Popout S is 8% — an 18px speck on a 226px track, which reads
|
|
2253
|
+
* as a rendering artifact rather than a scrollbar. Floored at 18% it stays recognisably a thumb,
|
|
2254
|
+
* and it still travels the whole track, so the position it reports remains truthful even though
|
|
2255
|
+
* its length no longer is. That is the right trade: length is decoration, position is information.
|
|
2256
|
+
*/
|
|
2257
|
+
const SCROLL_THUMB_MIN = 0.18;
|
|
2258
|
+
/** Resolve one axis of a scroll region into everything a renderer needs to draw its affordance. */
|
|
2259
|
+
function scrollHint(m) {
|
|
2260
|
+
const view = Math.max(0, m.clientHeight);
|
|
2261
|
+
const content = Math.max(0, m.scrollHeight);
|
|
2262
|
+
const maxScroll = Math.max(0, content - view);
|
|
2263
|
+
if (maxScroll <= EPSILON || view <= 0) {
|
|
2264
|
+
// Both edges are "the edge" when there is nowhere to go — callers gate every fade on the
|
|
2265
|
+
// matching flag, so a region that fits draws nothing without needing to check `overflowing`.
|
|
2266
|
+
return { overflowing: false, atStart: true, atEnd: true, thumbSize: 1, thumbOffset: 0, maxScroll: 0 };
|
|
2267
|
+
}
|
|
2268
|
+
const at = Math.max(0, Math.min(maxScroll, m.scrollTop));
|
|
2269
|
+
const thumbSize = Math.min(1, Math.max(SCROLL_THUMB_MIN, view / content));
|
|
2270
|
+
const progress = at / maxScroll;
|
|
2271
|
+
return {
|
|
2272
|
+
overflowing: true,
|
|
2273
|
+
atStart: at <= EDGE_EPSILON,
|
|
2274
|
+
atEnd: maxScroll - at <= EDGE_EPSILON,
|
|
2275
|
+
thumbSize,
|
|
2276
|
+
thumbOffset: progress * (1 - thumbSize),
|
|
2277
|
+
maxScroll,
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function scrollEdge(h) {
|
|
2281
|
+
if (!h.overflowing)
|
|
2282
|
+
return 'none';
|
|
2283
|
+
if (h.atStart)
|
|
2284
|
+
return 'start';
|
|
2285
|
+
return h.atEnd ? 'end' : 'mid';
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2215
2288
|
const NO_INSET = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
2216
2289
|
/** Create a shell with an explicit renderer instance (custom or a built-in HtmlRenderer/PixiRenderer).
|
|
2217
2290
|
* Built-in renderers also have the createGameShell/createPixiShell sugar in /html and /pixi.
|
|
@@ -4137,11 +4210,207 @@ function SPIN_POP_MOBILE() {
|
|
|
4137
4210
|
return (SPIN - M_CTRL_H) / 2; // 11
|
|
4138
4211
|
}
|
|
4139
4212
|
|
|
4213
|
+
/** Thickness of the thumb, and how far it is inset from the edge it rides. */
|
|
4214
|
+
const THUMB_THICK = 4;
|
|
4215
|
+
const THUMB_INSET = 3;
|
|
4216
|
+
/** Shortest thumb we will draw in pixels — the fractional floor still degenerates on a tiny frame. */
|
|
4217
|
+
const THUMB_MIN_PX = 22;
|
|
4218
|
+
/** Depth of the edge scrim. */
|
|
4219
|
+
const SCRIM = 34;
|
|
4220
|
+
const SCRIM_ALPHA = 0.72;
|
|
4221
|
+
/** The overlay chrome — plaques, bar, backdrop — is scheme-independent in this shell (see
|
|
4222
|
+
* resolveTheme: those tokens are fixed, only the palette behind them flips). The affordance is
|
|
4223
|
+
* chrome, so it is a fixed neutral too: white marks over a tint of the same hue as the veil, which
|
|
4224
|
+
* makes the fade read as content dissolving INTO the overlay rather than as a grey band on top. */
|
|
4225
|
+
const SCRIM_TINT = 0x0c111c;
|
|
4226
|
+
const CUE_R = 11;
|
|
4227
|
+
const CUE_BOB = 4;
|
|
4228
|
+
const CUE_PERIOD = 1600; // ms
|
|
4229
|
+
/**
|
|
4230
|
+
* The scrim's alpha ramp, densest AT the edge and clear by `depth` inward.
|
|
4231
|
+
*
|
|
4232
|
+
* Exported because the direction is the whole point and is easy to get backwards: an inverted ramp
|
|
4233
|
+
* draws a hard-edged dark band floating over the content instead of a fade into the frame, and it
|
|
4234
|
+
* looks deliberate enough that nobody questions it. The squared falloff keeps the outer half of the
|
|
4235
|
+
* ramp faint, so the scrim reads as content thinning out rather than as a panel laid on top.
|
|
4236
|
+
*/
|
|
4237
|
+
function scrimRamp(depth) {
|
|
4238
|
+
const slices = Math.max(1, Math.round(depth));
|
|
4239
|
+
const thickness = depth / slices;
|
|
4240
|
+
const out = [];
|
|
4241
|
+
for (let i = 0; i < slices; i++) {
|
|
4242
|
+
const offset = i * thickness;
|
|
4243
|
+
const t = 1 - (offset + thickness / 2) / depth; // 1 at the edge → 0 at `depth`
|
|
4244
|
+
out.push({ offset, thickness, alpha: SCRIM_ALPHA * t * t });
|
|
4245
|
+
}
|
|
4246
|
+
return out;
|
|
4247
|
+
}
|
|
4248
|
+
class ScrollAffordanceView extends pixi_js.Container {
|
|
4249
|
+
thumb = new pixi_js.Graphics();
|
|
4250
|
+
scrim = new pixi_js.Graphics();
|
|
4251
|
+
cue = new pixi_js.Container();
|
|
4252
|
+
ticker;
|
|
4253
|
+
bounds = null;
|
|
4254
|
+
cueOn = false;
|
|
4255
|
+
// Once the region has moved the player knows the gesture; re-offering it on every return to the
|
|
4256
|
+
// top would nag rather than inform.
|
|
4257
|
+
cueRetired = false;
|
|
4258
|
+
cueBaseY = 0;
|
|
4259
|
+
elapsed = 0;
|
|
4260
|
+
tick;
|
|
4261
|
+
constructor(ticker) {
|
|
4262
|
+
super();
|
|
4263
|
+
this.ticker = ticker;
|
|
4264
|
+
this.eventMode = 'none'; // decoration: never intercept the drag it is advertising
|
|
4265
|
+
this.addChild(this.scrim, this.thumb, this.cue);
|
|
4266
|
+
this.buildCue();
|
|
4267
|
+
this.visible = false;
|
|
4268
|
+
}
|
|
4269
|
+
/** Where the thumb is drawn, in the parent's space — `null` when nothing is drawn. */
|
|
4270
|
+
get thumbBounds() {
|
|
4271
|
+
return this.visible ? this.bounds : null;
|
|
4272
|
+
}
|
|
4273
|
+
get cueVisible() {
|
|
4274
|
+
return this.visible && this.cueOn;
|
|
4275
|
+
}
|
|
4276
|
+
/** Re-draw for a viewport and a hint. Cheap enough to call on every scroll frame. */
|
|
4277
|
+
update(vp, hint) {
|
|
4278
|
+
if (this.destroyed)
|
|
4279
|
+
return;
|
|
4280
|
+
if (!hint.overflowing) {
|
|
4281
|
+
this.visible = false;
|
|
4282
|
+
this.bounds = null;
|
|
4283
|
+
this.showCue(false);
|
|
4284
|
+
return;
|
|
4285
|
+
}
|
|
4286
|
+
this.visible = true;
|
|
4287
|
+
// Any movement off the start retires the cue permanently.
|
|
4288
|
+
if (!hint.atStart)
|
|
4289
|
+
this.cueRetired = true;
|
|
4290
|
+
this.drawThumb(vp, hint);
|
|
4291
|
+
this.drawScrim(vp, hint);
|
|
4292
|
+
this.placeCue(vp);
|
|
4293
|
+
this.showCue(hint.atStart && !this.cueRetired);
|
|
4294
|
+
}
|
|
4295
|
+
drawThumb(vp, hint) {
|
|
4296
|
+
const along = vp.axis === 'y' ? vp.h : vp.w;
|
|
4297
|
+
const track = Math.max(0, along - THUMB_INSET * 2);
|
|
4298
|
+
const len = Math.max(Math.min(track, THUMB_MIN_PX), track * hint.thumbSize);
|
|
4299
|
+
// Recover the travel from the (possibly floored) length so the thumb still reaches both ends.
|
|
4300
|
+
const travel = Math.max(0, track - len);
|
|
4301
|
+
const progress = hint.thumbSize < 1 ? hint.thumbOffset / (1 - hint.thumbSize) : 0;
|
|
4302
|
+
const at = THUMB_INSET + travel * progress;
|
|
4303
|
+
this.bounds =
|
|
4304
|
+
vp.axis === 'y'
|
|
4305
|
+
? { x: vp.x + vp.w - THUMB_INSET - THUMB_THICK, y: vp.y + at, w: THUMB_THICK, h: len }
|
|
4306
|
+
: { x: vp.x + at, y: vp.y + vp.h - THUMB_INSET - THUMB_THICK, w: len, h: THUMB_THICK };
|
|
4307
|
+
const b = this.bounds;
|
|
4308
|
+
this.thumb.clear();
|
|
4309
|
+
// A faint track behind the thumb: on a dark overlay a lone thumb can read as a stray highlight;
|
|
4310
|
+
// with the groove behind it, it reads as a scrollbar.
|
|
4311
|
+
if (vp.axis === 'y') {
|
|
4312
|
+
this.thumb
|
|
4313
|
+
.roundRect(b.x, vp.y + THUMB_INSET, THUMB_THICK, track, THUMB_THICK / 2)
|
|
4314
|
+
.fill({ color: 0xffffff, alpha: 0.1 });
|
|
4315
|
+
}
|
|
4316
|
+
else {
|
|
4317
|
+
this.thumb
|
|
4318
|
+
.roundRect(vp.x + THUMB_INSET, b.y, track, THUMB_THICK, THUMB_THICK / 2)
|
|
4319
|
+
.fill({ color: 0xffffff, alpha: 0.1 });
|
|
4320
|
+
}
|
|
4321
|
+
this.thumb.roundRect(b.x, b.y, b.w, b.h, THUMB_THICK / 2).fill({ color: 0xffffff, alpha: 0.55 });
|
|
4322
|
+
}
|
|
4323
|
+
/** Stacked 1px slices rather than a gradient fill: no texture, no backend-specific paths, and at
|
|
4324
|
+
* one device pixel per slice there is nothing left to band. */
|
|
4325
|
+
drawScrim(vp, hint) {
|
|
4326
|
+
this.scrim.clear();
|
|
4327
|
+
const depth = Math.min(SCRIM, (vp.axis === 'y' ? vp.h : vp.w) / 3);
|
|
4328
|
+
if (depth <= 0)
|
|
4329
|
+
return;
|
|
4330
|
+
for (const { offset, thickness, alpha } of scrimRamp(depth)) {
|
|
4331
|
+
// `offset` is measured INWARD from whichever edge is being faded.
|
|
4332
|
+
if (!hint.atEnd) {
|
|
4333
|
+
if (vp.axis === 'y')
|
|
4334
|
+
this.scrim.rect(vp.x, vp.y + vp.h - offset - thickness, vp.w, thickness);
|
|
4335
|
+
else
|
|
4336
|
+
this.scrim.rect(vp.x + vp.w - offset - thickness, vp.y, thickness, vp.h);
|
|
4337
|
+
this.scrim.fill({ color: SCRIM_TINT, alpha });
|
|
4338
|
+
}
|
|
4339
|
+
if (!hint.atStart) {
|
|
4340
|
+
if (vp.axis === 'y')
|
|
4341
|
+
this.scrim.rect(vp.x, vp.y + offset, vp.w, thickness);
|
|
4342
|
+
else
|
|
4343
|
+
this.scrim.rect(vp.x + offset, vp.y, thickness, vp.h);
|
|
4344
|
+
this.scrim.fill({ color: SCRIM_TINT, alpha });
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
buildCue() {
|
|
4349
|
+
const disc = new pixi_js.Graphics();
|
|
4350
|
+
disc.circle(0, 0, CUE_R).fill({ color: 0x0b0f18, alpha: 0.9 });
|
|
4351
|
+
disc.circle(0, 0, CUE_R).stroke({ color: 0xffffff, alpha: 0.22, width: 1 });
|
|
4352
|
+
const size = Math.round(CUE_R * 1.3);
|
|
4353
|
+
const glyph = makeIcon('chevronDown', size, '#ffffff');
|
|
4354
|
+
glyph.position.set(-size / 2, -size / 2);
|
|
4355
|
+
this.cue.addChild(disc, glyph);
|
|
4356
|
+
this.cue.visible = false;
|
|
4357
|
+
}
|
|
4358
|
+
placeCue(vp) {
|
|
4359
|
+
// Bottom-centre for a vertical region; hugging the trailing edge for a horizontal one.
|
|
4360
|
+
if (vp.axis === 'y') {
|
|
4361
|
+
this.cue.x = vp.x + vp.w / 2;
|
|
4362
|
+
this.cueBaseY = vp.y + vp.h - CUE_R - 6;
|
|
4363
|
+
}
|
|
4364
|
+
else {
|
|
4365
|
+
this.cue.x = vp.x + vp.w - CUE_R - 6;
|
|
4366
|
+
this.cueBaseY = vp.y + vp.h / 2;
|
|
4367
|
+
this.cue.rotation = -Math.PI / 2; // chevron points the way the strip moves
|
|
4368
|
+
}
|
|
4369
|
+
this.cue.y = this.cueBaseY;
|
|
4370
|
+
}
|
|
4371
|
+
showCue(on) {
|
|
4372
|
+
if (on === this.cueOn)
|
|
4373
|
+
return;
|
|
4374
|
+
this.cueOn = on;
|
|
4375
|
+
this.cue.visible = on;
|
|
4376
|
+
if (on)
|
|
4377
|
+
this.startBob();
|
|
4378
|
+
else
|
|
4379
|
+
this.stopBob();
|
|
4380
|
+
}
|
|
4381
|
+
startBob() {
|
|
4382
|
+
// A player who asked for stillness still needs the hint; they just do not need it moving.
|
|
4383
|
+
if (this.tick || !this.ticker || prefersReducedMotion())
|
|
4384
|
+
return;
|
|
4385
|
+
this.elapsed = 0;
|
|
4386
|
+
this.tick = (t) => {
|
|
4387
|
+
this.elapsed += t.deltaMS;
|
|
4388
|
+
const phase = (this.elapsed % CUE_PERIOD) / CUE_PERIOD;
|
|
4389
|
+
this.cue.y = this.cueBaseY + Math.sin(phase * Math.PI * 2) * (CUE_BOB / 2) + CUE_BOB / 2;
|
|
4390
|
+
};
|
|
4391
|
+
this.ticker.add(this.tick);
|
|
4392
|
+
}
|
|
4393
|
+
stopBob() {
|
|
4394
|
+
if (!this.tick)
|
|
4395
|
+
return;
|
|
4396
|
+
this.ticker?.remove(this.tick);
|
|
4397
|
+
this.tick = undefined;
|
|
4398
|
+
this.cue.y = this.cueBaseY;
|
|
4399
|
+
}
|
|
4400
|
+
destroy(options) {
|
|
4401
|
+
this.stopBob();
|
|
4402
|
+
super.destroy(options);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4140
4406
|
/** A vertically-scrolling viewport — mask + drag + mouse-wheel, the Pixi analogue of the
|
|
4141
4407
|
* overlay's `overflow-y:auto` scroll region. Add content to `.content`; call `refresh()`
|
|
4142
4408
|
* after content changes or a resize. */
|
|
4143
4409
|
class ScrollBox extends pixi_js.Container {
|
|
4144
4410
|
content = new pixi_js.Container();
|
|
4411
|
+
/** The drawn "this scrolls" marks — thumb, edge scrim, chevron. Public so the overlay that owns
|
|
4412
|
+
* the box (and the tests) can read what a player would see. */
|
|
4413
|
+
affordance;
|
|
4145
4414
|
maskG = new pixi_js.Graphics();
|
|
4146
4415
|
viewW = 0;
|
|
4147
4416
|
viewH = 0;
|
|
@@ -4152,10 +4421,13 @@ class ScrollBox extends pixi_js.Container {
|
|
|
4152
4421
|
moved = false;
|
|
4153
4422
|
canvas;
|
|
4154
4423
|
wheelHandler;
|
|
4155
|
-
constructor(canvas) {
|
|
4424
|
+
constructor(canvas, ticker) {
|
|
4156
4425
|
super();
|
|
4157
4426
|
this.canvas = canvas;
|
|
4158
|
-
this.
|
|
4427
|
+
this.affordance = new ScrollAffordanceView(ticker);
|
|
4428
|
+
// Added AFTER content, and never masked: the marks describe the viewport, so clipping them to
|
|
4429
|
+
// the content they describe would hide them exactly when the content overflows.
|
|
4430
|
+
this.addChild(this.content, this.affordance);
|
|
4159
4431
|
// maskG is added to the scene only while scrolling (see refresh) — a leftover unused mask
|
|
4160
4432
|
// graphic renders as a white rect. (Masking does NOT gate pointer events to the content, despite
|
|
4161
4433
|
// what an earlier version of this comment claimed — see the correction in refresh() below.)
|
|
@@ -4210,6 +4482,7 @@ class ScrollBox extends pixi_js.Container {
|
|
|
4210
4482
|
}
|
|
4211
4483
|
this.eventMode = scrollable ? 'static' : 'passive';
|
|
4212
4484
|
this.setScroll(this.scrollY);
|
|
4485
|
+
this.syncAffordance();
|
|
4213
4486
|
}
|
|
4214
4487
|
/** Max scrollable distance (0 when content fits) — exposed for tests. */
|
|
4215
4488
|
get maxScrollY() {
|
|
@@ -4226,6 +4499,12 @@ class ScrollBox extends pixi_js.Container {
|
|
|
4226
4499
|
return;
|
|
4227
4500
|
this.scrollY = Math.max(0, Math.min(this.maxScroll, y)) || 0; // || 0 converts -0 to 0
|
|
4228
4501
|
this.content.y = this.scrollY === 0 ? 0 : -this.scrollY;
|
|
4502
|
+
this.syncAffordance();
|
|
4503
|
+
}
|
|
4504
|
+
syncAffordance() {
|
|
4505
|
+
if (this.destroyed || this.affordance.destroyed)
|
|
4506
|
+
return;
|
|
4507
|
+
this.affordance.update({ x: 0, y: 0, w: this.viewW, h: this.viewH, axis: 'y' }, scrollHint({ scrollTop: this.scrollY, scrollHeight: this.viewH + this.maxScroll, clientHeight: this.viewH }));
|
|
4229
4508
|
}
|
|
4230
4509
|
onDown = (e) => {
|
|
4231
4510
|
this.dragging = true;
|
|
@@ -4796,7 +5075,7 @@ class Overlay extends pixi_js.Container {
|
|
|
4796
5075
|
this.host = host;
|
|
4797
5076
|
this.opts = opts;
|
|
4798
5077
|
this.tag = opts.tag;
|
|
4799
|
-
this.scroll = new ScrollBox(host.canvas);
|
|
5078
|
+
this.scroll = new ScrollBox(host.canvas, host.ticker);
|
|
4800
5079
|
this.addChild(this.veil, this.scroll, this.header);
|
|
4801
5080
|
this.veil.eventMode = 'static'; // swallow clicks to the game behind
|
|
4802
5081
|
this.resize(host.screenW, host.screenH);
|
|
@@ -5519,6 +5798,10 @@ class BuyBonusOverlay extends pixi_js.Container {
|
|
|
5519
5798
|
header = new pixi_js.Container();
|
|
5520
5799
|
strip = new pixi_js.Container(); // cards live here (drag-scrollable when wide)
|
|
5521
5800
|
stripMask = new pixi_js.Graphics();
|
|
5801
|
+
/** "the strip moves" — the drawn marks over the card band. Unmasked and outside `strip`, so a
|
|
5802
|
+
* drag moves the cards under it while the marks hold still. Public for the same reason
|
|
5803
|
+
* ScrollBox.affordance is: it is the only readable account of what a player sees here. */
|
|
5804
|
+
scrollCue;
|
|
5522
5805
|
footer = new pixi_js.Container();
|
|
5523
5806
|
confirm;
|
|
5524
5807
|
/** The bonus shown in the current confirm dialog (set by openConfirm, cleared by removeConfirm). */
|
|
@@ -5555,7 +5838,8 @@ class BuyBonusOverlay extends pixi_js.Container {
|
|
|
5555
5838
|
super();
|
|
5556
5839
|
this.host = host;
|
|
5557
5840
|
this.bonuses = bonuses;
|
|
5558
|
-
this.
|
|
5841
|
+
this.scrollCue = new ScrollAffordanceView(host.ticker);
|
|
5842
|
+
this.addChild(this.veil, this.strip, this.scrollCue, this.header, this.footer);
|
|
5559
5843
|
this.veil.eventMode = 'static';
|
|
5560
5844
|
this.strip.mask = this.stripMask;
|
|
5561
5845
|
this.addChild(this.stripMask);
|
|
@@ -5588,6 +5872,7 @@ class BuyBonusOverlay extends pixi_js.Container {
|
|
|
5588
5872
|
this.strip.position.x = this.dragBaseX + this.dragX;
|
|
5589
5873
|
else
|
|
5590
5874
|
this.strip.position.y = this.dragBaseY + this.dragX;
|
|
5875
|
+
this.syncScrollCue();
|
|
5591
5876
|
};
|
|
5592
5877
|
onDragUp = () => {
|
|
5593
5878
|
this.dragging = false;
|
|
@@ -5605,6 +5890,14 @@ class BuyBonusOverlay extends pixi_js.Container {
|
|
|
5605
5890
|
this.dragging = false;
|
|
5606
5891
|
this.strip.eventMode = 'auto'; // visual-only; its children (cards) are still hit-tested in-band
|
|
5607
5892
|
this.strip.position.set(baseX, baseY);
|
|
5893
|
+
this.syncScrollCue();
|
|
5894
|
+
}
|
|
5895
|
+
/** Re-draw the band's scroll marks from the current drag state. `dragX` runs [-dragMax, 0] as the
|
|
5896
|
+
* strip is pulled, so the scroll position is its negation. */
|
|
5897
|
+
syncScrollCue() {
|
|
5898
|
+
if (this.destroyed || this.scrollCue.destroyed)
|
|
5899
|
+
return;
|
|
5900
|
+
this.scrollCue.update({ x: 0, y: this.bandTop, w: this.w, h: this.bandH, axis: this.dragAxis }, scrollHint({ scrollTop: -this.dragX, scrollHeight: this.bandH + this.dragMax, clientHeight: this.bandH }));
|
|
5608
5901
|
}
|
|
5609
5902
|
resize(w, h) {
|
|
5610
5903
|
this.w = w;
|
|
@@ -7187,10 +7480,12 @@ function removePixiShell() {
|
|
|
7187
7480
|
|
|
7188
7481
|
exports.DEFAULT_ACCENT = DEFAULT_ACCENT;
|
|
7189
7482
|
exports.DEFAULT_MENU = DEFAULT_MENU;
|
|
7483
|
+
exports.DISCLAIMER_LINES = DISCLAIMER_LINES;
|
|
7190
7484
|
exports.PACKAGE_VERSION = PACKAGE_VERSION;
|
|
7191
7485
|
exports.POPOVER = POPOVER;
|
|
7192
7486
|
exports.PixiRenderer = PixiRenderer;
|
|
7193
7487
|
exports.SCHEMES = SCHEMES;
|
|
7488
|
+
exports.SCROLL_THUMB_MIN = SCROLL_THUMB_MIN;
|
|
7194
7489
|
exports.ShellController = ShellController;
|
|
7195
7490
|
exports.createI18n = createI18n;
|
|
7196
7491
|
exports.createPixiShell = createPixiShell;
|
|
@@ -7204,6 +7499,8 @@ exports.removePixiShell = removePixiShell;
|
|
|
7204
7499
|
exports.resolveConfig = resolveConfig;
|
|
7205
7500
|
exports.resolveMenu = resolveMenu;
|
|
7206
7501
|
exports.resolveTheme = resolveTheme;
|
|
7502
|
+
exports.scrollEdge = scrollEdge;
|
|
7503
|
+
exports.scrollHint = scrollHint;
|
|
7207
7504
|
exports.seedMenuValues = seedMenuValues;
|
|
7208
7505
|
exports.socialize = socialize;
|
|
7209
7506
|
//# sourceMappingURL=pixi.cjs.js.map
|