cx 24.6.4 → 24.7.2

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/src/ui/Cx.js CHANGED
@@ -1,313 +1,313 @@
1
- import { Widget, VDOM, getContent } from "./Widget";
2
- import { Instance } from "./Instance";
3
- import { RenderingContext } from "./RenderingContext";
4
- import { debug, appDataFlag } from "../util/Debug";
5
- import { Timing, now, appLoopFlag, vdomRenderFlag } from "../util/Timing";
6
- import { isBatchingUpdates, notifyBatchedUpdateStarting, notifyBatchedUpdateCompleted } from "./batchUpdates";
7
- import { shallowEquals } from "../util/shallowEquals";
8
- import { PureContainer } from "./PureContainer";
9
- import { onIdleCallback } from "../util/onIdleCallback";
10
- import { getCurrentCulture, pushCulture, popCulture } from "./Culture";
11
-
12
- export class Cx extends VDOM.Component {
13
- constructor(props) {
14
- super(props);
15
-
16
- if (props.instance) {
17
- this.widget = props.instance.widget;
18
- this.store = props.instance.store;
19
- } else {
20
- this.widget = PureContainer.create({ items: props.widget || props.items });
21
-
22
- if (props.parentInstance) {
23
- this.parentInstance = props.parentInstance;
24
- this.store = props.store || this.parentInstance.store;
25
- } else {
26
- this.parentInstance = new Instance(this.widget, 0);
27
- this.store = props.store;
28
- }
29
-
30
- if (!this.store) throw new Error("Cx component requires store.");
31
- }
32
-
33
- this.state = {
34
- deferToken: 0,
35
- };
36
-
37
- if (props.subscribe) {
38
- this.unsubscribe = this.store.subscribe(this.update.bind(this));
39
- this.state.data = this.store.getData();
40
- }
41
-
42
- this.flags = {};
43
- this.renderCount = 0;
44
-
45
- if (props.onError) this.componentDidCatch = this.componentDidCatchHandler.bind(this);
46
-
47
- this.forceUpdateCallback = this.forceUpdate.bind(this);
48
-
49
- this.deferCounter = 0;
50
- this.waitForIdle();
51
- }
52
-
53
- UNSAFE_componentWillReceiveProps(props) {
54
- let newStore = props.instance ? props.instance.store : props.store ? props.store : props.parentInstance.store;
55
-
56
- if (newStore != this.store) {
57
- this.store = newStore;
58
- if (this.unsubscribe) this.unsubscribe();
59
- if (props.subscribe) this.unsubscribe = this.store.subscribe(this.update.bind(this));
60
- }
61
-
62
- if (props.subscribe) {
63
- let data = this.store.getData();
64
- if (data !== this.state.data) {
65
- this.waitForIdle();
66
- this.setState({ data });
67
- }
68
- }
69
- }
70
-
71
- getInstance() {
72
- if (this.props.instance) return this.props.instance;
73
-
74
- if (this.instance && this.instance.widget === this.widget) {
75
- if (this.instance.store != this.store) this.instance.setStore(this.store);
76
- return this.instance;
77
- }
78
-
79
- if (this.widget && this.parentInstance)
80
- return (this.instance = this.parentInstance.getDetachedChild(this.widget, 0, this.store));
81
-
82
- throw new Error("Could not resolve a widget instance in the Cx component.");
83
- }
84
-
85
- render() {
86
- if (this.props.deferredUntilIdle && this.state.deferToken < this.deferCounter) return null;
87
-
88
- let cultureInfo = this.props.cultureInfo ?? getCurrentCulture();
89
-
90
- return (
91
- <CxContext
92
- instance={this.getInstance()}
93
- flags={this.flags}
94
- options={this.props.options}
95
- buster={++this.renderCount}
96
- contentFactory={this.props.contentFactory}
97
- forceUpdate={this.forceUpdateCallback}
98
- cultureInfo={cultureInfo}
99
- />
100
- );
101
- }
102
-
103
- componentDidMount() {
104
- this.componentDidUpdate();
105
-
106
- if (this.props.options && this.props.options.onPipeUpdate)
107
- this.props.options.onPipeUpdate(this.update.bind(this));
108
- }
109
-
110
- componentDidUpdate() {
111
- if (this.flags.dirty) {
112
- this.update();
113
- }
114
- }
115
-
116
- update() {
117
- let data = this.store.getData();
118
- debug(appDataFlag, data);
119
- if (this.flags.preparing) this.flags.dirty = true;
120
- else if (isBatchingUpdates() || this.props.immediate) {
121
- notifyBatchedUpdateStarting();
122
- this.setState({ data: data }, notifyBatchedUpdateCompleted);
123
- } else {
124
- //in standard mode sequential store commands are batched
125
- if (!this.pendingUpdateTimer) {
126
- notifyBatchedUpdateStarting();
127
- this.pendingUpdateTimer = setTimeout(() => {
128
- delete this.pendingUpdateTimer;
129
- this.setState({ data: data }, notifyBatchedUpdateCompleted);
130
- }, 0);
131
- }
132
- }
133
- }
134
-
135
- waitForIdle() {
136
- if (!this.props.deferredUntilIdle) return;
137
-
138
- if (this.unsubscribeIdleRequest) this.unsubscribeIdleRequest();
139
-
140
- let token = ++this.deferCounter;
141
- this.unsubscribeIdleRequest = onIdleCallback(
142
- () => {
143
- this.setState({ deferToken: token });
144
- },
145
- {
146
- timeout: this.props.idleTimeout || 30000,
147
- },
148
- );
149
- }
150
-
151
- componentWillUnmount() {
152
- if (this.pendingUpdateTimer) clearTimeout(this.pendingUpdateTimer);
153
- if (this.unsubscribeIdleRequest) this.unsubscribeIdleRequest();
154
- if (this.unsubscribe) this.unsubscribe();
155
- if (this.props.options && this.props.options.onPipeUpdate) this.props.options.onPipeUpdate(null);
156
- }
157
-
158
- shouldComponentUpdate(props, state) {
159
- if (props.deferredUntilIdle && state.deferToken != this.deferCounter) return false;
160
-
161
- return (
162
- state !== this.state ||
163
- !props.params ||
164
- !shallowEquals(props.params, this.props.params) ||
165
- props.instance !== this.props.instance ||
166
- props.widget !== this.props.widget ||
167
- props.store !== this.props.store ||
168
- props.parentInstance !== this.props.parentInstance ||
169
- props.cultureInfo !== this.props.cultureInfo
170
- );
171
- }
172
-
173
- componentDidCatchHandler(error, info) {
174
- this.flags.preparing = false;
175
- this.props.onError(error, this.getInstance(), info);
176
- }
177
- }
178
-
179
- class CxContext extends VDOM.Component {
180
- constructor(props) {
181
- super(props);
182
- this.renderCount = 0;
183
- this.UNSAFE_componentWillReceiveProps(props);
184
- }
185
-
186
- UNSAFE_componentWillReceiveProps(props) {
187
- this.timings = {
188
- start: now(),
189
- };
190
-
191
- let { instance, options, contentFactory } = props;
192
- let count = 0,
193
- visible,
194
- context;
195
-
196
- //should not be tracked by parents for destroy
197
- if (!instance.detached)
198
- throw new Error("The instance passed to a Cx component should be detached from its parent.");
199
-
200
- if (this.props.instance !== instance && this.props.instance.destroyTracked) this.props.instance.destroy();
201
-
202
- this.props.flags.preparing = true;
203
-
204
- if (this.props.cultureInfo) pushCulture(this.props.cultureInfo);
205
-
206
- try {
207
- do {
208
- context = new RenderingContext(options);
209
- context.forceUpdate = this.props.forceUpdate;
210
- this.props.flags.dirty = false;
211
- instance.assignedRenderList = context.getRootRenderList();
212
- visible = instance.scheduleExploreIfVisible(context);
213
- if (visible) {
214
- while (!context.exploreStack.empty()) {
215
- let inst = context.exploreStack.pop();
216
- //console.log("EXPLORE", inst.widget.constructor.name, inst.widget.tag, inst.widget.widgetId);
217
- inst.explore(context);
218
- }
219
- } else if (instance.destroyTracked) {
220
- instance.destroy();
221
- break;
222
- }
223
- } while (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8);
224
-
225
- if (visible) {
226
- this.timings.afterExplore = now();
227
-
228
- for (let i = 0; i < context.prepareList.length; i++) context.prepareList[i].prepare(context);
229
- this.timings.afterPrepare = now();
230
-
231
- //walk in reverse order so children get rendered first
232
- let renderList = context.getRootRenderList();
233
- while (renderList) {
234
- for (let i = renderList.data.length - 1; i >= 0; i--) {
235
- renderList.data[i].render(context);
236
- }
237
- renderList = renderList.right;
238
- }
239
-
240
- this.content = getContent(instance.vdom);
241
- if (contentFactory) this.content = contentFactory({ children: this.content });
242
- this.timings.afterRender = now();
243
- for (let i = 0; i < context.cleanupList.length; i++) context.cleanupList[i].cleanup(context);
244
- } else {
245
- this.content = null;
246
- this.timings.afterExplore = this.timings.afterPrepare = this.timings.afterRender = now();
247
- }
248
- } finally {
249
- if (this.props.cultureInfo) popCulture(this.props.cultureInfo);
250
- }
251
-
252
- this.timings.beforeVDOMRender = now();
253
- this.props.flags.preparing = false;
254
- this.props.flags.rendering = true;
255
- this.renderingContext = context;
256
- }
257
-
258
- render() {
259
- return this.content;
260
- }
261
-
262
- componentDidMount() {
263
- this.componentDidUpdate();
264
- }
265
-
266
- componentDidUpdate() {
267
- this.props.flags.rendering = false;
268
- this.timings.afterVDOMRender = now();
269
-
270
- //let {instance} = this.props;
271
- //instance.cleanup(this.renderingContext);
272
-
273
- this.timings.afterCleanup = now();
274
- this.renderCount++;
275
-
276
- if (process.env.NODE_ENV !== "production") {
277
- let { start, beforeVDOMRender, afterVDOMRender, afterPrepare, afterExplore, afterRender, afterCleanup } =
278
- this.timings;
279
-
280
- Timing.log(
281
- vdomRenderFlag,
282
- this.renderCount,
283
- "cx",
284
- (beforeVDOMRender - start + afterCleanup - afterVDOMRender).toFixed(2) + "ms",
285
- "vdom",
286
- (afterVDOMRender - beforeVDOMRender).toFixed(2) + "ms",
287
- );
288
-
289
- Timing.log(
290
- appLoopFlag,
291
- this.renderCount,
292
- this.renderingContext.options.name || "main",
293
- "total",
294
- (afterCleanup - start).toFixed(1) + "ms",
295
- "explore",
296
- (afterExplore - start).toFixed(1) + "ms",
297
- "prepare",
298
- (afterPrepare - afterExplore).toFixed(1),
299
- "render",
300
- (afterRender - afterPrepare).toFixed(1),
301
- "vdom",
302
- (afterVDOMRender - beforeVDOMRender).toFixed(1),
303
- "cleanup",
304
- (afterCleanup - afterVDOMRender).toFixed(1),
305
- );
306
- }
307
- }
308
-
309
- componentWillUnmount() {
310
- let { instance } = this.props;
311
- if (instance.destroyTracked) instance.destroy();
312
- }
313
- }
1
+ import { Widget, VDOM, getContent } from "./Widget";
2
+ import { Instance } from "./Instance";
3
+ import { RenderingContext } from "./RenderingContext";
4
+ import { debug, appDataFlag } from "../util/Debug";
5
+ import { Timing, now, appLoopFlag, vdomRenderFlag } from "../util/Timing";
6
+ import { isBatchingUpdates, notifyBatchedUpdateStarting, notifyBatchedUpdateCompleted } from "./batchUpdates";
7
+ import { shallowEquals } from "../util/shallowEquals";
8
+ import { PureContainer } from "./PureContainer";
9
+ import { onIdleCallback } from "../util/onIdleCallback";
10
+ import { getCurrentCulture, pushCulture, popCulture } from "./Culture";
11
+
12
+ export class Cx extends VDOM.Component {
13
+ constructor(props) {
14
+ super(props);
15
+
16
+ if (props.instance) {
17
+ this.widget = props.instance.widget;
18
+ this.store = props.instance.store;
19
+ } else {
20
+ this.widget = PureContainer.create({ items: props.widget || props.items });
21
+
22
+ if (props.parentInstance) {
23
+ this.parentInstance = props.parentInstance;
24
+ this.store = props.store || this.parentInstance.store;
25
+ } else {
26
+ this.parentInstance = new Instance(this.widget, 0);
27
+ this.store = props.store;
28
+ }
29
+
30
+ if (!this.store) throw new Error("Cx component requires store.");
31
+ }
32
+
33
+ this.state = {
34
+ deferToken: 0,
35
+ };
36
+
37
+ if (props.subscribe) {
38
+ this.unsubscribe = this.store.subscribe(this.update.bind(this));
39
+ this.state.data = this.store.getData();
40
+ }
41
+
42
+ this.flags = {};
43
+ this.renderCount = 0;
44
+
45
+ if (props.onError) this.componentDidCatch = this.componentDidCatchHandler.bind(this);
46
+
47
+ this.forceUpdateCallback = this.forceUpdate.bind(this);
48
+
49
+ this.deferCounter = 0;
50
+ this.waitForIdle();
51
+ }
52
+
53
+ UNSAFE_componentWillReceiveProps(props) {
54
+ let newStore = props.instance ? props.instance.store : props.store ? props.store : props.parentInstance.store;
55
+
56
+ if (newStore != this.store) {
57
+ this.store = newStore;
58
+ if (this.unsubscribe) this.unsubscribe();
59
+ if (props.subscribe) this.unsubscribe = this.store.subscribe(this.update.bind(this));
60
+ }
61
+
62
+ if (props.subscribe) {
63
+ let data = this.store.getData();
64
+ if (data !== this.state.data) {
65
+ this.waitForIdle();
66
+ this.setState({ data });
67
+ }
68
+ }
69
+ }
70
+
71
+ getInstance() {
72
+ if (this.props.instance) return this.props.instance;
73
+
74
+ if (this.instance && this.instance.widget === this.widget) {
75
+ if (this.instance.store != this.store) this.instance.setStore(this.store);
76
+ return this.instance;
77
+ }
78
+
79
+ if (this.widget && this.parentInstance)
80
+ return (this.instance = this.parentInstance.getDetachedChild(this.widget, 0, this.store));
81
+
82
+ throw new Error("Could not resolve a widget instance in the Cx component.");
83
+ }
84
+
85
+ render() {
86
+ if (this.props.deferredUntilIdle && this.state.deferToken < this.deferCounter) return null;
87
+
88
+ let cultureInfo = this.props.cultureInfo ?? getCurrentCulture();
89
+
90
+ return (
91
+ <CxContext
92
+ instance={this.getInstance()}
93
+ flags={this.flags}
94
+ options={this.props.options}
95
+ buster={++this.renderCount}
96
+ contentFactory={this.props.contentFactory}
97
+ forceUpdate={this.forceUpdateCallback}
98
+ cultureInfo={cultureInfo}
99
+ />
100
+ );
101
+ }
102
+
103
+ componentDidMount() {
104
+ this.componentDidUpdate();
105
+
106
+ if (this.props.options && this.props.options.onPipeUpdate)
107
+ this.props.options.onPipeUpdate(this.update.bind(this));
108
+ }
109
+
110
+ componentDidUpdate() {
111
+ if (this.flags.dirty) {
112
+ this.update();
113
+ }
114
+ }
115
+
116
+ update() {
117
+ let data = this.store.getData();
118
+ debug(appDataFlag, data);
119
+ if (this.flags.preparing) this.flags.dirty = true;
120
+ else if (isBatchingUpdates() || this.props.immediate) {
121
+ notifyBatchedUpdateStarting();
122
+ this.setState({ data: data }, notifyBatchedUpdateCompleted);
123
+ } else {
124
+ //in standard mode sequential store commands are batched
125
+ if (!this.pendingUpdateTimer) {
126
+ notifyBatchedUpdateStarting();
127
+ this.pendingUpdateTimer = setTimeout(() => {
128
+ delete this.pendingUpdateTimer;
129
+ this.setState({ data: data }, notifyBatchedUpdateCompleted);
130
+ }, 0);
131
+ }
132
+ }
133
+ }
134
+
135
+ waitForIdle() {
136
+ if (!this.props.deferredUntilIdle) return;
137
+
138
+ if (this.unsubscribeIdleRequest) this.unsubscribeIdleRequest();
139
+
140
+ let token = ++this.deferCounter;
141
+ this.unsubscribeIdleRequest = onIdleCallback(
142
+ () => {
143
+ this.setState({ deferToken: token });
144
+ },
145
+ {
146
+ timeout: this.props.idleTimeout || 30000,
147
+ },
148
+ );
149
+ }
150
+
151
+ componentWillUnmount() {
152
+ if (this.pendingUpdateTimer) clearTimeout(this.pendingUpdateTimer);
153
+ if (this.unsubscribeIdleRequest) this.unsubscribeIdleRequest();
154
+ if (this.unsubscribe) this.unsubscribe();
155
+ if (this.props.options && this.props.options.onPipeUpdate) this.props.options.onPipeUpdate(null);
156
+ }
157
+
158
+ shouldComponentUpdate(props, state) {
159
+ if (props.deferredUntilIdle && state.deferToken != this.deferCounter) return false;
160
+
161
+ return (
162
+ state !== this.state ||
163
+ !props.params ||
164
+ !shallowEquals(props.params, this.props.params) ||
165
+ props.instance !== this.props.instance ||
166
+ props.widget !== this.props.widget ||
167
+ props.store !== this.props.store ||
168
+ props.parentInstance !== this.props.parentInstance ||
169
+ props.cultureInfo !== this.props.cultureInfo
170
+ );
171
+ }
172
+
173
+ componentDidCatchHandler(error, info) {
174
+ this.flags.preparing = false;
175
+ this.props.onError(error, this.getInstance(), info);
176
+ }
177
+ }
178
+
179
+ class CxContext extends VDOM.Component {
180
+ constructor(props) {
181
+ super(props);
182
+ this.renderCount = 0;
183
+ this.UNSAFE_componentWillReceiveProps(props);
184
+ }
185
+
186
+ UNSAFE_componentWillReceiveProps(props) {
187
+ this.timings = {
188
+ start: now(),
189
+ };
190
+
191
+ let { instance, options, contentFactory } = props;
192
+ let count = 0,
193
+ visible,
194
+ context;
195
+
196
+ //should not be tracked by parents for destroy
197
+ if (!instance.detached)
198
+ throw new Error("The instance passed to a Cx component should be detached from its parent.");
199
+
200
+ if (this.props.instance !== instance && this.props.instance.destroyTracked) this.props.instance.destroy();
201
+
202
+ this.props.flags.preparing = true;
203
+
204
+ if (this.props.cultureInfo) pushCulture(this.props.cultureInfo);
205
+
206
+ try {
207
+ do {
208
+ context = new RenderingContext(options);
209
+ context.forceUpdate = this.props.forceUpdate;
210
+ this.props.flags.dirty = false;
211
+ instance.assignedRenderList = context.getRootRenderList();
212
+ visible = instance.scheduleExploreIfVisible(context);
213
+ if (visible) {
214
+ while (!context.exploreStack.empty()) {
215
+ let inst = context.exploreStack.pop();
216
+ //console.log("EXPLORE", inst.widget.constructor.name, inst.widget.tag, inst.widget.widgetId);
217
+ inst.explore(context);
218
+ }
219
+ } else if (instance.destroyTracked) {
220
+ instance.destroy();
221
+ break;
222
+ }
223
+ } while (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8);
224
+
225
+ if (visible) {
226
+ this.timings.afterExplore = now();
227
+
228
+ for (let i = 0; i < context.prepareList.length; i++) context.prepareList[i].prepare(context);
229
+ this.timings.afterPrepare = now();
230
+
231
+ //walk in reverse order so children get rendered first
232
+ let renderList = context.getRootRenderList();
233
+ while (renderList) {
234
+ for (let i = renderList.data.length - 1; i >= 0; i--) {
235
+ renderList.data[i].render(context);
236
+ }
237
+ renderList = renderList.right;
238
+ }
239
+
240
+ this.content = getContent(instance.vdom);
241
+ if (contentFactory) this.content = contentFactory({ children: this.content });
242
+ this.timings.afterRender = now();
243
+ for (let i = 0; i < context.cleanupList.length; i++) context.cleanupList[i].cleanup(context);
244
+ } else {
245
+ this.content = null;
246
+ this.timings.afterExplore = this.timings.afterPrepare = this.timings.afterRender = now();
247
+ }
248
+ } finally {
249
+ if (this.props.cultureInfo) popCulture(this.props.cultureInfo);
250
+ }
251
+
252
+ this.timings.beforeVDOMRender = now();
253
+ this.props.flags.preparing = false;
254
+ this.props.flags.rendering = true;
255
+ this.renderingContext = context;
256
+ }
257
+
258
+ render() {
259
+ return this.content;
260
+ }
261
+
262
+ componentDidMount() {
263
+ this.componentDidUpdate();
264
+ }
265
+
266
+ componentDidUpdate() {
267
+ this.props.flags.rendering = false;
268
+ this.timings.afterVDOMRender = now();
269
+
270
+ //let {instance} = this.props;
271
+ //instance.cleanup(this.renderingContext);
272
+
273
+ this.timings.afterCleanup = now();
274
+ this.renderCount++;
275
+
276
+ if (process.env.NODE_ENV !== "production") {
277
+ let { start, beforeVDOMRender, afterVDOMRender, afterPrepare, afterExplore, afterRender, afterCleanup } =
278
+ this.timings;
279
+
280
+ Timing.log(
281
+ vdomRenderFlag,
282
+ this.renderCount,
283
+ "cx",
284
+ (beforeVDOMRender - start + afterCleanup - afterVDOMRender).toFixed(2) + "ms",
285
+ "vdom",
286
+ (afterVDOMRender - beforeVDOMRender).toFixed(2) + "ms",
287
+ );
288
+
289
+ Timing.log(
290
+ appLoopFlag,
291
+ this.renderCount,
292
+ this.renderingContext.options.name || "main",
293
+ "total",
294
+ (afterCleanup - start).toFixed(1) + "ms",
295
+ "explore",
296
+ (afterExplore - start).toFixed(1) + "ms",
297
+ "prepare",
298
+ (afterPrepare - afterExplore).toFixed(1),
299
+ "render",
300
+ (afterRender - afterPrepare).toFixed(1),
301
+ "vdom",
302
+ (afterVDOMRender - beforeVDOMRender).toFixed(1),
303
+ "cleanup",
304
+ (afterCleanup - afterVDOMRender).toFixed(1),
305
+ );
306
+ }
307
+ }
308
+
309
+ componentWillUnmount() {
310
+ let { instance } = this.props;
311
+ if (instance.destroyTracked) instance.destroy();
312
+ }
313
+ }
package/src/ui/VDOM.d.ts CHANGED
@@ -1,4 +1,12 @@
1
+ import { HtmlElement, react } from "cx/widgets";
2
+ import { ReactElement } from "react";
1
3
  export interface VDOM {
2
4
  createElement(type, props, ...children);
3
5
  allowRenderOutputCaching?: boolean;
4
- }
6
+
7
+ DOM: {
8
+ render(reactElement: ReactElement, container: HtmlElement): void;
9
+ unmountComponentAtNode(container: HtmlElement): void;
10
+ createRoot?(container: HtmlElement): any;
11
+ };
12
+ }