@dcl/react-ecs 7.22.5-24854109737.commit-b6da827 → 7.22.6-25007982108.commit-83012ab

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, ...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
  }
@@ -1,19 +1,50 @@
1
+ import { EventSystemCallback, InputAction } from '@dcl/ecs';
1
2
  /**
2
- * Callback function to be triggered on a specified event
3
- * @public
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?: Callback;
27
+ onMouseDown?: EventSystemCallback;
13
28
  /** triggered on mouse up event */
14
- onMouseUp?: Callback;
29
+ onMouseUp?: EventSystemCallback;
15
30
  /** triggered on mouse hover event */
16
- onMouseEnter?: Callback;
31
+ onMouseEnter?: EventSystemCallback;
17
32
  /** triggered on mouse leave event */
18
- onMouseLeave?: Callback;
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,5 +1,5 @@
1
- import { UiTransformProps } from './types';
2
1
  import { PBUiTransform } from '@dcl/ecs/dist/components';
2
+ import { UiTransformProps } from './types';
3
3
  /**
4
4
  * @public
5
5
  */
@@ -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
+ }
@@ -1,5 +1,5 @@
1
- import { PBUiBackground, PBUiText, PBUiTransform, PBUiInput, PBUiDropdown } from '@dcl/ecs';
2
- import { Callback, Key } from './components';
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: Callback;
22
- onMouseUp: Callback;
23
- onMouseEnter: Callback;
24
- onMouseLeave: Callback;
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 declare function createReconciler(engine: Pick<IEngine, 'getComponent' | 'addEntity' | 'removeEntity' | 'defineComponentFromSchema' | 'getEntitiesWith'>, pointerEvents: PointerEventsSystem): {
4
- update: (component: ReactEcs.JSX.ReactNode) => number;
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;
@@ -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 getPointerEnum(pointerKey) {
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
- clickEvents.get(instance.entity)?.delete(getPointerEnum(update.component));
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 pointerEvent = getPointerEnum(update.component);
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' && pointerEvents.onPointerHoverLeave;
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
- pointerEventSystem({
82
- entity: instance.entity,
83
- opts: {
84
- button: 0 /* InputAction.IA_POINTER */,
85
- // We add this showFeedBack so the pointerEventSystem creates a PointerEvent component with our entity
86
- // This is needed for the renderer to know which entities are clickeables
87
- showFeedback: true
88
- }
89
- }, () => pointerEventCallback(instance.entity, pointerEvent));
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
- if (childIndex !== -1) {
182
- const childToModify = parentInstance._child[childIndex + 1];
183
- if (childToModify) {
184
- childToModify.rightOf = child.rightOf;
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
- upsertListener(instance, {
231
- type: 'add',
232
- props: props[keyTyped],
233
- component: keyTyped
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),
@@ -35,6 +35,14 @@ const entityComponent = {
35
35
  onMouseUp: undefined,
36
36
  onMouseEnter: undefined,
37
37
  onMouseLeave: undefined,
38
+ onMouseDrag: undefined,
39
+ onMouseDragLocked: undefined,
40
+ onMouseDragEnd: undefined,
41
+ onInputDown: undefined,
42
+ onInputUp: undefined,
43
+ onInputDrag: undefined,
44
+ onInputDragLocked: undefined,
45
+ onInputDragEnd: undefined,
38
46
  uiInput: undefined,
39
47
  uiDropdown: undefined
40
48
  };
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
@@ -7,10 +7,11 @@ import { getUiScaleFactor, resetUiScaleFactor, setUiScaleFactor } from './compon
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');
@@ -51,6 +52,10 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
51
52
  else {
52
53
  renderer.update(null);
53
54
  }
55
+ // Update texture renderers (deprecated, kept for backwards compatibility)
56
+ for (const [textureRenderer, ui] of textureRenderersAndUis) {
57
+ textureRenderer.update(React.createElement(ui));
58
+ }
54
59
  }
55
60
  function UiScaleSystem() {
56
61
  const activeVirtualSize = getActiveVirtualSize();
@@ -82,11 +87,19 @@ export function createReactBasedUiSystem(engine, pointerSystem) {
82
87
  for (const entity of renderer.getEntities()) {
83
88
  engine.removeEntity(entity);
84
89
  }
90
+ for (const [textureRenderer, _] of textureRenderersAndUis) {
91
+ for (const entity of textureRenderer.getEntities()) {
92
+ engine.removeEntity(entity);
93
+ }
94
+ }
85
95
  },
86
96
  setUiRenderer(ui, options) {
87
97
  uiComponent = ui;
88
98
  virtualSize = options;
89
99
  },
100
+ setTextureRenderer(entity, ui) {
101
+ textureRenderersAndUis.push([createReconciler(engine, pointerSystem, entity), ui]);
102
+ },
90
103
  addUiRenderer(entity, ui, options) {
91
104
  additionalRenderers.set(entity, { ui, options });
92
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.22.5-24854109737.commit-b6da827",
4
+ "version": "7.22.6-25007982108.commit-83012ab",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
7
7
  "dependencies": {
8
- "@dcl/ecs": "7.22.5-24854109737.commit-b6da827",
8
+ "@dcl/ecs": "7.22.6-25007982108.commit-83012ab",
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": "b6da827dbab8b88795119b50ab578d457e552d0c"
43
+ "commit": "83012ab4c851bdd7da6c424a20094149b7707019"
44
44
  }