@energy8platform/game-engine 0.14.10 → 0.15.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/ui.d.ts CHANGED
@@ -37,16 +37,35 @@ interface FlexItemConfig {
37
37
  layoutHeight?: number | string;
38
38
  /** Override parent's alignItems for this child */
39
39
  alignSelf?: AlignSelf;
40
- /** Exclude from flex layout (acts like position: absolute) */
40
+ /**
41
+ * Exclude from flex layout (acts like position: absolute).
42
+ *
43
+ * Positioning props (`top`/`right`/`bottom`/`left`/`centerX`/`centerY`) describe
44
+ * the **visual bounding rectangle**, so children with a centered internal
45
+ * origin (Button, Label — `anchor = 0.5`) are positioned intuitively.
46
+ * `left: 100` places the visible left edge at 100px, regardless of origin.
47
+ */
41
48
  flexExclude?: boolean;
42
- /** Absolute positioning for flexExclude children (distance from top edge) */
49
+ /** Distance from top edge of parent content area to the child's visual top */
43
50
  top?: number;
44
- /** Absolute positioning for flexExclude children (distance from right edge) */
51
+ /** Distance from right edge of parent content area to the child's visual right */
45
52
  right?: number;
46
- /** Absolute positioning for flexExclude children (distance from bottom edge) */
53
+ /** Distance from bottom edge of parent content area to the child's visual bottom */
47
54
  bottom?: number;
48
- /** Absolute positioning for flexExclude children (distance from left edge) */
55
+ /** Distance from left edge of parent content area to the child's visual left */
49
56
  left?: number;
57
+ /**
58
+ * X coordinate of the child's visual center within parent content area.
59
+ * Accepts pixels (`200`) or a percentage of parent content width (`'50%'`).
60
+ * Takes precedence over `left`/`right`.
61
+ */
62
+ centerX?: number | string;
63
+ /**
64
+ * Y coordinate of the child's visual center within parent content area.
65
+ * Accepts pixels or a percentage of parent content height (`'50%'`).
66
+ * Takes precedence over `top`/`bottom`.
67
+ */
68
+ centerY?: number | string;
50
69
  }
51
70
  interface FlexContainerConfig {
52
71
  /** Layout direction (default: 'row') */
@@ -391,6 +410,62 @@ declare class Label extends Container {
391
410
  private fitText;
392
411
  }
393
412
 
413
+ type LabelValueAlign = 'start' | 'center' | 'end';
414
+ interface LabelValueConfig {
415
+ /** Caption text (top row, typically smaller/muted) */
416
+ label?: string;
417
+ /** Value text (bottom row, typically larger/prominent) */
418
+ value?: string;
419
+ /** Style for the caption Label */
420
+ labelStyle?: Partial<TextStyle>;
421
+ /** Style for the value Label */
422
+ valueStyle?: Partial<TextStyle>;
423
+ /** Gap in pixels between caption and value (default: 4) */
424
+ gap?: number;
425
+ /** Horizontal alignment of both rows (default: 'center') */
426
+ align?: LabelValueAlign;
427
+ /** Maximum width — value will be auto-scaled to fit when exceeded */
428
+ maxWidth?: number;
429
+ /** Optional padding (forwarded to the underlying FlexContainer) */
430
+ padding?: FlexContainerConfig['padding'];
431
+ }
432
+ /**
433
+ * Two-row text cell with a muted caption above and a prominent value below —
434
+ * the archetypal casino UI pattern (BALANCE/€500, BET/€1, WIN/€0.00).
435
+ *
436
+ * Extends FlexContainer, so it participates in parent flex layouts directly
437
+ * (you can pass `flexGrow`, `alignSelf`, etc. when it's a child of another
438
+ * FlexContainer) and auto-sizes to its contents when no explicit size is set.
439
+ *
440
+ * @example
441
+ * ```tsx
442
+ * <labelValue
443
+ * label="BALANCE"
444
+ * value={`€${balance.toFixed(2)}`}
445
+ * labelStyle={{ fontSize: 12, fill: 0x888888 }}
446
+ * valueStyle={{ fontSize: 22, fill: 0xffffff, fontWeight: 'bold' }}
447
+ * gap={6}
448
+ * align="center"
449
+ * />
450
+ * ```
451
+ */
452
+ declare class LabelValue extends FlexContainer {
453
+ private readonly _labelEl;
454
+ private readonly _valueEl;
455
+ private _valueMaxWidth;
456
+ constructor(config?: LabelValueConfig);
457
+ /** Get the caption Label (for advanced styling / animation) */
458
+ get labelElement(): Label;
459
+ /** Get the value Label */
460
+ get valueElement(): Label;
461
+ /** Update caption text */
462
+ setLabel(text: string): void;
463
+ /** Update value text */
464
+ setValue(text: string): void;
465
+ /** React reconciler update hook */
466
+ updateConfig(changed: Record<string, any>): void;
467
+ }
468
+
394
469
  interface PanelConfig {
395
470
  /** Width */
396
471
  width?: number;
@@ -1037,5 +1112,5 @@ declare class Toggle extends Container {
1037
1112
  }): void;
1038
1113
  }
1039
1114
 
1040
- export { BalanceDisplay, Button, FlexContainer, Label, Layout, Modal, Panel, ProgressBar, ScrollContainer, Slider, Toast, Toggle, WinDisplay, resolveView };
1041
- export type { AlignContent, AlignItems, AlignSelf, BalanceDisplayConfig, ButtonConfig, ButtonState, FlexContainerConfig, FlexDirection, FlexItemConfig, JustifyContent, LabelConfig, LayoutAlignment, LayoutAnchor, LayoutConfig, LayoutDirection, ModalConfig, PanelConfig, ProgressBarConfig, ScrollContainerConfig, ScrollDirection, SliderConfig, ToastConfig, ToastType, ToggleConfig, ViewInput, WinDisplayConfig };
1115
+ export { BalanceDisplay, Button, FlexContainer, Label, LabelValue, Layout, Modal, Panel, ProgressBar, ScrollContainer, Slider, Toast, Toggle, WinDisplay, resolveView };
1116
+ export type { AlignContent, AlignItems, AlignSelf, BalanceDisplayConfig, ButtonConfig, ButtonState, FlexContainerConfig, FlexDirection, FlexItemConfig, JustifyContent, LabelConfig, LabelValueAlign, LabelValueConfig, LayoutAlignment, LayoutAnchor, LayoutConfig, LayoutDirection, ModalConfig, PanelConfig, ProgressBarConfig, ScrollContainerConfig, ScrollDirection, SliderConfig, ToastConfig, ToastType, ToggleConfig, ViewInput, WinDisplayConfig };
package/dist/ui.esm.js CHANGED
@@ -558,22 +558,31 @@ class FlexContainer extends Container {
558
558
  this._computedWidth = this._explicitWidth > 0 ? this._explicitWidth : (pl + totalCrossNatural + pr);
559
559
  this._computedHeight = this._explicitHeight > 0 ? this._explicitHeight : (pt + naturalMainSize + pb);
560
560
  }
561
- // Position flexExclude children (absolute positioning)
561
+ // Position flexExclude children (absolute positioning).
562
+ // All position props describe the visual (bounds) rectangle, so we subtract
563
+ // `ox/oy` to compensate for children with a centered origin (Button, Label).
564
+ // `centerX/centerY` take precedence over `left/right` / `top/bottom` on each axis.
562
565
  for (const child of this._layoutChildren) {
563
566
  if (!child._flexConfig?.flexExclude)
564
567
  continue;
565
568
  const cfg = child._flexConfig;
566
- const { w, h } = measureChild(child, pctRefW, pctRefH);
569
+ const { w, h, ox, oy } = measureChild(child, pctRefW, pctRefH);
567
570
  const cw = this._computedWidth;
568
571
  const ch = this._computedHeight;
569
- if (cfg.left !== undefined)
570
- child.x = cfg.left;
572
+ const centerX = resolveDimension(cfg.centerX, pctRefW);
573
+ if (centerX !== undefined)
574
+ child.x = centerX - w / 2 - ox;
575
+ else if (cfg.left !== undefined)
576
+ child.x = cfg.left - ox;
571
577
  else if (cfg.right !== undefined)
572
- child.x = cw - w - cfg.right;
573
- if (cfg.top !== undefined)
574
- child.y = cfg.top;
578
+ child.x = cw - w - cfg.right - ox;
579
+ const centerY = resolveDimension(cfg.centerY, pctRefH);
580
+ if (centerY !== undefined)
581
+ child.y = centerY - h / 2 - oy;
582
+ else if (cfg.top !== undefined)
583
+ child.y = cfg.top - oy;
575
584
  else if (cfg.bottom !== undefined)
576
- child.y = ch - h - cfg.bottom;
585
+ child.y = ch - h - cfg.bottom - oy;
577
586
  }
578
587
  }
579
588
  /** Computed content size (after layout) */
@@ -1310,6 +1319,97 @@ class Label extends Container {
1310
1319
  }
1311
1320
  }
1312
1321
 
1322
+ /**
1323
+ * Two-row text cell with a muted caption above and a prominent value below —
1324
+ * the archetypal casino UI pattern (BALANCE/€500, BET/€1, WIN/€0.00).
1325
+ *
1326
+ * Extends FlexContainer, so it participates in parent flex layouts directly
1327
+ * (you can pass `flexGrow`, `alignSelf`, etc. when it's a child of another
1328
+ * FlexContainer) and auto-sizes to its contents when no explicit size is set.
1329
+ *
1330
+ * @example
1331
+ * ```tsx
1332
+ * <labelValue
1333
+ * label="BALANCE"
1334
+ * value={`€${balance.toFixed(2)}`}
1335
+ * labelStyle={{ fontSize: 12, fill: 0x888888 }}
1336
+ * valueStyle={{ fontSize: 22, fill: 0xffffff, fontWeight: 'bold' }}
1337
+ * gap={6}
1338
+ * align="center"
1339
+ * />
1340
+ * ```
1341
+ */
1342
+ class LabelValue extends FlexContainer {
1343
+ _labelEl;
1344
+ _valueEl;
1345
+ _valueMaxWidth;
1346
+ constructor(config = {}) {
1347
+ super({
1348
+ direction: 'column',
1349
+ alignItems: toAlignItems(config.align ?? 'center'),
1350
+ gap: config.gap ?? 4,
1351
+ padding: config.padding,
1352
+ });
1353
+ this._valueMaxWidth = config.maxWidth ?? Infinity;
1354
+ this._labelEl = new Label({
1355
+ text: config.label ?? '',
1356
+ style: config.labelStyle,
1357
+ });
1358
+ this._valueEl = new Label({
1359
+ text: config.value ?? '',
1360
+ style: config.valueStyle,
1361
+ maxWidth: config.maxWidth,
1362
+ autoFit: config.maxWidth !== undefined,
1363
+ });
1364
+ this.addFlexChild(this._labelEl);
1365
+ this.addFlexChild(this._valueEl);
1366
+ }
1367
+ /** Get the caption Label (for advanced styling / animation) */
1368
+ get labelElement() {
1369
+ return this._labelEl;
1370
+ }
1371
+ /** Get the value Label */
1372
+ get valueElement() {
1373
+ return this._valueEl;
1374
+ }
1375
+ /** Update caption text */
1376
+ setLabel(text) {
1377
+ this._labelEl.text = text;
1378
+ this.updateLayout();
1379
+ }
1380
+ /** Update value text */
1381
+ setValue(text) {
1382
+ this._valueEl.text = text;
1383
+ this.updateLayout();
1384
+ }
1385
+ /** React reconciler update hook */
1386
+ updateConfig(changed) {
1387
+ if ('label' in changed)
1388
+ this._labelEl.text = changed.label ?? '';
1389
+ if ('value' in changed)
1390
+ this._valueEl.text = changed.value ?? '';
1391
+ if ('labelStyle' in changed && typeof changed.labelStyle === 'object') {
1392
+ Object.assign(this._labelEl.style, changed.labelStyle);
1393
+ }
1394
+ if ('valueStyle' in changed && typeof changed.valueStyle === 'object') {
1395
+ Object.assign(this._valueEl.style, changed.valueStyle);
1396
+ }
1397
+ if ('maxWidth' in changed) {
1398
+ this._valueMaxWidth = changed.maxWidth ?? Infinity;
1399
+ this._valueEl.maxWidth = this._valueMaxWidth;
1400
+ }
1401
+ if ('gap' in changed)
1402
+ this.setGap(changed.gap);
1403
+ if ('align' in changed)
1404
+ this.setAlignItems(toAlignItems(changed.align));
1405
+ // Forward remaining FlexContainer props (e.g. padding, width, height)
1406
+ super.updateConfig(changed);
1407
+ }
1408
+ }
1409
+ function toAlignItems(align) {
1410
+ return align;
1411
+ }
1412
+
1313
1413
  /**
1314
1414
  * Background panel with optional flexbox content layout.
1315
1415
  *
@@ -2869,5 +2969,5 @@ class Toggle extends Container {
2869
2969
  }
2870
2970
  }
2871
2971
 
2872
- export { BalanceDisplay, Button, FlexContainer, Label, Layout, Modal, Panel, ProgressBar, ScrollContainer, Slider, Toast, Toggle, WinDisplay, resolveView };
2972
+ export { BalanceDisplay, Button, FlexContainer, Label, LabelValue, Layout, Modal, Panel, ProgressBar, ScrollContainer, Slider, Toast, Toggle, WinDisplay, resolveView };
2873
2973
  //# sourceMappingURL=ui.esm.js.map