@dcl/react-ecs 7.28.1-34907286167.commit-d2f06b5 → 7.28.1-34955459026.commit-7db2d46

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.
@@ -46,10 +46,7 @@ export function createReconciler(engine, pointerEvents) {
46
46
  return;
47
47
  }
48
48
  function updateTree(instance, props) {
49
- // `rightOf` is a required int32 where 0 means "first sibling". A missing left
50
- // neighbour is written as 0 so the first child of any parent (the canvas root
51
- // included) serializes the same way as an explicit `rightOf: 0`.
52
- upsertComponent(instance, { ...props, rightOf: props.rightOf ?? 0 }, 'uiTransform');
49
+ upsertComponent(instance, props, 'uiTransform');
53
50
  }
54
51
  function upsertListener(instance, update) {
55
52
  if (update.type === 'delete' || !update.props) {
@@ -150,6 +147,8 @@ export function createReconciler(engine, pointerEvents) {
150
147
  }
151
148
  }
152
149
  function appendChild(parent, child) {
150
+ if (!child || !Object.keys(parent).length)
151
+ return;
153
152
  const isReorder = parent._child.find((c) => c.entity === child.entity);
154
153
  // If its a reorder its seems that its a mutation of an array with key prop
155
154
  // We need to move the child to the end of the array
@@ -192,36 +191,6 @@ export function createReconciler(engine, pointerEvents) {
192
191
  }
193
192
  removeChildEntity(child);
194
193
  }
195
- function insertBefore(parentInstance, child, beforeChild) {
196
- // Handle reorder: if child already exists in this parent, remove it from its old position
197
- // and fix up the old neighbor's rightOf before inserting at the new position.
198
- const existingIndex = parentInstance._child.findIndex((c) => c.entity === child.entity);
199
- if (existingIndex !== -1) {
200
- // If beforeChild.rightOf already points to child, child is already in the correct
201
- // position in the rightOf chain. Setting child.rightOf = beforeChild.rightOf would
202
- // produce a self-cycle (child.rightOf = child.entity).
203
- if (beforeChild.rightOf === child.entity) {
204
- return;
205
- }
206
- const oldNextSibling = parentInstance._child[existingIndex + 1];
207
- if (oldNextSibling) {
208
- oldNextSibling.rightOf = child.rightOf;
209
- updateTree(oldNextSibling, { rightOf: oldNextSibling.rightOf });
210
- }
211
- parentInstance._child.splice(existingIndex, 1);
212
- }
213
- const beforeChildIndex = parentInstance._child.findIndex((c) => c.entity === beforeChild.entity);
214
- parentInstance._child = [
215
- ...parentInstance._child.slice(0, beforeChildIndex),
216
- child,
217
- ...parentInstance._child.slice(beforeChildIndex)
218
- ];
219
- child.rightOf = beforeChild.rightOf;
220
- beforeChild.rightOf = child.entity;
221
- child.parent = parentInstance.entity;
222
- updateTree(child, { rightOf: child.rightOf, parent: child.parent });
223
- updateTree(beforeChild, { rightOf: beforeChild.rightOf });
224
- }
225
194
  function updateOnChange(entity, componentId, state) {
226
195
  const hasEvent = changeEvents.has(entity);
227
196
  const event = changeEvents.get(entity) || changeEvents.set(entity, new Map()).get(entity);
@@ -276,7 +245,6 @@ export function createReconciler(engine, pointerEvents) {
276
245
  appendChild,
277
246
  appendChildToContainer: appendChild,
278
247
  appendInitialChild: appendChild,
279
- insertInContainerBefore: insertBefore,
280
248
  removeChild: removeChild,
281
249
  prepareUpdate(_instance, _type, oldProps, newProps) {
282
250
  return componentKeys
@@ -297,20 +265,42 @@ export function createReconciler(engine, pointerEvents) {
297
265
  }
298
266
  }
299
267
  },
300
- insertBefore,
301
- removeChildFromContainer: removeChild
268
+ insertBefore(parentInstance, child, beforeChild) {
269
+ // Handle reorder: if child already exists in this parent, remove it from its old position
270
+ // and fix up the old neighbor's rightOf before inserting at the new position.
271
+ const existingIndex = parentInstance._child.findIndex((c) => c.entity === child.entity);
272
+ if (existingIndex !== -1) {
273
+ // If beforeChild.rightOf already points to child, child is already in the correct
274
+ // position in the rightOf chain. Setting child.rightOf = beforeChild.rightOf would
275
+ // produce a self-cycle (child.rightOf = child.entity).
276
+ if (beforeChild.rightOf === child.entity) {
277
+ return;
278
+ }
279
+ const oldNextSibling = parentInstance._child[existingIndex + 1];
280
+ if (oldNextSibling) {
281
+ oldNextSibling.rightOf = child.rightOf;
282
+ updateTree(oldNextSibling, { rightOf: oldNextSibling.rightOf });
283
+ }
284
+ parentInstance._child.splice(existingIndex, 1);
285
+ }
286
+ const beforeChildIndex = parentInstance._child.findIndex((c) => c.entity === beforeChild.entity);
287
+ parentInstance._child = [
288
+ ...parentInstance._child.slice(0, beforeChildIndex),
289
+ child,
290
+ ...parentInstance._child.slice(beforeChildIndex)
291
+ ];
292
+ child.rightOf = beforeChild.rightOf;
293
+ beforeChild.rightOf = child.entity;
294
+ child.parent = parentInstance.entity;
295
+ updateTree(child, { rightOf: child.rightOf, parent: child.parent });
296
+ updateTree(beforeChild, { rightOf: beforeChild.rightOf });
297
+ },
298
+ removeChildFromContainer(parenInstance, child) {
299
+ removeChildEntity(child);
300
+ }
302
301
  };
303
302
  const reconciler = Reconciler(hostConfig);
304
- // The container is a real Instance standing in for the canvas root, so
305
- // top-level elements get the same parent/rightOf bookkeeping as any other
306
- // sibling group. It is never rendered itself.
307
- const containerInstance = {
308
- entity: CANVAS_ROOT_ENTITY,
309
- _child: [],
310
- parent: undefined,
311
- rightOf: undefined
312
- };
313
- const root = reconciler.createContainer(containerInstance, 0, null, false, null, '',
303
+ const root = reconciler.createContainer({}, 0, null, false, null, '',
314
304
  /* istanbul ignore next */
315
305
  function () { }, null);
316
306
  return {
package/dist/system.d.ts CHANGED
@@ -27,16 +27,6 @@ export type UiRendererOptions = {
27
27
  * can use different insets simultaneously.
28
28
  */
29
29
  screenInset?: UiScreenInset;
30
- /**
31
- * Stacking order of this renderer's UI relative to the other renderers (the main
32
- * one and every `addUiRenderer`). Higher values render in front. When omitted,
33
- * renderers stack in the order they first render, later ones on top; among those
34
- * first rendered in the same tick the main UI goes at the back.
35
- *
36
- * It is applied to the renderer's root container entity, so it only orders whole
37
- * renderers against each other; elements inside a renderer keep their own `zIndex`.
38
- */
39
- zIndex?: number;
40
30
  };
41
31
  /**
42
32
  * @public
package/dist/system.js CHANGED
@@ -3,7 +3,7 @@ import * as ecsComponents from '@dcl/ecs/dist/components';
3
3
  import React from 'react';
4
4
  import { createReconciler } from './reconciler';
5
5
  import { getUiScaleFactor, resetInteractableArea, resetScreenInsetArea, resetUiScaleFactor, setInteractableArea, setScreenInsetArea, setUiScaleFactor } from './components/utils';
6
- import { InteractableArea, ScreenInsetArea, UiEntity } from './components';
6
+ import { InteractableArea, ScreenInsetArea } from './components';
7
7
  import { isMobile } from './platform';
8
8
  /**
9
9
  * Default virtual screen size used on mobile platforms, and the size 16:9
@@ -47,14 +47,6 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
47
47
  let uiComponent = undefined;
48
48
  let mainOptions = undefined;
49
49
  const additionalRenderers = new Map();
50
- // Renderers are laid out in the order they first render, later ones on top, with
51
- // the main UI first among those first rendered in the same tick. That is the order
52
- // their entities were created in before the stacking was explicit, so scenes that
53
- // set no zIndex keep the stacking they had. A renderer gets its sequence number the
54
- // first time it renders and keeps it when replaced; the Map iterates in insertion
55
- // order, so it is already sorted by `order` and only the main UI has to be slotted in.
56
- let nextOrder = 0;
57
- let mainOrder = undefined;
58
50
  const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
59
51
  // Unique owner to prevent other UI systems resetting this scale factor.
60
52
  const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
@@ -127,58 +119,37 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
127
119
  /**
128
120
  * Wraps a renderer's component in a container positioned within the selected
129
121
  * screen inset area. `'none'` adds no wrapper, leaving the UI on the whole
130
- * screen, unless a `zIndex` needs a root entity to live on. Applied per
131
- * renderer, so each renderer can use a different inset.
122
+ * screen. Applied per renderer, so each renderer can use a different inset.
132
123
  */
133
- function wrapWithScreenInset(ui, options, key) {
134
- const zIndex = options?.zIndex;
135
- // Only forward a provided zIndex: `{ zIndex: undefined }` would override the
136
- // transform parser default and change what is written for every renderer.
137
- const wrapperProps = zIndex === undefined ? { key } : { key, uiTransform: { zIndex } };
124
+ function wrapWithScreenInset(ui, inset, key) {
138
125
  // An omitted inset is resolved to DEFAULT_SCREEN_INSET ('device') before the
139
126
  // switch, so it lands on `case 'device'`. Only an explicit 'none' reaches the
140
127
  // `default` clause below — the two are unrelated despite sharing a name.
141
- switch (options?.screenInset ?? DEFAULT_SCREEN_INSET) {
128
+ switch (inset ?? DEFAULT_SCREEN_INSET) {
142
129
  case 'device':
143
- return React.createElement(ScreenInsetArea, wrapperProps, React.createElement(ui));
130
+ return React.createElement(ScreenInsetArea, { key }, React.createElement(ui));
144
131
  case 'interactable':
145
- return React.createElement(InteractableArea, wrapperProps, React.createElement(ui));
132
+ return React.createElement(InteractableArea, { key }, React.createElement(ui));
146
133
  // 'none' — the whole screen, no wrapper entity
147
134
  default:
148
- if (zIndex === undefined)
149
- return React.createElement(ui, { key });
150
- // A whole-screen container: absolutely positioned on every edge so it
151
- // fills the canvas, with the zIndex the renderer is stacked by.
152
- return React.createElement(UiEntity, {
153
- key,
154
- uiTransform: { positionType: 'absolute', position: { top: 0, left: 0, right: 0, bottom: 0 }, zIndex }
155
- }, React.createElement(ui));
135
+ return React.createElement(ui, { key });
156
136
  }
157
137
  }
158
138
  function ReactBasedUiSystem() {
159
139
  const components = [];
160
- if (uiComponent !== undefined && mainOrder === undefined)
161
- mainOrder = nextOrder++;
162
- let mainPushed = uiComponent === undefined;
140
+ // Add main UI component
141
+ if (uiComponent) {
142
+ components.push(wrapWithScreenInset(uiComponent, mainOptions?.screenInset, '__main__'));
143
+ }
163
144
  const entitiesToRemove = [];
164
145
  for (const [entity, entry] of additionalRenderers) {
165
146
  // Check for entity-based cleanup
166
147
  if (engine.getEntityState(entity) === EntityState.Removed) {
167
148
  entitiesToRemove.push(entity);
168
- continue;
169
149
  }
170
- if (entry.order === undefined)
171
- entry.order = nextOrder++;
172
- // The main UI goes right before the first renderer that rendered after it.
173
- if (!mainPushed && mainOrder < entry.order) {
174
- components.push(wrapWithScreenInset(uiComponent, mainOptions, '__main__'));
175
- mainPushed = true;
150
+ else {
151
+ components.push(wrapWithScreenInset(entry.ui, entry.options?.screenInset, `__entity_${entity}__`));
176
152
  }
177
- components.push(wrapWithScreenInset(entry.ui, entry.options, `__entity_${entity}__`));
178
- }
179
- // Main UI first rendered after every other renderer (or alone): it goes on top.
180
- if (!mainPushed) {
181
- components.push(wrapWithScreenInset(uiComponent, mainOptions, '__main__'));
182
153
  }
183
154
  // Entity-based cleanup
184
155
  for (const entity of entitiesToRemove) {
@@ -254,8 +225,7 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
254
225
  mainOptions = options;
255
226
  },
256
227
  addUiRenderer(entity, ui, options) {
257
- // A replacement keeps its place in the stacking order.
258
- additionalRenderers.set(entity, { ui, options, order: additionalRenderers.get(entity)?.order });
228
+ additionalRenderers.set(entity, { ui, options });
259
229
  },
260
230
  removeUiRenderer(entity) {
261
231
  additionalRenderers.delete(entity);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dcl/react-ecs",
3
3
  "description": "Decentraland ECS",
4
- "version": "7.28.1-34907286167.commit-d2f06b5",
4
+ "version": "7.28.1-34955459026.commit-7db2d46",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
7
7
  "dependencies": {
8
- "@dcl/ecs": "7.28.1-34907286167.commit-d2f06b5",
8
+ "@dcl/ecs": "7.28.1-34955459026.commit-7db2d46",
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": "d2f06b5eb1a2ab3377a3f99d045f957ae979c051"
43
+ "commit": "7db2d4613572a8d72af9166b3df228a3b8f8c427"
44
44
  }