@dcl/react-ecs 7.17.1-21043804740.commit-3c928e0 → 7.17.1-21366395728.commit-ada88b6

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.
@@ -1,4 +1,4 @@
1
- import { calcOnViewport, getScaleCtx } from '../utils';
1
+ import { calcOnViewport, getScaleCtx, getUiScaleFactor } from '../utils';
2
2
  const parseFont = {
3
3
  'sans-serif': 0 /* Font.F_SANS_SERIF */,
4
4
  serif: 1 /* Font.F_SERIF */,
@@ -39,7 +39,7 @@ export function getFontSize(fontSize) {
39
39
  return undefined;
40
40
  if (typeof fontSize === 'string')
41
41
  return { fontSize: calcOnViewport(fontSize) };
42
- return { fontSize };
42
+ return { fontSize: fontSize * getUiScaleFactor() };
43
43
  }
44
44
  /**
45
45
  * Scales a font size depending on a context's width/height
@@ -1,4 +1,4 @@
1
- import { calcOnViewport } from '../utils';
1
+ import { calcOnViewport, getUiScaleFactor } from '../utils';
2
2
  function capitalize(value) {
3
3
  return `${value[0].toUpperCase()}${value.slice(1, value.length)}`;
4
4
  }
@@ -14,6 +14,9 @@ function isVw(val) {
14
14
  function isVh(val) {
15
15
  return typeof val === 'string' && val.endsWith('vh');
16
16
  }
17
+ function scalePixelValue(value) {
18
+ return value * getUiScaleFactor();
19
+ }
17
20
  function parsePositionUnit(val) {
18
21
  function getValue(key, value) {
19
22
  return Number(value.slice(0, value.indexOf(key)));
@@ -25,13 +28,13 @@ function parsePositionUnit(val) {
25
28
  return [0, 3 /* YGUnit.YGU_AUTO */];
26
29
  }
27
30
  if (typeof val === 'number' || (typeof val === 'string' && !isNaN(Number(val)))) {
28
- return [Number(val), 1 /* YGUnit.YGU_POINT */];
31
+ return [scalePixelValue(Number(val)), 1 /* YGUnit.YGU_POINT */];
29
32
  }
30
33
  if (isPercent(val)) {
31
34
  return [getValue('%', val), 2 /* YGUnit.YGU_PERCENT */];
32
35
  }
33
36
  if (isPoint(val)) {
34
- return [getValue('px', val), 1 /* YGUnit.YGU_POINT */];
37
+ return [scalePixelValue(getValue('px', val)), 1 /* YGUnit.YGU_POINT */];
35
38
  }
36
39
  if (isVw(val) || isVh(val)) {
37
40
  return [calcOnViewport(val), 1 /* YGUnit.YGU_POINT */];
@@ -1,6 +1,8 @@
1
1
  import { engine, UiCanvasInformation as engineUiCanvasInformation } from '@dcl/ecs';
2
2
  import { parseUiBackground } from './uiBackground';
3
3
  import { parseUiTransform } from './uiTransform';
4
+ let uiScaleFactor = 1;
5
+ let uiScaleOwner = undefined;
4
6
  /**
5
7
  * @internal
6
8
  */
@@ -45,6 +47,34 @@ export function getScaleCtx(_engine = engine) {
45
47
  const { width, height, devicePixelRatio } = canvasInfo;
46
48
  return { width, height, ratio: devicePixelRatio };
47
49
  }
50
+ /**
51
+ * @internal
52
+ */
53
+ export function getUiScaleFactor() {
54
+ return uiScaleFactor;
55
+ }
56
+ /**
57
+ * @internal
58
+ */
59
+ export function setUiScaleFactor(nextScale, owner) {
60
+ if (!Number.isFinite(nextScale) || nextScale < 0)
61
+ return;
62
+ if (owner) {
63
+ // Mark ownership so only that system can reset the scale.
64
+ uiScaleOwner = owner;
65
+ }
66
+ uiScaleFactor = nextScale;
67
+ }
68
+ /**
69
+ * @internal
70
+ */
71
+ export function resetUiScaleFactor(owner) {
72
+ // Ignore resets from non-owners to avoid stomping active scale.
73
+ if (owner && uiScaleOwner !== owner)
74
+ return;
75
+ uiScaleOwner = undefined;
76
+ uiScaleFactor = 1;
77
+ }
48
78
  /**
49
79
  * @internal
50
80
  */
package/dist/system.d.ts CHANGED
@@ -1,15 +1,49 @@
1
- import type { IEngine, PointerEventsSystem } from '@dcl/ecs';
1
+ import { type Entity, type IEngine, type PointerEventsSystem } from '@dcl/ecs';
2
2
  import type { ReactEcs } from './react-ecs';
3
3
  /**
4
4
  * @public
5
5
  */
6
6
  export type UiComponent = () => ReactEcs.JSX.ReactNode;
7
+ /**
8
+ * @public
9
+ */
10
+ export type UiRendererOptions = {
11
+ virtualWidth: number;
12
+ virtualHeight: number;
13
+ };
7
14
  /**
8
15
  * @public
9
16
  */
10
17
  export interface ReactBasedUiSystem {
18
+ /**
19
+ * Destroy all UI entities and unregister related systems.
20
+ */
11
21
  destroy(): void;
12
- setUiRenderer(ui: UiComponent): void;
22
+ /**
23
+ * Set the main UI renderer. Optional virtual size defines the global UI scale factor.
24
+ */
25
+ setUiRenderer(ui: UiComponent, options?: UiRendererOptions): void;
26
+ /**
27
+ * Add a UI renderer associated with an entity. The UI will be automatically cleaned up
28
+ * when the entity is removed from the engine.
29
+ *
30
+ * If a renderer is already associated with the given entity, it will be replaced.
31
+ *
32
+ * This allows dynamically adding UI Renderers that are rendered alongside the main
33
+ * UI set via setUiRenderer().
34
+ *
35
+ * @param entity - The entity to associate with this UI renderer. When the entity is removed,
36
+ * the UI renderer is automatically cleaned up.
37
+ * @param ui - The UI component to render
38
+ * @param options - Optional virtual size used for UI scale factor when main UI has none
39
+ */
40
+ addUiRenderer(entity: Entity, ui: UiComponent, options?: UiRendererOptions): void;
41
+ /**
42
+ * Remove a previously added UI renderer by its associated entity.
43
+ * It does not affect the main UI renderer.
44
+ * @param entity - The entity whose UI renderer should be removed
45
+ */
46
+ removeUiRenderer(entity: Entity): void;
13
47
  }
14
48
  /**
15
49
  * @public
package/dist/system.js CHANGED
@@ -1,25 +1,97 @@
1
+ import { EntityState } from '@dcl/ecs';
2
+ import * as ecsComponents from '@dcl/ecs/dist/components';
1
3
  import React from 'react';
2
4
  import { createReconciler } from './reconciler';
5
+ import { getUiScaleFactor, resetUiScaleFactor, setUiScaleFactor } from './components/utils';
3
6
  /**
4
7
  * @public
5
8
  */
6
9
  export function createReactBasedUiSystem(engine, pointerSystem) {
7
10
  const renderer = createReconciler(engine, pointerSystem);
8
11
  let uiComponent = undefined;
12
+ let virtualSize = undefined;
13
+ const additionalRenderers = new Map();
14
+ const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
15
+ // Unique owner to prevent other UI systems resetting this scale factor.
16
+ const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
17
+ function getActiveVirtualSize() {
18
+ // Main renderer options win; otherwise use the first additional renderer option.
19
+ if (virtualSize)
20
+ return virtualSize;
21
+ for (const entry of additionalRenderers.values()) {
22
+ if (entry.options)
23
+ return entry.options;
24
+ }
25
+ return undefined;
26
+ }
9
27
  function ReactBasedUiSystem() {
10
- if (uiComponent)
11
- renderer.update(React.createElement(uiComponent));
28
+ const components = [];
29
+ // Add main UI component
30
+ if (uiComponent) {
31
+ components.push(React.createElement(uiComponent, { key: '__main__' }));
32
+ }
33
+ const entitiesToRemove = [];
34
+ for (const [entity, entry] of additionalRenderers) {
35
+ // Check for entity-based cleanup
36
+ if (engine.getEntityState(entity) === EntityState.Removed) {
37
+ entitiesToRemove.push(entity);
38
+ }
39
+ else {
40
+ components.push(React.createElement(entry.ui, { key: `__entity_${entity}__` }));
41
+ }
42
+ }
43
+ // Entity-based cleanup
44
+ for (const entity of entitiesToRemove) {
45
+ additionalRenderers.delete(entity);
46
+ }
47
+ // Always update the renderer - pass null when empty to clear the UI
48
+ if (components.length > 0) {
49
+ renderer.update(React.createElement(React.Fragment, null, ...components));
50
+ }
51
+ else {
52
+ renderer.update(null);
53
+ }
12
54
  }
55
+ function UiScaleSystem() {
56
+ const activeVirtualSize = getActiveVirtualSize();
57
+ if (!activeVirtualSize) {
58
+ // Reset only if this system owns the scale factor.
59
+ resetUiScaleFactor(uiScaleFactorOwner);
60
+ return;
61
+ }
62
+ const canvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity);
63
+ if (!canvasInfo)
64
+ return;
65
+ const { width, height } = canvasInfo;
66
+ const { virtualWidth, virtualHeight } = activeVirtualSize;
67
+ if (!virtualWidth || !virtualHeight)
68
+ return;
69
+ const nextScale = Math.min(width / virtualWidth, height / virtualHeight);
70
+ if (Number.isFinite(nextScale) && nextScale !== getUiScaleFactor()) {
71
+ // Track ownership when updating to avoid cross-system conflicts.
72
+ setUiScaleFactor(nextScale, uiScaleFactorOwner);
73
+ }
74
+ }
75
+ engine.addSystem(UiScaleSystem, 100e3 + 1, '@dcl/react-ecs-ui-scale');
13
76
  engine.addSystem(ReactBasedUiSystem, 100e3, '@dcl/react-ecs');
14
77
  return {
15
78
  destroy() {
79
+ engine.removeSystem(UiScaleSystem);
16
80
  engine.removeSystem(ReactBasedUiSystem);
81
+ resetUiScaleFactor(uiScaleFactorOwner);
17
82
  for (const entity of renderer.getEntities()) {
18
83
  engine.removeEntity(entity);
19
84
  }
20
85
  },
21
- setUiRenderer(ui) {
86
+ setUiRenderer(ui, options) {
22
87
  uiComponent = ui;
88
+ virtualSize = options;
89
+ },
90
+ addUiRenderer(entity, ui, options) {
91
+ additionalRenderers.set(entity, { ui, options });
92
+ },
93
+ removeUiRenderer(entity) {
94
+ additionalRenderers.delete(entity);
23
95
  }
24
96
  };
25
97
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dcl/react-ecs",
3
3
  "description": "Decentraland ECS",
4
- "version": "7.17.1-21043804740.commit-3c928e0",
4
+ "version": "7.17.1-21366395728.commit-ada88b6",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
7
7
  "dependencies": {
8
- "@dcl/ecs": "7.17.1-21043804740.commit-3c928e0",
8
+ "@dcl/ecs": "7.17.1-21366395728.commit-ada88b6",
9
9
  "react": "^18.2.0",
10
10
  "react-reconciler": "^0.29.0"
11
11
  },
@@ -40,5 +40,5 @@
40
40
  "tsconfig": "./tsconfig.json"
41
41
  },
42
42
  "types": "./dist/index.d.ts",
43
- "commit": "3c928e0a4e46c8020bd70368e02a57efb6f5788c"
43
+ "commit": "ada88b6fe8e93150056e0cade4b94e0c1e7dc850"
44
44
  }