@dcl/react-ecs 7.29.1-35901015152.commit-d1b7b96 → 7.29.1-35917671376.commit-046b268

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.
@@ -29,7 +29,7 @@ function getButtonProps(props) {
29
29
  */
30
30
  /* @__PURE__ */
31
31
  export function Button(props) {
32
- const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...otherProps } = props;
32
+ const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...otherProps } = props;
33
33
  const buttonProps = getButtonProps(props);
34
34
  const uiBackgroundProps = parseUiBackground({
35
35
  ...buttonProps.uiBackground,
@@ -58,5 +58,5 @@ export function Button(props) {
58
58
  uiBackgroundProps.color = { ...uiBackgroundProps.color, a: uiBackgroundProps.color.a / 2 };
59
59
  }
60
60
  }
61
- return (ReactEcs.createElement("entity", { onMouseDown: !!props.disabled ? undefined : onMouseDown, onMouseUp: !!props.disabled ? undefined : onMouseUp, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDrag: onMouseDrag, onMouseDragLocked: onMouseDragLocked, onMouseDragEnd: onMouseDragEnd, uiTransform: uiTransformProps, uiText: textProps, uiBackground: uiBackgroundProps }));
61
+ return (ReactEcs.createElement("entity", { onMouseDown: !!props.disabled ? undefined : onMouseDown, onMouseUp: !!props.disabled ? undefined : onMouseUp, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, uiTransform: uiTransformProps, uiText: textProps, uiBackground: uiBackgroundProps }));
62
62
  }
@@ -34,7 +34,7 @@ function parseUiDropdown(props) {
34
34
  */
35
35
  /* @__PURE__ */
36
36
  export function Dropdown(props) {
37
- const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...otherProps } = props;
37
+ const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...otherProps } = props;
38
38
  const dropdownProps = parseUiDropdown(otherProps);
39
39
  const commonProps = parseProps({
40
40
  uiTransform,
@@ -42,10 +42,7 @@ export function Dropdown(props) {
42
42
  onMouseDown,
43
43
  onMouseUp,
44
44
  onMouseEnter,
45
- onMouseLeave,
46
- onMouseDrag,
47
- onMouseDragLocked,
48
- onMouseDragEnd
45
+ onMouseLeave
49
46
  });
50
47
  return ReactEcs.createElement("entity", { ...commonProps, uiDropdown: dropdownProps });
51
48
  }
@@ -35,7 +35,7 @@ function parseUiInput(props) {
35
35
  * @category Component
36
36
  */ /* @__PURE__ */
37
37
  export function Input(props) {
38
- const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...otherProps } = props;
38
+ const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...otherProps } = props;
39
39
  const inputProps = parseUiInput(otherProps);
40
40
  const commonProps = parseProps({
41
41
  uiTransform,
@@ -43,10 +43,7 @@ export function Input(props) {
43
43
  onMouseDown,
44
44
  onMouseUp,
45
45
  onMouseEnter,
46
- onMouseLeave,
47
- onMouseDrag,
48
- onMouseDragLocked,
49
- onMouseDragEnd
46
+ onMouseLeave
50
47
  });
51
48
  return ReactEcs.createElement("entity", { ...commonProps, uiInput: inputProps });
52
49
  }
@@ -16,17 +16,14 @@ export { scaleFontSize } from './utils';
16
16
  */
17
17
  /* @__PURE__ */
18
18
  export function Label(props) {
19
- const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...uiTextProps } = props;
19
+ const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...uiTextProps } = props;
20
20
  const commonProps = parseProps({
21
21
  uiTransform,
22
22
  uiBackground,
23
23
  onMouseDown,
24
24
  onMouseUp,
25
25
  onMouseEnter,
26
- onMouseLeave,
27
- onMouseDrag,
28
- onMouseDragLocked,
29
- onMouseDragEnd
26
+ onMouseLeave
30
27
  });
31
28
  const { font, textAlign, fontSize, textWrap, ...textProps } = uiTextProps;
32
29
  const uiText = {
@@ -17,8 +17,6 @@ export interface UiLabelProps {
17
17
  font?: UiFontType | undefined;
18
18
  /** Behaviour when text reached. @defaultValue 'wrap' */
19
19
  textWrap?: UiTextWrapType | undefined;
20
- /** A font file in the scene, or a Google Fonts family name; `font` is the fallback. @defaultValue undefined */
21
- fontSrc?: string | undefined;
22
20
  }
23
21
  /**
24
22
  * @public
@@ -1,10 +1,8 @@
1
- import { PBPointerEventsResult } from '@dcl/ecs';
2
1
  /**
3
- * Callback function to be triggered on a specified event. Receives the pointer event that
4
- * fired it (button, hit, and for drags the pointer delta), which handlers may ignore.
2
+ * Callback function to be triggered on a specified event
5
3
  * @public
6
4
  */
7
- export type Callback = (event: PBPointerEventsResult) => void;
5
+ export type Callback = () => void;
8
6
  /**
9
7
  * User key event Listeners
10
8
  * @public
@@ -18,10 +16,4 @@ export type Listeners = {
18
16
  onMouseEnter?: Callback;
19
17
  /** triggered on mouse leave event */
20
18
  onMouseLeave?: Callback;
21
- /** triggered while the pointer is dragged after pressing on the element */
22
- onMouseDrag?: Callback;
23
- /** triggered while the pointer is dragged with the cursor locked in place */
24
- onMouseDragLocked?: Callback;
25
- /** triggered when a drag that started on the element ends */
26
- onMouseDragEnd?: Callback;
27
19
  };
@@ -2,10 +2,7 @@ const listeners = {
2
2
  onMouseDown: undefined,
3
3
  onMouseUp: undefined,
4
4
  onMouseEnter: undefined,
5
- onMouseLeave: undefined,
6
- onMouseDrag: undefined,
7
- onMouseDragLocked: undefined,
8
- onMouseDragEnd: undefined
5
+ onMouseLeave: undefined
9
6
  };
10
7
  const listenersKey = Object.keys(listeners);
11
8
  /**
@@ -1,4 +1,4 @@
1
- import { BorderRect, Entity } from '@dcl/ecs';
1
+ import { BorderRect } from '@dcl/ecs';
2
2
  import { Color4 } from '@dcl/ecs/dist/components/generated/pb/decentraland/common/colors.gen';
3
3
  /**
4
4
  * @public
@@ -15,8 +15,6 @@ export interface UiBackgroundProps {
15
15
  uvs?: number[];
16
16
  /** AvatarTexture for the background */
17
17
  avatarTexture?: UiAvatarTexture;
18
- /** VideoTexture for the background: the frames of a VideoPlayer on another entity */
19
- videoTexture?: UiVideoTexture;
20
18
  /** Texture for the background */
21
19
  texture?: UiTexture;
22
20
  }
@@ -29,15 +27,6 @@ export interface UiAvatarTexture {
29
27
  wrapMode?: TextureWrapType;
30
28
  filterMode?: TextureFilterType;
31
29
  }
32
- /**
33
- * Video Texture
34
- * @public
35
- */
36
- export interface UiVideoTexture {
37
- videoPlayerEntity: Entity;
38
- wrapMode?: TextureWrapType;
39
- filterMode?: TextureFilterType;
40
- }
41
30
  /**
42
31
  * Texture
43
32
  * @public
@@ -30,14 +30,6 @@ export function getTexture(props) {
30
30
  }
31
31
  };
32
32
  }
33
- if (props.videoTexture) {
34
- return {
35
- tex: {
36
- $case: 'videoTexture',
37
- videoTexture: parseTexture(props.videoTexture)
38
- }
39
- };
40
- }
41
33
  return undefined;
42
34
  }
43
35
  function parseTexture(texture) {
@@ -1,4 +1,4 @@
1
- import { getAlign, getDisplay, getFlexDirection, getFlexWrap, getJustify, getOverflow, getPointerFilter, getPositionType, getScrollPosition, getScrollVisible, parseBorderColor, parseBorderRadius, parseBorderWidth, parsePosition, parseSize } from './utils';
1
+ import { getAlign, getDisplay, getFlexDirection, getFlexWrap, getJustify, getOverflow, getPointerFilter, getPositionType, parseBorderColor, parseBorderRadius, parseBorderWidth, parsePosition, parseSize } from './utils';
2
2
  /**
3
3
  * @internal
4
4
  */
@@ -60,7 +60,7 @@ const defaultUiTransform = {
60
60
  */
61
61
  /* @__PURE__ */
62
62
  export function parseUiTransform(props = {}) {
63
- const { height, minHeight, maxHeight, width, minWidth, maxWidth, alignItems, alignContent, flexWrap, borderRadius, borderWidth, borderColor, scrollPosition, scrollVisible, ...otherProps } = props;
63
+ const { height, minHeight, maxHeight, width, minWidth, maxWidth, alignItems, alignContent, flexWrap, borderRadius, borderWidth, borderColor, ...otherProps } = props;
64
64
  return {
65
65
  ...defaultUiTransform,
66
66
  ...otherProps,
@@ -86,8 +86,6 @@ export function parseUiTransform(props = {}) {
86
86
  ...(flexWrap && getFlexWrap(flexWrap)),
87
87
  ...(borderRadius && parseBorderRadius(borderRadius)),
88
88
  ...(borderWidth && parseBorderWidth(borderWidth)),
89
- ...(borderColor && parseBorderColor(borderColor)),
90
- ...(scrollPosition && getScrollPosition(scrollPosition)),
91
- ...(scrollVisible && getScrollVisible(scrollVisible))
89
+ ...(borderColor && parseBorderColor(borderColor))
92
90
  };
93
91
  }
@@ -1,5 +1,4 @@
1
1
  import { Color4 } from '@dcl/ecs/dist/components/generated/pb/decentraland/common/colors.gen';
2
- import { Vector2 } from '@dcl/ecs/dist/components/generated/pb/decentraland/common/vectors.gen';
3
2
  import { ScaleUnit } from '../types';
4
3
  /**
5
4
  * unit value specified. i.e. 1 || '100%' || '1px' || '10vw'
@@ -132,15 +131,4 @@ export interface UiTransformProps {
132
131
  opacity?: number;
133
132
  /** default 0 */
134
133
  zIndex?: number;
135
- /** A reference id for the element, e.g. as a scrollPosition target or for SetUiFocus. default empty */
136
- elementId?: string;
137
- /** Scroll offset when overflow is 'scroll': a position, or the elementId of a child to scroll to. default (0, 0) */
138
- scrollPosition?: Vector2 | string;
139
- /** Which scrollbars to show when overflow is 'scroll'. default 'both' */
140
- scrollVisible?: ScrollVisibleType;
141
134
  }
142
- /**
143
- * Which scrollbars are shown when the overflow is scrollable
144
- * @public
145
- */
146
- export type ScrollVisibleType = 'horizontal' | 'vertical' | 'both' | 'hidden';
@@ -244,24 +244,3 @@ const parsePointerFilter = {
244
244
  none: 0 /* PointerFilterMode.PFM_NONE */,
245
245
  block: 1 /* PointerFilterMode.PFM_BLOCK */
246
246
  };
247
- /**
248
- * @internal
249
- */
250
- export function getScrollPosition(scrollPosition) {
251
- if (typeof scrollPosition === 'string') {
252
- return { scrollPosition: { value: { $case: 'reference', reference: scrollPosition } } };
253
- }
254
- return { scrollPosition: { value: { $case: 'position', position: scrollPosition } } };
255
- }
256
- const parseScrollVisible = {
257
- both: 0 /* ShowScrollBar.SSB_BOTH */,
258
- hidden: 3 /* ShowScrollBar.SSB_HIDDEN */,
259
- horizontal: 2 /* ShowScrollBar.SSB_ONLY_HORIZONTAL */,
260
- vertical: 1 /* ShowScrollBar.SSB_ONLY_VERTICAL */
261
- };
262
- /**
263
- * @internal
264
- */
265
- export function getScrollVisible(scrollVisible) {
266
- return { scrollVisible: parseScrollVisible[scrollVisible] };
267
- }
@@ -23,9 +23,6 @@ export type EntityComponents = {
23
23
  onMouseUp: Callback;
24
24
  onMouseEnter: Callback;
25
25
  onMouseLeave: Callback;
26
- onMouseDrag: Callback;
27
- onMouseDragLocked: Callback;
28
- onMouseDragEnd: Callback;
29
26
  };
30
27
  /**
31
28
  * @hidden
@@ -1,6 +1,6 @@
1
1
  import { Entity, IEngine, PointerEventsSystem } from '@dcl/ecs';
2
2
  import { ReactEcs } from '../react-ecs';
3
- export declare function createReconciler(engine: Pick<IEngine, 'getComponent' | 'addEntity' | 'removeEntity' | 'defineComponentFromSchema' | 'getEntitiesWith'>, pointerEvents: PointerEventsSystem, rootEntity?: Entity): {
3
+ export declare function createReconciler(engine: Pick<IEngine, 'getComponent' | 'addEntity' | 'removeEntity' | 'defineComponentFromSchema' | 'getEntitiesWith'>, pointerEvents: PointerEventsSystem): {
4
4
  update: (component: ReactEcs.JSX.ReactNode) => number;
5
5
  getEntities: () => Entity[];
6
6
  };
@@ -8,17 +8,11 @@ function getPointerEnum(pointerKey) {
8
8
  onMouseDown: 1 /* PointerEventType.PET_DOWN */,
9
9
  onMouseUp: 0 /* PointerEventType.PET_UP */,
10
10
  onMouseEnter: 2 /* PointerEventType.PET_HOVER_ENTER */,
11
- onMouseLeave: 3 /* PointerEventType.PET_HOVER_LEAVE */,
12
- onMouseDrag: 7 /* PointerEventType.PET_DRAG */,
13
- onMouseDragLocked: 6 /* PointerEventType.PET_DRAG_LOCKED */,
14
- onMouseDragEnd: 8 /* PointerEventType.PET_DRAG_END */
11
+ onMouseLeave: 3 /* PointerEventType.PET_HOVER_LEAVE */
15
12
  };
16
13
  return pointers[pointerKey];
17
14
  }
18
- export function createReconciler(engine, pointerEvents,
19
- // When set, every entity this reconciler creates is parented to it -- a UiCanvas on the root
20
- // entity then renders the tree to a texture instead of the screen.
21
- rootEntity) {
15
+ export function createReconciler(engine, pointerEvents) {
22
16
  // Store all the entities so when we destroy the UI we can also destroy them
23
17
  const entities = new Set();
24
18
  // Store the onChange callbacks to be runned every time a Result has changed
@@ -35,7 +29,6 @@ rootEntity) {
35
29
  const UiInputResult = components.UiInputResult(engine);
36
30
  const UiDropdown = components.UiDropdown(engine);
37
31
  const UiDropdownResult = components.UiDropdownResult(engine);
38
- const Transform = components.Transform(engine);
39
32
  const UiInputBinding = components.UiInputBinding(engine);
40
33
  // Component ID Helper
41
34
  const getComponentId = {
@@ -46,14 +39,17 @@ rootEntity) {
46
39
  uiDropdown: UiDropdown.componentId,
47
40
  uiInputBinding: UiInputBinding.componentId
48
41
  };
49
- function pointerEventCallback(entity, pointerEvent, event) {
42
+ function pointerEventCallback(entity, pointerEvent) {
50
43
  const callback = clickEvents.get(entity)?.get(pointerEvent);
51
44
  if (callback)
52
- callback(event);
45
+ callback();
53
46
  return;
54
47
  }
55
48
  function updateTree(instance, props) {
56
- upsertComponent(instance, props, 'uiTransform');
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');
57
53
  }
58
54
  function upsertListener(instance, update) {
59
55
  if (update.type === 'delete' || !update.props) {
@@ -70,15 +66,6 @@ rootEntity) {
70
66
  else if (update.component === 'onMouseLeave') {
71
67
  pointerEvents.removeOnPointerHoverLeave(instance.entity);
72
68
  }
73
- else if (update.component === 'onMouseDrag') {
74
- pointerEvents.removeOnPointerDrag(instance.entity);
75
- }
76
- else if (update.component === 'onMouseDragLocked') {
77
- pointerEvents.removeOnPointerDragLocked(instance.entity);
78
- }
79
- else if (update.component === 'onMouseDragEnd') {
80
- pointerEvents.removeOnPointerDragEnd(instance.entity);
81
- }
82
69
  return;
83
70
  }
84
71
  if (update.props) {
@@ -94,13 +81,7 @@ rootEntity) {
94
81
  ? pointerEvents.onPointerUp
95
82
  : update.component === 'onMouseEnter'
96
83
  ? pointerEvents.onPointerHoverEnter
97
- : update.component === 'onMouseLeave'
98
- ? pointerEvents.onPointerHoverLeave
99
- : update.component === 'onMouseDrag'
100
- ? pointerEvents.onPointerDrag
101
- : update.component === 'onMouseDragLocked'
102
- ? pointerEvents.onPointerDragLocked
103
- : update.component === 'onMouseDragEnd' && pointerEvents.onPointerDragEnd;
84
+ : update.component === 'onMouseLeave' && pointerEvents.onPointerHoverLeave;
104
85
  if (pointerEventSystem) {
105
86
  pointerEventSystem({
106
87
  entity: instance.entity,
@@ -110,7 +91,7 @@ rootEntity) {
110
91
  // This is needed for the renderer to know which entities are clickeables
111
92
  showFeedback: true
112
93
  }
113
- }, (event) => pointerEventCallback(instance.entity, pointerEvent, event));
94
+ }, () => pointerEventCallback(instance.entity, pointerEvent));
114
95
  }
115
96
  }
116
97
  }
@@ -169,8 +150,6 @@ rootEntity) {
169
150
  }
170
151
  }
171
152
  function appendChild(parent, child) {
172
- if (!child || !Object.keys(parent).length)
173
- return;
174
153
  const isReorder = parent._child.find((c) => c.entity === child.entity);
175
154
  // If its a reorder its seems that its a mutation of an array with key prop
176
155
  // We need to move the child to the end of the array
@@ -213,6 +192,36 @@ rootEntity) {
213
192
  }
214
193
  removeChildEntity(child);
215
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
+ }
216
225
  function updateOnChange(entity, componentId, state) {
217
226
  const hasEvent = changeEvents.has(entity);
218
227
  const event = changeEvents.get(entity) || changeEvents.set(entity, new Map()).get(entity);
@@ -239,9 +248,6 @@ rootEntity) {
239
248
  ...noopConfig,
240
249
  createInstance(type, props) {
241
250
  const entity = engine.addEntity();
242
- if (rootEntity !== undefined) {
243
- Transform.createOrReplace(entity, { parent: rootEntity });
244
- }
245
251
  entities.add(entity);
246
252
  const instance = {
247
253
  entity,
@@ -270,6 +276,7 @@ rootEntity) {
270
276
  appendChild,
271
277
  appendChildToContainer: appendChild,
272
278
  appendInitialChild: appendChild,
279
+ insertInContainerBefore: insertBefore,
273
280
  removeChild: removeChild,
274
281
  prepareUpdate(_instance, _type, oldProps, newProps) {
275
282
  return componentKeys
@@ -290,42 +297,20 @@ rootEntity) {
290
297
  }
291
298
  }
292
299
  },
293
- insertBefore(parentInstance, child, beforeChild) {
294
- // Handle reorder: if child already exists in this parent, remove it from its old position
295
- // and fix up the old neighbor's rightOf before inserting at the new position.
296
- const existingIndex = parentInstance._child.findIndex((c) => c.entity === child.entity);
297
- if (existingIndex !== -1) {
298
- // If beforeChild.rightOf already points to child, child is already in the correct
299
- // position in the rightOf chain. Setting child.rightOf = beforeChild.rightOf would
300
- // produce a self-cycle (child.rightOf = child.entity).
301
- if (beforeChild.rightOf === child.entity) {
302
- return;
303
- }
304
- const oldNextSibling = parentInstance._child[existingIndex + 1];
305
- if (oldNextSibling) {
306
- oldNextSibling.rightOf = child.rightOf;
307
- updateTree(oldNextSibling, { rightOf: oldNextSibling.rightOf });
308
- }
309
- parentInstance._child.splice(existingIndex, 1);
310
- }
311
- const beforeChildIndex = parentInstance._child.findIndex((c) => c.entity === beforeChild.entity);
312
- parentInstance._child = [
313
- ...parentInstance._child.slice(0, beforeChildIndex),
314
- child,
315
- ...parentInstance._child.slice(beforeChildIndex)
316
- ];
317
- child.rightOf = beforeChild.rightOf;
318
- beforeChild.rightOf = child.entity;
319
- child.parent = parentInstance.entity;
320
- updateTree(child, { rightOf: child.rightOf, parent: child.parent });
321
- updateTree(beforeChild, { rightOf: beforeChild.rightOf });
322
- },
323
- removeChildFromContainer(parenInstance, child) {
324
- removeChildEntity(child);
325
- }
300
+ insertBefore,
301
+ removeChildFromContainer: removeChild
326
302
  };
327
303
  const reconciler = Reconciler(hostConfig);
328
- const root = reconciler.createContainer({}, 0, null, false, null, '',
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, '',
329
314
  /* istanbul ignore next */
330
315
  function () { }, null);
331
316
  return {
@@ -49,9 +49,6 @@ const entityComponent = {
49
49
  onMouseUp: undefined,
50
50
  onMouseEnter: undefined,
51
51
  onMouseLeave: undefined,
52
- onMouseDrag: undefined,
53
- onMouseDragLocked: undefined,
54
- onMouseDragEnd: undefined,
55
52
  uiInput: undefined,
56
53
  uiDropdown: undefined,
57
54
  uiInputBinding: undefined
package/dist/system.d.ts CHANGED
@@ -27,6 +27,17 @@ 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; a renderer zIndex of 0 is interpreted as "unset"
38
+ * (positional order); elements inside a renderer keep their own `zIndex`.
39
+ */
40
+ zIndex?: number;
30
41
  };
31
42
  /**
32
43
  * @public
@@ -74,20 +85,6 @@ export interface ReactBasedUiSystem {
74
85
  * @param entity - The entity whose UI renderer should be removed
75
86
  */
76
87
  removeUiRenderer(entity: Entity): void;
77
- /**
78
- * Render a UI tree to a texture. The tree is rooted at `entity`, which must carry a UiCanvas
79
- * describing the texture; the result can be sampled through `TextureUnion.uiTexture`. Replaces
80
- * any texture renderer already set on the entity, and is torn down when the entity is removed.
81
- *
82
- * @param entity - The UiCanvas entity the UI tree hangs from
83
- * @param ui - The UI component to render
84
- */
85
- setTextureRenderer(entity: Entity, ui: UiComponent): void;
86
- /**
87
- * Remove a texture renderer previously set with setTextureRenderer(), destroying its entities.
88
- * @param entity - The UiCanvas entity the renderer was set on
89
- */
90
- removeTextureRenderer(entity: Entity): void;
91
88
  }
92
89
  /**
93
90
  * @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,18 +47,14 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
47
47
  let uiComponent = undefined;
48
48
  let mainOptions = undefined;
49
49
  const additionalRenderers = new Map();
50
- // UI trees rendered to a texture, keyed by their UiCanvas entity. Each needs its own reconciler
51
- // because the root parent is a per-reconciler property.
52
- const textureRenderers = new Map();
53
- function destroyTextureRenderer(entity) {
54
- const entry = textureRenderers.get(entity);
55
- if (!entry)
56
- return;
57
- textureRenderers.delete(entity);
58
- for (const uiEntity of entry.renderer.getEntities()) {
59
- engine.removeEntity(uiEntity);
60
- }
61
- }
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;
62
58
  const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
63
59
  // Unique owner to prevent other UI systems resetting this scale factor.
64
60
  const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
@@ -131,37 +127,58 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
131
127
  /**
132
128
  * Wraps a renderer's component in a container positioned within the selected
133
129
  * screen inset area. `'none'` adds no wrapper, leaving the UI on the whole
134
- * screen. Applied per renderer, so each renderer can use a different inset.
130
+ * screen, unless a `zIndex` needs a root entity to live on. Applied per
131
+ * renderer, so each renderer can use a different inset.
135
132
  */
136
- function wrapWithScreenInset(ui, inset, key) {
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 } };
137
138
  // An omitted inset is resolved to DEFAULT_SCREEN_INSET ('device') before the
138
139
  // switch, so it lands on `case 'device'`. Only an explicit 'none' reaches the
139
140
  // `default` clause below — the two are unrelated despite sharing a name.
140
- switch (inset ?? DEFAULT_SCREEN_INSET) {
141
+ switch (options?.screenInset ?? DEFAULT_SCREEN_INSET) {
141
142
  case 'device':
142
- return React.createElement(ScreenInsetArea, { key }, React.createElement(ui));
143
+ return React.createElement(ScreenInsetArea, wrapperProps, React.createElement(ui));
143
144
  case 'interactable':
144
- return React.createElement(InteractableArea, { key }, React.createElement(ui));
145
+ return React.createElement(InteractableArea, wrapperProps, React.createElement(ui));
145
146
  // 'none' — the whole screen, no wrapper entity
146
147
  default:
147
- return React.createElement(ui, { key });
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));
148
156
  }
149
157
  }
150
158
  function ReactBasedUiSystem() {
151
159
  const components = [];
152
- // Add main UI component
153
- if (uiComponent) {
154
- components.push(wrapWithScreenInset(uiComponent, mainOptions?.screenInset, '__main__'));
155
- }
160
+ if (uiComponent !== undefined && mainOrder === undefined)
161
+ mainOrder = nextOrder++;
162
+ let mainPushed = uiComponent === undefined;
156
163
  const entitiesToRemove = [];
157
164
  for (const [entity, entry] of additionalRenderers) {
158
165
  // Check for entity-based cleanup
159
166
  if (engine.getEntityState(entity) === EntityState.Removed) {
160
167
  entitiesToRemove.push(entity);
168
+ continue;
161
169
  }
162
- else {
163
- components.push(wrapWithScreenInset(entry.ui, entry.options?.screenInset, `__entity_${entity}__`));
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;
164
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__'));
165
182
  }
166
183
  // Entity-based cleanup
167
184
  for (const entity of entitiesToRemove) {
@@ -174,14 +191,6 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
174
191
  else {
175
192
  renderer.update(null);
176
193
  }
177
- for (const [entity, entry] of textureRenderers) {
178
- if (engine.getEntityState(entity) === EntityState.Removed) {
179
- destroyTextureRenderer(entity);
180
- }
181
- else {
182
- entry.renderer.update(React.createElement(entry.ui));
183
- }
184
- }
185
194
  }
186
195
  function UiScaleSystem() {
187
196
  const canvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity);
@@ -239,26 +248,17 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
239
248
  for (const entity of renderer.getEntities()) {
240
249
  engine.removeEntity(entity);
241
250
  }
242
- for (const entity of textureRenderers.keys()) {
243
- destroyTextureRenderer(entity);
244
- }
245
251
  },
246
252
  setUiRenderer(ui, options) {
247
253
  uiComponent = ui;
248
254
  mainOptions = options;
249
255
  },
250
256
  addUiRenderer(entity, ui, options) {
251
- additionalRenderers.set(entity, { ui, options });
257
+ // A replacement keeps its place in the stacking order.
258
+ additionalRenderers.set(entity, { ui, options, order: additionalRenderers.get(entity)?.order });
252
259
  },
253
260
  removeUiRenderer(entity) {
254
261
  additionalRenderers.delete(entity);
255
- },
256
- setTextureRenderer(entity, ui) {
257
- destroyTextureRenderer(entity);
258
- textureRenderers.set(entity, { renderer: createReconciler(engine, pointerSystem, entity), ui });
259
- },
260
- removeTextureRenderer(entity) {
261
- destroyTextureRenderer(entity);
262
262
  }
263
263
  };
264
264
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dcl/react-ecs",
3
3
  "description": "Decentraland ECS",
4
- "version": "7.29.1-35901015152.commit-d1b7b96",
4
+ "version": "7.29.1-35917671376.commit-046b268",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
7
7
  "dependencies": {
8
- "@dcl/ecs": "7.29.1-35901015152.commit-d1b7b96",
8
+ "@dcl/ecs": "7.29.1-35917671376.commit-046b268",
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": "d1b7b964cd184465c1b65118e47b5dc6be68a184"
43
+ "commit": "046b268beee66a9d2d67dda7834087a8052f4f9c"
44
44
  }