@energy8platform/game-engine 0.14.9 → 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/react.cjs.js CHANGED
@@ -24,7 +24,7 @@ function extend(components) {
24
24
 
25
25
  const RESERVED = new Set(['children', 'key', 'ref']);
26
26
  /** Props handled by the reconciler as flex item config, not forwarded to components */
27
- const FLEX_ITEM_PROPS$1 = new Set(['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude', 'top', 'right', 'bottom', 'left']);
27
+ const FLEX_ITEM_PROPS$1 = new Set(['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude', 'top', 'right', 'bottom', 'left', 'centerX', 'centerY']);
28
28
  /** Base Container props applied directly to the instance (not via config) */
29
29
  const CONTAINER_PROPS = new Set([
30
30
  'x', 'y', 'alpha', 'visible', 'rotation', 'angle', 'zIndex',
@@ -151,6 +151,34 @@ function hasEventProps(props) {
151
151
  }
152
152
  return false;
153
153
  }
154
+ /**
155
+ * Graphics does not have an intrinsic size — its bounds come from the `draw` callback.
156
+ * PixiJS's `Container.width` setter applies a scale transform to match the requested
157
+ * value, which silently makes the rendered size diverge from `getLocalBounds()` and
158
+ * breaks downstream flex layout. For Graphics we treat `width`/`height` as layout
159
+ * hints instead, storing them on `_flexConfig.layoutWidth`/`layoutHeight` so parent
160
+ * FlexContainers measure correctly without touching the child's scale.
161
+ */
162
+ function isGraphicsLike(instance) {
163
+ return typeof instance?.clear === 'function'
164
+ && typeof instance?.fill === 'function'
165
+ && typeof instance?.rect === 'function';
166
+ }
167
+ /** Redirect a width/height write on a Graphics into its flex layout hint. Returns true if handled. */
168
+ function applyGraphicsDimension(instance, key, value) {
169
+ if ((key !== 'width' && key !== 'height') || !isGraphicsLike(instance))
170
+ return false;
171
+ const target = key === 'width' ? 'layoutWidth' : 'layoutHeight';
172
+ if (value === undefined) {
173
+ if (instance._flexConfig)
174
+ delete instance._flexConfig[target];
175
+ return true;
176
+ }
177
+ if (!instance._flexConfig)
178
+ instance._flexConfig = {};
179
+ instance._flexConfig[target] = value;
180
+ return true;
181
+ }
154
182
  function setNestedValue(target, path, value) {
155
183
  let obj = target;
156
184
  for (let i = 0; i < path.length - 1; i++) {
@@ -215,6 +243,7 @@ function applyProps(instance, newProps, oldProps = {}) {
215
243
  }
216
244
  else if (key === 'draw') ;
217
245
  else if (key.includes('-')) ;
246
+ else if (applyGraphicsDimension(instance, key, undefined)) ;
218
247
  else {
219
248
  try {
220
249
  instance[key] = undefined;
@@ -241,6 +270,7 @@ function applyProps(instance, newProps, oldProps = {}) {
241
270
  const parts = key.split('-');
242
271
  setNestedValue(instance, parts, value);
243
272
  }
273
+ else if (applyGraphicsDimension(instance, key, value)) ;
244
274
  else {
245
275
  try {
246
276
  instance[key] = value;
@@ -789,22 +819,31 @@ class FlexContainer extends pixi_js.Container {
789
819
  this._computedWidth = this._explicitWidth > 0 ? this._explicitWidth : (pl + totalCrossNatural + pr);
790
820
  this._computedHeight = this._explicitHeight > 0 ? this._explicitHeight : (pt + naturalMainSize + pb);
791
821
  }
792
- // Position flexExclude children (absolute positioning)
822
+ // Position flexExclude children (absolute positioning).
823
+ // All position props describe the visual (bounds) rectangle, so we subtract
824
+ // `ox/oy` to compensate for children with a centered origin (Button, Label).
825
+ // `centerX/centerY` take precedence over `left/right` / `top/bottom` on each axis.
793
826
  for (const child of this._layoutChildren) {
794
827
  if (!child._flexConfig?.flexExclude)
795
828
  continue;
796
829
  const cfg = child._flexConfig;
797
- const { w, h } = measureChild(child, pctRefW, pctRefH);
830
+ const { w, h, ox, oy } = measureChild(child, pctRefW, pctRefH);
798
831
  const cw = this._computedWidth;
799
832
  const ch = this._computedHeight;
800
- if (cfg.left !== undefined)
801
- child.x = cfg.left;
833
+ const centerX = resolveDimension(cfg.centerX, pctRefW);
834
+ if (centerX !== undefined)
835
+ child.x = centerX - w / 2 - ox;
836
+ else if (cfg.left !== undefined)
837
+ child.x = cfg.left - ox;
802
838
  else if (cfg.right !== undefined)
803
- child.x = cw - w - cfg.right;
804
- if (cfg.top !== undefined)
805
- child.y = cfg.top;
839
+ child.x = cw - w - cfg.right - ox;
840
+ const centerY = resolveDimension(cfg.centerY, pctRefH);
841
+ if (centerY !== undefined)
842
+ child.y = centerY - h / 2 - oy;
843
+ else if (cfg.top !== undefined)
844
+ child.y = cfg.top - oy;
806
845
  else if (cfg.bottom !== undefined)
807
- child.y = ch - h - cfg.bottom;
846
+ child.y = ch - h - cfg.bottom - oy;
808
847
  }
809
848
  }
810
849
  /** Computed content size (after layout) */
@@ -863,7 +902,7 @@ class FlexContainer extends pixi_js.Container {
863
902
  }
864
903
 
865
904
  /** Flex item prop names that should be forwarded to _flexConfig on the child */
866
- const FLEX_ITEM_PROPS = ['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude', 'top', 'right', 'bottom', 'left'];
905
+ const FLEX_ITEM_PROPS = ['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude', 'top', 'right', 'bottom', 'left', 'centerX', 'centerY'];
867
906
  function isSuspendable(obj) {
868
907
  return typeof obj?.suspendLayout === 'function' && typeof obj?.resumeLayout === 'function';
869
908
  }
@@ -1862,6 +1901,97 @@ class Label extends pixi_js.Container {
1862
1901
  }
1863
1902
  }
1864
1903
 
1904
+ /**
1905
+ * Two-row text cell with a muted caption above and a prominent value below —
1906
+ * the archetypal casino UI pattern (BALANCE/€500, BET/€1, WIN/€0.00).
1907
+ *
1908
+ * Extends FlexContainer, so it participates in parent flex layouts directly
1909
+ * (you can pass `flexGrow`, `alignSelf`, etc. when it's a child of another
1910
+ * FlexContainer) and auto-sizes to its contents when no explicit size is set.
1911
+ *
1912
+ * @example
1913
+ * ```tsx
1914
+ * <labelValue
1915
+ * label="BALANCE"
1916
+ * value={`€${balance.toFixed(2)}`}
1917
+ * labelStyle={{ fontSize: 12, fill: 0x888888 }}
1918
+ * valueStyle={{ fontSize: 22, fill: 0xffffff, fontWeight: 'bold' }}
1919
+ * gap={6}
1920
+ * align="center"
1921
+ * />
1922
+ * ```
1923
+ */
1924
+ class LabelValue extends FlexContainer {
1925
+ _labelEl;
1926
+ _valueEl;
1927
+ _valueMaxWidth;
1928
+ constructor(config = {}) {
1929
+ super({
1930
+ direction: 'column',
1931
+ alignItems: toAlignItems(config.align ?? 'center'),
1932
+ gap: config.gap ?? 4,
1933
+ padding: config.padding,
1934
+ });
1935
+ this._valueMaxWidth = config.maxWidth ?? Infinity;
1936
+ this._labelEl = new Label({
1937
+ text: config.label ?? '',
1938
+ style: config.labelStyle,
1939
+ });
1940
+ this._valueEl = new Label({
1941
+ text: config.value ?? '',
1942
+ style: config.valueStyle,
1943
+ maxWidth: config.maxWidth,
1944
+ autoFit: config.maxWidth !== undefined,
1945
+ });
1946
+ this.addFlexChild(this._labelEl);
1947
+ this.addFlexChild(this._valueEl);
1948
+ }
1949
+ /** Get the caption Label (for advanced styling / animation) */
1950
+ get labelElement() {
1951
+ return this._labelEl;
1952
+ }
1953
+ /** Get the value Label */
1954
+ get valueElement() {
1955
+ return this._valueEl;
1956
+ }
1957
+ /** Update caption text */
1958
+ setLabel(text) {
1959
+ this._labelEl.text = text;
1960
+ this.updateLayout();
1961
+ }
1962
+ /** Update value text */
1963
+ setValue(text) {
1964
+ this._valueEl.text = text;
1965
+ this.updateLayout();
1966
+ }
1967
+ /** React reconciler update hook */
1968
+ updateConfig(changed) {
1969
+ if ('label' in changed)
1970
+ this._labelEl.text = changed.label ?? '';
1971
+ if ('value' in changed)
1972
+ this._valueEl.text = changed.value ?? '';
1973
+ if ('labelStyle' in changed && typeof changed.labelStyle === 'object') {
1974
+ Object.assign(this._labelEl.style, changed.labelStyle);
1975
+ }
1976
+ if ('valueStyle' in changed && typeof changed.valueStyle === 'object') {
1977
+ Object.assign(this._valueEl.style, changed.valueStyle);
1978
+ }
1979
+ if ('maxWidth' in changed) {
1980
+ this._valueMaxWidth = changed.maxWidth ?? Infinity;
1981
+ this._valueEl.maxWidth = this._valueMaxWidth;
1982
+ }
1983
+ if ('gap' in changed)
1984
+ this.setGap(changed.gap);
1985
+ if ('align' in changed)
1986
+ this.setAlignItems(toAlignItems(changed.align));
1987
+ // Forward remaining FlexContainer props (e.g. padding, width, height)
1988
+ super.updateConfig(changed);
1989
+ }
1990
+ }
1991
+ function toAlignItems(align) {
1992
+ return align;
1993
+ }
1994
+
1865
1995
  /**
1866
1996
  * Background panel with optional flexbox content layout.
1867
1997
  *
@@ -3459,7 +3589,7 @@ function extendPixiElements() {
3459
3589
  */
3460
3590
  function extendUIElements() {
3461
3591
  extend({
3462
- Button, Label, Panel, FlexContainer, ProgressBar,
3592
+ Button, Label, LabelValue, Panel, FlexContainer, ProgressBar,
3463
3593
  ScrollContainer, Modal, Toast, BalanceDisplay, WinDisplay, Layout,
3464
3594
  Slider, Toggle,
3465
3595
  });