cx 24.3.9 → 24.3.10

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