@dcl/react-ecs 7.7.6 → 7.7.7-13655406288.commit-7b2a671

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/README.md CHANGED
@@ -1,131 +1 @@
1
- # @dcl/react-ecs
2
-
3
- React bindings for Decentraland's Entity Component System (ECS), providing a declarative way to build UIs in Decentraland scenes using React's component model and JSX syntax.
4
-
5
- ## Features
6
-
7
- - **Flexbox-based Layout**: Implements a subset of CSS Flexbox for powerful and intuitive UI layouts
8
- - **React Components**: Familiar React-like component API for building UIs
9
- - **Type Safety**: Full TypeScript support with proper type definitions
10
- - **Event Handling**: Support for mouse events and user interactions
11
- - **Performance**: Optimized reconciliation for minimal runtime overhead
12
- - **Theme Support**: Built-in light and dark theme system with context-based switching
13
-
14
- ## Component Guidelines
15
-
16
- All components in @dcl/react-ecs must:
17
-
18
- - Return JSX.Elements for consistency and composability
19
- - Support theme integration through context
20
- - Be reusable and composable with other components
21
-
22
- ## Installation
23
-
24
- ```bash
25
- npm install @dcl/react-ecs
26
- ```
27
-
28
- ## Basic Usage
29
-
30
- ```tsx
31
- import { ReactEcs, UiEntity, Label, Button } from '@dcl/react-ecs'
32
-
33
- export function MyUI() {
34
- return (
35
- <UiEntity
36
- uiTransform={{
37
- width: 300,
38
- height: 100,
39
- display: 'flex',
40
- justifyContent: 'center',
41
- alignItems: 'center'
42
- }}
43
- >
44
- <Label value="Hello Decentraland!" />
45
- <Button value="Click Me!" onMouseDown={() => console.log('clicked!')} />
46
- </UiEntity>
47
- )
48
- }
49
- ```
50
-
51
- ## Theme System
52
-
53
- The package includes a theme system for consistent UI styling:
54
-
55
- ```tsx
56
- import { ReactEcs, ThemeProvider, Button } from '@dcl/react-ecs'
57
-
58
- // Wrap your UI with ThemeProvider
59
- export function MyUI() {
60
- return (
61
- <ThemeProvider>
62
- <Button value="Theme-Aware Button" />
63
- </ThemeProvider>
64
- )
65
- }
66
-
67
- // Use themes in custom components
68
- function CustomComponent() {
69
- const { theme, toggleTheme } = React.useContext(ThemeContext)
70
-
71
- return (
72
- <UiEntity
73
- uiBackground={{
74
- color: theme === 'light' ? '#FFFFFF' : '#000000'
75
- }}
76
- onMouseDown={toggleTheme}
77
- />
78
- )
79
- }
80
- ```
81
-
82
- ## Components
83
-
84
- ### Core Components
85
-
86
- - **UiEntity**: Base component for UI elements
87
- - **Label**: Text display component
88
- - **Button**: Interactive button component
89
- - **Input**: Text input field
90
- - **Dropdown**: Selection dropdown menu
91
-
92
- ### Layout System
93
-
94
- The layout system is based on Flexbox and supports the following properties:
95
-
96
- - `display: 'flex'`
97
- - `flexDirection: 'row' | 'column'`
98
- - `justifyContent: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around'`
99
- - `alignItems: 'flex-start' | 'center' | 'flex-end' | 'stretch'`
100
- - `width`, `height`
101
- - `margin`, `padding`
102
- - `positionType: 'absolute' | 'relative'`
103
-
104
- ## Event Handling
105
-
106
- Components support the following mouse events:
107
-
108
- ```tsx
109
- <Button onMouseDown={() => {}} onMouseUp={() => {}} onMouseEnter={() => {}} onMouseLeave={() => {}} />
110
- ```
111
-
112
- ## Technical Details
113
-
114
- The package implements a custom React reconciler that bridges React's component model with Decentraland's ECS. It:
115
-
116
- 1. Translates JSX into ECS entities and components
117
- 2. Manages component lifecycle and updates
118
- 3. Handles event delegation and bubbling
119
- 4. Provides a performant update mechanism
120
-
121
- ## Related Documentation
122
-
123
- For more details about the UI system architecture and design decisions:
124
-
125
- - [ADR-124: Implementing Flexbox-based UI](https://adr.decentraland.org/adr/ADR-124)
126
- - [ADR-125: User Interface Components](https://adr.decentraland.org/adr/ADR-125)
127
- - [ADR-237: SDK 7 Custom UI Components](https://adr.decentraland.org/adr/ADR-237)
128
-
129
- ## License
130
-
131
- Apache 2.0
1
+ # React ECS
@@ -124,7 +124,6 @@ export function createReconciler(engine, pointerEvents) {
124
124
  }
125
125
  function removeChildEntity(instance) {
126
126
  changeEvents.delete(instance.entity);
127
- clickEvents.delete(instance.entity);
128
127
  engine.removeEntity(instance.entity);
129
128
  for (const child of instance._child) {
130
129
  removeChildEntity(child);
@@ -171,23 +170,13 @@ export function createReconciler(engine, pointerEvents) {
171
170
  removeChildEntity(child);
172
171
  }
173
172
  function updateOnChange(entity, componentId, state) {
174
- const hasEvent = changeEvents.has(entity);
175
173
  const event = changeEvents.get(entity) || changeEvents.set(entity, new Map()).get(entity);
174
+ const oldState = event.get(componentId);
176
175
  const onChangeCallback = state?.onChangeCallback;
177
176
  const onSubmitCallback = state?.onSubmitCallback;
178
- event.set(componentId, { onChangeCallback, onSubmitCallback });
179
- // Create onChange callback if its the first callback event for this entity
180
- if (!hasEvent) {
181
- const resultComponentId = componentId === UiDropdown.componentId ? UiDropdownResult.componentId : UiInputResult.componentId;
182
- engine.getComponent(resultComponentId).onChange(entity, (value) => {
183
- if (value?.isSubmit) {
184
- const onSubmit = changeEvents.get(entity)?.get(componentId)?.onSubmitCallback;
185
- onSubmit && onSubmit(value?.value);
186
- }
187
- const onChange = changeEvents.get(entity)?.get(componentId)?.onChangeCallback;
188
- onChange && onChange(value?.value);
189
- });
190
- }
177
+ const value = state?.value ?? oldState?.value;
178
+ const isSubmit = state?.isSubmit ?? oldState?.isSubmit;
179
+ event.set(componentId, { onChangeCallback, onSubmitCallback, value, isSubmit });
191
180
  }
192
181
  const hostConfig = {
193
182
  ...noopConfig,
@@ -262,8 +251,32 @@ export function createReconciler(engine, pointerEvents) {
262
251
  const root = reconciler.createContainer({}, 0, null, false, null, '',
263
252
  /* istanbul ignore next */
264
253
  function () { }, null);
254
+ // Maybe this could be something similar to Input system, but since we
255
+ // are going to use this only here, i prefer to scope it here.
256
+ function handleOnChange(componentId, resultComponent) {
257
+ for (const [entity, Result] of engine.getEntitiesWith(resultComponent)) {
258
+ const entityState = changeEvents.get(entity)?.get(componentId);
259
+ const isSubmit = !!Result.isSubmit;
260
+ if (entityState?.onChangeCallback && Result.value !== entityState.value) {
261
+ entityState.onChangeCallback(Result.value);
262
+ }
263
+ if (entityState?.onSubmitCallback && isSubmit && !entityState.isSubmit) {
264
+ entityState.onSubmitCallback(Result.value);
265
+ }
266
+ updateOnChange(entity, componentId, {
267
+ onChangeCallback: entityState?.onChangeCallback,
268
+ onSubmitCallback: entityState?.onSubmitCallback,
269
+ value: Result.value,
270
+ isSubmit
271
+ });
272
+ }
273
+ }
265
274
  return {
266
275
  update: function (component) {
276
+ if (changeEvents.size) {
277
+ handleOnChange(UiInput.componentId, UiInputResult);
278
+ handleOnChange(UiDropdown.componentId, UiDropdownResult);
279
+ }
267
280
  return reconciler.updateContainer(component, root, null);
268
281
  },
269
282
  getEntities: () => Array.from(entities)
package/dist/system.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { IEngine, PointerEventsSystem } from '@dcl/ecs';
2
1
  import type { ReactEcs } from './react-ecs';
3
2
  /**
4
3
  * @public
@@ -11,7 +10,3 @@ export interface ReactBasedUiSystem {
11
10
  destroy(): void;
12
11
  setUiRenderer(ui: UiComponent): void;
13
12
  }
14
- /**
15
- * @public
16
- */
17
- export declare function createReactBasedUiSystem(engine: IEngine, pointerSystem: PointerEventsSystem): ReactBasedUiSystem;
package/dist/system.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createReconciler } from './reconciler';
2
2
  /**
3
- * @public
3
+ * @internal
4
4
  */
5
5
  export function createReactBasedUiSystem(engine, pointerSystem) {
6
6
  const renderer = createReconciler(engine, pointerSystem);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dcl/react-ecs",
3
3
  "description": "Decentraland ECS",
4
- "version": "7.7.6",
4
+ "version": "7.7.7-13655406288.commit-7b2a671",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues",
7
7
  "dependencies": {
8
- "@dcl/ecs": "7.7.6",
8
+ "@dcl/ecs": "7.7.7-13655406288.commit-7b2a671",
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": "0f0d261556e5c6d495156f6a6e03233997a07a5e"
43
+ "commit": "7b2a6716919850a45b2f84abc9d05a1500ad6af3"
44
44
  }