@dcl/react-ecs 7.24.5-28985931443.commit-4a0bcfc → 7.24.5-29247185020.commit-e92dfe6
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 +10 -2
- package/dist/components/Input/index.js +10 -2
- package/dist/components/Label/index.js +13 -3
- package/dist/components/Label/types.d.ts +4 -0
- package/dist/components/index.d.ts +0 -4
- package/dist/components/index.js +0 -4
- package/dist/components/listeners/types.d.ts +37 -6
- package/dist/components/listeners/types.js +9 -1
- package/dist/components/uiBackground/types.d.ts +12 -1
- package/dist/components/uiBackground/utils.js +8 -0
- package/dist/components/uiTransform/index.d.ts +1 -1
- package/dist/components/uiTransform/index.js +6 -3
- package/dist/components/uiTransform/types.d.ts +12 -0
- package/dist/components/uiTransform/utils.js +37 -0
- package/dist/components/utils.js +2 -89
- package/dist/react-ecs.d.ts +14 -6
- package/dist/reconciler/index.d.ts +4 -3
- package/dist/reconciler/index.js +68 -91
- package/dist/reconciler/utils.js +12 -4
- package/dist/system.d.ts +5 -0
- package/dist/system.js +18 -24
- package/package.json +3 -3
- package/dist/components/InteractableArea/index.d.ts +0 -23
- package/dist/components/InteractableArea/index.js +0 -33
- package/dist/components/InteractableArea/types.d.ts +0 -23
- package/dist/components/InteractableArea/types.js +0 -1
- package/dist/components/ScreenInsetArea/index.d.ts +0 -22
- package/dist/components/ScreenInsetArea/index.js +0 -32
- package/dist/components/ScreenInsetArea/types.d.ts +0 -22
- package/dist/components/ScreenInsetArea/types.js +0 -1
|
@@ -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, onInputDown, onInputUp, onInputDrag, onInputDragLocked, onInputDragEnd, ...otherProps } = props;
|
|
33
33
|
const buttonProps = getButtonProps(props);
|
|
34
34
|
const uiBackgroundProps = parseUiBackground({
|
|
35
35
|
...buttonProps.uiBackground,
|
|
@@ -54,5 +54,5 @@ export function Button(props) {
|
|
|
54
54
|
if (uiBackgroundProps && uiBackgroundProps.color)
|
|
55
55
|
uiBackgroundProps.color.a /= 2;
|
|
56
56
|
}
|
|
57
|
-
return (ReactEcs.createElement("entity", { onMouseDown: !!props.disabled ? undefined : onMouseDown, onMouseUp: !!props.disabled ? undefined : onMouseUp, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, uiTransform: uiTransformProps, uiText: textProps, uiBackground: uiBackgroundProps }));
|
|
57
|
+
return (ReactEcs.createElement("entity", { onMouseDown: !!props.disabled ? undefined : onMouseDown, onMouseUp: !!props.disabled ? undefined : onMouseUp, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDrag: onMouseDrag, onMouseDragLocked: onMouseDragLocked, onMouseDragEnd: onMouseDragEnd, onInputDown: !!props.disabled ? undefined : onInputDown, onInputUp: !!props.disabled ? undefined : onInputUp, onInputDrag: onInputDrag, onInputDragLocked: onInputDragLocked, onInputDragEnd: onInputDragEnd, uiTransform: uiTransformProps, uiText: textProps, uiBackground: uiBackgroundProps }));
|
|
58
58
|
}
|
|
@@ -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, onInputDown, onInputUp, onInputDrag, onInputDragLocked, onInputDragEnd, ...otherProps } = props;
|
|
38
38
|
const dropdownProps = parseUiDropdown(otherProps);
|
|
39
39
|
const commonProps = parseProps({
|
|
40
40
|
uiTransform,
|
|
@@ -42,7 +42,15 @@ export function Dropdown(props) {
|
|
|
42
42
|
onMouseDown,
|
|
43
43
|
onMouseUp,
|
|
44
44
|
onMouseEnter,
|
|
45
|
-
onMouseLeave
|
|
45
|
+
onMouseLeave,
|
|
46
|
+
onMouseDrag,
|
|
47
|
+
onMouseDragLocked,
|
|
48
|
+
onMouseDragEnd,
|
|
49
|
+
onInputDown,
|
|
50
|
+
onInputUp,
|
|
51
|
+
onInputDrag,
|
|
52
|
+
onInputDragLocked,
|
|
53
|
+
onInputDragEnd
|
|
46
54
|
});
|
|
47
55
|
return ReactEcs.createElement("entity", { ...commonProps, uiDropdown: dropdownProps });
|
|
48
56
|
}
|
|
@@ -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, onInputDown, onInputUp, onInputDrag, onInputDragLocked, onInputDragEnd, ...otherProps } = props;
|
|
39
39
|
const inputProps = parseUiInput(otherProps);
|
|
40
40
|
const commonProps = parseProps({
|
|
41
41
|
uiTransform,
|
|
@@ -43,7 +43,15 @@ export function Input(props) {
|
|
|
43
43
|
onMouseDown,
|
|
44
44
|
onMouseUp,
|
|
45
45
|
onMouseEnter,
|
|
46
|
-
onMouseLeave
|
|
46
|
+
onMouseLeave,
|
|
47
|
+
onMouseDrag,
|
|
48
|
+
onMouseDragLocked,
|
|
49
|
+
onMouseDragEnd,
|
|
50
|
+
onInputDown,
|
|
51
|
+
onInputUp,
|
|
52
|
+
onInputDrag,
|
|
53
|
+
onInputDragLocked,
|
|
54
|
+
onInputDragEnd
|
|
47
55
|
});
|
|
48
56
|
return ReactEcs.createElement("entity", { ...commonProps, uiInput: inputProps });
|
|
49
57
|
}
|
|
@@ -16,14 +16,22 @@ 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, onInputDown, onInputUp, onInputDrag, onInputDragLocked, onInputDragEnd, ...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,
|
|
30
|
+
onInputDown,
|
|
31
|
+
onInputUp,
|
|
32
|
+
onInputDrag,
|
|
33
|
+
onInputDragLocked,
|
|
34
|
+
onInputDragEnd
|
|
27
35
|
});
|
|
28
36
|
const { font, textAlign, fontSize, textWrap, ...textProps } = uiTextProps;
|
|
29
37
|
const uiText = {
|
|
@@ -31,7 +39,9 @@ export function Label(props) {
|
|
|
31
39
|
...getFont(font),
|
|
32
40
|
...getTextAlign(textAlign),
|
|
33
41
|
...getFontSize(fontSize),
|
|
34
|
-
...getTextWrap(textWrap)
|
|
42
|
+
...getTextWrap(textWrap),
|
|
43
|
+
outlineWidth: props.outlineWidth,
|
|
44
|
+
outlineColor: props.outlineColor
|
|
35
45
|
};
|
|
36
46
|
return ReactEcs.createElement("entity", { ...commonProps, uiText: uiText });
|
|
37
47
|
}
|
|
@@ -15,6 +15,10 @@ export interface UiLabelProps {
|
|
|
15
15
|
textAlign?: TextAlignType | undefined;
|
|
16
16
|
/** Label font type. @defaultValue 'sans-serif' */
|
|
17
17
|
font?: UiFontType | undefined;
|
|
18
|
+
/** Outline width of the text. @defaultValue 0 */
|
|
19
|
+
outlineWidth?: number | undefined;
|
|
20
|
+
/** Outline color of the text. @defaultValue `{ r: 0, g: 0, b: 0, a: 1 }` */
|
|
21
|
+
outlineColor?: Color4 | undefined;
|
|
18
22
|
/** Behaviour when text reached. @defaultValue 'wrap' */
|
|
19
23
|
textWrap?: UiTextWrapType | undefined;
|
|
20
24
|
}
|
|
@@ -9,14 +9,10 @@ export * from './uiBackground/types';
|
|
|
9
9
|
export * from './Dropdown/types';
|
|
10
10
|
export * from './Label/types';
|
|
11
11
|
export * from './Button/types';
|
|
12
|
-
export * from './ScreenInsetArea/types';
|
|
13
|
-
export * from './InteractableArea/types';
|
|
14
12
|
export { Dropdown } from './Dropdown';
|
|
15
13
|
export { Input } from './Input';
|
|
16
14
|
export { Label, scaleFontSize } from './Label';
|
|
17
15
|
export { Button } from './Button';
|
|
18
|
-
export { ScreenInsetArea } from './ScreenInsetArea';
|
|
19
|
-
export { InteractableArea } from './InteractableArea';
|
|
20
16
|
/**
|
|
21
17
|
* @public
|
|
22
18
|
* @category Component
|
package/dist/components/index.js
CHANGED
|
@@ -9,14 +9,10 @@ export * from './uiBackground/types';
|
|
|
9
9
|
export * from './Dropdown/types';
|
|
10
10
|
export * from './Label/types';
|
|
11
11
|
export * from './Button/types';
|
|
12
|
-
export * from './ScreenInsetArea/types';
|
|
13
|
-
export * from './InteractableArea/types';
|
|
14
12
|
export { Dropdown } from './Dropdown';
|
|
15
13
|
export { Input } from './Input';
|
|
16
14
|
export { Label, scaleFontSize } from './Label';
|
|
17
15
|
export { Button } from './Button';
|
|
18
|
-
export { ScreenInsetArea } from './ScreenInsetArea';
|
|
19
|
-
export { InteractableArea } from './InteractableArea';
|
|
20
16
|
/**
|
|
21
17
|
* @public
|
|
22
18
|
* @category Component
|
|
@@ -1,19 +1,50 @@
|
|
|
1
|
+
import { EventSystemCallback, InputAction } from '@dcl/ecs';
|
|
1
2
|
/**
|
|
2
|
-
* Callback function
|
|
3
|
-
*
|
|
3
|
+
* legacy Callback function
|
|
4
|
+
*
|
|
5
|
+
* @public @deprecated This type is no longer used in the sdk api, EventSystemCallback is
|
|
6
|
+
* used for listeners instead
|
|
4
7
|
*/
|
|
5
8
|
export type Callback = () => void;
|
|
9
|
+
/**
|
|
10
|
+
* a record object mapping `InputAction`s to functions.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* onInputDown={{
|
|
14
|
+
* [InputAction.IA_PRIMARY]: (eventData) => { console.log("primary") },
|
|
15
|
+
* [InputAction.IA_SECONDARY]: () => { console.log("secondary") },
|
|
16
|
+
* }}
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type MultiCallback = Partial<Record<InputAction, EventSystemCallback>>;
|
|
6
21
|
/**
|
|
7
22
|
* User key event Listeners
|
|
8
23
|
* @public
|
|
9
24
|
*/
|
|
10
25
|
export type Listeners = {
|
|
11
26
|
/** triggered on mouse down event */
|
|
12
|
-
onMouseDown?:
|
|
27
|
+
onMouseDown?: EventSystemCallback;
|
|
13
28
|
/** triggered on mouse up event */
|
|
14
|
-
onMouseUp?:
|
|
29
|
+
onMouseUp?: EventSystemCallback;
|
|
15
30
|
/** triggered on mouse hover event */
|
|
16
|
-
onMouseEnter?:
|
|
31
|
+
onMouseEnter?: EventSystemCallback;
|
|
17
32
|
/** triggered on mouse leave event */
|
|
18
|
-
onMouseLeave?:
|
|
33
|
+
onMouseLeave?: EventSystemCallback;
|
|
34
|
+
/** triggered on mouse drag event */
|
|
35
|
+
onMouseDrag?: EventSystemCallback;
|
|
36
|
+
/** triggered on mouse drag event */
|
|
37
|
+
onMouseDragLocked?: EventSystemCallback;
|
|
38
|
+
/** triggered on mouse drag event */
|
|
39
|
+
onMouseDragEnd?: EventSystemCallback;
|
|
40
|
+
/** triggered on input down event */
|
|
41
|
+
onInputDown?: MultiCallback;
|
|
42
|
+
/** triggered on input up event */
|
|
43
|
+
onInputUp?: MultiCallback;
|
|
44
|
+
/** triggered on input drag event */
|
|
45
|
+
onInputDrag?: MultiCallback;
|
|
46
|
+
/** triggered on input drag event */
|
|
47
|
+
onInputDragLocked?: MultiCallback;
|
|
48
|
+
/** triggered on input drag event */
|
|
49
|
+
onInputDragEnd?: MultiCallback;
|
|
19
50
|
};
|
|
@@ -2,7 +2,15 @@ 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,
|
|
9
|
+
onInputDown: undefined,
|
|
10
|
+
onInputUp: undefined,
|
|
11
|
+
onInputDrag: undefined,
|
|
12
|
+
onInputDragLocked: undefined,
|
|
13
|
+
onInputDragEnd: undefined
|
|
6
14
|
};
|
|
7
15
|
const listenersKey = Object.keys(listeners);
|
|
8
16
|
/**
|
|
@@ -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 */
|
|
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
|
+
* 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, parseBorderColor, parseBorderRadius, parseBorderWidth, getScrollPosition, getScrollVisible, 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 { scrollPosition, scrollVisible, height, minHeight, maxHeight, width, minWidth, maxWidth, alignItems, alignContent, flexWrap, borderRadius, borderWidth, borderColor, zIndex, ...otherProps } = props;
|
|
64
64
|
return {
|
|
65
65
|
...defaultUiTransform,
|
|
66
66
|
...otherProps,
|
|
@@ -86,6 +86,9 @@ 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)),
|
|
92
|
+
...(zIndex && { zIndex })
|
|
90
93
|
};
|
|
91
94
|
}
|
|
@@ -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'
|
|
@@ -73,6 +74,11 @@ export type PositionType = 'absolute' | 'relative';
|
|
|
73
74
|
* The pointer filter property determines if the ui element blocks the pointer or not (elements with pointer events always block the pointer regardless of this property)
|
|
74
75
|
*/
|
|
75
76
|
export type PointerFilterType = 'none' | 'block';
|
|
77
|
+
/**
|
|
78
|
+
* @public
|
|
79
|
+
* The scroll-visible determines if the scrollbars are shown when the scroll overflow is enabled
|
|
80
|
+
*/
|
|
81
|
+
export type ScrollVisibleType = 'horizontal' | 'vertical' | 'both' | 'hidden';
|
|
76
82
|
/**
|
|
77
83
|
* Layout props to position things in the canvas
|
|
78
84
|
* @public
|
|
@@ -129,6 +135,12 @@ export interface UiTransformProps {
|
|
|
129
135
|
borderWidth?: Partial<Position> | PositionUnit;
|
|
130
136
|
/** The opacity property sets the opacity level for an element, it's accumulated across children @defaultValue 1 */
|
|
131
137
|
opacity?: number;
|
|
138
|
+
/** A reference value to identify the element, default empty */
|
|
139
|
+
elementId?: string;
|
|
140
|
+
/** default position=(0,0) if it aplies, a vector or a reference-id */
|
|
141
|
+
scrollPosition?: Vector2 | string;
|
|
142
|
+
/** default ShowScrollBar.SSB_BOTH */
|
|
143
|
+
scrollVisible?: ScrollVisibleType;
|
|
132
144
|
/** default 0 */
|
|
133
145
|
zIndex?: number;
|
|
134
146
|
}
|
|
@@ -244,3 +244,40 @@ 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 {
|
|
253
|
+
scrollPosition: {
|
|
254
|
+
value: {
|
|
255
|
+
$case: 'reference',
|
|
256
|
+
reference: scrollPosition
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
return {
|
|
263
|
+
scrollPosition: {
|
|
264
|
+
value: {
|
|
265
|
+
$case: 'position',
|
|
266
|
+
position: scrollPosition
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const parseScrollVisible = {
|
|
273
|
+
both: 0 /* ShowScrollBar.SSB_BOTH */,
|
|
274
|
+
hidden: 3 /* ShowScrollBar.SSB_HIDDEN */,
|
|
275
|
+
horizontal: 2 /* ShowScrollBar.SSB_ONLY_HORIZONTAL */,
|
|
276
|
+
vertical: 1 /* ShowScrollBar.SSB_ONLY_VERTICAL */
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
export function getScrollVisible(scrollVisible) {
|
|
282
|
+
return { scrollVisible: parseScrollVisible[scrollVisible] };
|
|
283
|
+
}
|
package/dist/components/utils.js
CHANGED
|
@@ -3,11 +3,6 @@ import { parseUiBackground } from './uiBackground';
|
|
|
3
3
|
import { parseUiTransform } from './uiTransform';
|
|
4
4
|
let uiScaleFactor = 1;
|
|
5
5
|
let uiScaleOwner = undefined;
|
|
6
|
-
const ZERO_INSETS = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
7
|
-
let screenInsetArea = { ...ZERO_INSETS };
|
|
8
|
-
let screenInsetAreaOwner = undefined;
|
|
9
|
-
let interactableArea = { ...ZERO_INSETS };
|
|
10
|
-
let interactableAreaOwner = undefined;
|
|
11
6
|
/**
|
|
12
7
|
* @internal
|
|
13
8
|
*/
|
|
@@ -59,22 +54,13 @@ export function getUiScaleFactor() {
|
|
|
59
54
|
return uiScaleFactor;
|
|
60
55
|
}
|
|
61
56
|
/**
|
|
62
|
-
* Sets the global UI scale factor.
|
|
63
|
-
*
|
|
64
|
-
* The `owner` symbol implements a cooperative reset-protection scheme shared
|
|
65
|
-
* with {@link resetUiScaleFactor}:
|
|
66
|
-
* - Writes always succeed — last writer claims ownership (the most recent
|
|
67
|
-
* `owner` passed to `set` is the one allowed to `reset`).
|
|
68
|
-
* - Resets from a non-matching owner are ignored, so a stale system can't
|
|
69
|
-
* stomp the active scale while another system is driving it.
|
|
70
|
-
* - A reset called without an owner always wins (used by tests / teardown).
|
|
71
|
-
*
|
|
72
57
|
* @internal
|
|
73
58
|
*/
|
|
74
59
|
export function setUiScaleFactor(nextScale, owner) {
|
|
75
60
|
if (!Number.isFinite(nextScale) || nextScale < 0)
|
|
76
61
|
return;
|
|
77
62
|
if (owner) {
|
|
63
|
+
// Mark ownership so only that system can reset the scale.
|
|
78
64
|
uiScaleOwner = owner;
|
|
79
65
|
}
|
|
80
66
|
uiScaleFactor = nextScale;
|
|
@@ -83,85 +69,12 @@ export function setUiScaleFactor(nextScale, owner) {
|
|
|
83
69
|
* @internal
|
|
84
70
|
*/
|
|
85
71
|
export function resetUiScaleFactor(owner) {
|
|
86
|
-
//
|
|
87
|
-
// A reset with no owner always wins — used by tests and teardown.
|
|
72
|
+
// Ignore resets from non-owners to avoid stomping active scale.
|
|
88
73
|
if (owner && uiScaleOwner !== owner)
|
|
89
74
|
return;
|
|
90
75
|
uiScaleOwner = undefined;
|
|
91
76
|
uiScaleFactor = 1;
|
|
92
77
|
}
|
|
93
|
-
/**
|
|
94
|
-
* @internal
|
|
95
|
-
*/
|
|
96
|
-
export function getScreenInsetArea() {
|
|
97
|
-
return { ...screenInsetArea };
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Sets the global screen inset area.
|
|
101
|
-
*
|
|
102
|
-
* The `owner` symbol implements a cooperative reset-protection scheme shared
|
|
103
|
-
* with {@link resetScreenInsetArea}:
|
|
104
|
-
* - Writes always succeed — last writer claims ownership (the most recent
|
|
105
|
-
* `owner` passed to `set` is the one allowed to `reset`).
|
|
106
|
-
* - Resets from a non-matching owner are ignored, so a stale system can't
|
|
107
|
-
* stomp the active insets while another system is driving them.
|
|
108
|
-
* - A reset called without an owner always wins (used by tests / teardown).
|
|
109
|
-
*
|
|
110
|
-
* @internal
|
|
111
|
-
*/
|
|
112
|
-
export function setScreenInsetArea(next, owner) {
|
|
113
|
-
if (owner) {
|
|
114
|
-
screenInsetAreaOwner = owner;
|
|
115
|
-
}
|
|
116
|
-
screenInsetArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom };
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* @internal
|
|
120
|
-
*/
|
|
121
|
-
export function resetScreenInsetArea(owner) {
|
|
122
|
-
// No-op for non-owners (see ownership rules on `setScreenInsetArea`).
|
|
123
|
-
// A reset with no owner always wins — used by tests and teardown.
|
|
124
|
-
if (owner && screenInsetAreaOwner !== owner)
|
|
125
|
-
return;
|
|
126
|
-
screenInsetAreaOwner = undefined;
|
|
127
|
-
screenInsetArea = { ...ZERO_INSETS };
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* @internal
|
|
131
|
-
*/
|
|
132
|
-
export function getInteractableArea() {
|
|
133
|
-
return { ...interactableArea };
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Sets the global interactable area.
|
|
137
|
-
*
|
|
138
|
-
* The `owner` symbol implements a cooperative reset-protection scheme shared
|
|
139
|
-
* with {@link resetInteractableArea}:
|
|
140
|
-
* - Writes always succeed — last writer claims ownership (the most recent
|
|
141
|
-
* `owner` passed to `set` is the one allowed to `reset`).
|
|
142
|
-
* - Resets from a non-matching owner are ignored, so a stale system can't
|
|
143
|
-
* stomp the active area while another system is driving it.
|
|
144
|
-
* - A reset called without an owner always wins (used by tests / teardown).
|
|
145
|
-
*
|
|
146
|
-
* @internal
|
|
147
|
-
*/
|
|
148
|
-
export function setInteractableArea(next, owner) {
|
|
149
|
-
if (owner) {
|
|
150
|
-
interactableAreaOwner = owner;
|
|
151
|
-
}
|
|
152
|
-
interactableArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom };
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* @internal
|
|
156
|
-
*/
|
|
157
|
-
export function resetInteractableArea(owner) {
|
|
158
|
-
// No-op for non-owners (see ownership rules on `setInteractableArea`).
|
|
159
|
-
// A reset with no owner always wins — used by tests and teardown.
|
|
160
|
-
if (owner && interactableAreaOwner !== owner)
|
|
161
|
-
return;
|
|
162
|
-
interactableAreaOwner = undefined;
|
|
163
|
-
interactableArea = { ...ZERO_INSETS };
|
|
164
|
-
}
|
|
165
78
|
/**
|
|
166
79
|
* @internal
|
|
167
80
|
*/
|
package/dist/react-ecs.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PBUiBackground, PBUiText, PBUiTransform, PBUiInput, PBUiDropdown } from '@dcl/ecs';
|
|
2
|
-
import {
|
|
1
|
+
import { PBUiBackground, PBUiText, PBUiTransform, PBUiInput, PBUiDropdown, EventSystemCallback } from '@dcl/ecs';
|
|
2
|
+
import { Key, MultiCallback } from './components';
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
5
5
|
*/
|
|
@@ -18,10 +18,18 @@ export type EntityComponents = {
|
|
|
18
18
|
uiBackground: PBUiBackground;
|
|
19
19
|
uiInput: PBUiInput;
|
|
20
20
|
uiDropdown: PBUiDropdown;
|
|
21
|
-
onMouseDown:
|
|
22
|
-
onMouseUp:
|
|
23
|
-
onMouseEnter:
|
|
24
|
-
onMouseLeave:
|
|
21
|
+
onMouseDown: EventSystemCallback;
|
|
22
|
+
onMouseUp: EventSystemCallback;
|
|
23
|
+
onMouseEnter: EventSystemCallback;
|
|
24
|
+
onMouseLeave: EventSystemCallback;
|
|
25
|
+
onMouseDrag: EventSystemCallback;
|
|
26
|
+
onMouseDragLocked: EventSystemCallback;
|
|
27
|
+
onMouseDragEnd: EventSystemCallback;
|
|
28
|
+
onInputDown: MultiCallback;
|
|
29
|
+
onInputUp: MultiCallback;
|
|
30
|
+
onInputDrag: MultiCallback;
|
|
31
|
+
onInputDragLocked: MultiCallback;
|
|
32
|
+
onInputDragEnd: MultiCallback;
|
|
25
33
|
};
|
|
26
34
|
/**
|
|
27
35
|
* @hidden
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entity, IEngine, PointerEventsSystem } from '@dcl/ecs';
|
|
2
2
|
import { ReactEcs } from '../react-ecs';
|
|
3
|
-
export
|
|
4
|
-
update: (component: ReactEcs.JSX.ReactNode) =>
|
|
3
|
+
export interface DclReconciler {
|
|
4
|
+
update: (component: ReactEcs.JSX.ReactNode) => void;
|
|
5
5
|
getEntities: () => Entity[];
|
|
6
|
-
}
|
|
6
|
+
}
|
|
7
|
+
export declare function createReconciler(engine: Pick<IEngine, 'getComponent' | 'addEntity' | 'removeEntity' | 'defineComponentFromSchema' | 'getEntitiesWith'>, pointerEvents: PointerEventsSystem, rootEntity: Entity | undefined): DclReconciler;
|
package/dist/reconciler/index.js
CHANGED
|
@@ -3,24 +3,11 @@ import Reconciler from 'react-reconciler';
|
|
|
3
3
|
import { isListener } from '../components';
|
|
4
4
|
import { CANVAS_ROOT_ENTITY } from '../components/uiTransform';
|
|
5
5
|
import { componentKeys, isNotUndefined, noopConfig, propsChanged } from './utils';
|
|
6
|
-
function
|
|
7
|
-
const pointers = {
|
|
8
|
-
onMouseDown: 1 /* PointerEventType.PET_DOWN */,
|
|
9
|
-
onMouseUp: 0 /* PointerEventType.PET_UP */,
|
|
10
|
-
onMouseEnter: 2 /* PointerEventType.PET_HOVER_ENTER */,
|
|
11
|
-
onMouseLeave: 3 /* PointerEventType.PET_HOVER_LEAVE */
|
|
12
|
-
};
|
|
13
|
-
return pointers[pointerKey];
|
|
14
|
-
}
|
|
15
|
-
export function createReconciler(engine, pointerEvents) {
|
|
6
|
+
export function createReconciler(engine, pointerEvents, rootEntity) {
|
|
16
7
|
// Store all the entities so when we destroy the UI we can also destroy them
|
|
17
8
|
const entities = new Set();
|
|
18
9
|
// Store the onChange callbacks to be runned every time a Result has changed
|
|
19
10
|
const changeEvents = new Map();
|
|
20
|
-
const clickEvents = new Map();
|
|
21
|
-
// Track the last value reported by the renderer for each input entity,
|
|
22
|
-
// so we can avoid echoing it back and causing keystroke drops.
|
|
23
|
-
const lastInputResultValues = new Map();
|
|
24
11
|
// Initialize components
|
|
25
12
|
const UiTransform = components.UiTransform(engine);
|
|
26
13
|
const UiText = components.UiText(engine);
|
|
@@ -29,6 +16,8 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
29
16
|
const UiInputResult = components.UiInputResult(engine);
|
|
30
17
|
const UiDropdown = components.UiDropdown(engine);
|
|
31
18
|
const UiDropdownResult = components.UiDropdownResult(engine);
|
|
19
|
+
const UiScrollResult = components.UiScrollResult(engine);
|
|
20
|
+
const Transform = components.Transform(engine);
|
|
32
21
|
// Component ID Helper
|
|
33
22
|
const getComponentId = {
|
|
34
23
|
uiTransform: UiTransform.componentId,
|
|
@@ -37,22 +26,15 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
37
26
|
uiInput: UiInput.componentId,
|
|
38
27
|
uiDropdown: UiDropdown.componentId
|
|
39
28
|
};
|
|
40
|
-
function pointerEventCallback(entity, pointerEvent) {
|
|
41
|
-
const callback = clickEvents.get(entity)?.get(pointerEvent);
|
|
42
|
-
if (callback)
|
|
43
|
-
callback();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
29
|
function updateTree(instance, props) {
|
|
47
30
|
upsertComponent(instance, props, 'uiTransform');
|
|
48
31
|
}
|
|
49
32
|
function upsertListener(instance, update) {
|
|
50
33
|
if (update.type === 'delete' || !update.props) {
|
|
51
|
-
|
|
52
|
-
if (update.component === 'onMouseDown') {
|
|
34
|
+
if (update.component === 'onMouseDown' || update.component === 'onInputDown') {
|
|
53
35
|
pointerEvents.removeOnPointerDown(instance.entity);
|
|
54
36
|
}
|
|
55
|
-
else if (update.component === 'onMouseUp') {
|
|
37
|
+
else if (update.component === 'onMouseUp' || update.component === 'onInputUp') {
|
|
56
38
|
pointerEvents.removeOnPointerUp(instance.entity);
|
|
57
39
|
}
|
|
58
40
|
else if (update.component === 'onMouseEnter') {
|
|
@@ -61,32 +43,57 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
61
43
|
else if (update.component === 'onMouseLeave') {
|
|
62
44
|
pointerEvents.removeOnPointerHoverLeave(instance.entity);
|
|
63
45
|
}
|
|
46
|
+
else if (update.component === 'onMouseDrag' || update.component === 'onInputDrag') {
|
|
47
|
+
pointerEvents.removeOnPointerDrag(instance.entity);
|
|
48
|
+
}
|
|
49
|
+
else if (update.component === 'onMouseDragLocked' || update.component === 'onInputDragLocked') {
|
|
50
|
+
pointerEvents.removeOnPointerDragLocked(instance.entity);
|
|
51
|
+
}
|
|
52
|
+
else if (update.component === 'onMouseDragEnd' || update.component === 'onInputDragEnd') {
|
|
53
|
+
pointerEvents.removeOnPointerDragEnd(instance.entity);
|
|
54
|
+
}
|
|
64
55
|
return;
|
|
65
56
|
}
|
|
66
57
|
if (update.props) {
|
|
67
|
-
const
|
|
68
|
-
const entityEvent = clickEvents.get(instance.entity) || clickEvents.set(instance.entity, new Map()).get(instance.entity);
|
|
69
|
-
const alreadyHasPointerEvent = entityEvent.get(pointerEvent);
|
|
70
|
-
entityEvent.set(pointerEvent, update.props);
|
|
71
|
-
if (alreadyHasPointerEvent)
|
|
72
|
-
return;
|
|
73
|
-
const pointerEventSystem = update.component === 'onMouseDown'
|
|
58
|
+
const pointerEventSystem = update.component === 'onMouseDown' || update.component === 'onInputDown'
|
|
74
59
|
? pointerEvents.onPointerDown
|
|
75
|
-
: update.component === 'onMouseUp'
|
|
60
|
+
: update.component === 'onMouseUp' || update.component === 'onInputUp'
|
|
76
61
|
? pointerEvents.onPointerUp
|
|
77
62
|
: update.component === 'onMouseEnter'
|
|
78
63
|
? pointerEvents.onPointerHoverEnter
|
|
79
|
-
: update.component === 'onMouseLeave'
|
|
64
|
+
: update.component === 'onMouseLeave'
|
|
65
|
+
? pointerEvents.onPointerHoverLeave
|
|
66
|
+
: update.component === 'onMouseDrag' || update.component === 'onInputDrag'
|
|
67
|
+
? pointerEvents.onPointerDrag
|
|
68
|
+
: update.component === 'onMouseDragLocked' || update.component === 'onInputDragLocked'
|
|
69
|
+
? pointerEvents.onPointerDragLocked
|
|
70
|
+
: (update.component === 'onMouseDragEnd' || update.component === 'onInputDragEnd') &&
|
|
71
|
+
pointerEvents.onPointerDragEnd;
|
|
80
72
|
if (pointerEventSystem) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
73
|
+
if (typeof update.props === 'function') {
|
|
74
|
+
pointerEventSystem({
|
|
75
|
+
entity: instance.entity,
|
|
76
|
+
opts: {
|
|
77
|
+
button: 0 /* InputAction.IA_POINTER */,
|
|
78
|
+
// We add this showFeedBack so the pointerEventSystem creates a PointerEvent component with our entity
|
|
79
|
+
// This is needed for the renderer to know which entities are clickeables
|
|
80
|
+
showFeedback: true
|
|
81
|
+
}
|
|
82
|
+
}, update.props);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// force the right overload (the single arg version doesn't exist for onPointerHoverEnter or onPointerHoverLeave,
|
|
86
|
+
// but we don't have onInputXXX components for those)
|
|
87
|
+
;
|
|
88
|
+
pointerEventSystem({
|
|
89
|
+
entity: instance.entity,
|
|
90
|
+
optsList: Object.entries(update.props).map(([button, cb]) => ({
|
|
91
|
+
button: Number(button),
|
|
92
|
+
showFeedback: true,
|
|
93
|
+
cb
|
|
94
|
+
}))
|
|
95
|
+
});
|
|
96
|
+
}
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
}
|
|
@@ -114,15 +121,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
114
121
|
delete props.onChange;
|
|
115
122
|
delete props.onSubmit;
|
|
116
123
|
}
|
|
117
|
-
// Prevent keystroke drops: when React echoes back the same value the renderer
|
|
118
|
-
// reported, strip it from the props so the component isn't marked dirty for it.
|
|
119
|
-
// This avoids sending a stale value that overwrites what the user is currently typing.
|
|
120
|
-
if (componentName === 'uiInput' &&
|
|
121
|
-
'value' in props &&
|
|
122
|
-
lastInputResultValues.has(instance.entity) &&
|
|
123
|
-
props.value === lastInputResultValues.get(instance.entity)) {
|
|
124
|
-
delete props.value;
|
|
125
|
-
}
|
|
126
124
|
// We check if there is any key pending to be changed to avoid updating the existing component
|
|
127
125
|
if (!Object.keys(props).length) {
|
|
128
126
|
return;
|
|
@@ -136,8 +134,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
136
134
|
}
|
|
137
135
|
function removeChildEntity(instance) {
|
|
138
136
|
changeEvents.delete(instance.entity);
|
|
139
|
-
clickEvents.delete(instance.entity);
|
|
140
|
-
lastInputResultValues.delete(instance.entity);
|
|
141
137
|
engine.removeEntity(instance.entity);
|
|
142
138
|
for (const child of instance._child) {
|
|
143
139
|
removeChildEntity(child);
|
|
@@ -156,15 +152,11 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
156
152
|
const rightOfChild = parent._child.find((c) => c.rightOf === child.entity);
|
|
157
153
|
if (rightOfChild) {
|
|
158
154
|
rightOfChild.rightOf = child.rightOf;
|
|
155
|
+
// Re-order parent._child array
|
|
156
|
+
parent._child = parent._child.filter((c) => c.entity !== child.entity);
|
|
157
|
+
parent._child.push(child);
|
|
159
158
|
updateTree(rightOfChild, { rightOf: rightOfChild.rightOf });
|
|
160
159
|
}
|
|
161
|
-
// Always remove from old position and push to end for reorders.
|
|
162
|
-
// Previously, filter/push was inside the if(rightOfChild) block, so when
|
|
163
|
-
// rightOfChild was not found (child was last), the child stayed at its old
|
|
164
|
-
// position. This caused _child[length-2] to potentially be the child itself,
|
|
165
|
-
// producing a self-referencing rightOf.
|
|
166
|
-
parent._child = parent._child.filter((c) => c.entity !== child.entity);
|
|
167
|
-
parent._child.push(child);
|
|
168
160
|
// Its a re-order. We are the last element, so we need to fetch the element before us.
|
|
169
161
|
child.rightOf = parent._child[parent._child.length - 2]?.entity;
|
|
170
162
|
}
|
|
@@ -178,14 +170,13 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
178
170
|
}
|
|
179
171
|
function removeChild(parentInstance, child) {
|
|
180
172
|
const childIndex = parentInstance._child.findIndex((c) => c.entity === child.entity);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
updateTree(childToModify, { rightOf: child.rightOf });
|
|
186
|
-
}
|
|
187
|
-
parentInstance._child.splice(childIndex, 1);
|
|
173
|
+
const childToModify = parentInstance._child[childIndex + 1];
|
|
174
|
+
if (childToModify) {
|
|
175
|
+
childToModify.rightOf = child.rightOf;
|
|
176
|
+
updateTree(childToModify, { rightOf: child.rightOf });
|
|
188
177
|
}
|
|
178
|
+
// Mutate 💀
|
|
179
|
+
parentInstance._child.splice(childIndex, 1);
|
|
189
180
|
removeChildEntity(child);
|
|
190
181
|
}
|
|
191
182
|
function updateOnChange(entity, componentId, state) {
|
|
@@ -198,9 +189,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
198
189
|
if (!hasEvent) {
|
|
199
190
|
const resultComponentId = componentId === UiDropdown.componentId ? UiDropdownResult.componentId : UiInputResult.componentId;
|
|
200
191
|
engine.getComponent(resultComponentId).onChange(entity, (value) => {
|
|
201
|
-
if (resultComponentId === UiInputResult.componentId) {
|
|
202
|
-
lastInputResultValues.set(entity, value?.value);
|
|
203
|
-
}
|
|
204
192
|
if (value?.isSubmit) {
|
|
205
193
|
const onSubmit = changeEvents.get(entity)?.get(componentId)?.onSubmitCallback;
|
|
206
194
|
onSubmit && onSubmit(value?.value);
|
|
@@ -214,6 +202,10 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
214
202
|
...noopConfig,
|
|
215
203
|
createInstance(type, props) {
|
|
216
204
|
const entity = engine.addEntity();
|
|
205
|
+
// set root
|
|
206
|
+
if (rootEntity !== undefined) {
|
|
207
|
+
Transform.createOrReplace(entity, { parent: rootEntity });
|
|
208
|
+
}
|
|
217
209
|
entities.add(entity);
|
|
218
210
|
const instance = {
|
|
219
211
|
entity,
|
|
@@ -227,11 +219,13 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
227
219
|
continue;
|
|
228
220
|
}
|
|
229
221
|
if (isListener(keyTyped)) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
222
|
+
if (props[keyTyped] !== undefined) {
|
|
223
|
+
upsertListener(instance, {
|
|
224
|
+
type: 'add',
|
|
225
|
+
props: props[keyTyped],
|
|
226
|
+
component: keyTyped
|
|
227
|
+
});
|
|
228
|
+
}
|
|
235
229
|
}
|
|
236
230
|
else {
|
|
237
231
|
upsertComponent(instance, props[keyTyped], keyTyped);
|
|
@@ -263,23 +257,6 @@ export function createReconciler(engine, pointerEvents) {
|
|
|
263
257
|
}
|
|
264
258
|
},
|
|
265
259
|
insertBefore(parentInstance, child, beforeChild) {
|
|
266
|
-
// Handle reorder: if child already exists in this parent, remove it from its old position
|
|
267
|
-
// and fix up the old neighbor's rightOf before inserting at the new position.
|
|
268
|
-
const existingIndex = parentInstance._child.findIndex((c) => c.entity === child.entity);
|
|
269
|
-
if (existingIndex !== -1) {
|
|
270
|
-
// If beforeChild.rightOf already points to child, child is already in the correct
|
|
271
|
-
// position in the rightOf chain. Setting child.rightOf = beforeChild.rightOf would
|
|
272
|
-
// produce a self-cycle (child.rightOf = child.entity).
|
|
273
|
-
if (beforeChild.rightOf === child.entity) {
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
const oldNextSibling = parentInstance._child[existingIndex + 1];
|
|
277
|
-
if (oldNextSibling) {
|
|
278
|
-
oldNextSibling.rightOf = child.rightOf;
|
|
279
|
-
updateTree(oldNextSibling, { rightOf: oldNextSibling.rightOf });
|
|
280
|
-
}
|
|
281
|
-
parentInstance._child.splice(existingIndex, 1);
|
|
282
|
-
}
|
|
283
260
|
const beforeChildIndex = parentInstance._child.findIndex((c) => c.entity === beforeChild.entity);
|
|
284
261
|
parentInstance._child = [
|
|
285
262
|
...parentInstance._child.slice(0, beforeChildIndex),
|
package/dist/reconciler/utils.js
CHANGED
|
@@ -23,10 +23,10 @@ export function propsChanged(component, prevProps, nextProps) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
// ...then catch keys present only in nextProps (an optional prop set for the first time,
|
|
26
|
-
// e.g.
|
|
27
|
-
// Two allocation-free loops are used instead of a Set union to keep this hot
|
|
28
|
-
// These keys aren't in prevProps, so emitting them whenever they're defined is
|
|
29
|
-
// than (and avoids the falsy-value trap of) an isEqual(undefined, ...)
|
|
26
|
+
// e.g. `scrollPosition` going from undefined to a value). Iterating prevProps alone would
|
|
27
|
+
// drop these. Two allocation-free loops are used instead of a Set union to keep this hot
|
|
28
|
+
// path cheap. These keys aren't in prevProps, so emitting them whenever they're defined is
|
|
29
|
+
// both cheaper than (and avoids the falsy-value trap of) an isEqual(undefined, ...) compare.
|
|
30
30
|
for (const k in nextProps) {
|
|
31
31
|
if (k in prevProps)
|
|
32
32
|
continue;
|
|
@@ -49,6 +49,14 @@ const entityComponent = {
|
|
|
49
49
|
onMouseUp: undefined,
|
|
50
50
|
onMouseEnter: undefined,
|
|
51
51
|
onMouseLeave: undefined,
|
|
52
|
+
onMouseDrag: undefined,
|
|
53
|
+
onMouseDragLocked: undefined,
|
|
54
|
+
onMouseDragEnd: undefined,
|
|
55
|
+
onInputDown: undefined,
|
|
56
|
+
onInputUp: undefined,
|
|
57
|
+
onInputDrag: undefined,
|
|
58
|
+
onInputDragLocked: undefined,
|
|
59
|
+
onInputDragEnd: undefined,
|
|
52
60
|
uiInput: undefined,
|
|
53
61
|
uiDropdown: undefined
|
|
54
62
|
};
|
package/dist/system.d.ts
CHANGED
|
@@ -23,6 +23,11 @@ export interface ReactBasedUiSystem {
|
|
|
23
23
|
* Set the main UI renderer. Optional virtual size defines the global UI scale factor.
|
|
24
24
|
*/
|
|
25
25
|
setUiRenderer(ui: UiComponent, options?: UiRendererOptions): void;
|
|
26
|
+
/**
|
|
27
|
+
* Set a texture renderer for a specific entity.
|
|
28
|
+
* @deprecated Use addUiRenderer instead
|
|
29
|
+
*/
|
|
30
|
+
setTextureRenderer(entity: Entity, ui: UiComponent): void;
|
|
26
31
|
/**
|
|
27
32
|
* Add a UI renderer associated with an entity. The UI will be automatically cleaned up
|
|
28
33
|
* when the entity is removed from the engine.
|
package/dist/system.js
CHANGED
|
@@ -2,22 +2,19 @@ import { EntityState } from '@dcl/ecs';
|
|
|
2
2
|
import * as ecsComponents from '@dcl/ecs/dist/components';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { createReconciler } from './reconciler';
|
|
5
|
-
import { getUiScaleFactor,
|
|
5
|
+
import { getUiScaleFactor, resetUiScaleFactor, setUiScaleFactor } from './components/utils';
|
|
6
6
|
/**
|
|
7
7
|
* @public
|
|
8
8
|
*/
|
|
9
9
|
export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
10
|
-
const renderer = createReconciler(engine, pointerSystem);
|
|
10
|
+
const renderer = createReconciler(engine, pointerSystem, undefined);
|
|
11
11
|
let uiComponent = undefined;
|
|
12
12
|
let virtualSize = undefined;
|
|
13
13
|
const additionalRenderers = new Map();
|
|
14
|
+
const textureRenderersAndUis = [];
|
|
14
15
|
const UiCanvasInformation = ecsComponents.UiCanvasInformation(engine);
|
|
15
16
|
// Unique owner to prevent other UI systems resetting this scale factor.
|
|
16
17
|
const uiScaleFactorOwner = Symbol('react-ecs-ui-scale');
|
|
17
|
-
// Unique owner for the screen inset module variable.
|
|
18
|
-
const screenInsetAreaOwner = Symbol('react-ecs-screen-inset-area');
|
|
19
|
-
// Unique owner for the interactable area module variable.
|
|
20
|
-
const interactableAreaOwner = Symbol('react-ecs-interactable-area');
|
|
21
18
|
function getActiveVirtualSize() {
|
|
22
19
|
// Main renderer options win; otherwise use the first additional renderer option.
|
|
23
20
|
if (virtualSize)
|
|
@@ -55,35 +52,26 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
55
52
|
else {
|
|
56
53
|
renderer.update(null);
|
|
57
54
|
}
|
|
55
|
+
// Update texture renderers (deprecated, kept for backwards compatibility)
|
|
56
|
+
for (const [textureRenderer, ui] of textureRenderersAndUis) {
|
|
57
|
+
textureRenderer.update(React.createElement(ui));
|
|
58
|
+
}
|
|
58
59
|
}
|
|
59
60
|
function UiScaleSystem() {
|
|
60
|
-
const canvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity);
|
|
61
|
-
// Update the screen inset module variable unconditionally — it is
|
|
62
|
-
// independent of the virtual size and useful even when the renderer has no
|
|
63
|
-
// virtual canvas.
|
|
64
|
-
if (canvasInfo?.screenInsetArea) {
|
|
65
|
-
setScreenInsetArea(canvasInfo.screenInsetArea, screenInsetAreaOwner);
|
|
66
|
-
}
|
|
67
|
-
// Update the interactable area module variable unconditionally.
|
|
68
|
-
if (canvasInfo?.interactableArea) {
|
|
69
|
-
setInteractableArea(canvasInfo.interactableArea, interactableAreaOwner);
|
|
70
|
-
}
|
|
71
61
|
const activeVirtualSize = getActiveVirtualSize();
|
|
72
62
|
if (!activeVirtualSize) {
|
|
73
63
|
// Reset only if this system owns the scale factor.
|
|
74
64
|
resetUiScaleFactor(uiScaleFactorOwner);
|
|
75
65
|
return;
|
|
76
66
|
}
|
|
67
|
+
const canvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity);
|
|
77
68
|
if (!canvasInfo)
|
|
78
69
|
return;
|
|
79
|
-
const { width, height
|
|
70
|
+
const { width, height } = canvasInfo;
|
|
80
71
|
const { virtualWidth, virtualHeight } = activeVirtualSize;
|
|
81
72
|
if (!virtualWidth || !virtualHeight)
|
|
82
73
|
return;
|
|
83
|
-
|
|
84
|
-
// vw/vh path); without it the scale was inflated on high-dpr mobile screens.
|
|
85
|
-
const ratio = devicePixelRatio || 1;
|
|
86
|
-
const nextScale = Math.min(width / virtualWidth, height / virtualHeight) / ratio;
|
|
74
|
+
const nextScale = Math.min(width / virtualWidth, height / virtualHeight);
|
|
87
75
|
if (Number.isFinite(nextScale) && nextScale !== getUiScaleFactor()) {
|
|
88
76
|
// Track ownership when updating to avoid cross-system conflicts.
|
|
89
77
|
setUiScaleFactor(nextScale, uiScaleFactorOwner);
|
|
@@ -96,16 +84,22 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
|
|
|
96
84
|
engine.removeSystem(UiScaleSystem);
|
|
97
85
|
engine.removeSystem(ReactBasedUiSystem);
|
|
98
86
|
resetUiScaleFactor(uiScaleFactorOwner);
|
|
99
|
-
resetScreenInsetArea(screenInsetAreaOwner);
|
|
100
|
-
resetInteractableArea(interactableAreaOwner);
|
|
101
87
|
for (const entity of renderer.getEntities()) {
|
|
102
88
|
engine.removeEntity(entity);
|
|
103
89
|
}
|
|
90
|
+
for (const [textureRenderer, _] of textureRenderersAndUis) {
|
|
91
|
+
for (const entity of textureRenderer.getEntities()) {
|
|
92
|
+
engine.removeEntity(entity);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
104
95
|
},
|
|
105
96
|
setUiRenderer(ui, options) {
|
|
106
97
|
uiComponent = ui;
|
|
107
98
|
virtualSize = options;
|
|
108
99
|
},
|
|
100
|
+
setTextureRenderer(entity, ui) {
|
|
101
|
+
textureRenderersAndUis.push([createReconciler(engine, pointerSystem, entity), ui]);
|
|
102
|
+
},
|
|
109
103
|
addUiRenderer(entity, ui, options) {
|
|
110
104
|
additionalRenderers.set(entity, { ui, options });
|
|
111
105
|
},
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcl/react-ecs",
|
|
3
3
|
"description": "Decentraland ECS",
|
|
4
|
-
"version": "7.24.5-
|
|
4
|
+
"version": "7.24.5-29247185020.commit-e92dfe6",
|
|
5
5
|
"author": "DCL",
|
|
6
6
|
"bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@dcl/ecs": "7.24.5-
|
|
8
|
+
"@dcl/ecs": "7.24.5-29247185020.commit-e92dfe6",
|
|
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": "e92dfe6298e3d808e58add843780cdbe7eb5af7d"
|
|
44
44
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ReactEcs } from '../../react-ecs';
|
|
2
|
-
import { UiInteractableAreaProps } from './types';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @public
|
|
6
|
-
* InteractableArea component
|
|
7
|
-
*
|
|
8
|
-
* Constrains its children to the area inside the renderer-reported interactable
|
|
9
|
-
* area. This is the portion of the screen not covered by client UI such as the
|
|
10
|
-
* minimap, chat window, or other platform overlays. On the Unity desktop client
|
|
11
|
-
* the left 25% of the screen is reserved for client UI, so this container
|
|
12
|
-
* positions its children within the remaining 75%.
|
|
13
|
-
*
|
|
14
|
-
* The container is absolutely positioned with top/left/right/bottom matching
|
|
15
|
-
* the current `UiCanvasInformation.interactableArea`, so a child sized
|
|
16
|
-
* 100%x100% fills the interactable area exactly.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* <InteractableArea><MyHud /></InteractableArea>
|
|
20
|
-
*
|
|
21
|
-
* @category Component
|
|
22
|
-
*/
|
|
23
|
-
export declare function InteractableArea(props: UiInteractableAreaProps): ReactEcs.JSX.Element;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { ReactEcs } from '../../react-ecs';
|
|
2
|
-
import { UiEntity } from '../index';
|
|
3
|
-
import { getInteractableArea } from '../utils';
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* @public
|
|
7
|
-
* InteractableArea component
|
|
8
|
-
*
|
|
9
|
-
* Constrains its children to the area inside the renderer-reported interactable
|
|
10
|
-
* area. This is the portion of the screen not covered by client UI such as the
|
|
11
|
-
* minimap, chat window, or other platform overlays. On the Unity desktop client
|
|
12
|
-
* the left 25% of the screen is reserved for client UI, so this container
|
|
13
|
-
* positions its children within the remaining 75%.
|
|
14
|
-
*
|
|
15
|
-
* The container is absolutely positioned with top/left/right/bottom matching
|
|
16
|
-
* the current `UiCanvasInformation.interactableArea`, so a child sized
|
|
17
|
-
* 100%x100% fills the interactable area exactly.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* <InteractableArea><MyHud /></InteractableArea>
|
|
21
|
-
*
|
|
22
|
-
* @category Component
|
|
23
|
-
*/
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
export function InteractableArea(props) {
|
|
26
|
-
const { top, left, right, bottom } = getInteractableArea();
|
|
27
|
-
const { uiTransform, ...otherProps } = props;
|
|
28
|
-
return (ReactEcs.createElement(UiEntity, { ...otherProps, uiTransform: {
|
|
29
|
-
...uiTransform,
|
|
30
|
-
positionType: 'absolute',
|
|
31
|
-
position: { top, left, right, bottom }
|
|
32
|
-
} }));
|
|
33
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { EntityPropTypes } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* InteractableArea component props
|
|
4
|
-
*
|
|
5
|
-
* The container reads the current `interactableArea` reported by the renderer
|
|
6
|
-
* via `UiCanvasInformation` (the HUD-safe zone — the portion of the screen not
|
|
7
|
-
* covered by client UI such as the minimap, chat window, or other overlays) and
|
|
8
|
-
* constrains its children to the area inside those insets using absolute
|
|
9
|
-
* positioning. Layout props that control the container's own position
|
|
10
|
-
* (`positionType`, `position`) are owned by the component and are not
|
|
11
|
-
* configurable from props — every other layout, background and event prop is
|
|
12
|
-
* forwarded as usual.
|
|
13
|
-
*
|
|
14
|
-
* @public
|
|
15
|
-
*/
|
|
16
|
-
export type UiInteractableAreaProps = Omit<EntityPropTypes, 'uiTransform'> & {
|
|
17
|
-
/**
|
|
18
|
-
* Layout overrides forwarded to the underlying entity. The
|
|
19
|
-
* `positionType` and `position` fields are reserved by the container and
|
|
20
|
-
* any value provided here is ignored.
|
|
21
|
-
*/
|
|
22
|
-
uiTransform?: Omit<NonNullable<EntityPropTypes['uiTransform']>, 'positionType' | 'position'>;
|
|
23
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ReactEcs } from '../../react-ecs';
|
|
2
|
-
import { UiScreenInsetAreaProps } from './types';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @public
|
|
6
|
-
* ScreenInsetArea component
|
|
7
|
-
*
|
|
8
|
-
* Constrains its children to the area inside the renderer-reported screen
|
|
9
|
-
* inset (safe margins). On mobile this is the area excluding the notch,
|
|
10
|
-
* status bar, home indicator and rounded corners. On desktop the insets are
|
|
11
|
-
* typically zero, so the container fills the canvas.
|
|
12
|
-
*
|
|
13
|
-
* The container is absolutely positioned with top/left/right/bottom matching
|
|
14
|
-
* the current `UiCanvasInformation.screenInsetArea`, so a child sized
|
|
15
|
-
* 100%x100% fills the safe area exactly.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* <ScreenInsetArea><MyHud /></ScreenInsetArea>
|
|
19
|
-
*
|
|
20
|
-
* @category Component
|
|
21
|
-
*/
|
|
22
|
-
export declare function ScreenInsetArea(props: UiScreenInsetAreaProps): ReactEcs.JSX.Element;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { ReactEcs } from '../../react-ecs';
|
|
2
|
-
import { UiEntity } from '../index';
|
|
3
|
-
import { getScreenInsetArea } from '../utils';
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* @public
|
|
7
|
-
* ScreenInsetArea component
|
|
8
|
-
*
|
|
9
|
-
* Constrains its children to the area inside the renderer-reported screen
|
|
10
|
-
* inset (safe margins). On mobile this is the area excluding the notch,
|
|
11
|
-
* status bar, home indicator and rounded corners. On desktop the insets are
|
|
12
|
-
* typically zero, so the container fills the canvas.
|
|
13
|
-
*
|
|
14
|
-
* The container is absolutely positioned with top/left/right/bottom matching
|
|
15
|
-
* the current `UiCanvasInformation.screenInsetArea`, so a child sized
|
|
16
|
-
* 100%x100% fills the safe area exactly.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* <ScreenInsetArea><MyHud /></ScreenInsetArea>
|
|
20
|
-
*
|
|
21
|
-
* @category Component
|
|
22
|
-
*/
|
|
23
|
-
/* @__PURE__ */
|
|
24
|
-
export function ScreenInsetArea(props) {
|
|
25
|
-
const { top, left, right, bottom } = getScreenInsetArea();
|
|
26
|
-
const { uiTransform, ...otherProps } = props;
|
|
27
|
-
return (ReactEcs.createElement(UiEntity, { ...otherProps, uiTransform: {
|
|
28
|
-
...uiTransform,
|
|
29
|
-
positionType: 'absolute',
|
|
30
|
-
position: { top, left, right, bottom }
|
|
31
|
-
} }));
|
|
32
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { EntityPropTypes } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* ScreenInsetArea component props
|
|
4
|
-
*
|
|
5
|
-
* The container reads the current `screenInsetArea` reported by the renderer
|
|
6
|
-
* via `UiCanvasInformation` (the device safe margins — notch, status bar,
|
|
7
|
-
* home indicator, rounded corners) and constrains its children to the area
|
|
8
|
-
* inside those insets using absolute positioning. Layout props that control
|
|
9
|
-
* the container's own position (`positionType`, `position`) are owned by the
|
|
10
|
-
* component and are not configurable from props — every other layout,
|
|
11
|
-
* background and event prop is forwarded as usual.
|
|
12
|
-
*
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
export type UiScreenInsetAreaProps = Omit<EntityPropTypes, 'uiTransform'> & {
|
|
16
|
-
/**
|
|
17
|
-
* Layout overrides forwarded to the underlying entity. The
|
|
18
|
-
* `positionType` and `position` fields are reserved by the container and
|
|
19
|
-
* any value provided here is ignored.
|
|
20
|
-
*/
|
|
21
|
-
uiTransform?: Omit<NonNullable<EntityPropTypes['uiTransform']>, 'positionType' | 'position'>;
|
|
22
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|