cx 24.8.3 → 24.8.5

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,318 +1,317 @@
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
-
224
- if (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8)
225
- continue;
226
-
227
- if (visible) {
228
- this.timings.afterExplore = now();
229
-
230
- for (let i = 0; i < context.prepareList.length; i++) context.prepareList[i].prepare(context);
231
- this.timings.afterPrepare = now();
232
- }
233
- } while (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8);
234
-
235
- if (visible) {
236
- //walk in reverse order so children get rendered first
237
- let renderList = context.getRootRenderList();
238
- while (renderList) {
239
- for (let i = renderList.data.length - 1; i >= 0; i--) {
240
- renderList.data[i].render(context);
241
- }
242
- renderList = renderList.right;
243
- }
244
-
245
- this.content = getContent(instance.vdom);
246
- if (contentFactory) this.content = contentFactory({ children: this.content });
247
- this.timings.afterRender = now();
248
- for (let i = 0; i < context.cleanupList.length; i++) context.cleanupList[i].cleanup(context);
249
- } else {
250
- this.content = null;
251
- this.timings.afterExplore = this.timings.afterPrepare = this.timings.afterRender = now();
252
- }
253
- } finally {
254
- if (this.props.cultureInfo) popCulture(this.props.cultureInfo);
255
- }
256
-
257
- this.timings.beforeVDOMRender = now();
258
- this.props.flags.preparing = false;
259
- this.props.flags.rendering = true;
260
- this.renderingContext = context;
261
- }
262
-
263
- render() {
264
- return this.content;
265
- }
266
-
267
- componentDidMount() {
268
- this.componentDidUpdate();
269
- }
270
-
271
- componentDidUpdate() {
272
- this.props.flags.rendering = false;
273
- this.timings.afterVDOMRender = now();
274
-
275
- //let {instance} = this.props;
276
- //instance.cleanup(this.renderingContext);
277
-
278
- this.timings.afterCleanup = now();
279
- this.renderCount++;
280
-
281
- if (process.env.NODE_ENV !== "production") {
282
- let { start, beforeVDOMRender, afterVDOMRender, afterPrepare, afterExplore, afterRender, afterCleanup } =
283
- this.timings;
284
-
285
- Timing.log(
286
- vdomRenderFlag,
287
- this.renderCount,
288
- "cx",
289
- (beforeVDOMRender - start + afterCleanup - afterVDOMRender).toFixed(2) + "ms",
290
- "vdom",
291
- (afterVDOMRender - beforeVDOMRender).toFixed(2) + "ms",
292
- );
293
-
294
- Timing.log(
295
- appLoopFlag,
296
- this.renderCount,
297
- this.renderingContext.options.name || "main",
298
- "total",
299
- (afterCleanup - start).toFixed(1) + "ms",
300
- "explore",
301
- (afterExplore - start).toFixed(1) + "ms",
302
- "prepare",
303
- (afterPrepare - afterExplore).toFixed(1),
304
- "render",
305
- (afterRender - afterPrepare).toFixed(1),
306
- "vdom",
307
- (afterVDOMRender - beforeVDOMRender).toFixed(1),
308
- "cleanup",
309
- (afterCleanup - afterVDOMRender).toFixed(1),
310
- );
311
- }
312
- }
313
-
314
- componentWillUnmount() {
315
- let { instance } = this.props;
316
- if (instance.destroyTracked) instance.destroy();
317
- }
318
- }
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
+ }
222
+
223
+ if (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8)
224
+ continue;
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
+ } while (this.props.flags.dirty && ++count <= 3 && Widget.optimizePrepare && now() - this.timings.start < 8);
233
+
234
+ if (visible) {
235
+ //walk in reverse order so children get rendered first
236
+ let renderList = context.getRootRenderList();
237
+ while (renderList) {
238
+ for (let i = renderList.data.length - 1; i >= 0; i--) {
239
+ renderList.data[i].render(context);
240
+ }
241
+ renderList = renderList.right;
242
+ }
243
+
244
+ this.content = getContent(instance.vdom);
245
+ if (contentFactory) this.content = contentFactory({ children: this.content });
246
+ this.timings.afterRender = now();
247
+ for (let i = 0; i < context.cleanupList.length; i++) context.cleanupList[i].cleanup(context);
248
+ } else {
249
+ this.content = null;
250
+ this.timings.afterExplore = this.timings.afterPrepare = this.timings.afterRender = now();
251
+ }
252
+ } finally {
253
+ if (this.props.cultureInfo) popCulture(this.props.cultureInfo);
254
+ }
255
+
256
+ this.timings.beforeVDOMRender = now();
257
+ this.props.flags.preparing = false;
258
+ this.props.flags.rendering = true;
259
+ this.renderingContext = context;
260
+ }
261
+
262
+ render() {
263
+ return this.content;
264
+ }
265
+
266
+ componentDidMount() {
267
+ this.componentDidUpdate();
268
+ }
269
+
270
+ componentDidUpdate() {
271
+ this.props.flags.rendering = false;
272
+ this.timings.afterVDOMRender = now();
273
+
274
+ //let {instance} = this.props;
275
+ //instance.cleanup(this.renderingContext);
276
+
277
+ this.timings.afterCleanup = now();
278
+ this.renderCount++;
279
+
280
+ if (process.env.NODE_ENV !== "production") {
281
+ let { start, beforeVDOMRender, afterVDOMRender, afterPrepare, afterExplore, afterRender, afterCleanup } =
282
+ this.timings;
283
+
284
+ Timing.log(
285
+ vdomRenderFlag,
286
+ this.renderCount,
287
+ "cx",
288
+ (beforeVDOMRender - start + afterCleanup - afterVDOMRender).toFixed(2) + "ms",
289
+ "vdom",
290
+ (afterVDOMRender - beforeVDOMRender).toFixed(2) + "ms",
291
+ );
292
+
293
+ Timing.log(
294
+ appLoopFlag,
295
+ this.renderCount,
296
+ this.renderingContext.options.name || "main",
297
+ "total",
298
+ (afterCleanup - start).toFixed(1) + "ms",
299
+ "explore",
300
+ (afterExplore - start).toFixed(1) + "ms",
301
+ "prepare",
302
+ (afterPrepare - afterExplore).toFixed(1),
303
+ "render",
304
+ (afterRender - afterPrepare).toFixed(1),
305
+ "vdom",
306
+ (afterVDOMRender - beforeVDOMRender).toFixed(1),
307
+ "cleanup",
308
+ (afterCleanup - afterVDOMRender).toFixed(1),
309
+ );
310
+ }
311
+ }
312
+
313
+ componentWillUnmount() {
314
+ let { instance } = this.props;
315
+ if (instance.destroyTracked) instance.destroy();
316
+ }
317
+ }
@@ -279,7 +279,7 @@ function tooltipParentWillUnmount(parentInstance) {
279
279
 
280
280
  function tooltipParentDidUpdate(element, parentInstance, tooltip) {
281
281
  let instance = getTooltipInstance(element, parentInstance, tooltip);
282
- if (instance && instance.visible && instance.data.alwaysVisible && instance.tooltipComponent)
282
+ if (instance && instance.visible && instance.data?.alwaysVisible && instance.tooltipComponent)
283
283
  instance.widget.updateDropdownPosition(instance, instance.tooltipComponent);
284
284
  }
285
285