@energy8platform/shell 0.10.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 +262 -8
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +56 -4
- package/dist/html.esm.js +260 -9
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +64 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +56 -4
- package/dist/index.esm.js +62 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +288 -5
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +56 -4
- package/dist/pixi.esm.js +286 -6
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/index.ts +3 -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.d.ts
CHANGED
|
@@ -643,7 +643,7 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
|
|
|
643
643
|
tokens: ShellTokens;
|
|
644
644
|
layout: ShellLayoutMode;
|
|
645
645
|
soundOn: boolean;
|
|
646
|
-
readonly engineVersion = "0.
|
|
646
|
+
readonly engineVersion = "0.11.0";
|
|
647
647
|
readonly actions: ShellActions;
|
|
648
648
|
private renderer;
|
|
649
649
|
private i18n;
|
|
@@ -802,7 +802,59 @@ declare function createI18n(opts: I18nOptions): I18n;
|
|
|
802
802
|
declare const DISCLAIMER_LINES: readonly string[];
|
|
803
803
|
|
|
804
804
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
805
|
-
declare const PACKAGE_VERSION = "0.
|
|
805
|
+
declare const PACKAGE_VERSION = "0.11.0";
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* What a scrollable region should ADVERTISE about itself.
|
|
809
|
+
*
|
|
810
|
+
* A slot in a Stake popout is 400×225. At that size the game-info overlay holds twelve screens of
|
|
811
|
+
* content, the buy-bonus switches to a vertical card stack, and the menu popover hides its last
|
|
812
|
+
* row — all of them scroll, and (before this module) none of them said so. macOS makes it worse:
|
|
813
|
+
* overlay scrollbars stay invisible until something is already scrolling, so the very affordance a
|
|
814
|
+
* player needs BEFORE they touch anything is the one the OS withholds. A certification reviewer
|
|
815
|
+
* reads that as content the player can't reach.
|
|
816
|
+
*
|
|
817
|
+
* The maths lives here, apart from both renderers, for one reason: the DOM shell reads
|
|
818
|
+
* `scrollTop`/`scrollHeight` while the Pixi shell tracks its own offset against a mask, and those
|
|
819
|
+
* two must never disagree about whether a fade belongs at the bottom edge. Renderers decide how a
|
|
820
|
+
* thumb LOOKS; this decides when there is one and where it sits.
|
|
821
|
+
*/
|
|
822
|
+
/**
|
|
823
|
+
* Shortest thumb we will draw, as a fraction of its track.
|
|
824
|
+
*
|
|
825
|
+
* The honest ratio for game info at Popout S is 8% — an 18px speck on a 226px track, which reads
|
|
826
|
+
* as a rendering artifact rather than a scrollbar. Floored at 18% it stays recognisably a thumb,
|
|
827
|
+
* and it still travels the whole track, so the position it reports remains truthful even though
|
|
828
|
+
* its length no longer is. That is the right trade: length is decoration, position is information.
|
|
829
|
+
*/
|
|
830
|
+
declare const SCROLL_THUMB_MIN = 0.18;
|
|
831
|
+
/** One axis of a scroll region. Named for the Y axis because that is the common case; the
|
|
832
|
+
* buy-bonus strip passes its width metrics through the same fields. */
|
|
833
|
+
interface ScrollMetrics {
|
|
834
|
+
scrollTop: number;
|
|
835
|
+
scrollHeight: number;
|
|
836
|
+
clientHeight: number;
|
|
837
|
+
}
|
|
838
|
+
interface ScrollHint {
|
|
839
|
+
/** There is content past an edge — draw the affordance at all. */
|
|
840
|
+
overflowing: boolean;
|
|
841
|
+
/** Nothing above/left of the viewport: suppress the leading fade. */
|
|
842
|
+
atStart: boolean;
|
|
843
|
+
/** Nothing below/right of the viewport: suppress the trailing fade and the chevron. */
|
|
844
|
+
atEnd: boolean;
|
|
845
|
+
/** Thumb length as a fraction of the track, in `[SCROLL_THUMB_MIN, 1]`. */
|
|
846
|
+
thumbSize: number;
|
|
847
|
+
/** Thumb's leading edge as a fraction of the track, in `[0, 1 - thumbSize]`. */
|
|
848
|
+
thumbOffset: number;
|
|
849
|
+
/** Scrollable distance in pixels — 0 when the content fits. */
|
|
850
|
+
maxScroll: number;
|
|
851
|
+
}
|
|
852
|
+
/** Resolve one axis of a scroll region into everything a renderer needs to draw its affordance. */
|
|
853
|
+
declare function scrollHint(m: ScrollMetrics): ScrollHint;
|
|
854
|
+
/** The three-state tag both renderers put on a scroll region, so CSS and tests can name it.
|
|
855
|
+
* `none` when the content fits — the attribute is removed rather than set to it. */
|
|
856
|
+
type ScrollEdge = 'none' | 'start' | 'mid' | 'end';
|
|
857
|
+
declare function scrollEdge(h: ScrollHint): ScrollEdge;
|
|
806
858
|
|
|
807
859
|
/** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
|
|
808
860
|
* an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
|
|
@@ -938,5 +990,5 @@ declare function createPixiShell(config: PixiShellConfig): PixiGameShell;
|
|
|
938
990
|
* Resolves when removed — mirrors `removePixiShell` in the legacy package. */
|
|
939
991
|
declare function removePixiShell(): Promise<void>;
|
|
940
992
|
|
|
941
|
-
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, PACKAGE_VERSION, POPOVER, PixiRenderer, SCHEMES, ShellController, createI18n, createPixiShell, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removePixiShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
942
|
-
export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, MenuHost, MenuItem, MenuPresetId, MenuRow, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, PopoverPlacement, Rect as PopoverRect, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, VolumeKey, VolumeLevels, WinSection };
|
|
993
|
+
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, PACKAGE_VERSION, POPOVER, PixiRenderer, SCHEMES, SCROLL_THUMB_MIN, ShellController, createI18n, createPixiShell, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removePixiShell, resolveConfig, resolveMenu, resolveTheme, scrollEdge, scrollHint, seedMenuValues, socialize };
|
|
994
|
+
export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, MenuHost, MenuItem, MenuPresetId, MenuRow, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, PopoverPlacement, Rect as PopoverRect, ReplayModalOptions, ResolvedShellConfig, SafeArea, ScrollEdge, ScrollHint, ScrollMetrics, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, VolumeKey, VolumeLevels, WinSection };
|
package/dist/pixi.esm.js
CHANGED
|
@@ -1737,7 +1737,7 @@ function keyboardCapable(win = typeof window === 'undefined' ? undefined : windo
|
|
|
1737
1737
|
|
|
1738
1738
|
// AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
|
|
1739
1739
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
1740
|
-
const PACKAGE_VERSION = '0.
|
|
1740
|
+
const PACKAGE_VERSION = '0.11.0';
|
|
1741
1741
|
|
|
1742
1742
|
/** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
|
|
1743
1743
|
function resolveConfig(config) {
|
|
@@ -2223,6 +2223,66 @@ function placePopover(anchor, surface, size, pointer = null) {
|
|
|
2223
2223
|
return { x, y, maxH, arrowX, below };
|
|
2224
2224
|
}
|
|
2225
2225
|
|
|
2226
|
+
/**
|
|
2227
|
+
* What a scrollable region should ADVERTISE about itself.
|
|
2228
|
+
*
|
|
2229
|
+
* A slot in a Stake popout is 400×225. At that size the game-info overlay holds twelve screens of
|
|
2230
|
+
* content, the buy-bonus switches to a vertical card stack, and the menu popover hides its last
|
|
2231
|
+
* row — all of them scroll, and (before this module) none of them said so. macOS makes it worse:
|
|
2232
|
+
* overlay scrollbars stay invisible until something is already scrolling, so the very affordance a
|
|
2233
|
+
* player needs BEFORE they touch anything is the one the OS withholds. A certification reviewer
|
|
2234
|
+
* reads that as content the player can't reach.
|
|
2235
|
+
*
|
|
2236
|
+
* The maths lives here, apart from both renderers, for one reason: the DOM shell reads
|
|
2237
|
+
* `scrollTop`/`scrollHeight` while the Pixi shell tracks its own offset against a mask, and those
|
|
2238
|
+
* two must never disagree about whether a fade belongs at the bottom edge. Renderers decide how a
|
|
2239
|
+
* thumb LOOKS; this decides when there is one and where it sits.
|
|
2240
|
+
*/
|
|
2241
|
+
/** Fractions of a pixel are layout rounding, not reachable content. */
|
|
2242
|
+
const EPSILON = 1;
|
|
2243
|
+
/** Landing within half a pixel of an edge counts as arriving: a browser settles a flung scroll on
|
|
2244
|
+
* 199.5 of 200, and a fade left glowing over content the player has already reached reads as a
|
|
2245
|
+
* bug. */
|
|
2246
|
+
const EDGE_EPSILON = 0.5;
|
|
2247
|
+
/**
|
|
2248
|
+
* Shortest thumb we will draw, as a fraction of its track.
|
|
2249
|
+
*
|
|
2250
|
+
* The honest ratio for game info at Popout S is 8% — an 18px speck on a 226px track, which reads
|
|
2251
|
+
* as a rendering artifact rather than a scrollbar. Floored at 18% it stays recognisably a thumb,
|
|
2252
|
+
* and it still travels the whole track, so the position it reports remains truthful even though
|
|
2253
|
+
* its length no longer is. That is the right trade: length is decoration, position is information.
|
|
2254
|
+
*/
|
|
2255
|
+
const SCROLL_THUMB_MIN = 0.18;
|
|
2256
|
+
/** Resolve one axis of a scroll region into everything a renderer needs to draw its affordance. */
|
|
2257
|
+
function scrollHint(m) {
|
|
2258
|
+
const view = Math.max(0, m.clientHeight);
|
|
2259
|
+
const content = Math.max(0, m.scrollHeight);
|
|
2260
|
+
const maxScroll = Math.max(0, content - view);
|
|
2261
|
+
if (maxScroll <= EPSILON || view <= 0) {
|
|
2262
|
+
// Both edges are "the edge" when there is nowhere to go — callers gate every fade on the
|
|
2263
|
+
// matching flag, so a region that fits draws nothing without needing to check `overflowing`.
|
|
2264
|
+
return { overflowing: false, atStart: true, atEnd: true, thumbSize: 1, thumbOffset: 0, maxScroll: 0 };
|
|
2265
|
+
}
|
|
2266
|
+
const at = Math.max(0, Math.min(maxScroll, m.scrollTop));
|
|
2267
|
+
const thumbSize = Math.min(1, Math.max(SCROLL_THUMB_MIN, view / content));
|
|
2268
|
+
const progress = at / maxScroll;
|
|
2269
|
+
return {
|
|
2270
|
+
overflowing: true,
|
|
2271
|
+
atStart: at <= EDGE_EPSILON,
|
|
2272
|
+
atEnd: maxScroll - at <= EDGE_EPSILON,
|
|
2273
|
+
thumbSize,
|
|
2274
|
+
thumbOffset: progress * (1 - thumbSize),
|
|
2275
|
+
maxScroll,
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
function scrollEdge(h) {
|
|
2279
|
+
if (!h.overflowing)
|
|
2280
|
+
return 'none';
|
|
2281
|
+
if (h.atStart)
|
|
2282
|
+
return 'start';
|
|
2283
|
+
return h.atEnd ? 'end' : 'mid';
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2226
2286
|
const NO_INSET = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
2227
2287
|
/** Create a shell with an explicit renderer instance (custom or a built-in HtmlRenderer/PixiRenderer).
|
|
2228
2288
|
* Built-in renderers also have the createGameShell/createPixiShell sugar in /html and /pixi.
|
|
@@ -4148,11 +4208,207 @@ function SPIN_POP_MOBILE() {
|
|
|
4148
4208
|
return (SPIN - M_CTRL_H) / 2; // 11
|
|
4149
4209
|
}
|
|
4150
4210
|
|
|
4211
|
+
/** Thickness of the thumb, and how far it is inset from the edge it rides. */
|
|
4212
|
+
const THUMB_THICK = 4;
|
|
4213
|
+
const THUMB_INSET = 3;
|
|
4214
|
+
/** Shortest thumb we will draw in pixels — the fractional floor still degenerates on a tiny frame. */
|
|
4215
|
+
const THUMB_MIN_PX = 22;
|
|
4216
|
+
/** Depth of the edge scrim. */
|
|
4217
|
+
const SCRIM = 34;
|
|
4218
|
+
const SCRIM_ALPHA = 0.72;
|
|
4219
|
+
/** The overlay chrome — plaques, bar, backdrop — is scheme-independent in this shell (see
|
|
4220
|
+
* resolveTheme: those tokens are fixed, only the palette behind them flips). The affordance is
|
|
4221
|
+
* chrome, so it is a fixed neutral too: white marks over a tint of the same hue as the veil, which
|
|
4222
|
+
* makes the fade read as content dissolving INTO the overlay rather than as a grey band on top. */
|
|
4223
|
+
const SCRIM_TINT = 0x0c111c;
|
|
4224
|
+
const CUE_R = 11;
|
|
4225
|
+
const CUE_BOB = 4;
|
|
4226
|
+
const CUE_PERIOD = 1600; // ms
|
|
4227
|
+
/**
|
|
4228
|
+
* The scrim's alpha ramp, densest AT the edge and clear by `depth` inward.
|
|
4229
|
+
*
|
|
4230
|
+
* Exported because the direction is the whole point and is easy to get backwards: an inverted ramp
|
|
4231
|
+
* draws a hard-edged dark band floating over the content instead of a fade into the frame, and it
|
|
4232
|
+
* looks deliberate enough that nobody questions it. The squared falloff keeps the outer half of the
|
|
4233
|
+
* ramp faint, so the scrim reads as content thinning out rather than as a panel laid on top.
|
|
4234
|
+
*/
|
|
4235
|
+
function scrimRamp(depth) {
|
|
4236
|
+
const slices = Math.max(1, Math.round(depth));
|
|
4237
|
+
const thickness = depth / slices;
|
|
4238
|
+
const out = [];
|
|
4239
|
+
for (let i = 0; i < slices; i++) {
|
|
4240
|
+
const offset = i * thickness;
|
|
4241
|
+
const t = 1 - (offset + thickness / 2) / depth; // 1 at the edge → 0 at `depth`
|
|
4242
|
+
out.push({ offset, thickness, alpha: SCRIM_ALPHA * t * t });
|
|
4243
|
+
}
|
|
4244
|
+
return out;
|
|
4245
|
+
}
|
|
4246
|
+
class ScrollAffordanceView extends Container {
|
|
4247
|
+
thumb = new Graphics();
|
|
4248
|
+
scrim = new Graphics();
|
|
4249
|
+
cue = new Container();
|
|
4250
|
+
ticker;
|
|
4251
|
+
bounds = null;
|
|
4252
|
+
cueOn = false;
|
|
4253
|
+
// Once the region has moved the player knows the gesture; re-offering it on every return to the
|
|
4254
|
+
// top would nag rather than inform.
|
|
4255
|
+
cueRetired = false;
|
|
4256
|
+
cueBaseY = 0;
|
|
4257
|
+
elapsed = 0;
|
|
4258
|
+
tick;
|
|
4259
|
+
constructor(ticker) {
|
|
4260
|
+
super();
|
|
4261
|
+
this.ticker = ticker;
|
|
4262
|
+
this.eventMode = 'none'; // decoration: never intercept the drag it is advertising
|
|
4263
|
+
this.addChild(this.scrim, this.thumb, this.cue);
|
|
4264
|
+
this.buildCue();
|
|
4265
|
+
this.visible = false;
|
|
4266
|
+
}
|
|
4267
|
+
/** Where the thumb is drawn, in the parent's space — `null` when nothing is drawn. */
|
|
4268
|
+
get thumbBounds() {
|
|
4269
|
+
return this.visible ? this.bounds : null;
|
|
4270
|
+
}
|
|
4271
|
+
get cueVisible() {
|
|
4272
|
+
return this.visible && this.cueOn;
|
|
4273
|
+
}
|
|
4274
|
+
/** Re-draw for a viewport and a hint. Cheap enough to call on every scroll frame. */
|
|
4275
|
+
update(vp, hint) {
|
|
4276
|
+
if (this.destroyed)
|
|
4277
|
+
return;
|
|
4278
|
+
if (!hint.overflowing) {
|
|
4279
|
+
this.visible = false;
|
|
4280
|
+
this.bounds = null;
|
|
4281
|
+
this.showCue(false);
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
this.visible = true;
|
|
4285
|
+
// Any movement off the start retires the cue permanently.
|
|
4286
|
+
if (!hint.atStart)
|
|
4287
|
+
this.cueRetired = true;
|
|
4288
|
+
this.drawThumb(vp, hint);
|
|
4289
|
+
this.drawScrim(vp, hint);
|
|
4290
|
+
this.placeCue(vp);
|
|
4291
|
+
this.showCue(hint.atStart && !this.cueRetired);
|
|
4292
|
+
}
|
|
4293
|
+
drawThumb(vp, hint) {
|
|
4294
|
+
const along = vp.axis === 'y' ? vp.h : vp.w;
|
|
4295
|
+
const track = Math.max(0, along - THUMB_INSET * 2);
|
|
4296
|
+
const len = Math.max(Math.min(track, THUMB_MIN_PX), track * hint.thumbSize);
|
|
4297
|
+
// Recover the travel from the (possibly floored) length so the thumb still reaches both ends.
|
|
4298
|
+
const travel = Math.max(0, track - len);
|
|
4299
|
+
const progress = hint.thumbSize < 1 ? hint.thumbOffset / (1 - hint.thumbSize) : 0;
|
|
4300
|
+
const at = THUMB_INSET + travel * progress;
|
|
4301
|
+
this.bounds =
|
|
4302
|
+
vp.axis === 'y'
|
|
4303
|
+
? { x: vp.x + vp.w - THUMB_INSET - THUMB_THICK, y: vp.y + at, w: THUMB_THICK, h: len }
|
|
4304
|
+
: { x: vp.x + at, y: vp.y + vp.h - THUMB_INSET - THUMB_THICK, w: len, h: THUMB_THICK };
|
|
4305
|
+
const b = this.bounds;
|
|
4306
|
+
this.thumb.clear();
|
|
4307
|
+
// A faint track behind the thumb: on a dark overlay a lone thumb can read as a stray highlight;
|
|
4308
|
+
// with the groove behind it, it reads as a scrollbar.
|
|
4309
|
+
if (vp.axis === 'y') {
|
|
4310
|
+
this.thumb
|
|
4311
|
+
.roundRect(b.x, vp.y + THUMB_INSET, THUMB_THICK, track, THUMB_THICK / 2)
|
|
4312
|
+
.fill({ color: 0xffffff, alpha: 0.1 });
|
|
4313
|
+
}
|
|
4314
|
+
else {
|
|
4315
|
+
this.thumb
|
|
4316
|
+
.roundRect(vp.x + THUMB_INSET, b.y, track, THUMB_THICK, THUMB_THICK / 2)
|
|
4317
|
+
.fill({ color: 0xffffff, alpha: 0.1 });
|
|
4318
|
+
}
|
|
4319
|
+
this.thumb.roundRect(b.x, b.y, b.w, b.h, THUMB_THICK / 2).fill({ color: 0xffffff, alpha: 0.55 });
|
|
4320
|
+
}
|
|
4321
|
+
/** Stacked 1px slices rather than a gradient fill: no texture, no backend-specific paths, and at
|
|
4322
|
+
* one device pixel per slice there is nothing left to band. */
|
|
4323
|
+
drawScrim(vp, hint) {
|
|
4324
|
+
this.scrim.clear();
|
|
4325
|
+
const depth = Math.min(SCRIM, (vp.axis === 'y' ? vp.h : vp.w) / 3);
|
|
4326
|
+
if (depth <= 0)
|
|
4327
|
+
return;
|
|
4328
|
+
for (const { offset, thickness, alpha } of scrimRamp(depth)) {
|
|
4329
|
+
// `offset` is measured INWARD from whichever edge is being faded.
|
|
4330
|
+
if (!hint.atEnd) {
|
|
4331
|
+
if (vp.axis === 'y')
|
|
4332
|
+
this.scrim.rect(vp.x, vp.y + vp.h - offset - thickness, vp.w, thickness);
|
|
4333
|
+
else
|
|
4334
|
+
this.scrim.rect(vp.x + vp.w - offset - thickness, vp.y, thickness, vp.h);
|
|
4335
|
+
this.scrim.fill({ color: SCRIM_TINT, alpha });
|
|
4336
|
+
}
|
|
4337
|
+
if (!hint.atStart) {
|
|
4338
|
+
if (vp.axis === 'y')
|
|
4339
|
+
this.scrim.rect(vp.x, vp.y + offset, vp.w, thickness);
|
|
4340
|
+
else
|
|
4341
|
+
this.scrim.rect(vp.x + offset, vp.y, thickness, vp.h);
|
|
4342
|
+
this.scrim.fill({ color: SCRIM_TINT, alpha });
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
buildCue() {
|
|
4347
|
+
const disc = new Graphics();
|
|
4348
|
+
disc.circle(0, 0, CUE_R).fill({ color: 0x0b0f18, alpha: 0.9 });
|
|
4349
|
+
disc.circle(0, 0, CUE_R).stroke({ color: 0xffffff, alpha: 0.22, width: 1 });
|
|
4350
|
+
const size = Math.round(CUE_R * 1.3);
|
|
4351
|
+
const glyph = makeIcon('chevronDown', size, '#ffffff');
|
|
4352
|
+
glyph.position.set(-size / 2, -size / 2);
|
|
4353
|
+
this.cue.addChild(disc, glyph);
|
|
4354
|
+
this.cue.visible = false;
|
|
4355
|
+
}
|
|
4356
|
+
placeCue(vp) {
|
|
4357
|
+
// Bottom-centre for a vertical region; hugging the trailing edge for a horizontal one.
|
|
4358
|
+
if (vp.axis === 'y') {
|
|
4359
|
+
this.cue.x = vp.x + vp.w / 2;
|
|
4360
|
+
this.cueBaseY = vp.y + vp.h - CUE_R - 6;
|
|
4361
|
+
}
|
|
4362
|
+
else {
|
|
4363
|
+
this.cue.x = vp.x + vp.w - CUE_R - 6;
|
|
4364
|
+
this.cueBaseY = vp.y + vp.h / 2;
|
|
4365
|
+
this.cue.rotation = -Math.PI / 2; // chevron points the way the strip moves
|
|
4366
|
+
}
|
|
4367
|
+
this.cue.y = this.cueBaseY;
|
|
4368
|
+
}
|
|
4369
|
+
showCue(on) {
|
|
4370
|
+
if (on === this.cueOn)
|
|
4371
|
+
return;
|
|
4372
|
+
this.cueOn = on;
|
|
4373
|
+
this.cue.visible = on;
|
|
4374
|
+
if (on)
|
|
4375
|
+
this.startBob();
|
|
4376
|
+
else
|
|
4377
|
+
this.stopBob();
|
|
4378
|
+
}
|
|
4379
|
+
startBob() {
|
|
4380
|
+
// A player who asked for stillness still needs the hint; they just do not need it moving.
|
|
4381
|
+
if (this.tick || !this.ticker || prefersReducedMotion())
|
|
4382
|
+
return;
|
|
4383
|
+
this.elapsed = 0;
|
|
4384
|
+
this.tick = (t) => {
|
|
4385
|
+
this.elapsed += t.deltaMS;
|
|
4386
|
+
const phase = (this.elapsed % CUE_PERIOD) / CUE_PERIOD;
|
|
4387
|
+
this.cue.y = this.cueBaseY + Math.sin(phase * Math.PI * 2) * (CUE_BOB / 2) + CUE_BOB / 2;
|
|
4388
|
+
};
|
|
4389
|
+
this.ticker.add(this.tick);
|
|
4390
|
+
}
|
|
4391
|
+
stopBob() {
|
|
4392
|
+
if (!this.tick)
|
|
4393
|
+
return;
|
|
4394
|
+
this.ticker?.remove(this.tick);
|
|
4395
|
+
this.tick = undefined;
|
|
4396
|
+
this.cue.y = this.cueBaseY;
|
|
4397
|
+
}
|
|
4398
|
+
destroy(options) {
|
|
4399
|
+
this.stopBob();
|
|
4400
|
+
super.destroy(options);
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4151
4404
|
/** A vertically-scrolling viewport — mask + drag + mouse-wheel, the Pixi analogue of the
|
|
4152
4405
|
* overlay's `overflow-y:auto` scroll region. Add content to `.content`; call `refresh()`
|
|
4153
4406
|
* after content changes or a resize. */
|
|
4154
4407
|
class ScrollBox extends Container {
|
|
4155
4408
|
content = new Container();
|
|
4409
|
+
/** The drawn "this scrolls" marks — thumb, edge scrim, chevron. Public so the overlay that owns
|
|
4410
|
+
* the box (and the tests) can read what a player would see. */
|
|
4411
|
+
affordance;
|
|
4156
4412
|
maskG = new Graphics();
|
|
4157
4413
|
viewW = 0;
|
|
4158
4414
|
viewH = 0;
|
|
@@ -4163,10 +4419,13 @@ class ScrollBox extends Container {
|
|
|
4163
4419
|
moved = false;
|
|
4164
4420
|
canvas;
|
|
4165
4421
|
wheelHandler;
|
|
4166
|
-
constructor(canvas) {
|
|
4422
|
+
constructor(canvas, ticker) {
|
|
4167
4423
|
super();
|
|
4168
4424
|
this.canvas = canvas;
|
|
4169
|
-
this.
|
|
4425
|
+
this.affordance = new ScrollAffordanceView(ticker);
|
|
4426
|
+
// Added AFTER content, and never masked: the marks describe the viewport, so clipping them to
|
|
4427
|
+
// the content they describe would hide them exactly when the content overflows.
|
|
4428
|
+
this.addChild(this.content, this.affordance);
|
|
4170
4429
|
// maskG is added to the scene only while scrolling (see refresh) — a leftover unused mask
|
|
4171
4430
|
// graphic renders as a white rect. (Masking does NOT gate pointer events to the content, despite
|
|
4172
4431
|
// what an earlier version of this comment claimed — see the correction in refresh() below.)
|
|
@@ -4221,6 +4480,7 @@ class ScrollBox extends Container {
|
|
|
4221
4480
|
}
|
|
4222
4481
|
this.eventMode = scrollable ? 'static' : 'passive';
|
|
4223
4482
|
this.setScroll(this.scrollY);
|
|
4483
|
+
this.syncAffordance();
|
|
4224
4484
|
}
|
|
4225
4485
|
/** Max scrollable distance (0 when content fits) — exposed for tests. */
|
|
4226
4486
|
get maxScrollY() {
|
|
@@ -4237,6 +4497,12 @@ class ScrollBox extends Container {
|
|
|
4237
4497
|
return;
|
|
4238
4498
|
this.scrollY = Math.max(0, Math.min(this.maxScroll, y)) || 0; // || 0 converts -0 to 0
|
|
4239
4499
|
this.content.y = this.scrollY === 0 ? 0 : -this.scrollY;
|
|
4500
|
+
this.syncAffordance();
|
|
4501
|
+
}
|
|
4502
|
+
syncAffordance() {
|
|
4503
|
+
if (this.destroyed || this.affordance.destroyed)
|
|
4504
|
+
return;
|
|
4505
|
+
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 }));
|
|
4240
4506
|
}
|
|
4241
4507
|
onDown = (e) => {
|
|
4242
4508
|
this.dragging = true;
|
|
@@ -4807,7 +5073,7 @@ class Overlay extends Container {
|
|
|
4807
5073
|
this.host = host;
|
|
4808
5074
|
this.opts = opts;
|
|
4809
5075
|
this.tag = opts.tag;
|
|
4810
|
-
this.scroll = new ScrollBox(host.canvas);
|
|
5076
|
+
this.scroll = new ScrollBox(host.canvas, host.ticker);
|
|
4811
5077
|
this.addChild(this.veil, this.scroll, this.header);
|
|
4812
5078
|
this.veil.eventMode = 'static'; // swallow clicks to the game behind
|
|
4813
5079
|
this.resize(host.screenW, host.screenH);
|
|
@@ -5530,6 +5796,10 @@ class BuyBonusOverlay extends Container {
|
|
|
5530
5796
|
header = new Container();
|
|
5531
5797
|
strip = new Container(); // cards live here (drag-scrollable when wide)
|
|
5532
5798
|
stripMask = new Graphics();
|
|
5799
|
+
/** "the strip moves" — the drawn marks over the card band. Unmasked and outside `strip`, so a
|
|
5800
|
+
* drag moves the cards under it while the marks hold still. Public for the same reason
|
|
5801
|
+
* ScrollBox.affordance is: it is the only readable account of what a player sees here. */
|
|
5802
|
+
scrollCue;
|
|
5533
5803
|
footer = new Container();
|
|
5534
5804
|
confirm;
|
|
5535
5805
|
/** The bonus shown in the current confirm dialog (set by openConfirm, cleared by removeConfirm). */
|
|
@@ -5566,7 +5836,8 @@ class BuyBonusOverlay extends Container {
|
|
|
5566
5836
|
super();
|
|
5567
5837
|
this.host = host;
|
|
5568
5838
|
this.bonuses = bonuses;
|
|
5569
|
-
this.
|
|
5839
|
+
this.scrollCue = new ScrollAffordanceView(host.ticker);
|
|
5840
|
+
this.addChild(this.veil, this.strip, this.scrollCue, this.header, this.footer);
|
|
5570
5841
|
this.veil.eventMode = 'static';
|
|
5571
5842
|
this.strip.mask = this.stripMask;
|
|
5572
5843
|
this.addChild(this.stripMask);
|
|
@@ -5599,6 +5870,7 @@ class BuyBonusOverlay extends Container {
|
|
|
5599
5870
|
this.strip.position.x = this.dragBaseX + this.dragX;
|
|
5600
5871
|
else
|
|
5601
5872
|
this.strip.position.y = this.dragBaseY + this.dragX;
|
|
5873
|
+
this.syncScrollCue();
|
|
5602
5874
|
};
|
|
5603
5875
|
onDragUp = () => {
|
|
5604
5876
|
this.dragging = false;
|
|
@@ -5616,6 +5888,14 @@ class BuyBonusOverlay extends Container {
|
|
|
5616
5888
|
this.dragging = false;
|
|
5617
5889
|
this.strip.eventMode = 'auto'; // visual-only; its children (cards) are still hit-tested in-band
|
|
5618
5890
|
this.strip.position.set(baseX, baseY);
|
|
5891
|
+
this.syncScrollCue();
|
|
5892
|
+
}
|
|
5893
|
+
/** Re-draw the band's scroll marks from the current drag state. `dragX` runs [-dragMax, 0] as the
|
|
5894
|
+
* strip is pulled, so the scroll position is its negation. */
|
|
5895
|
+
syncScrollCue() {
|
|
5896
|
+
if (this.destroyed || this.scrollCue.destroyed)
|
|
5897
|
+
return;
|
|
5898
|
+
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 }));
|
|
5619
5899
|
}
|
|
5620
5900
|
resize(w, h) {
|
|
5621
5901
|
this.w = w;
|
|
@@ -7196,5 +7476,5 @@ function removePixiShell() {
|
|
|
7196
7476
|
return shell.destroy();
|
|
7197
7477
|
}
|
|
7198
7478
|
|
|
7199
|
-
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, PACKAGE_VERSION, POPOVER, PixiRenderer, SCHEMES, ShellController, createI18n, createPixiShell, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removePixiShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
7479
|
+
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, PACKAGE_VERSION, POPOVER, PixiRenderer, SCHEMES, SCROLL_THUMB_MIN, ShellController, createI18n, createPixiShell, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removePixiShell, resolveConfig, resolveMenu, resolveTheme, scrollEdge, scrollHint, seedMenuValues, socialize };
|
|
7200
7480
|
//# sourceMappingURL=pixi.esm.js.map
|