@dcl/react-ecs 7.29.1-35862852104.commit-1f44e80 → 7.29.1-35901015152.commit-d1b7b96
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/components/Button/index.js +2 -2
- package/dist/components/Dropdown/index.js +5 -2
- package/dist/components/Input/index.js +5 -2
- package/dist/components/Label/index.js +5 -2
- package/dist/components/Label/types.d.ts +2 -0
- package/dist/components/listeners/types.d.ts +10 -2
- package/dist/components/listeners/types.js +4 -1
- package/dist/components/uiBackground/types.d.ts +12 -1
- package/dist/components/uiBackground/utils.js +8 -0
- package/dist/components/uiTransform/index.js +5 -3
- package/dist/components/uiTransform/types.d.ts +12 -0
- package/dist/components/uiTransform/utils.js +21 -0
- package/dist/react-ecs.d.ts +3 -0
- package/dist/reconciler/index.d.ts +1 -1
- package/dist/reconciler/index.js +68 -53
- package/dist/reconciler/utils.js +3 -0
- package/dist/system.d.ts +14 -11
- package/dist/system.js +44 -44
- package/package.json +3 -3
|
@@ -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, ...otherProps } = props;
|
|
32
|
+
const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...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, 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, onMouseDrag: onMouseDrag, onMouseDragLocked: onMouseDragLocked, onMouseDragEnd: onMouseDragEnd, 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, ...otherProps } = props;
|
|
37
|
+
const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...otherProps } = props;
|
|
38
38
|
const dropdownProps = parseUiDropdown(otherProps);
|
|
39
39
|
const commonProps = parseProps({
|
|
40
40
|
uiTransform,
|
|
@@ -42,7 +42,10 @@ export function Dropdown(props) {
|
|
|
42
42
|
onMouseDown,
|
|
43
43
|
onMouseUp,
|
|
44
44
|
onMouseEnter,
|
|
45
|
-
onMouseLeave
|
|
45
|
+
onMouseLeave,
|
|
46
|
+
onMouseDrag,
|
|
47
|
+
onMouseDragLocked,
|
|
48
|
+
onMouseDragEnd
|
|
46
49
|
});
|
|
47
50
|
return ReactEcs.createElement("entity", { ...commonProps, uiDropdown: dropdownProps });
|
|
48
51
|
}
|
|
@@ -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, ...otherProps } = props;
|
|
38
|
+
const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...otherProps } = props;
|
|
39
39
|
const inputProps = parseUiInput(otherProps);
|
|
40
40
|
const commonProps = parseProps({
|
|
41
41
|
uiTransform,
|
|
@@ -43,7 +43,10 @@ export function Input(props) {
|
|
|
43
43
|
onMouseDown,
|
|
44
44
|
onMouseUp,
|
|
45
45
|
onMouseEnter,
|
|
46
|
-
onMouseLeave
|
|
46
|
+
onMouseLeave,
|
|
47
|
+
onMouseDrag,
|
|
48
|
+
onMouseDragLocked,
|
|
49
|
+
onMouseDragEnd
|
|
47
50
|
});
|
|
48
51
|
return ReactEcs.createElement("entity", { ...commonProps, uiInput: inputProps });
|
|
49
52
|
}
|
|
@@ -16,14 +16,17 @@ export { scaleFontSize } from './utils';
|
|
|
16
16
|
*/
|
|
17
17
|
/* @__PURE__ */
|
|
18
18
|
export function Label(props) {
|
|
19
|
-
const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...uiTextProps } = props;
|
|
19
|
+
const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseDrag, onMouseDragLocked, onMouseDragEnd, ...uiTextProps } = props;
|
|
20
20
|
const commonProps = parseProps({
|
|
21
21
|
uiTransform,
|
|
22
22
|
uiBackground,
|
|
23
23
|
onMouseDown,
|
|
24
24
|
onMouseUp,
|
|
25
25
|
onMouseEnter,
|
|
26
|
-
onMouseLeave
|
|
26
|
+
onMouseLeave,
|
|
27
|
+
onMouseDrag,
|
|
28
|
+
onMouseDragLocked,
|
|
29
|
+
onMouseDragEnd
|
|
27
30
|
});
|
|
28
31
|
const { font, textAlign, fontSize, textWrap, ...textProps } = uiTextProps;
|
|
29
32
|
const uiText = {
|
|
@@ -17,6 +17,8 @@ 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;
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* @public
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { PBPointerEventsResult } from '@dcl/ecs';
|
|
1
2
|
/**
|
|
2
|
-
* Callback function to be triggered on a specified event
|
|
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.
|
|
3
5
|
* @public
|
|
4
6
|
*/
|
|
5
|
-
export type Callback = () => void;
|
|
7
|
+
export type Callback = (event: PBPointerEventsResult) => void;
|
|
6
8
|
/**
|
|
7
9
|
* User key event Listeners
|
|
8
10
|
* @public
|
|
@@ -16,4 +18,10 @@ export type Listeners = {
|
|
|
16
18
|
onMouseEnter?: Callback;
|
|
17
19
|
/** triggered on mouse leave event */
|
|
18
20
|
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;
|
|
19
27
|
};
|
|
@@ -2,7 +2,10 @@ const listeners = {
|
|
|
2
2
|
onMouseDown: undefined,
|
|
3
3
|
onMouseUp: undefined,
|
|
4
4
|
onMouseEnter: undefined,
|
|
5
|
-
onMouseLeave: undefined
|
|
5
|
+
onMouseLeave: undefined,
|
|
6
|
+
onMouseDrag: undefined,
|
|
7
|
+
onMouseDragLocked: undefined,
|
|
8
|
+
onMouseDragEnd: undefined
|
|
6
9
|
};
|
|
7
10
|
const listenersKey = Object.keys(listeners);
|
|
8
11
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BorderRect } from '@dcl/ecs';
|
|
1
|
+
import { BorderRect, Entity } from '@dcl/ecs';
|
|
2
2
|
import { Color4 } from '@dcl/ecs/dist/components/generated/pb/decentraland/common/colors.gen';
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
@@ -15,6 +15,8 @@ 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;
|
|
18
20
|
/** Texture for the background */
|
|
19
21
|
texture?: UiTexture;
|
|
20
22
|
}
|
|
@@ -27,6 +29,15 @@ export interface UiAvatarTexture {
|
|
|
27
29
|
wrapMode?: TextureWrapType;
|
|
28
30
|
filterMode?: TextureFilterType;
|
|
29
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Video Texture
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
export interface UiVideoTexture {
|
|
37
|
+
videoPlayerEntity: Entity;
|
|
38
|
+
wrapMode?: TextureWrapType;
|
|
39
|
+
filterMode?: TextureFilterType;
|
|
40
|
+
}
|
|
30
41
|
/**
|
|
31
42
|
* Texture
|
|
32
43
|
* @public
|
|
@@ -30,6 +30,14 @@ 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
|
+
}
|
|
33
41
|
return undefined;
|
|
34
42
|
}
|
|
35
43
|
function parseTexture(texture) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAlign, getDisplay, getFlexDirection, getFlexWrap, getJustify, getOverflow, getPointerFilter, getPositionType, parseBorderColor, parseBorderRadius, parseBorderWidth, parsePosition, parseSize } from './utils';
|
|
1
|
+
import { getAlign, getDisplay, getFlexDirection, getFlexWrap, getJustify, getOverflow, getPointerFilter, getPositionType, getScrollPosition, getScrollVisible, 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, ...otherProps } = props;
|
|
63
|
+
const { height, minHeight, maxHeight, width, minWidth, maxWidth, alignItems, alignContent, flexWrap, borderRadius, borderWidth, borderColor, scrollPosition, scrollVisible, ...otherProps } = props;
|
|
64
64
|
return {
|
|
65
65
|
...defaultUiTransform,
|
|
66
66
|
...otherProps,
|
|
@@ -86,6 +86,8 @@ export function parseUiTransform(props = {}) {
|
|
|
86
86
|
...(flexWrap && getFlexWrap(flexWrap)),
|
|
87
87
|
...(borderRadius && parseBorderRadius(borderRadius)),
|
|
88
88
|
...(borderWidth && parseBorderWidth(borderWidth)),
|
|
89
|
-
...(borderColor && parseBorderColor(borderColor))
|
|
89
|
+
...(borderColor && parseBorderColor(borderColor)),
|
|
90
|
+
...(scrollPosition && getScrollPosition(scrollPosition)),
|
|
91
|
+
...(scrollVisible && getScrollVisible(scrollVisible))
|
|
90
92
|
};
|
|
91
93
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
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';
|
|
2
3
|
import { ScaleUnit } from '../types';
|
|
3
4
|
/**
|
|
4
5
|
* unit value specified. i.e. 1 || '100%' || '1px' || '10vw'
|
|
@@ -131,4 +132,15 @@ export interface UiTransformProps {
|
|
|
131
132
|
opacity?: number;
|
|
132
133
|
/** default 0 */
|
|
133
134
|
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;
|
|
134
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Which scrollbars are shown when the overflow is scrollable
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
146
|
+
export type ScrollVisibleType = 'horizontal' | 'vertical' | 'both' | 'hidden';
|
|
@@ -244,3 +244,24 @@ 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
|
+
}
|
package/dist/react-ecs.d.ts
CHANGED
|
@@ -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): {
|
|
3
|
+
export declare function createReconciler(engine: Pick<IEngine, 'getComponent' | 'addEntity' | 'removeEntity' | 'defineComponentFromSchema' | 'getEntitiesWith'>, pointerEvents: PointerEventsSystem, rootEntity?: Entity): {
|
|
4
4
|
update: (component: ReactEcs.JSX.ReactNode) => number;
|
|
5
5
|
getEntities: () => Entity[];
|
|
6
6
|
};
|
package/dist/reconciler/index.js
CHANGED
|
@@ -8,11 +8,17 @@ 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
|
|
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 */
|
|
12
15
|
};
|
|
13
16
|
return pointers[pointerKey];
|
|
14
17
|
}
|
|
15
|
-
export function createReconciler(engine, pointerEvents
|
|
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) {
|
|
16
22
|
// Store all the entities so when we destroy the UI we can also destroy them
|
|
17
23
|
const entities = new Set();
|
|
18
24
|
// Store the onChange callbacks to be runned every time a Result has changed
|
|
@@ -29,6 +35,7 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
29
35
|
const UiInputResult = components.UiInputResult(engine);
|
|
30
36
|
const UiDropdown = components.UiDropdown(engine);
|
|
31
37
|
const UiDropdownResult = components.UiDropdownResult(engine);
|
|
38
|
+
const Transform = components.Transform(engine);
|
|
32
39
|
const UiInputBinding = components.UiInputBinding(engine);
|
|
33
40
|
// Component ID Helper
|
|
34
41
|
const getComponentId = {
|
|
@@ -39,17 +46,14 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
39
46
|
uiDropdown: UiDropdown.componentId,
|
|
40
47
|
uiInputBinding: UiInputBinding.componentId
|
|
41
48
|
};
|
|
42
|
-
function pointerEventCallback(entity, pointerEvent) {
|
|
49
|
+
function pointerEventCallback(entity, pointerEvent, event) {
|
|
43
50
|
const callback = clickEvents.get(entity)?.get(pointerEvent);
|
|
44
51
|
if (callback)
|
|
45
|
-
callback();
|
|
52
|
+
callback(event);
|
|
46
53
|
return;
|
|
47
54
|
}
|
|
48
55
|
function updateTree(instance, props) {
|
|
49
|
-
|
|
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');
|
|
56
|
+
upsertComponent(instance, props, 'uiTransform');
|
|
53
57
|
}
|
|
54
58
|
function upsertListener(instance, update) {
|
|
55
59
|
if (update.type === 'delete' || !update.props) {
|
|
@@ -66,6 +70,15 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
66
70
|
else if (update.component === 'onMouseLeave') {
|
|
67
71
|
pointerEvents.removeOnPointerHoverLeave(instance.entity);
|
|
68
72
|
}
|
|
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
|
+
}
|
|
69
82
|
return;
|
|
70
83
|
}
|
|
71
84
|
if (update.props) {
|
|
@@ -81,7 +94,13 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
81
94
|
? pointerEvents.onPointerUp
|
|
82
95
|
: update.component === 'onMouseEnter'
|
|
83
96
|
? pointerEvents.onPointerHoverEnter
|
|
84
|
-
: update.component === 'onMouseLeave'
|
|
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;
|
|
85
104
|
if (pointerEventSystem) {
|
|
86
105
|
pointerEventSystem({
|
|
87
106
|
entity: instance.entity,
|
|
@@ -91,7 +110,7 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
91
110
|
// This is needed for the renderer to know which entities are clickeables
|
|
92
111
|
showFeedback: true
|
|
93
112
|
}
|
|
94
|
-
}, () => pointerEventCallback(instance.entity, pointerEvent));
|
|
113
|
+
}, (event) => pointerEventCallback(instance.entity, pointerEvent, event));
|
|
95
114
|
}
|
|
96
115
|
}
|
|
97
116
|
}
|
|
@@ -150,6 +169,8 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
150
169
|
}
|
|
151
170
|
}
|
|
152
171
|
function appendChild(parent, child) {
|
|
172
|
+
if (!child || !Object.keys(parent).length)
|
|
173
|
+
return;
|
|
153
174
|
const isReorder = parent._child.find((c) => c.entity === child.entity);
|
|
154
175
|
// If its a reorder its seems that its a mutation of an array with key prop
|
|
155
176
|
// We need to move the child to the end of the array
|
|
@@ -192,36 +213,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
192
213
|
}
|
|
193
214
|
removeChildEntity(child);
|
|
194
215
|
}
|
|
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
216
|
function updateOnChange(entity, componentId, state) {
|
|
226
217
|
const hasEvent = changeEvents.has(entity);
|
|
227
218
|
const event = changeEvents.get(entity) || changeEvents.set(entity, new Map()).get(entity);
|
|
@@ -248,6 +239,9 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
248
239
|
...noopConfig,
|
|
249
240
|
createInstance(type, props) {
|
|
250
241
|
const entity = engine.addEntity();
|
|
242
|
+
if (rootEntity !== undefined) {
|
|
243
|
+
Transform.createOrReplace(entity, { parent: rootEntity });
|
|
244
|
+
}
|
|
251
245
|
entities.add(entity);
|
|
252
246
|
const instance = {
|
|
253
247
|
entity,
|
|
@@ -276,7 +270,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
276
270
|
appendChild,
|
|
277
271
|
appendChildToContainer: appendChild,
|
|
278
272
|
appendInitialChild: appendChild,
|
|
279
|
-
insertInContainerBefore: insertBefore,
|
|
280
273
|
removeChild: removeChild,
|
|
281
274
|
prepareUpdate(_instance, _type, oldProps, newProps) {
|
|
282
275
|
return componentKeys
|
|
@@ -297,20 +290,42 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
297
290
|
}
|
|
298
291
|
}
|
|
299
292
|
},
|
|
300
|
-
insertBefore,
|
|
301
|
-
|
|
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
|
+
}
|
|
302
326
|
};
|
|
303
327
|
const reconciler = Reconciler(hostConfig);
|
|
304
|
-
|
|
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, '',
|
|
328
|
+
const root = reconciler.createContainer({}, 0, null, false, null, '',
|
|
314
329
|
/* istanbul ignore next */
|
|
315
330
|
function () { }, null);
|
|
316
331
|
return {
|
package/dist/reconciler/utils.js
CHANGED
|
@@ -49,6 +49,9 @@ const entityComponent = {
|
|
|
49
49
|
onMouseUp: undefined,
|
|
50
50
|
onMouseEnter: undefined,
|
|
51
51
|
onMouseLeave: undefined,
|
|
52
|
+
onMouseDrag: undefined,
|
|
53
|
+
onMouseDragLocked: undefined,
|
|
54
|
+
onMouseDragEnd: undefined,
|
|
52
55
|
uiInput: undefined,
|
|
53
56
|
uiDropdown: undefined,
|
|
54
57
|
uiInputBinding: undefined
|
package/dist/system.d.ts
CHANGED
|
@@ -27,17 +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; a renderer zIndex of 0 is interpreted as "unset"
|
|
38
|
-
* (positional order); elements inside a renderer keep their own `zIndex`.
|
|
39
|
-
*/
|
|
40
|
-
zIndex?: number;
|
|
41
30
|
};
|
|
42
31
|
/**
|
|
43
32
|
* @public
|
|
@@ -85,6 +74,20 @@ export interface ReactBasedUiSystem {
|
|
|
85
74
|
* @param entity - The entity whose UI renderer should be removed
|
|
86
75
|
*/
|
|
87
76
|
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;
|
|
88
91
|
}
|
|
89
92
|
/**
|
|
90
93
|
* @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
|
|
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,18 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
47
47
|
let uiComponent = undefined;
|
|
48
48
|
let mainOptions = undefined;
|
|
49
49
|
const additionalRenderers = new Map();
|
|
50
|
-
//
|
|
51
|
-
// the
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
}
|
|
58
62
|
const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
|
|
59
63
|
// Unique owner to prevent other UI systems resetting this scale factor.
|
|
60
64
|
const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
|
|
@@ -127,58 +131,37 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
127
131
|
/**
|
|
128
132
|
* Wraps a renderer's component in a container positioned within the selected
|
|
129
133
|
* screen inset area. `'none'` adds no wrapper, leaving the UI on the whole
|
|
130
|
-
* screen
|
|
131
|
-
* renderer, so each renderer can use a different inset.
|
|
134
|
+
* screen. Applied per renderer, so each renderer can use a different inset.
|
|
132
135
|
*/
|
|
133
|
-
function wrapWithScreenInset(ui,
|
|
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 } };
|
|
136
|
+
function wrapWithScreenInset(ui, inset, key) {
|
|
138
137
|
// An omitted inset is resolved to DEFAULT_SCREEN_INSET ('device') before the
|
|
139
138
|
// switch, so it lands on `case 'device'`. Only an explicit 'none' reaches the
|
|
140
139
|
// `default` clause below — the two are unrelated despite sharing a name.
|
|
141
|
-
switch (
|
|
140
|
+
switch (inset ?? DEFAULT_SCREEN_INSET) {
|
|
142
141
|
case 'device':
|
|
143
|
-
return React.createElement(ScreenInsetArea,
|
|
142
|
+
return React.createElement(ScreenInsetArea, { key }, React.createElement(ui));
|
|
144
143
|
case 'interactable':
|
|
145
|
-
return React.createElement(InteractableArea,
|
|
144
|
+
return React.createElement(InteractableArea, { key }, React.createElement(ui));
|
|
146
145
|
// 'none' — the whole screen, no wrapper entity
|
|
147
146
|
default:
|
|
148
|
-
|
|
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));
|
|
147
|
+
return React.createElement(ui, { key });
|
|
156
148
|
}
|
|
157
149
|
}
|
|
158
150
|
function ReactBasedUiSystem() {
|
|
159
151
|
const components = [];
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
152
|
+
// Add main UI component
|
|
153
|
+
if (uiComponent) {
|
|
154
|
+
components.push(wrapWithScreenInset(uiComponent, mainOptions?.screenInset, '__main__'));
|
|
155
|
+
}
|
|
163
156
|
const entitiesToRemove = [];
|
|
164
157
|
for (const [entity, entry] of additionalRenderers) {
|
|
165
158
|
// Check for entity-based cleanup
|
|
166
159
|
if (engine.getEntityState(entity) === EntityState.Removed) {
|
|
167
160
|
entitiesToRemove.push(entity);
|
|
168
|
-
continue;
|
|
169
161
|
}
|
|
170
|
-
|
|
171
|
-
entry.
|
|
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;
|
|
162
|
+
else {
|
|
163
|
+
components.push(wrapWithScreenInset(entry.ui, entry.options?.screenInset, `__entity_${entity}__`));
|
|
176
164
|
}
|
|
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
165
|
}
|
|
183
166
|
// Entity-based cleanup
|
|
184
167
|
for (const entity of entitiesToRemove) {
|
|
@@ -191,6 +174,14 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
191
174
|
else {
|
|
192
175
|
renderer.update(null);
|
|
193
176
|
}
|
|
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
|
+
}
|
|
194
185
|
}
|
|
195
186
|
function UiScaleSystem() {
|
|
196
187
|
const canvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity);
|
|
@@ -248,17 +239,26 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
248
239
|
for (const entity of renderer.getEntities()) {
|
|
249
240
|
engine.removeEntity(entity);
|
|
250
241
|
}
|
|
242
|
+
for (const entity of textureRenderers.keys()) {
|
|
243
|
+
destroyTextureRenderer(entity);
|
|
244
|
+
}
|
|
251
245
|
},
|
|
252
246
|
setUiRenderer(ui, options) {
|
|
253
247
|
uiComponent = ui;
|
|
254
248
|
mainOptions = options;
|
|
255
249
|
},
|
|
256
250
|
addUiRenderer(entity, ui, options) {
|
|
257
|
-
|
|
258
|
-
additionalRenderers.set(entity, { ui, options, order: additionalRenderers.get(entity)?.order });
|
|
251
|
+
additionalRenderers.set(entity, { ui, options });
|
|
259
252
|
},
|
|
260
253
|
removeUiRenderer(entity) {
|
|
261
254
|
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-
|
|
4
|
+
"version": "7.29.1-35901015152.commit-d1b7b96",
|
|
5
5
|
"author": "DCL",
|
|
6
6
|
"bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@dcl/ecs": "7.29.1-
|
|
8
|
+
"@dcl/ecs": "7.29.1-35901015152.commit-d1b7b96",
|
|
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": "d1b7b964cd184465c1b65118e47b5dc6be68a184"
|
|
44
44
|
}
|