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