cx 26.1.13 → 26.2.1

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.
Files changed (130) hide show
  1. package/build/charts/Marker.d.ts +1 -1
  2. package/build/charts/Marker.d.ts.map +1 -1
  3. package/build/charts/Marker.js +16 -6
  4. package/build/charts/MouseTracker.d.ts +2 -0
  5. package/build/charts/MouseTracker.d.ts.map +1 -1
  6. package/build/charts/helpers/PointReducer.d.ts +2 -2
  7. package/build/charts/helpers/PointReducer.d.ts.map +1 -1
  8. package/build/data/View.d.ts +5 -3
  9. package/build/data/View.d.ts.map +1 -1
  10. package/build/data/View.js +3 -1
  11. package/build/data/ops/findTreeNode.d.ts +20 -1
  12. package/build/data/ops/findTreeNode.d.ts.map +1 -1
  13. package/build/data/ops/findTreeNode.js +19 -0
  14. package/build/data/ops/findTreePath.d.ts +1 -1
  15. package/build/data/ops/findTreePath.d.ts.map +1 -1
  16. package/build/data/ops/findTreePath.js +1 -1
  17. package/build/data/ops/removeTreeNodes.d.ts +14 -1
  18. package/build/data/ops/removeTreeNodes.d.ts.map +1 -1
  19. package/build/data/ops/removeTreeNodes.js +13 -0
  20. package/build/data/ops/updateArray.d.ts +1 -1
  21. package/build/data/ops/updateArray.d.ts.map +1 -1
  22. package/build/data/ops/updateArray.js +1 -1
  23. package/build/data/ops/updateTree.d.ts +20 -1
  24. package/build/data/ops/updateTree.d.ts.map +1 -1
  25. package/build/data/ops/updateTree.js +19 -0
  26. package/build/jsx-runtime.d.ts +1 -0
  27. package/build/jsx-runtime.d.ts.map +1 -1
  28. package/build/jsx-runtime.js +3 -1
  29. package/build/svg/Rectangle.d.ts +6 -4
  30. package/build/svg/Rectangle.d.ts.map +1 -1
  31. package/build/svg/Rectangle.js +9 -7
  32. package/build/ui/Instance.d.ts +1 -1
  33. package/build/ui/Instance.d.ts.map +1 -1
  34. package/build/ui/Instance.js +18 -8
  35. package/build/ui/IsolatedScope.d.ts +2 -1
  36. package/build/ui/IsolatedScope.d.ts.map +1 -1
  37. package/build/ui/Prop.d.ts +1 -1
  38. package/build/ui/Prop.d.ts.map +1 -1
  39. package/build/ui/Widget.d.ts +2 -0
  40. package/build/ui/Widget.d.ts.map +1 -1
  41. package/build/ui/Widget.js +4 -0
  42. package/build/ui/adapter/GroupAdapter.d.ts +4 -4
  43. package/build/ui/adapter/GroupAdapter.d.ts.map +1 -1
  44. package/build/ui/adapter/GroupAdapter.js +4 -4
  45. package/build/ui/adapter/TreeAdapter.d.ts +5 -3
  46. package/build/ui/adapter/TreeAdapter.d.ts.map +1 -1
  47. package/build/ui/adapter/TreeAdapter.js +12 -5
  48. package/build/ui/app/startAppLoop.d.ts +2 -2
  49. package/build/ui/app/startAppLoop.d.ts.map +1 -1
  50. package/build/ui/app/startHotAppLoop.d.ts +4 -4
  51. package/build/ui/app/startHotAppLoop.d.ts.map +1 -1
  52. package/build/ui/app/startHotAppLoop.js +1 -1
  53. package/build/ui/batchUpdates.d.ts.map +1 -1
  54. package/build/ui/batchUpdates.js +3 -4
  55. package/build/widgets/Button.d.ts +0 -7
  56. package/build/widgets/Button.d.ts.map +1 -1
  57. package/build/widgets/HtmlElement.d.ts +2 -2
  58. package/build/widgets/HtmlElement.d.ts.map +1 -1
  59. package/build/widgets/form/Checkbox.d.ts +3 -3
  60. package/build/widgets/form/Checkbox.d.ts.map +1 -1
  61. package/build/widgets/form/Checkbox.js +11 -6
  62. package/build/widgets/form/DateTimeField.d.ts +4 -0
  63. package/build/widgets/form/DateTimeField.d.ts.map +1 -1
  64. package/build/widgets/form/LookupField.d.ts +14 -27
  65. package/build/widgets/form/LookupField.d.ts.map +1 -1
  66. package/build/widgets/form/TextField.d.ts +2 -2
  67. package/build/widgets/form/TextField.d.ts.map +1 -1
  68. package/build/widgets/grid/Grid.d.ts +20 -16
  69. package/build/widgets/grid/Grid.d.ts.map +1 -1
  70. package/build/widgets/grid/Grid.js +200 -86
  71. package/build/widgets/nav/Menu.d.ts +2 -0
  72. package/build/widgets/nav/Menu.d.ts.map +1 -1
  73. package/build/widgets/nav/Route.js +1 -1
  74. package/build/widgets/overlay/FlyweightTooltipTracker.d.ts +6 -4
  75. package/build/widgets/overlay/FlyweightTooltipTracker.d.ts.map +1 -1
  76. package/build/widgets/overlay/FlyweightTooltipTracker.js +3 -0
  77. package/build/widgets/overlay/Overlay.d.ts +2 -2
  78. package/build/widgets/overlay/Overlay.d.ts.map +1 -1
  79. package/dist/data.js +52 -1
  80. package/dist/jsx-runtime.js +4 -2
  81. package/dist/manifest.js +879 -873
  82. package/dist/svg.js +3 -0
  83. package/dist/ui.js +1548 -1544
  84. package/dist/widgets.css +1 -1
  85. package/dist/widgets.js +395 -4
  86. package/package.json +2 -2
  87. package/src/charts/Marker.tsx +448 -394
  88. package/src/charts/MouseTracker.tsx +3 -0
  89. package/src/charts/helpers/PointReducer.ts +2 -2
  90. package/src/data/View.ts +76 -19
  91. package/src/data/ops/findTreeNode.ts +20 -1
  92. package/src/data/ops/findTreePath.ts +7 -2
  93. package/src/data/ops/removeTreeNodes.ts +14 -1
  94. package/src/data/ops/updateArray.ts +4 -4
  95. package/src/data/ops/updateTree.ts +32 -6
  96. package/src/index.scss +6 -6
  97. package/src/jsx-runtime.spec.tsx +40 -0
  98. package/src/jsx-runtime.ts +87 -84
  99. package/src/svg/Rectangle.tsx +80 -73
  100. package/src/ui/DataProxy.ts +55 -55
  101. package/src/ui/Instance.ts +142 -45
  102. package/src/ui/IsolatedScope.ts +4 -2
  103. package/src/ui/Prop.ts +141 -141
  104. package/src/ui/Rescope.ts +50 -50
  105. package/src/ui/Widget.tsx +292 -234
  106. package/src/ui/adapter/ArrayAdapter.ts +229 -229
  107. package/src/ui/adapter/GroupAdapter.ts +8 -10
  108. package/src/ui/adapter/TreeAdapter.ts +75 -15
  109. package/src/ui/app/Url.spec.ts +1 -1
  110. package/src/ui/app/startAppLoop.tsx +56 -45
  111. package/src/ui/app/startHotAppLoop.ts +4 -4
  112. package/src/ui/batchUpdates.ts +16 -21
  113. package/src/ui/exprHelpers.ts +96 -96
  114. package/src/widgets/Button.tsx +0 -8
  115. package/src/widgets/HtmlElement.spec.tsx +100 -72
  116. package/src/widgets/HtmlElement.tsx +11 -10
  117. package/src/widgets/Sandbox.ts +104 -104
  118. package/src/widgets/Section.scss +55 -55
  119. package/src/widgets/drag-drop/DropZone.scss +74 -74
  120. package/src/widgets/form/Checkbox.tsx +296 -243
  121. package/src/widgets/form/DateTimeField.tsx +6 -0
  122. package/src/widgets/form/LookupField.tsx +70 -73
  123. package/src/widgets/form/TextField.tsx +2 -2
  124. package/src/widgets/grid/Grid.scss +43 -10
  125. package/src/widgets/grid/Grid.tsx +4401 -3848
  126. package/src/widgets/nav/Menu.tsx +3 -0
  127. package/src/widgets/nav/Route.ts +1 -1
  128. package/src/widgets/overlay/FlyweightTooltipTracker.ts +15 -4
  129. package/src/widgets/overlay/Overlay.tsx +2 -1
  130. package/src/widgets/overlay/index.d.ts +11 -11
package/dist/ui.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  computable,
3
3
  StoreProxy,
4
+ isAccessorChain,
4
5
  StructuredSelector,
5
6
  invalidateExpressionCache,
6
7
  invalidateStringTemplateCache,
@@ -14,7 +15,6 @@ import {
14
15
  ZoomIntoPropertyView,
15
16
  setGetExpressionCacheCallback,
16
17
  setGetStringTemplateCacheCallback,
17
- isAccessorChain,
18
18
  SubscribableView,
19
19
  Store,
20
20
  NestedDataView,
@@ -28,13 +28,21 @@ import {
28
28
  isArray,
29
29
  isFunction,
30
30
  parseStyle,
31
+ SubscriberList,
32
+ isNonEmptyArray,
33
+ GlobalCacheIdentifier,
34
+ debug,
35
+ processDataFlag,
36
+ renderFlag,
37
+ destroyFlag,
38
+ validatedDebounce,
39
+ throttle,
40
+ isUndefined,
31
41
  isString,
32
- Console,
42
+ isObject,
33
43
  isDefined,
44
+ Console,
34
45
  innerTextTrim,
35
- GlobalCacheIdentifier,
36
- isObject,
37
- SubscriberList,
38
46
  isSelfOrDescendant,
39
47
  getActiveElement,
40
48
  findFirst,
@@ -46,14 +54,6 @@ import {
46
54
  resolveMinMaxFractionDigits,
47
55
  parseDateInvariant,
48
56
  setGetFormatCacheCallback,
49
- isNonEmptyArray,
50
- debug,
51
- processDataFlag,
52
- renderFlag,
53
- destroyFlag,
54
- validatedDebounce,
55
- throttle,
56
- isUndefined,
57
57
  TraversalStack,
58
58
  reverseSlice,
59
59
  appDataFlag,
@@ -262,1722 +262,1725 @@ class CSS {
262
262
  CSS.classPrefix = "cx";
263
263
  CSSHelper.alias("cx", CSS);
264
264
 
265
- const VDOM = VDOM$1;
266
- let widgetId = 100;
267
- class Widget extends Component {
268
- // runtime values
269
- widgetId;
265
+ let isBatching = 0;
266
+ let promiseSubscribers = new SubscriberList();
267
+ function batchUpdates(callback) {
268
+ if (VDOM$1.DOM.unstable_batchedUpdates)
269
+ VDOM$1.DOM.unstable_batchedUpdates(() => {
270
+ isBatching++;
271
+ try {
272
+ callback();
273
+ } finally {
274
+ isBatching--;
275
+ }
276
+ });
277
+ else callback();
278
+ }
279
+ function isBatchingUpdates() {
280
+ return isBatching > 0;
281
+ }
282
+ function notifyBatchedUpdateStarting() {
283
+ promiseSubscribers.execute((x) => {
284
+ x.pending++;
285
+ });
286
+ }
287
+ function notifyBatchedUpdateCompleted() {
288
+ promiseSubscribers.execute((x) => {
289
+ let cb = x;
290
+ cb.finished++;
291
+ if (cb.finished >= cb.pending) cb.complete(true);
292
+ });
293
+ }
294
+ function batchUpdatesAndNotify(callback, notifyCallback, timeout = 1000) {
295
+ let done = false;
296
+ let timer;
297
+ let unsubscribe;
298
+ const update = {
299
+ pending: 0,
300
+ finished: 0,
301
+ complete: (success) => {
302
+ if (!done) {
303
+ done = true;
304
+ if (timer) clearInterval(timer);
305
+ if (unsubscribe) unsubscribe();
306
+ notifyCallback(!!success);
307
+ }
308
+ },
309
+ };
310
+ unsubscribe = promiseSubscribers.subscribe(update);
311
+ batchUpdates(callback);
312
+ if (update.pending <= update.finished) update.complete(true);
313
+ else timer = setTimeout(update.complete, timeout);
314
+ }
315
+
316
+ let instanceId = 1000;
317
+ /**
318
+ * Base Instance class
319
+ */
320
+ class Instance {
321
+ // Selectors
322
+ dataSelector;
323
+ // Lifecycle flags
270
324
  initialized;
271
- components;
325
+ destroySubscriptions;
272
326
  helpers;
273
- selector;
274
- nameMap;
275
- version;
276
- static optimizePrepare;
277
- constructor(config) {
278
- super(config);
279
- this.widgetId = widgetId++;
280
- if (isArray(this.jsxSpread)) {
281
- if (!this.jsxAttributes) this.jsxAttributes = [];
282
- this.jsxSpread.forEach((spread) => {
283
- for (const key in spread) {
284
- this[key] = spread[key];
285
- this.jsxAttributes.push(key);
286
- }
287
- });
288
- }
289
- }
290
- init() {
291
- if (this.styles) this.style = this.styles;
292
- if (this.styled) this.style = parseStyle(this.style);
293
- if (typeof this.if !== "undefined") this.visible = this.if;
294
- this.declareData();
295
- if (this.outerLayout) {
296
- if (isArray(this.outerLayout)) throw new Error("Only single element outer layout is supported.");
297
- //TODO: better handle the case when outer layout is an array. How to get around circular dependency to PureContainer
298
- this.outerLayout = Widget.create(this.outerLayout, {});
299
- }
300
- if (this.contentFor) this.putInto = this.contentFor;
301
- if (this.putInto) this.isContent = true;
302
- if (isString(this.CSS)) this.CSS = CSSHelper.get(this.CSS);
303
- this.initHelpers();
304
- this.initComponents();
305
- this.initialized = true;
327
+ components;
328
+ constructor(widget, key, parent, parentStore) {
329
+ this.widget = widget;
330
+ this.key = key;
331
+ this.id = String(++instanceId);
332
+ this.cached = {};
333
+ this.parent = parent;
334
+ this.parentStore = parentStore ?? parent?.store;
335
+ if (this.parentStore == null) throw new Error("Cannot create instance without a parent store.");
306
336
  }
307
- initComponents(...args) {
308
- if (args.length > 0) {
309
- this.components = Object.assign({}, ...args);
310
- for (const k in this.components) {
311
- if (!this.components[k]) delete this.components[k];
312
- }
313
- }
337
+ setParentStore(parentStore) {
338
+ this.parentStore = parentStore;
339
+ this.widget.applyParentStore(this);
314
340
  }
315
- initHelpers(...args) {
316
- if (args.length > 0) {
317
- this.helpers = Object.assign({}, ...args);
341
+ init(context) {
342
+ // widget is initialized when the first instance is initialized
343
+ if (!this.widget.initialized) {
344
+ this.widget.init();
345
+ this.widget.initialized = true;
318
346
  }
319
- }
320
- declareData(...args) {
321
- const options = {};
322
- if (this.styled) {
323
- options.class =
324
- options.className =
325
- options.style =
326
- {
327
- structured: true,
328
- };
347
+ if (!this.dataSelector) {
348
+ this.widget.selector.init(this.parentStore);
349
+ this.dataSelector = this.widget.selector.createStoreSelector();
329
350
  }
330
- const props = {
331
- visible: undefined,
332
- mod: {
333
- structured: true,
334
- },
335
- ...options,
336
- };
337
- Object.assign(props, ...args);
338
- this.selector = new StructuredSelector({
339
- props: props,
340
- values: this,
341
- });
342
- this.nameMap = this.selector.nameMap;
343
- }
344
- prepareCSS(_context, { data }) {
345
- data.classNames = this.CSS.expand(
346
- this.CSS.block(this.baseClass, data.mod, data.stateMods),
347
- data.class,
348
- data.className,
349
- );
350
- data.style = parseStyle(data.style);
351
- }
352
- prepareData(context, instance) {
353
- if (this.styled) this.prepareCSS(context, instance);
351
+ // init instance might change the store, so this must go before the controller initialization
352
+ this.widget.initInstance(context, this);
353
+ // initInstance can set the store, otherwise use parent store
354
+ if (!this.store) this.store = this.parentStore;
355
+ if (this.widget.onInit) this.widget.onInit(context, this);
356
+ this.widget.initState(context, this);
357
+ if (this.widget.controller)
358
+ this.controller = Controller.create(this.widget.controller, {
359
+ widget: this.widget,
360
+ instance: this,
361
+ store: this.store,
362
+ });
363
+ if (
364
+ this.widget.exploreCleanup ||
365
+ this.widget.outerLayout ||
366
+ this.widget.isContent ||
367
+ this.widget.controller ||
368
+ this.widget.prepareCleanup
369
+ )
370
+ this.needsExploreCleanup = true;
371
+ if (this.widget.prepare || this.widget.controller) this.needsPrepare = true;
372
+ if (this.widget.cleanup || this.widget.controller) this.needsCleanup = true;
373
+ this.initialized = true;
354
374
  }
355
- initInstance(_context, _instance) {}
356
- initState(_context, _instance) {}
357
- checkVisible(_context, _instance, data) {
358
- return data.visible;
375
+ checkVisible(context) {
376
+ if (!this.initialized) this.init(context);
377
+ const wasVisible = this.visible;
378
+ this.rawData = this.dataSelector(this.store);
379
+ this.visible = this.widget.checkVisible(context, this, this.rawData);
380
+ if (this.visible && !this.detached) this.parent.instanceCache.addChild(this);
381
+ this.explored = false;
382
+ this.prepared = false;
383
+ if (!this.visible && wasVisible) this.destroy();
384
+ return this.visible;
359
385
  }
360
- explore(context, instance, data) {
361
- if (this.components) {
362
- instance.components = {};
363
- for (const cmp in this.components) {
364
- const ins = instance.getChild(context, this.components[cmp], "cmp-" + cmp, instance.store);
365
- if (ins.scheduleExploreIfVisible(context)) instance.components[cmp] = ins;
366
- }
386
+ scheduleExploreIfVisible(context) {
387
+ if (this.checkVisible(context)) {
388
+ context.exploreStack.push(this);
389
+ if (this.needsExploreCleanup) context.exploreStack.push(this);
390
+ return true;
367
391
  }
392
+ return false;
368
393
  }
369
- render(context, instance, key) {
370
- Console.log(this);
371
- throw new Error(
372
- 'Widget\'s render method should be overridden. This error usually happens if with incorrect imports, i.e. import { TextField } from "cx/data". Please check the console for details about the component configuration.',
373
- );
374
- }
375
- update() {
376
- this.version = (this.version || 0) + 1;
377
- }
378
- applyParentStore(instance) {
379
- instance.store = instance.parentStore;
380
- // check when this is actually needed, perhaps this is needed only for tables and repeated elements
381
- // if (instance.cached) delete instance.cached.rawData; // force prepareData to execute again
394
+ cache(key, value) {
395
+ const oldValue = this.cached[key];
396
+ if (oldValue === value) return false;
397
+ if (!this.cacheList) this.cacheList = {};
398
+ this.cacheList[key] = value;
399
+ return true;
382
400
  }
383
- static resetCounter() {
384
- widgetId = 100;
385
- }
386
- }
387
- Widget.prototype.visible = true;
388
- Widget.prototype.memoize = true; //cache rendered content and use it if possible
389
- Widget.prototype.CSS = CSS;
390
- Widget.prototype.styled = false;
391
- Widget.namespace = "ui.";
392
- Widget.optimizePrepare = true;
393
- Widget.factory = (type, _config, _more) => {
394
- throw new Error(`Invalid widget type: ${type}.`);
395
- };
396
- function contentAppend(result, w, prependSpace) {
397
- if (w == null || w === false) return false;
398
- if (isArray(w)) w.forEach((c) => contentAppend(result, c));
399
- else if (isDefined(w.content) && !w.atomic) return contentAppend(result, w.content, false);
400
- else {
401
- if (prependSpace) result.push(" ");
402
- result.push(w);
403
- }
404
- return true;
405
- }
406
- function getContentArray(x) {
407
- let result = [];
408
- contentAppend(result, x, false);
409
- return result;
410
- }
411
- function getContent(x) {
412
- let result = getContentArray(x);
413
- if (result.length == 0) return null;
414
- if (result.length == 1) return result[0];
415
- return result;
416
- }
417
-
418
- class StaticText extends Widget {
419
- render() {
420
- return this.text;
421
- }
422
- }
423
- Widget.alias("static-text", StaticText);
424
-
425
- class Text extends Widget {
426
- constructor(config) {
427
- super(config);
401
+ markShouldUpdate(context) {
402
+ let ins = this;
403
+ let renderList = this.renderList;
404
+ renderList.markReverseIndex();
405
+ //notify all parents that child state changed to bust up caching
406
+ while (ins && !ins.shouldUpdate && ins.explored) {
407
+ if (ins.renderList !== renderList) {
408
+ renderList.reverse();
409
+ renderList = ins.renderList;
410
+ renderList.markReverseIndex();
411
+ }
412
+ ins.shouldUpdate = true;
413
+ renderList.data.push(ins);
414
+ ins = ins.widget.isContent
415
+ ? ins.contentPlaceholder
416
+ : ins.parent?.outerLayout === ins
417
+ ? ins.parent?.parent
418
+ : ins.parent;
419
+ }
420
+ renderList.reverse();
428
421
  }
429
- init() {
430
- if (!this.value && (this.tpl || this.expr || this.bind))
431
- this.value = {
432
- tpl: this.tpl,
433
- expr: this.expr,
434
- bind: this.bind,
422
+ explore(context) {
423
+ if (!this.visible) throw new Error("Explore invisible!");
424
+ if (this.explored) {
425
+ if (this.widget.prepareCleanup) context.prepareList.push(this);
426
+ if (this.widget.exploreCleanup) this.widget.exploreCleanup(context, this);
427
+ if (this.parent?.outerLayout === this) context.popNamedValue("content", "body");
428
+ if (this.widget.controller) context.pop("controller");
429
+ return;
430
+ }
431
+ this.explored = true;
432
+ if (this.needsPrepare) context.prepareList.push(this);
433
+ else this.prepared = true;
434
+ if (this.needsCleanup) context.cleanupList.push(this);
435
+ if (this.instanceCache) this.instanceCache.mark();
436
+ //controller may reconfigure the widget and need to go before shouldUpdate calculation
437
+ this.parentOptions = context.parentOptions;
438
+ if (!this.controller) {
439
+ if (context.controller) this.controller = context.controller;
440
+ else if (this.parent?.controller) this.controller = this.parent?.controller;
441
+ }
442
+ this.destroyTracked = false;
443
+ if (this.controller) {
444
+ if (this.widget.controller) {
445
+ if (!this.controller.initialized) {
446
+ this.controller.init(context);
447
+ this.controller.initialized = true;
448
+ }
449
+ context.push("controller", this.controller);
450
+ this.controller.explore(context);
451
+ if (this.controller.onDestroy && this.controller.widget == this.widget) this.trackDestroy();
452
+ }
453
+ }
454
+ if (this.widget.onDestroy || isNonEmptyArray(this.destroySubscriptions)) this.trackDestroy();
455
+ this.renderList = this.assignedRenderList || this.parent?.renderList || context.getRootRenderList();
456
+ let shouldUpdate =
457
+ this.rawData !== this.cached.rawData ||
458
+ this.state !== this.cached.state ||
459
+ this.widget.version !== this.cached.widgetVersion ||
460
+ this.cached.globalCacheIdentifier !== GlobalCacheIdentifier.get();
461
+ if (shouldUpdate) {
462
+ this.data = {
463
+ ...this.rawData,
435
464
  };
436
- super.init();
437
- }
438
- declareData(...args) {
439
- super.declareData(
440
- {
441
- value: undefined,
442
- },
443
- ...args,
444
- );
445
- }
446
- render(context, { data }, key) {
447
- return data.value != null ? data.value : "";
448
- }
449
- }
450
- Widget.alias("text", Text);
451
-
452
- function exploreChildren(context, instance, children, previousResult, key, store) {
453
- let newChildren = previousResult || [];
454
- let oldChildren = previousResult || newChildren;
455
- let identical = previousResult ? 0 : -1;
456
- for (let c = 0; c < children.length; c++) {
457
- let cell = instance.getChild(context, children[c], key, store);
458
- if (cell.checkVisible(context)) {
459
- if (identical >= 0) {
460
- if (cell == oldChildren[identical]) identical++;
461
- else {
462
- newChildren = newChildren.slice(0, identical);
463
- identical = -1;
464
- newChildren.push(cell);
465
+ this.widget.prepareData(context, this);
466
+ debug(processDataFlag, this.widget);
467
+ }
468
+ //onExplore might set the outer layout
469
+ if (this.widget.onExplore) this.widget.onExplore(context, this);
470
+ if (this.parent?.outerLayout === this) {
471
+ this.renderList = this.renderList.insertRight();
472
+ context.pushNamedValue("content", "body", this.parent);
473
+ }
474
+ if (this.widget.outerLayout) {
475
+ this.outerLayout = this.getChild(context, this.widget.outerLayout, null, this.store);
476
+ this.outerLayout.scheduleExploreIfVisible(context);
477
+ this.renderList = this.renderList.insertLeft();
478
+ }
479
+ if (this.widget.isContent) {
480
+ this.contentPlaceholder = context.contentPlaceholder && context.contentPlaceholder[this.widget.putInto];
481
+ if (this.contentPlaceholder) context.contentPlaceholder[this.widget.putInto](this);
482
+ else {
483
+ this.renderList = this.renderList.insertLeft();
484
+ context.pushNamedValue("content", this.widget.putInto, this);
485
+ if (!context.contentList) context.contentList = {};
486
+ let list = context.contentList[this.widget.putInto];
487
+ if (!list) list = context.contentList[this.widget.putInto] = [];
488
+ list.push(this);
489
+ }
490
+ }
491
+ this.shouldUpdate = false;
492
+ if (shouldUpdate || this.childStateDirty || !this.widget.memoize) this.markShouldUpdate(context);
493
+ context.exploreStack.hop();
494
+ if (this.widget.helpers) {
495
+ this.helpers = {};
496
+ for (let cmp in this.widget.helpers) {
497
+ let helper = this.widget.helpers[cmp];
498
+ if (helper) {
499
+ let ins = this.getChild(context, helper);
500
+ if (ins.scheduleExploreIfVisible(context)) this.helpers[cmp] = ins;
465
501
  }
466
- } else newChildren.push(cell);
467
- context.exploreStack.push(cell);
468
- if (cell.needsExploreCleanup) context.exploreStack.push(cell);
502
+ }
469
503
  }
504
+ //TODO: check do we need to pass data here?
505
+ this.widget.explore(context, this, this.data);
470
506
  }
471
- if (identical >= 0 && identical != newChildren.length) newChildren = newChildren.slice(0, identical);
472
- return newChildren;
473
- }
474
-
475
- // Base class for extending with custom Config types
476
- class ContainerBase extends Widget {
477
- init(context) {
478
- if (typeof this.ws !== "undefined") this.preserveWhitespace = this.ws;
479
- if (this.preserveWhitespace) this.trimWhitespace = false;
480
- let items = this.items || this.children || [];
481
- delete this.children;
482
- this.items = [];
483
- if (this.layout) {
484
- let layout = Container.create(this.layout, {
485
- items,
486
- });
487
- layout.init(context);
488
- this.layout = null;
489
- if ("noLayout" in layout && layout.noLayout) {
490
- this.useParentLayout = true;
491
- this.add(items);
492
- } else {
493
- this.add(layout);
494
- this.layout = layout;
507
+ prepare(context) {
508
+ if (!this.visible) throw new Error("Prepare invisible!");
509
+ if (this.prepared) {
510
+ if (this.widget.prepareCleanup) this.widget.prepareCleanup(context, this);
511
+ return;
512
+ }
513
+ this.prepared = true;
514
+ if (this.widget.prepare) this.widget.prepare(context, this);
515
+ if (this.widget.controller && this.controller?.prepare) this.controller.prepare(context);
516
+ }
517
+ render(context) {
518
+ if (!this.visible) throw new Error("Render invisible!");
519
+ if (this.shouldUpdate) {
520
+ debug(renderFlag, this.widget, this.key);
521
+ const vdom = renderResultFix(this.widget.render(context, this, this.key));
522
+ if (this.widget.isContent || this.outerLayout) this.contentVDOM = vdom;
523
+ else this.vdom = vdom;
524
+ }
525
+ if (this.cacheList) {
526
+ for (const key in this.cacheList) {
527
+ this.cached[key] = this.cacheList[key];
495
528
  }
496
- } else {
497
- this.add(items);
498
529
  }
499
- super.init();
530
+ this.cacheList = null;
531
+ this.cached.rawData = this.rawData;
532
+ this.cached.data = this.data;
533
+ this.cached.state = this.state;
534
+ this.cached.widgetVersion = this.widget.version;
535
+ this.cached.globalCacheIdentifier = GlobalCacheIdentifier.get();
536
+ this.childStateDirty = false;
537
+ if (this.instanceCache) this.instanceCache.sweep();
538
+ if (this.parent?.outerLayout === this) {
539
+ //if outer layouts are chained we need to find the originating element (last element with OL set)
540
+ let parent = this.parent;
541
+ while (parent.parent?.outerLayout == parent) parent = parent.parent;
542
+ parent.vdom = this.vdom;
543
+ }
544
+ return this.vdom;
500
545
  }
501
- exploreItems(context, instance, items) {
502
- instance.children = exploreChildren(context, instance, items, instance.cached.children, null, instance.store);
503
- if (instance.cache("children", instance.children)) instance.markShouldUpdate(context);
546
+ cleanup(context) {
547
+ if (this.widget.controller && this.controller?.cleanup) this.controller.cleanup(context);
548
+ if (this.widget.cleanup) this.widget.cleanup(context, this);
504
549
  }
505
- explore(context, instance) {
506
- super.explore(context, instance);
507
- this.exploreItems(context, instance, this.items);
550
+ trackDestroy() {
551
+ if (!this.destroyTracked) {
552
+ this.destroyTracked = true;
553
+ if (this.parent && !this.detached) this.parent.trackDestroyableChild(this);
554
+ }
508
555
  }
509
- render(context, instance, key) {
510
- return this.renderChildren(context, instance, key);
556
+ trackDestroyableChild(child) {
557
+ this.instanceCache.trackDestroy(child);
558
+ this.trackDestroy();
511
559
  }
512
- renderChildren(_context, instance, key) {
513
- let preserveComplexContent = this.useParentLayout;
514
- function append(result, r) {
515
- if (r == null) return;
516
- //react element
517
- if (!r.hasOwnProperty("content")) {
518
- contentAppend(result, r);
519
- return;
520
- }
521
- if (r.useParentLayout) return r.content.forEach((x) => append(result, x));
522
- if (r.atomic || preserveComplexContent) {
523
- result.push(r);
524
- } else {
525
- let first = true;
526
- for (let k in r) if (contentAppend(result, r[k], !first)) first = false;
560
+ subscribeOnDestroy(callback) {
561
+ if (!this.destroySubscriptions) this.destroySubscriptions = [];
562
+ this.destroySubscriptions.push(callback);
563
+ this.trackDestroy();
564
+ return () => {
565
+ if (this.destroySubscriptions) {
566
+ this.destroySubscriptions = this.destroySubscriptions.filter((cb) => cb !== callback);
527
567
  }
568
+ };
569
+ }
570
+ destroy() {
571
+ if (this.instanceCache) {
572
+ this.instanceCache.destroy();
573
+ this.instanceCache = null;
528
574
  }
529
- let result = [];
530
- for (let i = 0; i < instance.children.length; i++) {
531
- append(result, instance.children[i].vdom);
575
+ if (this.destroySubscriptions) {
576
+ this.destroySubscriptions.forEach((cb) => cb());
577
+ this.destroySubscriptions = null;
578
+ }
579
+ if (this.destroyTracked) {
580
+ debug(destroyFlag, this);
581
+ if (this.widget.onDestroy) this.widget.onDestroy(this);
582
+ if (
583
+ this.widget.controller &&
584
+ this.controller &&
585
+ this.controller.onDestroy &&
586
+ this.controller.widget == this.widget
587
+ )
588
+ this.controller.onDestroy();
589
+ this.destroyTracked = false;
532
590
  }
533
- if (this.useParentLayout)
534
- return {
535
- useParentLayout: true,
536
- content: result,
537
- };
538
- return result;
539
591
  }
540
- clear() {
541
- if (this.layout) this.layout.clear();
542
- else this.items = [];
592
+ setState(state) {
593
+ let skip = !!this.state;
594
+ if (this.state) {
595
+ for (const k in state) {
596
+ if (this.state[k] !== state[k]) {
597
+ skip = false;
598
+ break;
599
+ }
600
+ }
601
+ }
602
+ if (skip) return;
603
+ this.state = Object.assign({}, this.state, state);
604
+ let parent = this.parent;
605
+ //notify all parents that child state change to bust up caching
606
+ while (parent) {
607
+ parent.childStateDirty = true;
608
+ parent = parent.parent;
609
+ }
610
+ batchUpdates(() => {
611
+ this.store.notify();
612
+ });
543
613
  }
544
- add(...args) {
545
- if (this.layout) return this.layout.add(...args);
546
- for (let a of args) {
547
- if (!a) continue;
548
- if (isArray(a)) {
549
- for (let c of a) this.add(c);
550
- } else if (isString(a)) {
551
- if (this.trimWhitespace) a = innerTextTrim(a);
552
- if (a) this.addText(a);
553
- } else if (a.isComponent) this.items.push(this.wrapItem(a));
554
- else {
555
- this.add(Widget.create(a, this.itemDefaults, {}));
614
+ set(prop, value, options = {}) {
615
+ //skip re-rendering (used for reading state from uncontrolled components)
616
+ if (options.internal && this.rawData) {
617
+ this.rawData[prop] = value;
618
+ this.data[prop] = value;
619
+ }
620
+ const setter = this.setters && this.setters[prop];
621
+ if (setter) {
622
+ if (options.immediate && isFunction(setter.reset)) setter.reset(value);
623
+ else setter(value);
624
+ return true;
625
+ }
626
+ const p = this.widget[prop];
627
+ if (p && typeof p == "object") {
628
+ if (p.debounce) {
629
+ this.definePropertySetter(
630
+ prop,
631
+ validatedDebounce(
632
+ (value) => this.doSet(prop, value),
633
+ () => this.dataSelector(this.store)[prop],
634
+ p.debounce,
635
+ ),
636
+ );
637
+ this.set(prop, value, options);
638
+ return true;
639
+ }
640
+ if (p.throttle) {
641
+ this.definePropertySetter(
642
+ prop,
643
+ throttle((value) => this.doSet(prop, value), p.throttle),
644
+ );
645
+ this.set(prop, value, options);
646
+ return true;
556
647
  }
557
648
  }
649
+ return this.doSet(prop, value);
558
650
  }
559
- wrapItem(item) {
560
- return item;
651
+ definePropertySetter(prop, setter) {
652
+ if (!this.setters) this.setters = {};
653
+ this.setters[prop] = setter;
561
654
  }
562
- addText(text) {
563
- if (this.plainText || text.indexOf("{") == -1 || text.indexOf("}") == -1)
564
- this.add(
565
- Widget.create(
566
- StaticText,
567
- {
568
- text: text,
569
- },
570
- {},
571
- ),
655
+ doSet(prop, value) {
656
+ let changed = false;
657
+ batchUpdates(() => {
658
+ const p = this.widget[prop];
659
+ if (isObject(p)) {
660
+ const pObj = p;
661
+ if (pObj.set) {
662
+ if (isFunction(pObj.set)) {
663
+ pObj.set(value, this);
664
+ changed = true;
665
+ } else if (isString(pObj.set)) {
666
+ this.controller?.[pObj.set](value, this);
667
+ changed = true;
668
+ }
669
+ } else if (pObj.action) {
670
+ const action = pObj.action(value, this);
671
+ this.store.dispatch(action);
672
+ changed = true;
673
+ } else if (isString(pObj.bind) || isAccessorChain(pObj.bind)) {
674
+ changed = this.store.set(pObj.bind, value);
675
+ }
676
+ } else if (isAccessorChain(p)) {
677
+ changed = this.store.set(p.toString(), value);
678
+ }
679
+ });
680
+ return changed;
681
+ }
682
+ nestedDataSet(key, value, dataConfig, useParentStore) {
683
+ let config = dataConfig[key];
684
+ if (!config)
685
+ throw new Error(`Unknown nested data key ${key}. Known keys are ${Object.keys(dataConfig).join(", ")}.`);
686
+ if (isAccessorChain(config))
687
+ config = {
688
+ bind: config.toString(),
689
+ };
690
+ if (config.bind) {
691
+ let store = this.store;
692
+ //in case of Rescope or DataProxy, bindings point to the data in the parent store
693
+ if (useParentStore && store.store) store = store.store;
694
+ return isUndefined(value) ? store.deleteItem(config.bind) : store.setItem(config.bind, value);
695
+ }
696
+ if (!config.set)
697
+ throw new Error(
698
+ `Cannot change nested data value for ${key} as it's read-only. Either define it as a binding or define a set function.`,
572
699
  );
700
+ if (isString(config.set)) this.getControllerMethod(config.set)(value, this);
701
+ else if (isFunction(config.set)) config.set(value, this);
573
702
  else
574
- this.add(
575
- Widget.create(
576
- Text,
577
- {
578
- text: {
579
- tpl: text,
580
- },
581
- },
582
- {},
583
- ),
703
+ throw new Error(
704
+ `Cannot change nested data value for ${key} the defined setter is neither a function nor a controller method.`,
584
705
  );
706
+ return true;
585
707
  }
586
- find(filter, options) {
587
- if (!options) options = {};
588
- if (!filter || !this.items) return [];
589
- let alias = filter;
590
- if (isString(filter)) filter = (w) => w.componentAlias == alias;
591
- if (filter.isComponentType) filter = (w) => w instanceof alias;
592
- let results = [];
593
- for (let i = 0; i < this.items.length; i++) {
594
- let w = this.items[i];
595
- if (w && !w.initialized) w.init();
596
- if (filter(w)) {
597
- results.push(w);
598
- if (options.first) break;
599
- }
600
- if (w && w.find) results.push(...w.find(filter, options));
601
- }
602
- return results;
603
- }
604
- findFirst(filter, options) {
605
- return this.find(filter, {
606
- ...options,
607
- first: true,
608
- })[0];
609
- }
610
- }
611
- ContainerBase.prototype.trimWhitespace = true;
612
- ContainerBase.prototype.plainText = true;
613
- ContainerBase.prototype.styled = false;
614
- // Closed type for direct usage - preserves ControllerProp ThisType
615
- class Container extends ContainerBase {}
616
- // Base class for styled containers with custom Config types
617
- class StyledContainerBase extends ContainerBase {}
618
- StyledContainerBase.prototype.styled = true;
619
- // Closed type for direct usage
620
- class StyledContainer extends StyledContainerBase {}
621
-
622
- // Base class for extending with custom Config types
623
- class PureContainerBase extends ContainerBase {}
624
- PureContainerBase.prototype.isPureContainer = true;
625
- // Closed type for direct usage - preserves ControllerProp ThisType
626
- class PureContainer extends PureContainerBase {}
627
- PureContainer.alias("pure-container", PureContainer);
628
-
629
- class DataAdapter extends Component {
630
- constructor(config) {
631
- super(config);
708
+ replaceState(state) {
709
+ this.cached.state = this.state;
710
+ this.state = state;
711
+ this.store.notify();
632
712
  }
633
- setFilter(filterFn) {
634
- this.filterFn = filterFn;
713
+ getInstanceCache() {
714
+ if (!this.instanceCache)
715
+ this.instanceCache = new InstanceCache(this, this.widget.isPureContainer ? this.key : null);
716
+ return this.instanceCache;
635
717
  }
636
- sort(sorters) {}
637
- }
638
- DataAdapter.prototype.recordName = "$record";
639
- DataAdapter.prototype.indexName = "$index";
640
- DataAdapter.prototype.immutable = false;
641
-
642
- let contents = {};
643
- let localizations = {};
644
- let overrides = {};
645
- let defaults = {};
646
- let trackDefaults = false;
647
- class Localization {
648
- static register(key) {
649
- return (type) => {
650
- this.registerPrototype(key, type);
651
- return type;
652
- };
718
+ clearChildrenCache() {
719
+ if (this.instanceCache) this.instanceCache.destroy();
653
720
  }
654
- static registerPrototype(key, type) {
655
- contents[key] = type.prototype;
656
- if (overrides[key]) this.override(key, overrides[key]);
721
+ getChild(context, widget, key, store) {
722
+ return this.getInstanceCache().getChild(widget, store ?? this.store, key);
657
723
  }
658
- static trackDefaults() {
659
- trackDefaults = true;
724
+ getDetachedChild(widget, key, store) {
725
+ const child = widget.createInstance(key, this, store ?? this.store);
726
+ child.detached = true;
727
+ return child;
660
728
  }
661
- static restoreDefaults() {
662
- for (let type in defaults) {
663
- let proto = contents[type];
664
- if (!proto) continue;
665
- let d = defaults[type];
666
- for (let key in d) proto[key] = d[key];
667
- }
668
- defaults = {};
729
+ prepareRenderCleanupChild(widget, store, keyPrefix, options) {
730
+ return widget.prepareRenderCleanup(store ?? this.store, options, keyPrefix, this);
669
731
  }
670
- static override(key, values) {
671
- overrides[key] = values;
672
- let p = contents[key];
673
- if (p) {
674
- if (trackDefaults && !defaults[key]) {
675
- let d = (defaults[key] = {});
676
- for (let key in values) d[key] = p[key];
732
+ getJsxEventProps() {
733
+ const { widget } = this;
734
+ if (!isArray(widget.jsxAttributes)) return null;
735
+ const props = {};
736
+ widget.jsxAttributes.forEach((attr) => {
737
+ if (attr.indexOf("on") == 0 && attr.length > 2) {
738
+ props[attr] = (e) => this.invoke(attr, e, this);
677
739
  }
678
- Object.assign(p, values);
679
- }
740
+ });
741
+ return props;
680
742
  }
681
- static localize(culture, key, values) {
682
- let l = localizations[culture];
683
- if (!l) l = localizations[culture] = {};
684
- l[key] = {
685
- ...l[key],
686
- ...values,
687
- };
743
+ getCallback(methodName) {
744
+ const scope = this.widget;
745
+ const callback = scope[methodName];
746
+ if (typeof callback === "string") return this.getControllerMethod(callback);
747
+ if (typeof callback !== "function")
748
+ throw new Error(`Cannot invoke callback method ${methodName} as assigned value is not a function.`);
749
+ return callback.bind(scope);
688
750
  }
689
- static setCulture(culture) {
690
- var l = localizations[culture];
691
- if (l) {
692
- for (var key in l) {
693
- var content = contents[key];
694
- if (content) Object.assign(content, l[key]);
751
+ /**
752
+ * Finds the first controller in the instance tree matching the predicate
753
+ * @param predicate Function to test each controller
754
+ * @returns The matching controller or undefined
755
+ */
756
+ findController(predicate) {
757
+ let at = this;
758
+ while (at?.controller != null) {
759
+ if (predicate(at.controller)) {
760
+ return at.controller;
695
761
  }
762
+ at = at.parent;
696
763
  }
764
+ return undefined;
697
765
  }
698
- }
699
-
700
- // @ts-expect-error
701
- let stack = [
702
- {
703
- culture: "en",
704
- numberCulture: null,
705
- dateTimeCulture: null,
706
- cache: {},
707
- defaultCurrency: "USD",
708
- dateEncoding: (date) => date.toISOString(),
709
- timezone: null,
710
- },
711
- ];
712
- function getDefaultCulture() {
713
- return stack[0];
714
- }
715
- function getCurrentCulture() {
716
- return stack[stack.length - 1];
717
- }
718
- function getCurrentCultureCache() {
719
- return getCurrentCulture().cache;
720
- }
721
- function pushCulture(cultureInfo) {
722
- stack.push(cultureInfo);
723
- }
724
- function createCulture(cultureSpecs) {
725
- let current = getCurrentCulture();
726
- let info = {
727
- culture: current.culture,
728
- dateEncoding: current.dateEncoding,
729
- defaultCurrency: current.defaultCurrency,
730
- cache: {},
731
- };
732
- for (let key in cultureSpecs) {
733
- if (!cultureSpecs[key]) continue;
734
- info[key] = cultureSpecs[key];
735
- }
736
- return info;
737
- }
738
- function popCulture(cultureSpecs) {
739
- if (stack.length == 1) throw new Error("Cannot pop the last culture object.");
740
- if (cultureSpecs && stack[stack.length - 1] !== cultureSpecs) {
741
- Console.warn("Popped culture object does not match the current one.");
742
- }
743
- return stack.pop();
744
- }
745
- class Culture {
746
- static setCulture(cultureCode) {
747
- let cultureSpecs = getDefaultCulture();
748
- cultureSpecs.culture = cultureCode;
749
- cultureSpecs.cache = {};
750
- Localization.setCulture(cultureCode);
751
- this.invalidateCache();
752
- }
753
- static setNumberCulture(cultureCode) {
754
- let cultureSpecs = getDefaultCulture();
755
- cultureSpecs.numberCulture = cultureCode;
756
- delete cultureSpecs.cache.numberCulture;
757
- this.invalidateCache();
766
+ /**
767
+ * Finds a controller of the specified type in the instance tree
768
+ * @param type Controller class/constructor to find
769
+ * @returns The matching controller cast to the specified type, or undefined
770
+ */
771
+ findControllerByType(type) {
772
+ return this.findController((c) => c instanceof type);
758
773
  }
759
- static setDateTimeCulture(cultureCode) {
760
- let cultureSpecs = getDefaultCulture();
761
- cultureSpecs.dateTimeCulture = cultureCode;
762
- delete cultureSpecs.cache.dateTimeCulture;
763
- this.invalidateCache();
774
+ /**
775
+ * Gets the first controller in the instance tree matching the predicate
776
+ * @param predicate Function to test each controller
777
+ * @returns The matching controller
778
+ * @throws Error if no matching controller is found
779
+ */
780
+ getController(predicate) {
781
+ const controller = this.findController(predicate);
782
+ if (!controller) throw new Error("Cannot find a controller matching the given predicate in the instance tree.");
783
+ return controller;
764
784
  }
765
- static setDefaultCurrency(currencyCode) {
766
- let cultureSpecs = getDefaultCulture();
767
- cultureSpecs.defaultCurrency = currencyCode;
768
- this.invalidateCache();
785
+ /**
786
+ * Gets a controller of the specified type in the instance tree
787
+ * @param type Controller class/constructor to find
788
+ * @returns The matching controller cast to the specified type
789
+ * @throws Error if no controller of the specified type is found
790
+ */
791
+ getControllerByType(type) {
792
+ const controller = this.findControllerByType(type);
793
+ if (!controller) throw new Error(`Cannot find a controller of type "${type.name}" in the instance tree.`);
794
+ return controller;
769
795
  }
770
- static setDefaultTimezone(timezone) {
771
- let cultureSpecs = getDefaultCulture();
772
- cultureSpecs.timezone = timezone;
773
- this.invalidateCache();
796
+ getControllerMethod(methodName) {
797
+ if (!this.controller)
798
+ throw new Error(`Cannot invoke controller method "${methodName}" as controller is not assigned to the widget.`);
799
+ const controller = this.findController((c) => !!c[methodName]);
800
+ if (!controller)
801
+ throw new Error(
802
+ `Cannot invoke controller method "${methodName}". The method cannot be found in any of the assigned controllers.`,
803
+ );
804
+ return controller[methodName].bind(controller);
774
805
  }
775
- static setDefaultDateEncoding(encoding) {
776
- let cultureSpecs = getDefaultCulture();
777
- cultureSpecs.dateEncoding = encoding;
778
- this.invalidateCache();
806
+ invoke(methodName, ...args) {
807
+ return this.getCallback(methodName).apply(null, args);
779
808
  }
780
- static invalidateCache() {
781
- GlobalCacheIdentifier.change();
782
- invalidateExpressionCache();
783
- invalidateStringTemplateCache();
809
+ invokeControllerMethod(methodName, ...args) {
810
+ return this.getControllerMethod(methodName).apply(null, args);
784
811
  }
785
- static get defaultCurrency() {
786
- return getCurrentCulture().defaultCurrency;
812
+ }
813
+ function renderResultFix(res) {
814
+ return res != null && isDefined(res.content)
815
+ ? res
816
+ : {
817
+ content: res,
818
+ };
819
+ }
820
+ class InstanceCache {
821
+ constructor(parent, keyPrefix) {
822
+ this.children = {};
823
+ this.parent = parent;
824
+ this.marked = {};
825
+ this.monitored = null;
826
+ this.keyPrefix = keyPrefix != null ? keyPrefix + "-" : "";
787
827
  }
788
- static get culture() {
789
- return getCurrentCulture().culture;
828
+ getChild(widget, parentStore, key) {
829
+ const k = this.keyPrefix + (key != null ? key : widget.vdomKey || widget.widgetId);
830
+ let instance = this.children[k];
831
+ if (
832
+ !instance ||
833
+ instance.widget !== widget ||
834
+ (!instance.visible && (instance.widget.controller || instance.widget.onInit))
835
+ ) {
836
+ instance = widget.createInstance(k, this.parent, parentStore);
837
+ this.children[k] = instance;
838
+ } else if (instance.parentStore !== parentStore) {
839
+ instance.setParentStore(parentStore);
840
+ }
841
+ return instance;
790
842
  }
791
- static getNumberCulture() {
792
- let { cache, numberCulture, culture } = getCurrentCulture();
793
- if (!cache.numberCulture) cache.numberCulture = new NumberCulture(numberCulture ?? culture);
794
- return cache.numberCulture;
843
+ addChild(instance) {
844
+ this.marked[instance.key] = instance;
795
845
  }
796
- static getDateTimeCulture() {
797
- let { cache, dateTimeCulture, culture, timezone } = getCurrentCulture();
798
- if (!cache.dateTimeCulture)
799
- cache.dateTimeCulture = new DateTimeCulture(dateTimeCulture ?? culture, {
800
- defaultTimezone: timezone,
801
- });
802
- return cache.dateTimeCulture;
846
+ mark() {
847
+ this.marked = {};
803
848
  }
804
- static getDefaultDateEncoding() {
805
- return getCurrentCulture().dateEncoding;
849
+ trackDestroy(instance) {
850
+ if (!this.monitored) this.monitored = {};
851
+ this.monitored[instance.key] = instance;
806
852
  }
807
- static getComparer(options) {
808
- let { culture } = getCurrentCulture();
809
- if (typeof Intl.Collator != "undefined") return new Intl.Collator(culture, options).compare;
810
- return defaultCompare;
853
+ destroy() {
854
+ this.children = {};
855
+ this.marked = {};
856
+ if (!this.monitored) return;
857
+ for (const key in this.monitored) {
858
+ this.monitored[key].destroy();
859
+ }
860
+ this.monitored = null;
861
+ }
862
+ sweep() {
863
+ this.children = this.marked;
864
+ if (!this.monitored) return;
865
+ let activeCount = 0;
866
+ for (const key in this.monitored) {
867
+ const monitoredChild = this.monitored[key];
868
+ const child = this.children[key];
869
+ if (child !== monitoredChild || !monitoredChild.visible) {
870
+ monitoredChild.destroy();
871
+ delete this.monitored[key];
872
+ if (child === monitoredChild) delete this.children[key];
873
+ } else activeCount++;
874
+ }
875
+ if (activeCount === 0) this.monitored = null;
811
876
  }
812
877
  }
813
878
 
814
- class ArrayAdapter extends DataAdapter {
879
+ const VDOM = VDOM$1;
880
+ let widgetId = 100;
881
+ class Widget extends Component {
882
+ // runtime values
883
+ widgetId;
884
+ initialized;
885
+ components;
886
+ helpers;
887
+ selector;
888
+ nameMap;
889
+ version;
890
+ static optimizePrepare;
815
891
  constructor(config) {
816
892
  super(config);
893
+ this.widgetId = widgetId++;
894
+ if (isArray(this.jsxSpread)) {
895
+ if (!this.jsxAttributes) this.jsxAttributes = [];
896
+ this.jsxSpread.forEach((spread) => {
897
+ for (const key in spread) {
898
+ this[key] = spread[key];
899
+ this.jsxAttributes.push(key);
900
+ }
901
+ });
902
+ }
817
903
  }
818
904
  init() {
819
- this.recordsAccessor = this.recordsBinding ? getAccessor(this.recordsBinding) : getAccessor(this.recordsAccessor);
820
- this.recordName = this.recordName?.toString() || "$record";
821
- this.indexName = this.indexName?.toString() || "$index";
822
- }
823
- initInstance(context, instance) {
824
- if (!instance.recordStoreCache) {
825
- instance.recordStoreCache = new WeakMap();
826
- instance.cacheByKey = {};
827
- }
828
- if (!instance.recordsAccessor && this.recordsAccessor) {
829
- instance.recordsAccessor = this.recordsAccessor.bindInstance
830
- ? this.recordsAccessor.bindInstance(instance)
831
- : this.recordsAccessor;
905
+ if (this.styles) this.style = this.styles;
906
+ if (this.styled) this.style = parseStyle(this.style);
907
+ if (typeof this.if !== "undefined") this.visible = this.if;
908
+ this.declareData();
909
+ if (this.outerLayout) {
910
+ if (isArray(this.outerLayout)) throw new Error("Only single element outer layout is supported.");
911
+ //TODO: better handle the case when outer layout is an array. How to get around circular dependency to PureContainer
912
+ this.outerLayout = Widget.create(this.outerLayout, {});
832
913
  }
914
+ if (this.contentFor) this.putInto = this.contentFor;
915
+ if (this.putInto) this.isContent = true;
916
+ if (isString(this.CSS)) this.CSS = CSSHelper.get(this.CSS);
917
+ this.initHelpers();
918
+ this.initComponents();
919
+ this.initialized = true;
833
920
  }
834
- getRecords(context, instance, records, parentStore) {
835
- if (!instance.recordStoreCache) {
836
- this.initInstance(context, instance);
921
+ initComponents(...args) {
922
+ if (args.length > 0) {
923
+ this.components = Object.assign({}, ...args);
924
+ for (const k in this.components) {
925
+ if (!this.components[k]) delete this.components[k];
926
+ }
837
927
  }
838
- return this.mapRecords(context, instance, records, parentStore, instance.recordsAccessor);
839
928
  }
840
- mapRecords(context, instance, records, parentStore, recordsAccessor) {
841
- let result = [];
842
- if (!instance.recordStoreCache) {
843
- this.initInstance(context, instance);
844
- }
845
- if (isArray(records)) {
846
- records.forEach((data, index) => {
847
- if (this.filterFn && !this.filterFn(data)) return;
848
- const record = this.mapRecord(context, instance, data, parentStore, recordsAccessor, index);
849
- result.push(record);
850
- });
851
- }
852
- if (this.sorter && !this.preserveOrder) {
853
- result = this.sorter(result);
929
+ initHelpers(...args) {
930
+ if (args.length > 0) {
931
+ this.helpers = Object.assign({}, ...args);
854
932
  }
855
- return result;
856
933
  }
857
- mapRecord(context, instance, data, parentStore, recordsAccessor, index) {
858
- const key = this.cacheByKeyField && this.keyField && isObject(data) ? data[this.keyField] : null;
859
- let recordStore = key != null ? instance.cacheByKey[key] : instance.recordStoreCache.get(data);
860
- if (recordsAccessor) {
861
- if (!recordStore) {
862
- recordStore = new ArrayElementView({
863
- store: parentStore,
864
- arrayAccessor: recordsAccessor,
865
- itemIndex: index,
866
- recordAlias: this.recordName,
867
- indexAlias: this.indexName,
868
- immutable: this.immutable,
869
- sealed: this.sealed,
870
- });
871
- } else {
872
- recordStore.setStore(parentStore);
873
- recordStore.setIndex(index);
874
- }
875
- } else {
876
- if (!recordStore) {
877
- recordStore = new ReadOnlyDataView({
878
- store: parentStore,
879
- data: {
880
- [this.recordName]: data,
881
- [this.indexName]: index,
882
- },
883
- immutable: this.immutable,
884
- sealed: this.sealed,
885
- });
886
- } else {
887
- recordStore.setStore(parentStore);
888
- recordStore.setData({
889
- [this.recordName]: data,
890
- [this.indexName]: index,
891
- });
892
- }
893
- }
894
- if (key != null) {
895
- instance.cacheByKey[key] = recordStore;
896
- } else if (isObject(data)) {
897
- instance.recordStoreCache.set(data, recordStore);
934
+ declareData(...args) {
935
+ const options = {};
936
+ if (this.styled) {
937
+ options.class =
938
+ options.className =
939
+ options.style =
940
+ {
941
+ structured: true,
942
+ };
898
943
  }
899
- return {
900
- store: recordStore,
901
- index: index,
902
- data: data,
903
- type: "data",
904
- key: this.keyField && isObject(data) ? data[this.keyField] : index,
944
+ const props = {
945
+ visible: undefined,
946
+ mod: {
947
+ structured: true,
948
+ },
949
+ ...options,
905
950
  };
951
+ Object.assign(props, ...args);
952
+ this.selector = new StructuredSelector({
953
+ props: props,
954
+ values: this,
955
+ });
956
+ this.nameMap = this.selector.nameMap;
906
957
  }
907
- setFilter(filterFn) {
908
- this.filterFn = filterFn;
958
+ prepareCSS(_context, { data }) {
959
+ data.classNames = this.CSS.expand(
960
+ this.CSS.block(this.baseClass, data.mod, data.stateMods),
961
+ data.class,
962
+ data.className,
963
+ );
964
+ data.style = parseStyle(data.style);
909
965
  }
910
- getComparer(sortOptions) {
911
- return sortOptions ? Culture.getComparer(sortOptions) : undefined;
966
+ prepareData(context, instance) {
967
+ if (this.styled) this.prepareCSS(context, instance);
912
968
  }
913
- buildSorter(sorters) {
914
- if (isArray(sorters) && sorters.length > 0) {
915
- let dataAccessor;
916
- let fieldValueMapper;
917
- if (sorters.every((x) => x.field && x.value == null)) {
918
- dataAccessor = (x) => x.data;
919
- fieldValueMapper = (x) => ({
920
- bind: x.field,
921
- });
922
- } else {
923
- dataAccessor = (x) => x.store.getData();
924
- fieldValueMapper = (x) => ({
925
- bind: this.recordName + "." + x.field,
926
- });
969
+ createInstance(key, parent, parentStore) {
970
+ return new Instance(this, key, parent, parentStore);
971
+ }
972
+ initInstance(_context, _instance) {}
973
+ initState(_context, _instance) {}
974
+ checkVisible(_context, _instance, data) {
975
+ return data.visible;
976
+ }
977
+ explore(context, instance, data) {
978
+ if (this.components) {
979
+ instance.components = {};
980
+ for (const cmp in this.components) {
981
+ const ins = instance.getChild(context, this.components[cmp], "cmp-" + cmp, instance.store);
982
+ if (ins.scheduleExploreIfVisible(context)) instance.components[cmp] = ins;
927
983
  }
928
- this.sorter = sorter(
929
- sorters.map((x) => {
930
- const s = Object.assign({}, x);
931
- if (s.field && s.value == null) {
932
- s.value = fieldValueMapper(s);
933
- }
934
- if (!s.comparer) {
935
- s.comparer = this.getComparer(isDefined(s.sortOptions) ? s.sortOptions : this.sortOptions);
936
- }
937
- return s;
938
- }),
939
- dataAccessor,
940
- );
941
- } else {
942
- this.sorter = undefined;
943
984
  }
944
985
  }
945
- sort(sorters) {
946
- if (sorters) {
947
- this.buildSorter(sorters);
948
- }
986
+ render(context, instance, key) {
987
+ Console.log(this);
988
+ throw new Error(
989
+ 'Widget\'s render method should be overridden. This error usually happens if with incorrect imports, i.e. import { TextField } from "cx/data". Please check the console for details about the component configuration.',
990
+ );
991
+ }
992
+ update() {
993
+ this.version = (this.version || 0) + 1;
994
+ }
995
+ applyParentStore(instance) {
996
+ instance.store = instance.parentStore;
997
+ // check when this is actually needed, perhaps this is needed only for tables and repeated elements
998
+ // if (instance.cached) delete instance.cached.rawData; // force prepareData to execute again
999
+ }
1000
+ static resetCounter() {
1001
+ widgetId = 100;
1002
+ }
1003
+ }
1004
+ Widget.prototype.visible = true;
1005
+ Widget.prototype.memoize = true; //cache rendered content and use it if possible
1006
+ Widget.prototype.CSS = CSS;
1007
+ Widget.prototype.styled = false;
1008
+ Widget.namespace = "ui.";
1009
+ Widget.optimizePrepare = true;
1010
+ Widget.factory = (type, _config, _more) => {
1011
+ throw new Error(`Invalid widget type: ${type}.`);
1012
+ };
1013
+ function contentAppend(result, w, prependSpace) {
1014
+ if (w == null || w === false) return false;
1015
+ if (isArray(w)) w.forEach((c) => contentAppend(result, c));
1016
+ else if (isDefined(w.content) && !w.atomic) return contentAppend(result, w.content, false);
1017
+ else {
1018
+ if (prependSpace) result.push(" ");
1019
+ result.push(w);
949
1020
  }
1021
+ return true;
1022
+ }
1023
+ function getContentArray(x) {
1024
+ let result = [];
1025
+ contentAppend(result, x, false);
1026
+ return result;
1027
+ }
1028
+ function getContent(x) {
1029
+ let result = getContentArray(x);
1030
+ if (result.length == 0) return null;
1031
+ if (result.length == 1) return result[0];
1032
+ return result;
950
1033
  }
951
- ArrayAdapter.prototype.immutable = false;
952
- ArrayAdapter.prototype.sealed = false;
953
- ArrayAdapter.prototype.keyField = null;
954
- ArrayAdapter.prototype.cacheByKeyField = true;
955
- ArrayAdapter.prototype.isTreeAdapter = false;
956
- ArrayAdapter.autoInit = true;
957
1034
 
958
- class UseParentLayout extends PureContainer {}
959
- UseParentLayout.prototype.noLayout = true;
960
- UseParentLayout.prototype.useParentLayout = true;
1035
+ class StaticText extends Widget {
1036
+ render() {
1037
+ return this.text;
1038
+ }
1039
+ }
1040
+ Widget.alias("static-text", StaticText);
961
1041
 
962
- class Repeater extends ContainerBase {
963
- recordsAccessor;
964
- item;
965
- filter;
1042
+ class Text extends Widget {
1043
+ constructor(config) {
1044
+ super(config);
1045
+ }
1046
+ init() {
1047
+ if (!this.value && (this.tpl || this.expr || this.bind))
1048
+ this.value = {
1049
+ tpl: this.tpl,
1050
+ expr: this.expr,
1051
+ bind: this.bind,
1052
+ };
1053
+ super.init();
1054
+ }
966
1055
  declareData(...args) {
967
1056
  super.declareData(
968
1057
  {
969
- records: undefined,
970
- sorters: undefined,
971
- sortField: undefined,
972
- sortDirection: undefined,
973
- filterParams: {
974
- structured: true,
975
- },
1058
+ value: undefined,
976
1059
  },
977
1060
  ...args,
978
1061
  );
979
1062
  }
980
- init() {
981
- this.recordsAccessor = getAccessor(this.records);
982
- if (this.recordAlias) this.recordName = this.recordAlias;
983
- if (this.indexAlias) this.indexName = this.indexAlias;
984
- this.dataAdapter = ArrayAdapter.create({
985
- ...this.dataAdapter,
986
- recordName: this.recordName,
987
- indexName: this.indexName,
988
- keyField: this.keyField,
989
- immutable: this.immutable,
990
- sealed: this.sealed,
991
- recordsAccessor: this.recordsAccessor,
992
- sortOptions: this.sortOptions,
993
- });
994
- this.item = PureContainer.create({
995
- children: this.items || this.children,
996
- layout: UseParentLayout,
997
- });
1063
+ render(context, { data }, key) {
1064
+ return data.value != null ? data.value : "";
1065
+ }
1066
+ }
1067
+ Widget.alias("text", Text);
1068
+
1069
+ function exploreChildren(context, instance, children, previousResult, key, store) {
1070
+ let newChildren = previousResult || [];
1071
+ let oldChildren = previousResult || newChildren;
1072
+ let identical = previousResult ? 0 : -1;
1073
+ for (let c = 0; c < children.length; c++) {
1074
+ let cell = instance.getChild(context, children[c], key, store);
1075
+ if (cell.checkVisible(context)) {
1076
+ if (identical >= 0) {
1077
+ if (cell == oldChildren[identical]) identical++;
1078
+ else {
1079
+ newChildren = newChildren.slice(0, identical);
1080
+ identical = -1;
1081
+ newChildren.push(cell);
1082
+ }
1083
+ } else newChildren.push(cell);
1084
+ context.exploreStack.push(cell);
1085
+ if (cell.needsExploreCleanup) context.exploreStack.push(cell);
1086
+ }
1087
+ }
1088
+ if (identical >= 0 && identical != newChildren.length) newChildren = newChildren.slice(0, identical);
1089
+ return newChildren;
1090
+ }
1091
+
1092
+ // Base class for extending with custom Config types
1093
+ class ContainerBase extends Widget {
1094
+ init(context) {
1095
+ if (typeof this.ws !== "undefined") this.preserveWhitespace = this.ws;
1096
+ if (this.preserveWhitespace) this.trimWhitespace = false;
1097
+ let items = this.items || this.children || [];
998
1098
  delete this.children;
999
1099
  this.items = [];
1100
+ if (this.layout) {
1101
+ let layout = Container.create(this.layout, {
1102
+ items,
1103
+ });
1104
+ layout.init(context);
1105
+ this.layout = null;
1106
+ if ("noLayout" in layout && layout.noLayout) {
1107
+ this.useParentLayout = true;
1108
+ this.add(items);
1109
+ } else {
1110
+ this.add(layout);
1111
+ this.layout = layout;
1112
+ }
1113
+ } else {
1114
+ this.add(items);
1115
+ }
1000
1116
  super.init();
1001
1117
  }
1002
- initInstance(context, instance) {
1003
- this.dataAdapter.initInstance(context, instance);
1118
+ exploreItems(context, instance, items) {
1119
+ instance.children = exploreChildren(context, instance, items, instance.cached.children, null, instance.store);
1120
+ if (instance.cache("children", instance.children)) instance.markShouldUpdate(context);
1004
1121
  }
1005
- applyParentStore(instance) {
1006
- super.applyParentStore(instance);
1007
- // force prepareData to execute again and propagate the store change to the records
1008
- if (instance.cached) delete instance.cached.rawData;
1122
+ explore(context, instance) {
1123
+ super.explore(context, instance);
1124
+ this.exploreItems(context, instance, this.items);
1009
1125
  }
1010
- prepareData(context, instance) {
1011
- let { data } = instance;
1012
- if (data.sortField)
1013
- data.sorters = [
1014
- {
1015
- field: data.sortField,
1016
- direction: data.sortDirection || "ASC",
1017
- },
1018
- ];
1019
- this.dataAdapter.sort(data.sorters);
1020
- let filter = null;
1021
- if (this.onCreateFilter) filter = instance.invoke("onCreateFilter", data.filterParams, instance);
1022
- else if (this.filter) filter = (item) => this.filter(item, data.filterParams);
1023
- this.dataAdapter.setFilter(filter);
1024
- instance.mappedRecords = this.dataAdapter.getRecords(context, instance, data.records, instance.store);
1025
- if (this.onTrackMappedRecords) {
1026
- instance.invoke("onTrackMappedRecords", instance.mappedRecords, instance);
1027
- }
1028
- super.prepareData(context, instance);
1126
+ render(context, instance, key) {
1127
+ return this.renderChildren(context, instance, key);
1029
1128
  }
1030
- explore(context, instance, data) {
1031
- let instances = [];
1032
- instance.mappedRecords.forEach((record) => {
1033
- let subInstance = instance.getChild(context, this.item, record.key, record.store);
1034
- let changed = subInstance.cache("recordData", record.data) || subInstance.cache("key", record.key);
1035
- subInstance.record = record;
1036
- if (this.cached && !changed && subInstance.visible && !subInstance.childStateDirty) {
1037
- instances.push(subInstance);
1038
- subInstance.shouldUpdate = false;
1039
- } else if (subInstance.scheduleExploreIfVisible(context)) instances.push(subInstance);
1040
- });
1041
- instance.children = instances;
1129
+ renderChildren(_context, instance, key) {
1130
+ let preserveComplexContent = this.useParentLayout;
1131
+ function append(result, r) {
1132
+ if (r == null) return;
1133
+ //react element
1134
+ if (!r.hasOwnProperty("content")) {
1135
+ contentAppend(result, r);
1136
+ return;
1137
+ }
1138
+ if (r.useParentLayout) return r.content.forEach((x) => append(result, x));
1139
+ if (r.atomic || preserveComplexContent) {
1140
+ result.push(r);
1141
+ } else {
1142
+ let first = true;
1143
+ for (let k in r) if (contentAppend(result, r[k], !first)) first = false;
1144
+ }
1145
+ }
1146
+ let result = [];
1147
+ for (let i = 0; i < instance.children.length; i++) {
1148
+ append(result, instance.children[i].vdom);
1149
+ }
1150
+ if (this.useParentLayout)
1151
+ return {
1152
+ useParentLayout: true,
1153
+ content: result,
1154
+ };
1155
+ return result;
1042
1156
  }
1043
- }
1044
- Repeater.prototype.recordName = "$record";
1045
- Repeater.prototype.indexName = "$index";
1046
- Repeater.prototype.cached = false;
1047
- Repeater.prototype.immutable = false;
1048
- Repeater.prototype.sealed = false;
1049
- Repeater.prototype.isPureContainer = true;
1050
- Widget.alias("repeater", Repeater);
1051
-
1052
- class StructuredInstanceDataAccessor {
1053
- instance;
1054
- dataConfig;
1055
- useParentStore;
1056
- dataSelector;
1057
- constructor(config) {
1058
- this.instance = config.instance;
1059
- this.dataConfig = config.data;
1060
- this.useParentStore = config.useParentStore;
1061
- this.dataSelector = getSelector(config.data);
1062
- if (this.dataSelector.memoize) this.dataSelector = this.dataSelector.memoize();
1157
+ clear() {
1158
+ if (this.layout) this.layout.clear();
1159
+ else this.items = [];
1063
1160
  }
1064
- getSelector() {
1065
- return this.dataSelector;
1161
+ add(...args) {
1162
+ if (this.layout) return this.layout.add(...args);
1163
+ for (let a of args) {
1164
+ if (!a) continue;
1165
+ if (isArray(a)) {
1166
+ for (let c of a) this.add(c);
1167
+ } else if (isString(a)) {
1168
+ if (this.trimWhitespace) a = innerTextTrim(a);
1169
+ if (a) this.addText(a);
1170
+ } else if (a.isComponent) this.items.push(this.wrapItem(a));
1171
+ else {
1172
+ this.add(Widget.create(a, this.itemDefaults, {}));
1173
+ }
1174
+ }
1066
1175
  }
1067
- get() {
1068
- return this.dataSelector.get(this.instance.store.getData());
1176
+ wrapItem(item) {
1177
+ return item;
1069
1178
  }
1070
- setItem(key, value) {
1071
- return this.instance.nestedDataSet(key, value, this.dataConfig, this.useParentStore);
1179
+ addText(text) {
1180
+ if (this.plainText || text.indexOf("{") == -1 || text.indexOf("}") == -1)
1181
+ this.add(
1182
+ Widget.create(
1183
+ StaticText,
1184
+ {
1185
+ text: text,
1186
+ },
1187
+ {},
1188
+ ),
1189
+ );
1190
+ else
1191
+ this.add(
1192
+ Widget.create(
1193
+ Text,
1194
+ {
1195
+ text: {
1196
+ tpl: text,
1197
+ },
1198
+ },
1199
+ {},
1200
+ ),
1201
+ );
1072
1202
  }
1073
- containsKey(key) {
1074
- return this.dataConfig.hasOwnProperty(key);
1203
+ find(filter, options) {
1204
+ if (!options) options = {};
1205
+ if (!filter || !this.items) return [];
1206
+ let alias = filter;
1207
+ if (isString(filter)) filter = (w) => w.componentAlias == alias;
1208
+ if (filter.isComponentType) filter = (w) => w instanceof alias;
1209
+ let results = [];
1210
+ for (let i = 0; i < this.items.length; i++) {
1211
+ let w = this.items[i];
1212
+ if (w && !w.initialized) w.init();
1213
+ if (filter(w)) {
1214
+ results.push(w);
1215
+ if (options.first) break;
1216
+ }
1217
+ if (w && w.find) results.push(...w.find(filter, options));
1218
+ }
1219
+ return results;
1075
1220
  }
1076
- getKeys() {
1077
- return Object.keys(this.dataConfig);
1221
+ findFirst(filter, options) {
1222
+ return this.find(filter, {
1223
+ ...options,
1224
+ first: true,
1225
+ })[0];
1078
1226
  }
1079
1227
  }
1228
+ ContainerBase.prototype.trimWhitespace = true;
1229
+ ContainerBase.prototype.plainText = true;
1230
+ ContainerBase.prototype.styled = false;
1231
+ // Closed type for direct usage - preserves ControllerProp ThisType
1232
+ class Container extends ContainerBase {}
1233
+ // Base class for styled containers with custom Config types
1234
+ class StyledContainerBase extends ContainerBase {}
1235
+ StyledContainerBase.prototype.styled = true;
1236
+ // Closed type for direct usage
1237
+ class StyledContainer extends StyledContainerBase {}
1080
1238
 
1081
- class Rescope extends PureContainerBase {
1082
- init() {
1083
- this.binding = Binding.get(this.bind);
1084
- if (this.rootAlias) this.rootName = this.rootAlias;
1085
- super.init();
1086
- }
1087
- initInstance(context, instance) {
1088
- instance.store = new ZoomIntoPropertyView({
1089
- store: instance.parentStore,
1090
- binding: this.binding,
1091
- rootName: this.rootName,
1092
- nestedData: isObject(this.data)
1093
- ? new StructuredInstanceDataAccessor({
1094
- instance,
1095
- data: this.data,
1096
- useParentStore: true,
1097
- })
1098
- : undefined,
1099
- });
1100
- super.initInstance(context, instance);
1239
+ // Base class for extending with custom Config types
1240
+ class PureContainerBase extends ContainerBase {}
1241
+ PureContainerBase.prototype.isPureContainer = true;
1242
+ // Closed type for direct usage - preserves ControllerProp ThisType
1243
+ class PureContainer extends PureContainerBase {}
1244
+ PureContainer.alias("pure-container", PureContainer);
1245
+
1246
+ class DataAdapter extends Component {
1247
+ constructor(config) {
1248
+ super(config);
1101
1249
  }
1102
- applyParentStore(instance) {
1103
- instance.store.setStore(instance.parentStore);
1250
+ setFilter(filterFn) {
1251
+ this.filterFn = filterFn;
1104
1252
  }
1253
+ sort(sorters) {}
1105
1254
  }
1106
- Rescope.prototype.bind = "$page";
1107
- Rescope.prototype.rootName = "$root";
1108
- Widget.alias("rescope", Rescope);
1109
-
1110
- let isBatching = 0;
1111
- let promiseSubscribers = new SubscriberList();
1112
- function batchUpdates(callback) {
1113
- if (VDOM.DOM.unstable_batchedUpdates)
1114
- VDOM.DOM.unstable_batchedUpdates(() => {
1115
- isBatching++;
1116
- try {
1117
- callback();
1118
- } finally {
1119
- isBatching--;
1120
- }
1121
- });
1122
- else callback();
1123
- }
1124
- function isBatchingUpdates() {
1125
- return isBatching > 0;
1126
- }
1127
- function notifyBatchedUpdateStarting() {
1128
- promiseSubscribers.execute((x) => {
1129
- x.pending++;
1130
- });
1131
- }
1132
- function notifyBatchedUpdateCompleted() {
1133
- promiseSubscribers.execute((x) => {
1134
- let cb = x;
1135
- cb.finished++;
1136
- if (cb.finished >= cb.pending) cb.complete(true);
1137
- });
1138
- }
1139
- function batchUpdatesAndNotify(callback, notifyCallback, timeout = 1000) {
1140
- let done = false;
1141
- let timer;
1142
- let unsubscribe;
1143
- const update = {
1144
- pending: 0,
1145
- finished: 0,
1146
- complete: (success) => {
1147
- if (!done) {
1148
- done = true;
1149
- if (timer) clearInterval(timer);
1150
- if (unsubscribe) unsubscribe();
1151
- notifyCallback(!!success);
1152
- }
1153
- },
1154
- };
1155
- unsubscribe = promiseSubscribers.subscribe(update);
1156
- batchUpdates(callback);
1157
- if (update.pending <= update.finished) update.complete(true);
1158
- else timer = setTimeout(update.complete, timeout);
1159
- }
1255
+ DataAdapter.prototype.recordName = "$record";
1256
+ DataAdapter.prototype.indexName = "$index";
1257
+ DataAdapter.prototype.immutable = false;
1160
1258
 
1161
- /*
1162
- * Purpose of FocusManager is to provide focusout notifications.
1163
- * IE and Firefox do not provide relatedTarget info in blur events which makes it impossible
1164
- * to determine if focus went outside or stayed inside the component.
1165
- */
1166
- let subscribers$3 = new SubscriberList(),
1167
- timerInterval = 300,
1168
- timerId = null;
1169
- let lastActiveElement = null;
1170
- let pending = false;
1171
- class FocusManager {
1172
- static subscribe(callback) {
1173
- let unsubscribe = subscribers$3.subscribe(callback);
1174
- checkTimer();
1175
- return unsubscribe;
1259
+ let contents = {};
1260
+ let localizations = {};
1261
+ let overrides = {};
1262
+ let defaults = {};
1263
+ let trackDefaults = false;
1264
+ class Localization {
1265
+ static register(key) {
1266
+ return (type) => {
1267
+ this.registerPrototype(key, type);
1268
+ return type;
1269
+ };
1176
1270
  }
1177
- static onFocusOut(el, callback) {
1178
- let active = isSelfOrDescendant(el, getActiveElement());
1179
- return this.subscribe((focusedEl) => {
1180
- if (!active) active = isSelfOrDescendant(el, getActiveElement());
1181
- else if (!isSelfOrDescendant(el, focusedEl)) {
1182
- active = false;
1183
- callback(focusedEl);
1184
- }
1185
- });
1271
+ static registerPrototype(key, type) {
1272
+ contents[key] = type.prototype;
1273
+ if (overrides[key]) this.override(key, overrides[key]);
1186
1274
  }
1187
- static oneFocusOut(el, callback) {
1188
- this.nudge();
1189
- let off = this.subscribe((focusedEl) => {
1190
- if (!isSelfOrDescendant(el, focusedEl)) {
1191
- callback(focusedEl);
1192
- off();
1193
- }
1194
- });
1195
- return off;
1275
+ static trackDefaults() {
1276
+ trackDefaults = true;
1196
1277
  }
1197
- static nudge() {
1198
- if (typeof document !== "undefined" && getActiveElement() !== lastActiveElement) {
1199
- if (!pending) {
1200
- pending = true;
1201
- setTimeout(function () {
1202
- pending = false;
1203
- if (getActiveElement() !== lastActiveElement) {
1204
- lastActiveElement = getActiveElement();
1205
- batchUpdates(() => {
1206
- subscribers$3.notify(lastActiveElement);
1207
- });
1208
- checkTimer();
1209
- }
1210
- }, 0);
1211
- }
1278
+ static restoreDefaults() {
1279
+ for (let type in defaults) {
1280
+ let proto = contents[type];
1281
+ if (!proto) continue;
1282
+ let d = defaults[type];
1283
+ for (let key in d) proto[key] = d[key];
1212
1284
  }
1285
+ defaults = {};
1213
1286
  }
1214
- static focus(el) {
1215
- el.focus();
1216
- this.nudge();
1217
- }
1218
- static focusFirst(el) {
1219
- let focusable = findFirst(el, isFocusable);
1220
- if (focusable) this.focus(focusable);
1221
- return focusable;
1222
- }
1223
- static focusFirstChild(el) {
1224
- let focusable = findFirstChild(el, isFocusable);
1225
- if (focusable) this.focus(focusable);
1226
- return focusable;
1227
- }
1228
- static focusNext(el) {
1229
- let next = el,
1230
- skip = true;
1231
- do {
1232
- if (!skip) {
1233
- let focusable = this.focusFirst(next);
1234
- if (focusable) return focusable;
1235
- }
1236
- if (next.nextSibling) {
1237
- next = next.nextSibling;
1238
- skip = false;
1239
- } else {
1240
- next = next.parentNode;
1241
- skip = true;
1287
+ static override(key, values) {
1288
+ overrides[key] = values;
1289
+ let p = contents[key];
1290
+ if (p) {
1291
+ if (trackDefaults && !defaults[key]) {
1292
+ let d = (defaults[key] = {});
1293
+ for (let key in values) d[key] = p[key];
1242
1294
  }
1243
- } while (next);
1295
+ Object.assign(p, values);
1296
+ }
1244
1297
  }
1245
- static setInterval(interval) {
1246
- timerInterval = interval;
1247
- checkTimer();
1298
+ static localize(culture, key, values) {
1299
+ let l = localizations[culture];
1300
+ if (!l) l = localizations[culture] = {};
1301
+ l[key] = {
1302
+ ...l[key],
1303
+ ...values,
1304
+ };
1248
1305
  }
1249
- }
1250
- function oneFocusOut(component, el, callback) {
1251
- if (!component.oneFocusOut)
1252
- component.oneFocusOut = FocusManager.oneFocusOut(el, (focus) => {
1253
- delete component.oneFocusOut;
1254
- callback(focus);
1255
- });
1256
- }
1257
- function offFocusOut(component) {
1258
- if (component.oneFocusOut) {
1259
- component.oneFocusOut();
1260
- delete component.oneFocusOut;
1306
+ static setCulture(culture) {
1307
+ var l = localizations[culture];
1308
+ if (l) {
1309
+ for (var key in l) {
1310
+ var content = contents[key];
1311
+ if (content) Object.assign(content, l[key]);
1312
+ }
1313
+ }
1261
1314
  }
1262
1315
  }
1263
- function preventFocus(e) {
1264
- //Focus can be prevented only on mousedown event. On touchstart this will not work
1265
- //preventDefault cannot be used as it prevents scrolling
1266
- if (e.type !== "mousedown") return;
1267
- e.preventDefault();
1268
- unfocusElement(e.currentTarget, false);
1316
+
1317
+ // @ts-expect-error
1318
+ let stack = [
1319
+ {
1320
+ culture: "en",
1321
+ numberCulture: null,
1322
+ dateTimeCulture: null,
1323
+ cache: {},
1324
+ defaultCurrency: "USD",
1325
+ dateEncoding: (date) => date.toISOString(),
1326
+ timezone: null,
1327
+ },
1328
+ ];
1329
+ function getDefaultCulture() {
1330
+ return stack[0];
1269
1331
  }
1270
- function checkTimer() {
1271
- let shouldRun = !subscribers$3.isEmpty();
1272
- if (shouldRun && !timerId)
1273
- timerId = setInterval(() => {
1274
- FocusManager.nudge();
1275
- }, timerInterval);
1276
- if (!shouldRun && timerId) {
1277
- clearInterval(timerId);
1278
- timerId = null;
1279
- }
1332
+ function getCurrentCulture() {
1333
+ return stack[stack.length - 1];
1280
1334
  }
1281
- function preventFocusOnTouch(e, force = false) {
1282
- if (force || isTouchEvent()) preventFocus(e);
1335
+ function getCurrentCultureCache() {
1336
+ return getCurrentCulture().cache;
1283
1337
  }
1284
- function unfocusElement(target = null, unfocusParentOverlay = true) {
1285
- const activeElement = getActiveElement();
1286
- if (!target) target = activeElement;
1287
- if (unfocusParentOverlay) {
1288
- let focusableOverlayContainer = closestParent(target, (el) => el.dataset?.focusableOverlayContainer);
1289
- if (focusableOverlayContainer) target = focusableOverlayContainer;
1338
+ function pushCulture(cultureInfo) {
1339
+ stack.push(cultureInfo);
1340
+ }
1341
+ function createCulture(cultureSpecs) {
1342
+ let current = getCurrentCulture();
1343
+ let info = {
1344
+ culture: current.culture,
1345
+ dateEncoding: current.dateEncoding,
1346
+ defaultCurrency: current.defaultCurrency,
1347
+ cache: {},
1348
+ };
1349
+ for (let key in cultureSpecs) {
1350
+ if (!cultureSpecs[key]) continue;
1351
+ info[key] = cultureSpecs[key];
1290
1352
  }
1291
- //find the closest focusable parent of the target element and focus it instead
1292
- let focusableParent = closestParent(
1293
- target,
1294
- (el) => isFocusable(el) && (!unfocusParentOverlay || !!el.dataset?.focusableOverlayContainer),
1295
- );
1296
- if (focusableParent && focusableParent !== document.body)
1297
- focusableParent.focus({
1298
- preventScroll: true,
1299
- });
1300
- else activeElement.blur();
1301
- FocusManager.nudge();
1353
+ return info;
1302
1354
  }
1303
-
1304
- let subscribers$2 = new SubscriberList();
1305
- class ResizeManager {
1306
- static subscribe(callback) {
1307
- return subscribers$2.subscribe(callback);
1355
+ function popCulture(cultureSpecs) {
1356
+ if (stack.length == 1) throw new Error("Cannot pop the last culture object.");
1357
+ if (cultureSpecs && stack[stack.length - 1] !== cultureSpecs) {
1358
+ Console.warn("Popped culture object does not match the current one.");
1308
1359
  }
1309
- static notify() {
1310
- batchUpdates(() => {
1311
- subscribers$2.notify();
1312
- });
1360
+ return stack.pop();
1361
+ }
1362
+ class Culture {
1363
+ static setCulture(cultureCode) {
1364
+ let cultureSpecs = getDefaultCulture();
1365
+ cultureSpecs.culture = cultureCode;
1366
+ cultureSpecs.cache = {};
1367
+ Localization.setCulture(cultureCode);
1368
+ this.invalidateCache();
1313
1369
  }
1314
- static trackElement(el, callback) {
1315
- if (typeof ResizeObserver !== "function") return this.subscribe(() => callback([]));
1316
- let obs = new ResizeObserver(callback);
1317
- obs.observe(el);
1318
- return () => {
1319
- obs.disconnect();
1320
- };
1370
+ static setNumberCulture(cultureCode) {
1371
+ let cultureSpecs = getDefaultCulture();
1372
+ cultureSpecs.numberCulture = cultureCode;
1373
+ delete cultureSpecs.cache.numberCulture;
1374
+ this.invalidateCache();
1321
1375
  }
1322
- }
1323
- if (typeof window != "undefined") window.addEventListener("resize", () => ResizeManager.notify());
1324
-
1325
- let lastZIndex = 10000;
1326
- class ZIndexManager {
1327
- static next() {
1328
- return ++lastZIndex;
1376
+ static setDateTimeCulture(cultureCode) {
1377
+ let cultureSpecs = getDefaultCulture();
1378
+ cultureSpecs.dateTimeCulture = cultureCode;
1379
+ delete cultureSpecs.cache.dateTimeCulture;
1380
+ this.invalidateCache();
1329
1381
  }
1330
- static reset(zIndex) {
1331
- lastZIndex = zIndex;
1382
+ static setDefaultCurrency(currencyCode) {
1383
+ let cultureSpecs = getDefaultCulture();
1384
+ cultureSpecs.defaultCurrency = currencyCode;
1385
+ this.invalidateCache();
1332
1386
  }
1333
- }
1334
-
1335
- const Format = Format$1;
1336
- let cultureSensitiveFormatsRegistered = false;
1337
- function resolveNumberFormattingFlags(flags) {
1338
- if (!flags) return null;
1339
- let result = {};
1340
- if (flags.indexOf("+") >= 0) result.signDisplay = "exceptZero";
1341
- if (flags.indexOf("c") >= 0) result.notation = "compact";
1342
- if (flags.indexOf("a") >= 0) result.currencySign = "accounting";
1343
- return result;
1344
- }
1345
- function enableCultureSensitiveFormatting() {
1346
- if (cultureSensitiveFormatsRegistered) return;
1347
- cultureSensitiveFormatsRegistered = true;
1348
- Format$1.registerFactory(["number", "n"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1349
- let culture = Culture.getNumberCulture();
1350
- let formatter = culture.getFormatter({
1351
- ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1352
- ...resolveNumberFormattingFlags(flags),
1353
- });
1354
- return (value) => formatter.format(value);
1355
- });
1356
- Format$1.registerFactory("currency", (format, currency, minimumFractionDigits, maximumFractionDigits, flags) => {
1357
- let culture = Culture.getNumberCulture();
1358
- currency = currency || Culture.defaultCurrency;
1359
- let formatter = culture.getFormatter({
1360
- style: "currency",
1361
- currency: currency,
1362
- ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1363
- ...resolveNumberFormattingFlags(flags),
1364
- });
1365
- return (value) => formatter.format(value);
1366
- });
1367
- Format$1.registerFactory(["percentage", "p", "%"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1368
- let culture = Culture.getNumberCulture();
1369
- let formatter = culture.getFormatter({
1370
- style: "percent",
1371
- ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1372
- ...resolveNumberFormattingFlags(flags),
1373
- });
1374
- return (value) => formatter.format(value);
1375
- });
1376
- Format$1.registerFactory(["percentSign", "ps"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1377
- let culture = Culture.getNumberCulture();
1378
- let formatter = culture.getFormatter({
1379
- style: "percent",
1380
- ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1381
- ...resolveNumberFormattingFlags(flags),
1382
- });
1383
- return (value) => formatter.format(value / 100);
1384
- });
1385
- Format$1.registerFactory(["date", "d"], (fmt, format = "yyyyMMdd") => {
1386
- let culture = Culture.getDateTimeCulture();
1387
- let formatter = culture.getFormatter(format);
1388
- return (value) => formatter.format(parseDateInvariant(value));
1389
- });
1390
- Format$1.registerFactory(["time", "t"], (fmt, format = "hhmmss") => {
1391
- let culture = Culture.getDateTimeCulture();
1392
- let formatter = culture.getFormatter(format);
1393
- return (value) => formatter.format(parseDateInvariant(value));
1394
- });
1395
- Format$1.registerFactory(["datetime", "dt"], (fmt, format = "yyyyMd hhmm") => {
1396
- let culture = Culture.getDateTimeCulture();
1397
- let formatter = culture.getFormatter(format);
1398
- return (value) => formatter.format(parseDateInvariant(value));
1399
- });
1400
- setGetFormatCacheCallback(() => {
1401
- let cache = getCurrentCultureCache();
1402
- if (!cache.formatCache) cache.formatCache = {};
1403
- return cache.formatCache;
1404
- });
1405
- setGetExpressionCacheCallback(() => {
1406
- let cache = getCurrentCultureCache();
1407
- if (!cache.exprCache) cache.exprCache = {};
1408
- return cache.exprCache;
1409
- });
1410
- setGetStringTemplateCacheCallback(() => {
1411
- let cache = getCurrentCultureCache();
1412
- if (!cache.strTplCache) cache.strTplCache = {};
1413
- return cache.strTplCache;
1414
- });
1415
- GlobalCacheIdentifier.change();
1416
- }
1417
-
1418
- let instanceId = 1000;
1419
- /**
1420
- * Base Instance class
1421
- */
1422
- class Instance {
1423
- // Selectors
1424
- dataSelector;
1425
- // Lifecycle flags
1426
- initialized;
1427
- destroySubscriptions;
1428
- helpers;
1429
- components;
1430
- constructor(widget, key, parent, parentStore) {
1431
- this.widget = widget;
1432
- this.key = key;
1433
- this.id = String(++instanceId);
1434
- this.cached = {};
1435
- this.parent = parent;
1436
- this.parentStore = parentStore ?? parent?.store;
1437
- if (this.parentStore == null) throw new Error("Cannot create instance without a parent store.");
1387
+ static setDefaultTimezone(timezone) {
1388
+ let cultureSpecs = getDefaultCulture();
1389
+ cultureSpecs.timezone = timezone;
1390
+ this.invalidateCache();
1438
1391
  }
1439
- setParentStore(parentStore) {
1440
- this.parentStore = parentStore;
1441
- this.widget.applyParentStore(this);
1392
+ static setDefaultDateEncoding(encoding) {
1393
+ let cultureSpecs = getDefaultCulture();
1394
+ cultureSpecs.dateEncoding = encoding;
1395
+ this.invalidateCache();
1442
1396
  }
1443
- init(context) {
1444
- // widget is initialized when the first instance is initialized
1445
- if (!this.widget.initialized) {
1446
- this.widget.init();
1447
- this.widget.initialized = true;
1448
- }
1449
- if (!this.dataSelector) {
1450
- this.widget.selector.init(this.parentStore);
1451
- this.dataSelector = this.widget.selector.createStoreSelector();
1452
- }
1453
- // init instance might change the store, so this must go before the controller initialization
1454
- this.widget.initInstance(context, this);
1455
- // initInstance can set the store, otherwise use parent store
1456
- if (!this.store) this.store = this.parentStore;
1457
- if (this.widget.onInit) this.widget.onInit(context, this);
1458
- this.widget.initState(context, this);
1459
- if (this.widget.controller)
1460
- this.controller = Controller.create(this.widget.controller, {
1461
- widget: this.widget,
1462
- instance: this,
1463
- store: this.store,
1464
- });
1465
- if (
1466
- this.widget.exploreCleanup ||
1467
- this.widget.outerLayout ||
1468
- this.widget.isContent ||
1469
- this.widget.controller ||
1470
- this.widget.prepareCleanup
1471
- )
1472
- this.needsExploreCleanup = true;
1473
- if (this.widget.prepare || this.widget.controller) this.needsPrepare = true;
1474
- if (this.widget.cleanup || this.widget.controller) this.needsCleanup = true;
1475
- this.initialized = true;
1397
+ static invalidateCache() {
1398
+ GlobalCacheIdentifier.change();
1399
+ invalidateExpressionCache();
1400
+ invalidateStringTemplateCache();
1476
1401
  }
1477
- checkVisible(context) {
1478
- if (!this.initialized) this.init(context);
1479
- const wasVisible = this.visible;
1480
- this.rawData = this.dataSelector(this.store);
1481
- this.visible = this.widget.checkVisible(context, this, this.rawData);
1482
- if (this.visible && !this.detached) this.parent.instanceCache.addChild(this);
1483
- this.explored = false;
1484
- this.prepared = false;
1485
- if (!this.visible && wasVisible) this.destroy();
1486
- return this.visible;
1402
+ static get defaultCurrency() {
1403
+ return getCurrentCulture().defaultCurrency;
1487
1404
  }
1488
- scheduleExploreIfVisible(context) {
1489
- if (this.checkVisible(context)) {
1490
- context.exploreStack.push(this);
1491
- if (this.needsExploreCleanup) context.exploreStack.push(this);
1492
- return true;
1493
- }
1494
- return false;
1405
+ static get culture() {
1406
+ return getCurrentCulture().culture;
1495
1407
  }
1496
- cache(key, value) {
1497
- const oldValue = this.cached[key];
1498
- if (oldValue === value) return false;
1499
- if (!this.cacheList) this.cacheList = {};
1500
- this.cacheList[key] = value;
1501
- return true;
1408
+ static getNumberCulture() {
1409
+ let { cache, numberCulture, culture } = getCurrentCulture();
1410
+ if (!cache.numberCulture) cache.numberCulture = new NumberCulture(numberCulture ?? culture);
1411
+ return cache.numberCulture;
1502
1412
  }
1503
- markShouldUpdate(context) {
1504
- let ins = this;
1505
- let renderList = this.renderList;
1506
- renderList.markReverseIndex();
1507
- //notify all parents that child state changed to bust up caching
1508
- while (ins && !ins.shouldUpdate && ins.explored) {
1509
- if (ins.renderList !== renderList) {
1510
- renderList.reverse();
1511
- renderList = ins.renderList;
1512
- renderList.markReverseIndex();
1513
- }
1514
- ins.shouldUpdate = true;
1515
- renderList.data.push(ins);
1516
- ins = ins.widget.isContent
1517
- ? ins.contentPlaceholder
1518
- : ins.parent?.outerLayout === ins
1519
- ? ins.parent?.parent
1520
- : ins.parent;
1521
- }
1522
- renderList.reverse();
1413
+ static getDateTimeCulture() {
1414
+ let { cache, dateTimeCulture, culture, timezone } = getCurrentCulture();
1415
+ if (!cache.dateTimeCulture)
1416
+ cache.dateTimeCulture = new DateTimeCulture(dateTimeCulture ?? culture, {
1417
+ defaultTimezone: timezone,
1418
+ });
1419
+ return cache.dateTimeCulture;
1523
1420
  }
1524
- explore(context) {
1525
- if (!this.visible) throw new Error("Explore invisible!");
1526
- if (this.explored) {
1527
- if (this.widget.prepareCleanup) context.prepareList.push(this);
1528
- if (this.widget.exploreCleanup) this.widget.exploreCleanup(context, this);
1529
- if (this.parent?.outerLayout === this) context.popNamedValue("content", "body");
1530
- if (this.widget.controller) context.pop("controller");
1531
- return;
1532
- }
1533
- this.explored = true;
1534
- if (this.needsPrepare) context.prepareList.push(this);
1535
- else this.prepared = true;
1536
- if (this.needsCleanup) context.cleanupList.push(this);
1537
- if (this.instanceCache) this.instanceCache.mark();
1538
- //controller may reconfigure the widget and need to go before shouldUpdate calculation
1539
- this.parentOptions = context.parentOptions;
1540
- if (!this.controller) {
1541
- if (context.controller) this.controller = context.controller;
1542
- else if (this.parent?.controller) this.controller = this.parent?.controller;
1543
- }
1544
- this.destroyTracked = false;
1545
- if (this.controller) {
1546
- if (this.widget.controller) {
1547
- if (!this.controller.initialized) {
1548
- this.controller.init(context);
1549
- this.controller.initialized = true;
1550
- }
1551
- context.push("controller", this.controller);
1552
- this.controller.explore(context);
1553
- if (this.controller.onDestroy && this.controller.widget == this.widget) this.trackDestroy();
1554
- }
1555
- }
1556
- if (this.widget.onDestroy || isNonEmptyArray(this.destroySubscriptions)) this.trackDestroy();
1557
- this.renderList = this.assignedRenderList || this.parent?.renderList || context.getRootRenderList();
1558
- let shouldUpdate =
1559
- this.rawData !== this.cached.rawData ||
1560
- this.state !== this.cached.state ||
1561
- this.widget.version !== this.cached.widgetVersion ||
1562
- this.cached.globalCacheIdentifier !== GlobalCacheIdentifier.get();
1563
- if (shouldUpdate) {
1564
- this.data = {
1565
- ...this.rawData,
1566
- };
1567
- this.widget.prepareData(context, this);
1568
- debug(processDataFlag, this.widget);
1569
- }
1570
- //onExplore might set the outer layout
1571
- if (this.widget.onExplore) this.widget.onExplore(context, this);
1572
- if (this.parent?.outerLayout === this) {
1573
- this.renderList = this.renderList.insertRight();
1574
- context.pushNamedValue("content", "body", this.parent);
1575
- }
1576
- if (this.widget.outerLayout) {
1577
- this.outerLayout = this.getChild(context, this.widget.outerLayout, null, this.store);
1578
- this.outerLayout.scheduleExploreIfVisible(context);
1579
- this.renderList = this.renderList.insertLeft();
1580
- }
1581
- if (this.widget.isContent) {
1582
- this.contentPlaceholder = context.contentPlaceholder && context.contentPlaceholder[this.widget.putInto];
1583
- if (this.contentPlaceholder) context.contentPlaceholder[this.widget.putInto](this);
1584
- else {
1585
- this.renderList = this.renderList.insertLeft();
1586
- context.pushNamedValue("content", this.widget.putInto, this);
1587
- if (!context.contentList) context.contentList = {};
1588
- let list = context.contentList[this.widget.putInto];
1589
- if (!list) list = context.contentList[this.widget.putInto] = [];
1590
- list.push(this);
1591
- }
1592
- }
1593
- this.shouldUpdate = false;
1594
- if (shouldUpdate || this.childStateDirty || !this.widget.memoize) this.markShouldUpdate(context);
1595
- context.exploreStack.hop();
1596
- if (this.widget.helpers) {
1597
- this.helpers = {};
1598
- for (let cmp in this.widget.helpers) {
1599
- let helper = this.widget.helpers[cmp];
1600
- if (helper) {
1601
- let ins = this.getChild(context, helper);
1602
- if (ins.scheduleExploreIfVisible(context)) this.helpers[cmp] = ins;
1603
- }
1604
- }
1605
- }
1606
- //TODO: check do we need to pass data here?
1607
- this.widget.explore(context, this, this.data);
1421
+ static getDefaultDateEncoding() {
1422
+ return getCurrentCulture().dateEncoding;
1608
1423
  }
1609
- prepare(context) {
1610
- if (!this.visible) throw new Error("Prepare invisible!");
1611
- if (this.prepared) {
1612
- if (this.widget.prepareCleanup) this.widget.prepareCleanup(context, this);
1613
- return;
1614
- }
1615
- this.prepared = true;
1616
- if (this.widget.prepare) this.widget.prepare(context, this);
1617
- if (this.widget.controller && this.controller?.prepare) this.controller.prepare(context);
1424
+ static getComparer(options) {
1425
+ let { culture } = getCurrentCulture();
1426
+ if (typeof Intl.Collator != "undefined") return new Intl.Collator(culture, options).compare;
1427
+ return defaultCompare;
1618
1428
  }
1619
- render(context) {
1620
- if (!this.visible) throw new Error("Render invisible!");
1621
- if (this.shouldUpdate) {
1622
- debug(renderFlag, this.widget, this.key);
1623
- const vdom = renderResultFix(this.widget.render(context, this, this.key));
1624
- if (this.widget.isContent || this.outerLayout) this.contentVDOM = vdom;
1625
- else this.vdom = vdom;
1626
- }
1627
- if (this.cacheList) {
1628
- for (const key in this.cacheList) {
1629
- this.cached[key] = this.cacheList[key];
1630
- }
1631
- }
1632
- this.cacheList = null;
1633
- this.cached.rawData = this.rawData;
1634
- this.cached.data = this.data;
1635
- this.cached.state = this.state;
1636
- this.cached.widgetVersion = this.widget.version;
1637
- this.cached.globalCacheIdentifier = GlobalCacheIdentifier.get();
1638
- this.childStateDirty = false;
1639
- if (this.instanceCache) this.instanceCache.sweep();
1640
- if (this.parent?.outerLayout === this) {
1641
- //if outer layouts are chained we need to find the originating element (last element with OL set)
1642
- let parent = this.parent;
1643
- while (parent.parent?.outerLayout == parent) parent = parent.parent;
1644
- parent.vdom = this.vdom;
1645
- }
1646
- return this.vdom;
1429
+ }
1430
+
1431
+ class ArrayAdapter extends DataAdapter {
1432
+ constructor(config) {
1433
+ super(config);
1647
1434
  }
1648
- cleanup(context) {
1649
- if (this.widget.controller && this.controller?.cleanup) this.controller.cleanup(context);
1650
- if (this.widget.cleanup) this.widget.cleanup(context, this);
1435
+ init() {
1436
+ this.recordsAccessor = this.recordsBinding ? getAccessor(this.recordsBinding) : getAccessor(this.recordsAccessor);
1437
+ this.recordName = this.recordName?.toString() || "$record";
1438
+ this.indexName = this.indexName?.toString() || "$index";
1651
1439
  }
1652
- trackDestroy() {
1653
- if (!this.destroyTracked) {
1654
- this.destroyTracked = true;
1655
- if (this.parent && !this.detached) this.parent.trackDestroyableChild(this);
1440
+ initInstance(context, instance) {
1441
+ if (!instance.recordStoreCache) {
1442
+ instance.recordStoreCache = new WeakMap();
1443
+ instance.cacheByKey = {};
1444
+ }
1445
+ if (!instance.recordsAccessor && this.recordsAccessor) {
1446
+ instance.recordsAccessor = this.recordsAccessor.bindInstance
1447
+ ? this.recordsAccessor.bindInstance(instance)
1448
+ : this.recordsAccessor;
1656
1449
  }
1657
1450
  }
1658
- trackDestroyableChild(child) {
1659
- this.instanceCache.trackDestroy(child);
1660
- this.trackDestroy();
1661
- }
1662
- subscribeOnDestroy(callback) {
1663
- if (!this.destroySubscriptions) this.destroySubscriptions = [];
1664
- this.destroySubscriptions.push(callback);
1665
- this.trackDestroy();
1666
- return () => {
1667
- if (this.destroySubscriptions) {
1668
- this.destroySubscriptions = this.destroySubscriptions.filter((cb) => cb !== callback);
1669
- }
1670
- };
1451
+ getRecords(context, instance, records, parentStore) {
1452
+ if (!instance.recordStoreCache) {
1453
+ this.initInstance(context, instance);
1454
+ }
1455
+ return this.mapRecords(context, instance, records, parentStore, instance.recordsAccessor);
1671
1456
  }
1672
- destroy() {
1673
- if (this.instanceCache) {
1674
- this.instanceCache.destroy();
1675
- this.instanceCache = null;
1457
+ mapRecords(context, instance, records, parentStore, recordsAccessor) {
1458
+ let result = [];
1459
+ if (!instance.recordStoreCache) {
1460
+ this.initInstance(context, instance);
1676
1461
  }
1677
- if (this.destroySubscriptions) {
1678
- this.destroySubscriptions.forEach((cb) => cb());
1679
- this.destroySubscriptions = null;
1462
+ if (isArray(records)) {
1463
+ records.forEach((data, index) => {
1464
+ if (this.filterFn && !this.filterFn(data)) return;
1465
+ const record = this.mapRecord(context, instance, data, parentStore, recordsAccessor, index);
1466
+ result.push(record);
1467
+ });
1680
1468
  }
1681
- if (this.destroyTracked) {
1682
- debug(destroyFlag, this);
1683
- if (this.widget.onDestroy) this.widget.onDestroy(this);
1684
- if (
1685
- this.widget.controller &&
1686
- this.controller &&
1687
- this.controller.onDestroy &&
1688
- this.controller.widget == this.widget
1689
- )
1690
- this.controller.onDestroy();
1691
- this.destroyTracked = false;
1469
+ if (this.sorter && !this.preserveOrder) {
1470
+ result = this.sorter(result);
1692
1471
  }
1472
+ return result;
1693
1473
  }
1694
- setState(state) {
1695
- let skip = !!this.state;
1696
- if (this.state) {
1697
- for (const k in state) {
1698
- if (this.state[k] !== state[k]) {
1699
- skip = false;
1700
- break;
1701
- }
1474
+ mapRecord(context, instance, data, parentStore, recordsAccessor, index) {
1475
+ const key = this.cacheByKeyField && this.keyField && isObject(data) ? data[this.keyField] : null;
1476
+ let recordStore = key != null ? instance.cacheByKey[key] : instance.recordStoreCache.get(data);
1477
+ if (recordsAccessor) {
1478
+ if (!recordStore) {
1479
+ recordStore = new ArrayElementView({
1480
+ store: parentStore,
1481
+ arrayAccessor: recordsAccessor,
1482
+ itemIndex: index,
1483
+ recordAlias: this.recordName,
1484
+ indexAlias: this.indexName,
1485
+ immutable: this.immutable,
1486
+ sealed: this.sealed,
1487
+ });
1488
+ } else {
1489
+ recordStore.setStore(parentStore);
1490
+ recordStore.setIndex(index);
1491
+ }
1492
+ } else {
1493
+ if (!recordStore) {
1494
+ recordStore = new ReadOnlyDataView({
1495
+ store: parentStore,
1496
+ data: {
1497
+ [this.recordName]: data,
1498
+ [this.indexName]: index,
1499
+ },
1500
+ immutable: this.immutable,
1501
+ sealed: this.sealed,
1502
+ });
1503
+ } else {
1504
+ recordStore.setStore(parentStore);
1505
+ recordStore.setData({
1506
+ [this.recordName]: data,
1507
+ [this.indexName]: index,
1508
+ });
1702
1509
  }
1703
1510
  }
1704
- if (skip) return;
1705
- this.state = Object.assign({}, this.state, state);
1706
- let parent = this.parent;
1707
- //notify all parents that child state change to bust up caching
1708
- while (parent) {
1709
- parent.childStateDirty = true;
1710
- parent = parent.parent;
1511
+ if (key != null) {
1512
+ instance.cacheByKey[key] = recordStore;
1513
+ } else if (isObject(data)) {
1514
+ instance.recordStoreCache.set(data, recordStore);
1711
1515
  }
1712
- batchUpdates(() => {
1713
- this.store.notify();
1714
- });
1516
+ return {
1517
+ store: recordStore,
1518
+ index: index,
1519
+ data: data,
1520
+ type: "data",
1521
+ key: this.keyField && isObject(data) ? data[this.keyField] : index,
1522
+ };
1715
1523
  }
1716
- set(prop, value, options = {}) {
1717
- //skip re-rendering (used for reading state from uncontrolled components)
1718
- if (options.internal && this.rawData) {
1719
- this.rawData[prop] = value;
1720
- this.data[prop] = value;
1524
+ setFilter(filterFn) {
1525
+ this.filterFn = filterFn;
1526
+ }
1527
+ getComparer(sortOptions) {
1528
+ return sortOptions ? Culture.getComparer(sortOptions) : undefined;
1529
+ }
1530
+ buildSorter(sorters) {
1531
+ if (isArray(sorters) && sorters.length > 0) {
1532
+ let dataAccessor;
1533
+ let fieldValueMapper;
1534
+ if (sorters.every((x) => x.field && x.value == null)) {
1535
+ dataAccessor = (x) => x.data;
1536
+ fieldValueMapper = (x) => ({
1537
+ bind: x.field,
1538
+ });
1539
+ } else {
1540
+ dataAccessor = (x) => x.store.getData();
1541
+ fieldValueMapper = (x) => ({
1542
+ bind: this.recordName + "." + x.field,
1543
+ });
1544
+ }
1545
+ this.sorter = sorter(
1546
+ sorters.map((x) => {
1547
+ const s = Object.assign({}, x);
1548
+ if (s.field && s.value == null) {
1549
+ s.value = fieldValueMapper(s);
1550
+ }
1551
+ if (!s.comparer) {
1552
+ s.comparer = this.getComparer(isDefined(s.sortOptions) ? s.sortOptions : this.sortOptions);
1553
+ }
1554
+ return s;
1555
+ }),
1556
+ dataAccessor,
1557
+ );
1558
+ } else {
1559
+ this.sorter = undefined;
1721
1560
  }
1722
- const setter = this.setters && this.setters[prop];
1723
- if (setter) {
1724
- if (options.immediate && isFunction(setter.reset)) setter.reset(value);
1725
- else setter(value);
1726
- return true;
1561
+ }
1562
+ sort(sorters) {
1563
+ if (sorters) {
1564
+ this.buildSorter(sorters);
1727
1565
  }
1728
- const p = this.widget[prop];
1729
- if (p && typeof p == "object") {
1730
- if (p.debounce) {
1731
- this.definePropertySetter(
1732
- prop,
1733
- validatedDebounce(
1734
- (value) => this.doSet(prop, value),
1735
- () => this.dataSelector(this.store)[prop],
1736
- p.debounce,
1737
- ),
1738
- );
1739
- this.set(prop, value, options);
1740
- return true;
1741
- }
1742
- if (p.throttle) {
1743
- this.definePropertySetter(
1744
- prop,
1745
- throttle((value) => this.doSet(prop, value), p.throttle),
1746
- );
1747
- this.set(prop, value, options);
1748
- return true;
1749
- }
1566
+ }
1567
+ }
1568
+ ArrayAdapter.prototype.immutable = false;
1569
+ ArrayAdapter.prototype.sealed = false;
1570
+ ArrayAdapter.prototype.keyField = null;
1571
+ ArrayAdapter.prototype.cacheByKeyField = true;
1572
+ ArrayAdapter.prototype.isTreeAdapter = false;
1573
+ ArrayAdapter.autoInit = true;
1574
+
1575
+ class UseParentLayout extends PureContainer {}
1576
+ UseParentLayout.prototype.noLayout = true;
1577
+ UseParentLayout.prototype.useParentLayout = true;
1578
+
1579
+ class Repeater extends ContainerBase {
1580
+ recordsAccessor;
1581
+ item;
1582
+ filter;
1583
+ declareData(...args) {
1584
+ super.declareData(
1585
+ {
1586
+ records: undefined,
1587
+ sorters: undefined,
1588
+ sortField: undefined,
1589
+ sortDirection: undefined,
1590
+ filterParams: {
1591
+ structured: true,
1592
+ },
1593
+ },
1594
+ ...args,
1595
+ );
1596
+ }
1597
+ init() {
1598
+ this.recordsAccessor = getAccessor(this.records);
1599
+ if (this.recordAlias) this.recordName = this.recordAlias;
1600
+ if (this.indexAlias) this.indexName = this.indexAlias;
1601
+ this.dataAdapter = ArrayAdapter.create({
1602
+ ...this.dataAdapter,
1603
+ recordName: this.recordName,
1604
+ indexName: this.indexName,
1605
+ keyField: this.keyField,
1606
+ immutable: this.immutable,
1607
+ sealed: this.sealed,
1608
+ recordsAccessor: this.recordsAccessor,
1609
+ sortOptions: this.sortOptions,
1610
+ });
1611
+ this.item = PureContainer.create({
1612
+ children: this.items || this.children,
1613
+ layout: UseParentLayout,
1614
+ });
1615
+ delete this.children;
1616
+ this.items = [];
1617
+ super.init();
1618
+ }
1619
+ initInstance(context, instance) {
1620
+ this.dataAdapter.initInstance(context, instance);
1621
+ }
1622
+ applyParentStore(instance) {
1623
+ super.applyParentStore(instance);
1624
+ // force prepareData to execute again and propagate the store change to the records
1625
+ if (instance.cached) delete instance.cached.rawData;
1626
+ }
1627
+ prepareData(context, instance) {
1628
+ let { data } = instance;
1629
+ if (data.sortField)
1630
+ data.sorters = [
1631
+ {
1632
+ field: data.sortField,
1633
+ direction: data.sortDirection || "ASC",
1634
+ },
1635
+ ];
1636
+ this.dataAdapter.sort(data.sorters);
1637
+ let filter = null;
1638
+ if (this.onCreateFilter) filter = instance.invoke("onCreateFilter", data.filterParams, instance);
1639
+ else if (this.filter) filter = (item) => this.filter(item, data.filterParams);
1640
+ this.dataAdapter.setFilter(filter);
1641
+ instance.mappedRecords = this.dataAdapter.getRecords(context, instance, data.records, instance.store);
1642
+ if (this.onTrackMappedRecords) {
1643
+ instance.invoke("onTrackMappedRecords", instance.mappedRecords, instance);
1750
1644
  }
1751
- return this.doSet(prop, value);
1752
- }
1753
- definePropertySetter(prop, setter) {
1754
- if (!this.setters) this.setters = {};
1755
- this.setters[prop] = setter;
1645
+ super.prepareData(context, instance);
1756
1646
  }
1757
- doSet(prop, value) {
1758
- let changed = false;
1759
- batchUpdates(() => {
1760
- const p = this.widget[prop];
1761
- if (isObject(p)) {
1762
- const pObj = p;
1763
- if (pObj.set) {
1764
- if (isFunction(pObj.set)) {
1765
- pObj.set(value, this);
1766
- changed = true;
1767
- } else if (isString(pObj.set)) {
1768
- this.controller?.[pObj.set](value, this);
1769
- changed = true;
1770
- }
1771
- } else if (pObj.action) {
1772
- const action = pObj.action(value, this);
1773
- this.store.dispatch(action);
1774
- changed = true;
1775
- } else if (isString(pObj.bind) || isAccessorChain(pObj.bind)) {
1776
- changed = this.store.set(pObj.bind, value);
1777
- }
1778
- } else if (isAccessorChain(p)) {
1779
- changed = this.store.set(p.toString(), value);
1780
- }
1647
+ explore(context, instance, data) {
1648
+ let instances = [];
1649
+ instance.mappedRecords.forEach((record) => {
1650
+ let subInstance = instance.getChild(context, this.item, record.key, record.store);
1651
+ let changed = subInstance.cache("recordData", record.data) || subInstance.cache("key", record.key);
1652
+ subInstance.record = record;
1653
+ if (this.cached && !changed && subInstance.visible && !subInstance.childStateDirty) {
1654
+ instances.push(subInstance);
1655
+ subInstance.shouldUpdate = false;
1656
+ } else if (subInstance.scheduleExploreIfVisible(context)) instances.push(subInstance);
1781
1657
  });
1782
- return changed;
1658
+ instance.children = instances;
1783
1659
  }
1784
- nestedDataSet(key, value, dataConfig, useParentStore) {
1785
- let config = dataConfig[key];
1786
- if (!config)
1787
- throw new Error(`Unknown nested data key ${key}. Known keys are ${Object.keys(dataConfig).join(", ")}.`);
1788
- if (isAccessorChain(config))
1789
- config = {
1790
- bind: config.toString(),
1791
- };
1792
- if (config.bind) {
1793
- let store = this.store;
1794
- //in case of Rescope or DataProxy, bindings point to the data in the parent store
1795
- if (useParentStore && store.store) store = store.store;
1796
- return isUndefined(value) ? store.deleteItem(config.bind) : store.setItem(config.bind, value);
1797
- }
1798
- if (!config.set)
1799
- throw new Error(
1800
- `Cannot change nested data value for ${key} as it's read-only. Either define it as a binding or define a set function.`,
1801
- );
1802
- if (isString(config.set)) this.getControllerMethod(config.set)(value, this);
1803
- else if (isFunction(config.set)) config.set(value, this);
1804
- else
1805
- throw new Error(
1806
- `Cannot change nested data value for ${key} the defined setter is neither a function nor a controller method.`,
1807
- );
1808
- return true;
1660
+ }
1661
+ Repeater.prototype.recordName = "$record";
1662
+ Repeater.prototype.indexName = "$index";
1663
+ Repeater.prototype.cached = false;
1664
+ Repeater.prototype.immutable = false;
1665
+ Repeater.prototype.sealed = false;
1666
+ Repeater.prototype.isPureContainer = true;
1667
+ Widget.alias("repeater", Repeater);
1668
+
1669
+ class StructuredInstanceDataAccessor {
1670
+ instance;
1671
+ dataConfig;
1672
+ useParentStore;
1673
+ dataSelector;
1674
+ constructor(config) {
1675
+ this.instance = config.instance;
1676
+ this.dataConfig = config.data;
1677
+ this.useParentStore = config.useParentStore;
1678
+ this.dataSelector = getSelector(config.data);
1679
+ if (this.dataSelector.memoize) this.dataSelector = this.dataSelector.memoize();
1809
1680
  }
1810
- replaceState(state) {
1811
- this.cached.state = this.state;
1812
- this.state = state;
1813
- this.store.notify();
1681
+ getSelector() {
1682
+ return this.dataSelector;
1814
1683
  }
1815
- getInstanceCache() {
1816
- if (!this.instanceCache)
1817
- this.instanceCache = new InstanceCache(this, this.widget.isPureContainer ? this.key : null);
1818
- return this.instanceCache;
1684
+ get() {
1685
+ return this.dataSelector.get(this.instance.store.getData());
1819
1686
  }
1820
- clearChildrenCache() {
1821
- if (this.instanceCache) this.instanceCache.destroy();
1687
+ setItem(key, value) {
1688
+ return this.instance.nestedDataSet(key, value, this.dataConfig, this.useParentStore);
1822
1689
  }
1823
- getChild(context, widget, key, store) {
1824
- return this.getInstanceCache().getChild(widget, store ?? this.store, key);
1690
+ containsKey(key) {
1691
+ return this.dataConfig.hasOwnProperty(key);
1825
1692
  }
1826
- getDetachedChild(widget, key, store) {
1827
- const child = new Instance(widget, key, this, store ?? this.store);
1828
- child.detached = true;
1829
- return child;
1693
+ getKeys() {
1694
+ return Object.keys(this.dataConfig);
1830
1695
  }
1831
- prepareRenderCleanupChild(widget, store, keyPrefix, options) {
1832
- return widget.prepareRenderCleanup(store ?? this.store, options, keyPrefix, this);
1696
+ }
1697
+
1698
+ class Rescope extends PureContainerBase {
1699
+ init() {
1700
+ this.binding = Binding.get(this.bind);
1701
+ if (this.rootAlias) this.rootName = this.rootAlias;
1702
+ super.init();
1833
1703
  }
1834
- getJsxEventProps() {
1835
- const { widget } = this;
1836
- if (!isArray(widget.jsxAttributes)) return null;
1837
- const props = {};
1838
- widget.jsxAttributes.forEach((attr) => {
1839
- if (attr.indexOf("on") == 0 && attr.length > 2) {
1840
- props[attr] = (e) => this.invoke(attr, e, this);
1841
- }
1704
+ initInstance(context, instance) {
1705
+ instance.store = new ZoomIntoPropertyView({
1706
+ store: instance.parentStore,
1707
+ binding: this.binding,
1708
+ rootName: this.rootName,
1709
+ nestedData: isObject(this.data)
1710
+ ? new StructuredInstanceDataAccessor({
1711
+ instance,
1712
+ data: this.data,
1713
+ useParentStore: true,
1714
+ })
1715
+ : undefined,
1842
1716
  });
1843
- return props;
1717
+ super.initInstance(context, instance);
1844
1718
  }
1845
- getCallback(methodName) {
1846
- const scope = this.widget;
1847
- const callback = scope[methodName];
1848
- if (typeof callback === "string") return this.getControllerMethod(callback);
1849
- if (typeof callback !== "function")
1850
- throw new Error(`Cannot invoke callback method ${methodName} as assigned value is not a function.`);
1851
- return callback.bind(scope);
1719
+ applyParentStore(instance) {
1720
+ instance.store.setStore(instance.parentStore);
1852
1721
  }
1853
- /**
1854
- * Finds the first controller in the instance tree matching the predicate
1855
- * @param predicate Function to test each controller
1856
- * @returns The matching controller or undefined
1857
- */
1858
- findController(predicate) {
1859
- let at = this;
1860
- while (at?.controller != null) {
1861
- if (predicate(at.controller)) {
1862
- return at.controller;
1722
+ }
1723
+ Rescope.prototype.bind = "$page";
1724
+ Rescope.prototype.rootName = "$root";
1725
+ Widget.alias("rescope", Rescope);
1726
+
1727
+ /*
1728
+ * Purpose of FocusManager is to provide focusout notifications.
1729
+ * IE and Firefox do not provide relatedTarget info in blur events which makes it impossible
1730
+ * to determine if focus went outside or stayed inside the component.
1731
+ */
1732
+ let subscribers$3 = new SubscriberList(),
1733
+ timerInterval = 300,
1734
+ timerId = null;
1735
+ let lastActiveElement = null;
1736
+ let pending = false;
1737
+ class FocusManager {
1738
+ static subscribe(callback) {
1739
+ let unsubscribe = subscribers$3.subscribe(callback);
1740
+ checkTimer();
1741
+ return unsubscribe;
1742
+ }
1743
+ static onFocusOut(el, callback) {
1744
+ let active = isSelfOrDescendant(el, getActiveElement());
1745
+ return this.subscribe((focusedEl) => {
1746
+ if (!active) active = isSelfOrDescendant(el, getActiveElement());
1747
+ else if (!isSelfOrDescendant(el, focusedEl)) {
1748
+ active = false;
1749
+ callback(focusedEl);
1863
1750
  }
1864
- at = at.parent;
1865
- }
1866
- return undefined;
1751
+ });
1867
1752
  }
1868
- /**
1869
- * Finds a controller of the specified type in the instance tree
1870
- * @param type Controller class/constructor to find
1871
- * @returns The matching controller cast to the specified type, or undefined
1872
- */
1873
- findControllerByType(type) {
1874
- return this.findController((c) => c instanceof type);
1753
+ static oneFocusOut(el, callback) {
1754
+ this.nudge();
1755
+ let off = this.subscribe((focusedEl) => {
1756
+ if (!isSelfOrDescendant(el, focusedEl)) {
1757
+ callback(focusedEl);
1758
+ off();
1759
+ }
1760
+ });
1761
+ return off;
1875
1762
  }
1876
- /**
1877
- * Gets the first controller in the instance tree matching the predicate
1878
- * @param predicate Function to test each controller
1879
- * @returns The matching controller
1880
- * @throws Error if no matching controller is found
1881
- */
1882
- getController(predicate) {
1883
- const controller = this.findController(predicate);
1884
- if (!controller) throw new Error("Cannot find a controller matching the given predicate in the instance tree.");
1885
- return controller;
1763
+ static nudge() {
1764
+ if (typeof document !== "undefined" && getActiveElement() !== lastActiveElement) {
1765
+ if (!pending) {
1766
+ pending = true;
1767
+ setTimeout(function () {
1768
+ pending = false;
1769
+ if (getActiveElement() !== lastActiveElement) {
1770
+ lastActiveElement = getActiveElement();
1771
+ batchUpdates(() => {
1772
+ subscribers$3.notify(lastActiveElement);
1773
+ });
1774
+ checkTimer();
1775
+ }
1776
+ }, 0);
1777
+ }
1778
+ }
1886
1779
  }
1887
- /**
1888
- * Gets a controller of the specified type in the instance tree
1889
- * @param type Controller class/constructor to find
1890
- * @returns The matching controller cast to the specified type
1891
- * @throws Error if no controller of the specified type is found
1892
- */
1893
- getControllerByType(type) {
1894
- const controller = this.findControllerByType(type);
1895
- if (!controller) throw new Error(`Cannot find a controller of type "${type.name}" in the instance tree.`);
1896
- return controller;
1780
+ static focus(el) {
1781
+ el.focus();
1782
+ this.nudge();
1897
1783
  }
1898
- getControllerMethod(methodName) {
1899
- if (!this.controller)
1900
- throw new Error(`Cannot invoke controller method "${methodName}" as controller is not assigned to the widget.`);
1901
- const controller = this.findController((c) => !!c[methodName]);
1902
- if (!controller)
1903
- throw new Error(
1904
- `Cannot invoke controller method "${methodName}". The method cannot be found in any of the assigned controllers.`,
1905
- );
1906
- return controller[methodName].bind(controller);
1784
+ static focusFirst(el) {
1785
+ let focusable = findFirst(el, isFocusable);
1786
+ if (focusable) this.focus(focusable);
1787
+ return focusable;
1907
1788
  }
1908
- invoke(methodName, ...args) {
1909
- return this.getCallback(methodName).apply(null, args);
1789
+ static focusFirstChild(el) {
1790
+ let focusable = findFirstChild(el, isFocusable);
1791
+ if (focusable) this.focus(focusable);
1792
+ return focusable;
1910
1793
  }
1911
- invokeControllerMethod(methodName, ...args) {
1912
- return this.getControllerMethod(methodName).apply(null, args);
1794
+ static focusNext(el) {
1795
+ let next = el,
1796
+ skip = true;
1797
+ do {
1798
+ if (!skip) {
1799
+ let focusable = this.focusFirst(next);
1800
+ if (focusable) return focusable;
1801
+ }
1802
+ if (next.nextSibling) {
1803
+ next = next.nextSibling;
1804
+ skip = false;
1805
+ } else {
1806
+ next = next.parentNode;
1807
+ skip = true;
1808
+ }
1809
+ } while (next);
1810
+ }
1811
+ static setInterval(interval) {
1812
+ timerInterval = interval;
1813
+ checkTimer();
1913
1814
  }
1914
1815
  }
1915
- function renderResultFix(res) {
1916
- return res != null && isDefined(res.content)
1917
- ? res
1918
- : {
1919
- content: res,
1920
- };
1816
+ function oneFocusOut(component, el, callback) {
1817
+ if (!component.oneFocusOut)
1818
+ component.oneFocusOut = FocusManager.oneFocusOut(el, (focus) => {
1819
+ delete component.oneFocusOut;
1820
+ callback(focus);
1821
+ });
1921
1822
  }
1922
- class InstanceCache {
1923
- constructor(parent, keyPrefix) {
1924
- this.children = {};
1925
- this.parent = parent;
1926
- this.marked = {};
1927
- this.monitored = null;
1928
- this.keyPrefix = keyPrefix != null ? keyPrefix + "-" : "";
1823
+ function offFocusOut(component) {
1824
+ if (component.oneFocusOut) {
1825
+ component.oneFocusOut();
1826
+ delete component.oneFocusOut;
1929
1827
  }
1930
- getChild(widget, parentStore, key) {
1931
- const k = this.keyPrefix + (key != null ? key : widget.vdomKey || widget.widgetId);
1932
- let instance = this.children[k];
1933
- if (
1934
- !instance ||
1935
- instance.widget !== widget ||
1936
- (!instance.visible && (instance.widget.controller || instance.widget.onInit))
1937
- ) {
1938
- instance = new Instance(widget, k, this.parent, parentStore);
1939
- this.children[k] = instance;
1940
- } else if (instance.parentStore !== parentStore) {
1941
- instance.setParentStore(parentStore);
1942
- }
1943
- return instance;
1828
+ }
1829
+ function preventFocus(e) {
1830
+ //Focus can be prevented only on mousedown event. On touchstart this will not work
1831
+ //preventDefault cannot be used as it prevents scrolling
1832
+ if (e.type !== "mousedown") return;
1833
+ e.preventDefault();
1834
+ unfocusElement(e.currentTarget, false);
1835
+ }
1836
+ function checkTimer() {
1837
+ let shouldRun = !subscribers$3.isEmpty();
1838
+ if (shouldRun && !timerId)
1839
+ timerId = setInterval(() => {
1840
+ FocusManager.nudge();
1841
+ }, timerInterval);
1842
+ if (!shouldRun && timerId) {
1843
+ clearInterval(timerId);
1844
+ timerId = null;
1944
1845
  }
1945
- addChild(instance) {
1946
- this.marked[instance.key] = instance;
1846
+ }
1847
+ function preventFocusOnTouch(e, force = false) {
1848
+ if (force || isTouchEvent()) preventFocus(e);
1849
+ }
1850
+ function unfocusElement(target = null, unfocusParentOverlay = true) {
1851
+ const activeElement = getActiveElement();
1852
+ if (!target) target = activeElement;
1853
+ if (unfocusParentOverlay) {
1854
+ let focusableOverlayContainer = closestParent(target, (el) => el.dataset?.focusableOverlayContainer);
1855
+ if (focusableOverlayContainer) target = focusableOverlayContainer;
1947
1856
  }
1948
- mark() {
1949
- this.marked = {};
1857
+ //find the closest focusable parent of the target element and focus it instead
1858
+ let focusableParent = closestParent(
1859
+ target,
1860
+ (el) => isFocusable(el) && (!unfocusParentOverlay || !!el.dataset?.focusableOverlayContainer),
1861
+ );
1862
+ if (focusableParent && focusableParent !== document.body)
1863
+ focusableParent.focus({
1864
+ preventScroll: true,
1865
+ });
1866
+ else activeElement.blur();
1867
+ FocusManager.nudge();
1868
+ }
1869
+
1870
+ let subscribers$2 = new SubscriberList();
1871
+ class ResizeManager {
1872
+ static subscribe(callback) {
1873
+ return subscribers$2.subscribe(callback);
1950
1874
  }
1951
- trackDestroy(instance) {
1952
- if (!this.monitored) this.monitored = {};
1953
- this.monitored[instance.key] = instance;
1875
+ static notify() {
1876
+ batchUpdates(() => {
1877
+ subscribers$2.notify();
1878
+ });
1954
1879
  }
1955
- destroy() {
1956
- this.children = {};
1957
- this.marked = {};
1958
- if (!this.monitored) return;
1959
- for (const key in this.monitored) {
1960
- this.monitored[key].destroy();
1961
- }
1962
- this.monitored = null;
1880
+ static trackElement(el, callback) {
1881
+ if (typeof ResizeObserver !== "function") return this.subscribe(() => callback([]));
1882
+ let obs = new ResizeObserver(callback);
1883
+ obs.observe(el);
1884
+ return () => {
1885
+ obs.disconnect();
1886
+ };
1963
1887
  }
1964
- sweep() {
1965
- this.children = this.marked;
1966
- if (!this.monitored) return;
1967
- let activeCount = 0;
1968
- for (const key in this.monitored) {
1969
- const monitoredChild = this.monitored[key];
1970
- const child = this.children[key];
1971
- if (child !== monitoredChild || !monitoredChild.visible) {
1972
- monitoredChild.destroy();
1973
- delete this.monitored[key];
1974
- if (child === monitoredChild) delete this.children[key];
1975
- } else activeCount++;
1976
- }
1977
- if (activeCount === 0) this.monitored = null;
1888
+ }
1889
+ if (typeof window != "undefined") window.addEventListener("resize", () => ResizeManager.notify());
1890
+
1891
+ let lastZIndex = 10000;
1892
+ class ZIndexManager {
1893
+ static next() {
1894
+ return ++lastZIndex;
1895
+ }
1896
+ static reset(zIndex) {
1897
+ lastZIndex = zIndex;
1978
1898
  }
1979
1899
  }
1980
1900
 
1901
+ const Format = Format$1;
1902
+ let cultureSensitiveFormatsRegistered = false;
1903
+ function resolveNumberFormattingFlags(flags) {
1904
+ if (!flags) return null;
1905
+ let result = {};
1906
+ if (flags.indexOf("+") >= 0) result.signDisplay = "exceptZero";
1907
+ if (flags.indexOf("c") >= 0) result.notation = "compact";
1908
+ if (flags.indexOf("a") >= 0) result.currencySign = "accounting";
1909
+ return result;
1910
+ }
1911
+ function enableCultureSensitiveFormatting() {
1912
+ if (cultureSensitiveFormatsRegistered) return;
1913
+ cultureSensitiveFormatsRegistered = true;
1914
+ Format$1.registerFactory(["number", "n"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1915
+ let culture = Culture.getNumberCulture();
1916
+ let formatter = culture.getFormatter({
1917
+ ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1918
+ ...resolveNumberFormattingFlags(flags),
1919
+ });
1920
+ return (value) => formatter.format(value);
1921
+ });
1922
+ Format$1.registerFactory("currency", (format, currency, minimumFractionDigits, maximumFractionDigits, flags) => {
1923
+ let culture = Culture.getNumberCulture();
1924
+ currency = currency || Culture.defaultCurrency;
1925
+ let formatter = culture.getFormatter({
1926
+ style: "currency",
1927
+ currency: currency,
1928
+ ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1929
+ ...resolveNumberFormattingFlags(flags),
1930
+ });
1931
+ return (value) => formatter.format(value);
1932
+ });
1933
+ Format$1.registerFactory(["percentage", "p", "%"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1934
+ let culture = Culture.getNumberCulture();
1935
+ let formatter = culture.getFormatter({
1936
+ style: "percent",
1937
+ ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1938
+ ...resolveNumberFormattingFlags(flags),
1939
+ });
1940
+ return (value) => formatter.format(value);
1941
+ });
1942
+ Format$1.registerFactory(["percentSign", "ps"], (format, minimumFractionDigits, maximumFractionDigits, flags) => {
1943
+ let culture = Culture.getNumberCulture();
1944
+ let formatter = culture.getFormatter({
1945
+ style: "percent",
1946
+ ...resolveMinMaxFractionDigits(minimumFractionDigits, maximumFractionDigits),
1947
+ ...resolveNumberFormattingFlags(flags),
1948
+ });
1949
+ return (value) => formatter.format(value / 100);
1950
+ });
1951
+ Format$1.registerFactory(["date", "d"], (fmt, format = "yyyyMMdd") => {
1952
+ let culture = Culture.getDateTimeCulture();
1953
+ let formatter = culture.getFormatter(format);
1954
+ return (value) => formatter.format(parseDateInvariant(value));
1955
+ });
1956
+ Format$1.registerFactory(["time", "t"], (fmt, format = "hhmmss") => {
1957
+ let culture = Culture.getDateTimeCulture();
1958
+ let formatter = culture.getFormatter(format);
1959
+ return (value) => formatter.format(parseDateInvariant(value));
1960
+ });
1961
+ Format$1.registerFactory(["datetime", "dt"], (fmt, format = "yyyyMd hhmm") => {
1962
+ let culture = Culture.getDateTimeCulture();
1963
+ let formatter = culture.getFormatter(format);
1964
+ return (value) => formatter.format(parseDateInvariant(value));
1965
+ });
1966
+ setGetFormatCacheCallback(() => {
1967
+ let cache = getCurrentCultureCache();
1968
+ if (!cache.formatCache) cache.formatCache = {};
1969
+ return cache.formatCache;
1970
+ });
1971
+ setGetExpressionCacheCallback(() => {
1972
+ let cache = getCurrentCultureCache();
1973
+ if (!cache.exprCache) cache.exprCache = {};
1974
+ return cache.exprCache;
1975
+ });
1976
+ setGetStringTemplateCacheCallback(() => {
1977
+ let cache = getCurrentCultureCache();
1978
+ if (!cache.strTplCache) cache.strTplCache = {};
1979
+ return cache.strTplCache;
1980
+ });
1981
+ GlobalCacheIdentifier.change();
1982
+ }
1983
+
1981
1984
  class RenderingContext {
1982
1985
  options;
1983
1986
  exploreStack;
@@ -3952,6 +3955,7 @@ class TreeAdapter extends ArrayAdapter {
3952
3955
  next: new Set(),
3953
3956
  };
3954
3957
  }
3958
+ if (this.load) this.onLoad = this.load;
3955
3959
  }
3956
3960
  mapRecords(context, instance, data, parentStore, recordsAccessor) {
3957
3961
  const nodes = super.mapRecords(context, instance, data, parentStore, recordsAccessor);
@@ -3996,9 +4000,9 @@ class TreeAdapter extends ArrayAdapter {
3996
4000
  this.childrenAccessor,
3997
4001
  );
3998
4002
  this.processList(context, instance, level + 1, record.key + ":", childNodes, result);
3999
- } else if (this.load && !dataRecord[this.loadedField] && !dataRecord[this.loadingField]) {
4003
+ } else if (this.onLoad && !dataRecord[this.loadedField] && !dataRecord[this.loadingField]) {
4000
4004
  store.set(`${this.recordName}.${this.loadingField}`, true);
4001
- const response = this.load(context, instance, data);
4005
+ const response = this.onLoad(context, instance, data);
4002
4006
  Promise.resolve(response)
4003
4007
  .then((children) => {
4004
4008
  store.set(`${this.recordName}.${this.childrenField}`, children);