@dcl/react-ecs 7.28.1-34955459026.commit-7db2d46 → 7.29.1-34986384248.commit-bb45080
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/reconciler/index.js +47 -37
- package/dist/system.d.ts +10 -0
- package/dist/system.js +44 -14
- package/package.json +3 -3
package/dist/reconciler/index.js
CHANGED
|
@@ -46,7 +46,10 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
function updateTree(instance, props) {
|
|
49
|
-
|
|
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');
|
|
50
53
|
}
|
|
51
54
|
function upsertListener(instance, update) {
|
|
52
55
|
if (update.type === 'delete' || !update.props) {
|
|
@@ -147,8 +150,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
function appendChild(parent, child) {
|
|
150
|
-
if (!child || !Object.keys(parent).length)
|
|
151
|
-
return;
|
|
152
153
|
const isReorder = parent._child.find((c) => c.entity === child.entity);
|
|
153
154
|
// If its a reorder its seems that its a mutation of an array with key prop
|
|
154
155
|
// We need to move the child to the end of the array
|
|
@@ -191,6 +192,36 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
191
192
|
}
|
|
192
193
|
removeChildEntity(child);
|
|
193
194
|
}
|
|
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
|
+
}
|
|
194
225
|
function updateOnChange(entity, componentId, state) {
|
|
195
226
|
const hasEvent = changeEvents.has(entity);
|
|
196
227
|
const event = changeEvents.get(entity) || changeEvents.set(entity, new Map()).get(entity);
|
|
@@ -245,6 +276,7 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
245
276
|
appendChild,
|
|
246
277
|
appendChildToContainer: appendChild,
|
|
247
278
|
appendInitialChild: appendChild,
|
|
279
|
+
insertInContainerBefore: insertBefore,
|
|
248
280
|
removeChild: removeChild,
|
|
249
281
|
prepareUpdate(_instance, _type, oldProps, newProps) {
|
|
250
282
|
return componentKeys
|
|
@@ -265,42 +297,20 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
265
297
|
}
|
|
266
298
|
}
|
|
267
299
|
},
|
|
268
|
-
insertBefore
|
|
269
|
-
|
|
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
|
-
}
|
|
300
|
+
insertBefore,
|
|
301
|
+
removeChildFromContainer: removeChild
|
|
301
302
|
};
|
|
302
303
|
const reconciler = Reconciler(hostConfig);
|
|
303
|
-
|
|
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, '',
|
|
304
314
|
/* istanbul ignore next */
|
|
305
315
|
function () { }, null);
|
|
306
316
|
return {
|
package/dist/system.d.ts
CHANGED
|
@@ -27,6 +27,16 @@ 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;
|
|
30
40
|
};
|
|
31
41
|
/**
|
|
32
42
|
* @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 } from './components';
|
|
6
|
+
import { InteractableArea, ScreenInsetArea, UiEntity } 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,6 +47,14 @@ 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;
|
|
50
58
|
const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
|
|
51
59
|
// Unique owner to prevent other UI systems resetting this scale factor.
|
|
52
60
|
const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
|
|
@@ -119,37 +127,58 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
119
127
|
/**
|
|
120
128
|
* Wraps a renderer's component in a container positioned within the selected
|
|
121
129
|
* screen inset area. `'none'` adds no wrapper, leaving the UI on the whole
|
|
122
|
-
* screen
|
|
130
|
+
* screen, unless a `zIndex` needs a root entity to live on. Applied per
|
|
131
|
+
* renderer, so each renderer can use a different inset.
|
|
123
132
|
*/
|
|
124
|
-
function wrapWithScreenInset(ui,
|
|
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 } };
|
|
125
138
|
// An omitted inset is resolved to DEFAULT_SCREEN_INSET ('device') before the
|
|
126
139
|
// switch, so it lands on `case 'device'`. Only an explicit 'none' reaches the
|
|
127
140
|
// `default` clause below — the two are unrelated despite sharing a name.
|
|
128
|
-
switch (
|
|
141
|
+
switch (options?.screenInset ?? DEFAULT_SCREEN_INSET) {
|
|
129
142
|
case 'device':
|
|
130
|
-
return React.createElement(ScreenInsetArea,
|
|
143
|
+
return React.createElement(ScreenInsetArea, wrapperProps, React.createElement(ui));
|
|
131
144
|
case 'interactable':
|
|
132
|
-
return React.createElement(InteractableArea,
|
|
145
|
+
return React.createElement(InteractableArea, wrapperProps, React.createElement(ui));
|
|
133
146
|
// 'none' — the whole screen, no wrapper entity
|
|
134
147
|
default:
|
|
135
|
-
|
|
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));
|
|
136
156
|
}
|
|
137
157
|
}
|
|
138
158
|
function ReactBasedUiSystem() {
|
|
139
159
|
const components = [];
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
160
|
+
if (uiComponent !== undefined && mainOrder === undefined)
|
|
161
|
+
mainOrder = nextOrder++;
|
|
162
|
+
let mainPushed = uiComponent === undefined;
|
|
144
163
|
const entitiesToRemove = [];
|
|
145
164
|
for (const [entity, entry] of additionalRenderers) {
|
|
146
165
|
// Check for entity-based cleanup
|
|
147
166
|
if (engine.getEntityState(entity) === EntityState.Removed) {
|
|
148
167
|
entitiesToRemove.push(entity);
|
|
168
|
+
continue;
|
|
149
169
|
}
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
152
176
|
}
|
|
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__'));
|
|
153
182
|
}
|
|
154
183
|
// Entity-based cleanup
|
|
155
184
|
for (const entity of entitiesToRemove) {
|
|
@@ -225,7 +254,8 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
225
254
|
mainOptions = options;
|
|
226
255
|
},
|
|
227
256
|
addUiRenderer(entity, ui, options) {
|
|
228
|
-
|
|
257
|
+
// A replacement keeps its place in the stacking order.
|
|
258
|
+
additionalRenderers.set(entity, { ui, options, order: additionalRenderers.get(entity)?.order });
|
|
229
259
|
},
|
|
230
260
|
removeUiRenderer(entity) {
|
|
231
261
|
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.
|
|
4
|
+
"version": "7.29.1-34986384248.commit-bb45080",
|
|
5
5
|
"author": "DCL",
|
|
6
6
|
"bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@dcl/ecs": "7.
|
|
8
|
+
"@dcl/ecs": "7.29.1-34986384248.commit-bb45080",
|
|
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": "
|
|
43
|
+
"commit": "bb450800d3d88bfed04e25cca38b2c833eb3cd53"
|
|
44
44
|
}
|