neo.mjs 10.0.0-beta.3 → 10.0.0-beta.4

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 (44) hide show
  1. package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +41 -0
  2. package/ServiceWorker.mjs +2 -2
  3. package/apps/portal/index.html +1 -1
  4. package/apps/portal/view/ViewportController.mjs +1 -1
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  6. package/apps/portal/view/learn/MainContainerController.mjs +6 -6
  7. package/learn/guides/{Collections.md → datahandling/Collections.md} +6 -6
  8. package/learn/guides/datahandling/Grids.md +621 -0
  9. package/learn/guides/{Records.md → datahandling/Records.md} +4 -3
  10. package/learn/guides/{StateProviders.md → datahandling/StateProviders.md} +145 -1
  11. package/learn/guides/fundamentals/ExtendingNeoClasses.md +359 -0
  12. package/learn/guides/{Layouts.md → uibuildingblocks/Layouts.md} +40 -38
  13. package/learn/guides/{form_fields → userinteraction/form_fields}/ComboBox.md +3 -3
  14. package/learn/tree.json +63 -57
  15. package/package.json +2 -2
  16. package/src/DefaultConfig.mjs +2 -2
  17. package/src/Neo.mjs +37 -29
  18. package/src/collection/Base.mjs +29 -2
  19. package/src/component/Base.mjs +6 -16
  20. package/src/controller/Base.mjs +87 -63
  21. package/src/core/Base.mjs +72 -17
  22. package/src/core/Compare.mjs +3 -13
  23. package/src/core/Config.mjs +139 -0
  24. package/src/core/ConfigSymbols.mjs +3 -0
  25. package/src/core/Util.mjs +3 -18
  26. package/src/data/RecordFactory.mjs +22 -3
  27. package/src/util/Function.mjs +52 -5
  28. package/test/siesta/tests/ReactiveConfigs.mjs +112 -0
  29. package/learn/guides/ExtendingNeoClasses.md +0 -331
  30. /package/learn/guides/{Tables.md → datahandling/Tables.md} +0 -0
  31. /package/learn/guides/{ApplicationBootstrap.md → fundamentals/ApplicationBootstrap.md} +0 -0
  32. /package/learn/guides/{ConfigSystemDeepDive.md → fundamentals/ConfigSystemDeepDive.md} +0 -0
  33. /package/learn/guides/{DeclarativeComponentTreesVsImperativeVdom.md → fundamentals/DeclarativeComponentTreesVsImperativeVdom.md} +0 -0
  34. /package/learn/guides/{InstanceLifecycle.md → fundamentals/InstanceLifecycle.md} +0 -0
  35. /package/learn/guides/{MainThreadAddons.md → fundamentals/MainThreadAddons.md} +0 -0
  36. /package/learn/guides/{Mixins.md → specificfeatures/Mixins.md} +0 -0
  37. /package/learn/guides/{MultiWindow.md → specificfeatures/MultiWindow.md} +0 -0
  38. /package/learn/guides/{PortalApp.md → specificfeatures/PortalApp.md} +0 -0
  39. /package/learn/guides/{ComponentsAndContainers.md → uibuildingblocks/ComponentsAndContainers.md} +0 -0
  40. /package/learn/guides/{CustomComponents.md → uibuildingblocks/CustomComponents.md} +0 -0
  41. /package/learn/guides/{WorkingWithVDom.md → uibuildingblocks/WorkingWithVDom.md} +0 -0
  42. /package/learn/guides/{Forms.md → userinteraction/Forms.md} +0 -0
  43. /package/learn/guides/{events → userinteraction/events}/CustomEvents.md +0 -0
  44. /package/learn/guides/{events → userinteraction/events}/DomEvents.md +0 -0
@@ -46,9 +46,9 @@ In this example:
46
46
  For more complex scenarios, especially when dealing with large datasets or remote data, it's best practice to use a separate `Neo.data.Store` instance.
47
47
 
48
48
  ```javascript readonly
49
- import ComboBox from '../../src/form/field/ComboBox.mjs';
50
- import Store from '../../src/data/Store.mjs';
51
- import Model from '../../src/data/Model.mjs';
49
+ import ComboBox from '../../src/form/field/ComboBox.mjs';
50
+ import Store from '../../src/data/Store.mjs';
51
+ import Model from '../../src/data/Model.mjs';
52
52
 
53
53
  // Define a Model for your data
54
54
  class CountryModel extends Model {
package/learn/tree.json CHANGED
@@ -1,59 +1,65 @@
1
1
  {"data": [
2
- {"name": "Using These Topics", "parentId": null, "id": "UsingTheseTopics" },
3
- {"name": "Benefits", "parentId": null, "isLeaf": false, "id": "Benefits"},
4
- {"name": "Introduction ", "parentId": "Benefits", "id": "benefits.Introduction"},
5
- {"name": "Off the Main Thread", "parentId": "Benefits", "id": "benefits.OffTheMainThread"},
6
- {"name": "4 Environments", "parentId": "Benefits", "id": "benefits.FourEnvironments"},
7
- {"name": "Unified Config System", "parentId": "Benefits", "id": "benefits.ConfigSystem"},
8
- {"name": "RPC Layer", "parentId": "Benefits", "id": "benefits.RPCLayer"},
9
- {"name": "Extreme Speed", "parentId": "Benefits", "id": "benefits.Speed"},
10
- {"name": "Multi-Window Applications", "parentId": "Benefits", "id": "benefits.MultiWindow"},
11
- {"name": "Quick Application Development", "parentId": "Benefits", "id": "benefits.Quick"},
12
- {"name": "Complexity and Effort", "parentId": "Benefits", "id": "benefits.Effort"},
13
- {"name": "Forms Engine", "parentId": "Benefits", "id": "benefits.FormsEngine"},
14
- {"name": "Features and Benefits Summary", "parentId": "Benefits", "id": "benefits.Features"},
15
- {"name": "Getting Started", "parentId": null, "isLeaf": false, "id": "GettingStarted", "collapsed": true},
16
- {"name": "Setup", "parentId": "GettingStarted", "id": "gettingstarted.Setup"},
17
- {"name": "Workspaces and Applications", "parentId": "GettingStarted", "id": "gettingstarted.Workspaces"},
18
- {"name": "Describing a View", "parentId": "GettingStarted", "id": "gettingstarted.DescribingTheUI"},
19
- {"name": "Events", "parentId": "GettingStarted", "id": "gettingstarted.Events"},
20
- {"name": "Component References", "parentId": "GettingStarted", "id": "gettingstarted.References"},
21
- {"name": "Extending Classes", "parentId": "GettingStarted", "id": "gettingstarted.Extending"},
22
- {"name": "Config", "parentId": "GettingStarted", "id": "gettingstarted.Config"},
23
- {"name": "Shared Bindable Data", "parentId": "GettingStarted", "id": "gettingstarted.ComponentModels"},
24
- {"name": "Guides", "parentId": null, "isLeaf": false, "id": "InDepth", "collapsed": true},
25
- {"name": "Application Bootstrap", "parentId": "InDepth", "id": "guides.ApplicationBootstrap"},
26
- {"name": "Instance Lifecycle", "parentId": "InDepth", "id": "guides.InstanceLifecycle"},
27
- {"name": "Declarative Component Trees VS Imperative Vdom", "parentId": "InDepth", "id": "guides.DeclarativeComponentTreesVsImperativeVdom"},
28
- {"name": "Shared Bindable Data (State Providers)", "parentId": "InDepth", "id": "guides.StateProviders"},
29
- {"name": "Working with VDom", "parentId": "InDepth", "id": "guides.WorkingWithVDom"},
30
- {"name": "Config System Deep Dive", "parentId": "InDepth", "id": "guides.ConfigSystemDeepDive"},
31
- {"name": "Extending Neo Classes", "parentId": "InDepth", "id": "guides.ExtendingNeoClasses"},
32
- {"name": "Main Thread Addons: Interacting with the Browser", "parentId": "InDepth", "id": "guides.MainThreadAddons"},
33
- {"name": "User Input (Forms)", "parentId": "InDepth", "id": "guides.Forms"},
34
- {"name": "Form Fields", "parentId": "InDepth", "isLeaf": false, "id": "guides.form_fields"},
35
- {"name": "ComboBox", "parentId": "guides.form_fields", "id": "guides.form_fields.ComboBox"},
36
- {"name": "Component and Container Basics", "parentId": "InDepth", "id": "guides.ComponentsAndContainers"},
37
- {"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "guides.Layouts"},
38
- {"name": "Custom Components", "parentId": "InDepth", "id": "guides.CustomComponents"},
39
- {"name": "Events", "parentId": "InDepth", "isLeaf": false, "id": "GuideEvents"},
40
- {"name": "Custom Events", "parentId": "GuideEvents", "id": "guides.events.CustomEvents"},
41
- {"name": "DOM Events", "parentId": "GuideEvents", "id": "guides.events.DomEvents"},
42
- {"name": "Portal App", "parentId": "InDepth", "id": "guides.PortalApp"},
43
- {"name": "Tables (Stores)", "parentId": "InDepth", "id": "guides.Tables", "hidden": true},
44
- {"name": "Multi-Window Applications", "parentId": "InDepth", "id": "guides.MultiWindow", "hidden": true},
45
- {"name": "Mixins", "parentId": "InDepth", "id": "guides.Mixins", "hidden": true},
46
- {"name": "Collections", "parentId": "InDepth", "id": "guides.Collections"},
47
- {"name": "Records", "parentId": "InDepth", "id": "guides.Records"},
48
- {"name": "Tutorials", "parentId": null, "isLeaf": false, "id": "Tutorials", "collapsed": true},
49
- {"name": "Rock Scissors Paper", "parentId": "Tutorials", "id": "tutorials.RSP", "hidden": true},
50
- {"name": "Earthquakes", "parentId": "Tutorials", "id": "tutorials.Earthquakes"},
51
- {"name": "Todo List", "parentId": "Tutorials", "id": "tutorials.TodoList"},
52
- {"name": "JavaScript Classes", "parentId": null, "isLeaf": false, "id": "JavaScript", "hidden": true},
53
- {"name": "Classes, Properties, and Methods", "parentId": "JavaScript", "id": "javascript.Classes"},
54
- {"name": "Overriding Methods", "parentId": "JavaScript", "id": "javascript.Overrides"},
55
- {"name": "Other JavaScript Class Features", "parentId": "JavaScript", "id": "javascript.ClassFeatures"},
56
- {"name": "Super", "parentId": "JavaScript", "id": "javascript.Super"},
57
- {"name": "New Node", "parentId": "JavaScript", "id": "javascript.NewNode"},
58
- {"name": "Glossary", "parentId": null, "id": "Glossary"}
2
+ {"name": "Using These Topics", "parentId": null, "id": "UsingTheseTopics" },
3
+ {"name": "Benefits", "parentId": null, "isLeaf": false, "id": "Benefits"},
4
+ {"name": "Introduction ", "parentId": "Benefits", "id": "benefits/Introduction"},
5
+ {"name": "Off the Main Thread", "parentId": "Benefits", "id": "benefits/OffTheMainThread"},
6
+ {"name": "4 Environments", "parentId": "Benefits", "id": "benefits/FourEnvironments"},
7
+ {"name": "Unified Config System", "parentId": "Benefits", "id": "benefits/ConfigSystem"},
8
+ {"name": "RPC Layer", "parentId": "Benefits", "id": "benefits/RPCLayer"},
9
+ {"name": "Extreme Speed", "parentId": "Benefits", "id": "benefits/Speed"},
10
+ {"name": "Multi-Window Applications", "parentId": "Benefits", "id": "benefits/MultiWindow"},
11
+ {"name": "Quick Application Development", "parentId": "Benefits", "id": "benefits/Quick"},
12
+ {"name": "Complexity and Effort", "parentId": "Benefits", "id": "benefits/Effort"},
13
+ {"name": "Forms Engine", "parentId": "Benefits", "id": "benefits/FormsEngine"},
14
+ {"name": "Features and Benefits Summary", "parentId": "Benefits", "id": "benefits/Features"},
15
+ {"name": "Getting Started", "parentId": null, "isLeaf": false, "id": "GettingStarted", "collapsed": true},
16
+ {"name": "Setup", "parentId": "GettingStarted", "id": "gettingstarted/Setup"},
17
+ {"name": "Workspaces and Applications", "parentId": "GettingStarted", "id": "gettingstarted/Workspaces"},
18
+ {"name": "Describing a View", "parentId": "GettingStarted", "id": "gettingstarted/DescribingTheUI"},
19
+ {"name": "Events", "parentId": "GettingStarted", "id": "gettingstarted/Events"},
20
+ {"name": "Component References", "parentId": "GettingStarted", "id": "gettingstarted/References"},
21
+ {"name": "Extending Classes", "parentId": "GettingStarted", "id": "gettingstarted/Extending"},
22
+ {"name": "Config", "parentId": "GettingStarted", "id": "gettingstarted/Config"},
23
+ {"name": "Shared Bindable Data", "parentId": "GettingStarted", "id": "gettingstarted/ComponentModels"},
24
+ {"name": "Guides", "parentId": null, "isLeaf": false, "id": "guides", "collapsed": true},
25
+ {"name": "Fundamentals & Core Concepts", "parentId": "guides", "isLeaf": false, "id": "guides/fundamentals", "collapsed": true},
26
+ {"name": "Application Bootstrap", "parentId": "guides/fundamentals", "id": "guides/fundamentals/ApplicationBootstrap"},
27
+ {"name": "Instance Lifecycle", "parentId": "guides/fundamentals", "id": "guides/fundamentals/InstanceLifecycle"},
28
+ {"name": "Declarative Component Trees VS Imperative Vdom", "parentId": "guides/fundamentals", "id": "guides/fundamentals/DeclarativeComponentTreesVsImperativeVdom"},
29
+ {"name": "Config System Deep Dive", "parentId": "guides/fundamentals", "id": "guides/fundamentals/ConfigSystemDeepDive"},
30
+ {"name": "Extending Neo Classes", "parentId": "guides/fundamentals", "id": "guides/fundamentals/ExtendingNeoClasses"},
31
+ {"name": "Main Thread Addons: Interacting with the Browser", "parentId": "guides/fundamentals", "id": "guides/fundamentals/MainThreadAddons"},
32
+ {"name": "UI Building Blocks", "parentId": "guides", "isLeaf": false, "id": "guides/uibuildingblocks", "collapsed": true},
33
+ {"name": "Component and Container Basics", "parentId": "guides/uibuildingblocks", "id": "guides/uibuildingblocks/ComponentsAndContainers"},
34
+ {"name": "Layouts", "parentId": "guides/uibuildingblocks", "id": "guides/uibuildingblocks/Layouts"},
35
+ {"name": "Custom Components", "parentId": "guides/uibuildingblocks", "id": "guides/uibuildingblocks/CustomComponents"},
36
+ {"name": "Working with VDom", "parentId": "guides/uibuildingblocks", "id": "guides/uibuildingblocks/WorkingWithVDom"},
37
+ {"name": "Data Handling", "parentId": "guides", "isLeaf": false, "id": "guides/datahandling", "collapsed": true},
38
+ {"name": "Collections", "parentId": "guides/datahandling", "id": "guides/datahandling/Collections"},
39
+ {"name": "Records", "parentId": "guides/datahandling", "id": "guides/datahandling/Records"},
40
+ {"name": "Grids", "parentId": "guides/datahandling", "id": "guides/datahandling/Grids"},
41
+ {"name": "Tables (Stores)", "parentId": "guides/datahandling", "id": "guides/datahandling/Tables", "hidden": true},
42
+ {"name": "Shared Bindable Data (State Providers)", "parentId": "guides/datahandling", "id": "guides/datahandling/StateProviders"},
43
+ {"name": "User Interaction & Advanced Features", "parentId": "guides", "isLeaf": false, "id": "guides/userinteraction", "collapsed": true},
44
+ {"name": "User Input (Forms)", "parentId": "guides/userinteraction", "id": "guides/userinteraction/Forms"},
45
+ {"name": "Form Fields", "parentId": "guides/userinteraction", "isLeaf": false, "id": "guides/userinteraction/form_fields"},
46
+ {"name": "ComboBox", "parentId": "guides/userinteraction/form_fields", "id": "guides/userinteraction/form_fields/ComboBox"},
47
+ {"name": "Events", "parentId": "guides/userinteraction", "isLeaf": false, "id": "guides/userinteraction/events"},
48
+ {"name": "Custom Events", "parentId": "guides/userinteraction/events", "id": "guides/userinteraction/events/CustomEvents"},
49
+ {"name": "DOM Events", "parentId": "guides/userinteraction/events", "id": "guides/userinteraction/events/DomEvents"},
50
+ {"name": "Specific Applications/Features", "parentId": "guides", "isLeaf": false, "id": "guides/SpecificFeatures", "collapsed": true},
51
+ {"name": "Multi-Window Applications", "parentId": "guides/specificfeatures", "id": "guides/specificfeatures/MultiWindow", "hidden": true},
52
+ {"name": "Mixins", "parentId": "guides/specificfeatures", "id": "guides/specificfeatures/Mixins", "hidden": true},
53
+ {"name": "Portal App", "parentId": "guides/specificfeatures", "id": "guides/specificfeatures/PortalApp"},
54
+ {"name": "Tutorials", "parentId": null, "isLeaf": false, "id": "Tutorials", "collapsed": true},
55
+ {"name": "Rock Scissors Paper", "parentId": "Tutorials", "id": "tutorials/RSP", "hidden": true},
56
+ {"name": "Earthquakes", "parentId": "Tutorials", "id": "tutorials/Earthquakes"},
57
+ {"name": "Todo List", "parentId": "Tutorials", "id": "tutorials/TodoList"},
58
+ {"name": "JavaScript Classes", "parentId": null, "isLeaf": false, "id": "JavaScript", "hidden": true},
59
+ {"name": "Classes, Properties, and Methods", "parentId": "JavaScript", "id": "javascript/Classes"},
60
+ {"name": "Overriding Methods", "parentId": "JavaScript", "id": "javascript/Overrides"},
61
+ {"name": "Other JavaScript Class Features", "parentId": "JavaScript", "id": "javascript/ClassFeatures"},
62
+ {"name": "Super", "parentId": "JavaScript", "id": "javascript/Super"},
63
+ {"name": "New Node", "parentId": "JavaScript", "id": "javascript/NewNode"},
64
+ {"name": "Glossary", "parentId": null, "id": "Glossary"}
59
65
  ]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "neo.mjs",
3
- "version" : "10.0.0-beta.3",
3
+ "version" : "10.0.0-beta.4",
4
4
  "description" : "Neo.mjs: The multi-threaded UI framework for building ultra-fast, desktop-like web applications with uncompromised responsiveness, inherent security, and a transpilation-free dev mode.",
5
5
  "type" : "module",
6
6
  "repository" : {
@@ -86,7 +86,7 @@
86
86
  "fs-extra" : "^11.3.0",
87
87
  "highlightjs-line-numbers.js" : "^2.9.0",
88
88
  "html-minifier-terser" : "^7.2.0",
89
- "inquirer" : "^12.6.3",
89
+ "inquirer" : "^12.7.0",
90
90
  "marked" : "^16.0.0",
91
91
  "monaco-editor" : "0.50.0",
92
92
  "neo-jsdoc" : "1.0.1",
@@ -289,12 +289,12 @@ const DefaultConfig = {
289
289
  useVdomWorker: true,
290
290
  /**
291
291
  * buildScripts/injectPackageVersion.mjs will update this value
292
- * @default '10.0.0-beta.3'
292
+ * @default '10.0.0-beta.4'
293
293
  * @memberOf! module:Neo
294
294
  * @name config.version
295
295
  * @type String
296
296
  */
297
- version: '10.0.0-beta.3'
297
+ version: '10.0.0-beta.4'
298
298
  };
299
299
 
300
300
  Object.assign(DefaultConfig, {
package/src/Neo.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import DefaultConfig from './DefaultConfig.mjs';
2
+ import {isDescriptor} from './core/ConfigSymbols.mjs';
2
3
 
3
4
  const
4
5
  camelRegex = /-./g,
@@ -514,8 +515,8 @@ Neo = globalThis.Neo = Object.assign({
514
515
  autoGenerateGetSet(element, key)
515
516
  }
516
517
 
517
- // only apply properties which have no setters inside the prototype chain
518
- // those will get applied on create (Neo.core.Base -> initConfig)
518
+ // Only apply properties which have no setters inside the prototype chain.
519
+ // Those will get applied on create (Neo.core.Base -> initConfig)
519
520
  else if (!Neo.hasPropertySetter(element, key)) {
520
521
  Object.defineProperty(element, key, {
521
522
  enumerable: true,
@@ -682,18 +683,24 @@ function autoGenerateGetSet(proto, key) {
682
683
  throw('Config ' + key + '_ (' + proto.className + ') already has a set method, use beforeGet, beforeSet & afterSet instead')
683
684
  }
684
685
 
686
+ const
687
+ _key = '_' + key,
688
+ uKey = key[0].toUpperCase() + key.slice(1),
689
+ beforeGet = 'beforeGet' + uKey,
690
+ beforeSet = 'beforeSet' + uKey,
691
+ afterSet = 'afterSet' + uKey;
692
+
685
693
  if (!Neo[getSetCache]) {
686
694
  Neo[getSetCache] = {}
687
695
  }
688
696
 
689
697
  if (!Neo[getSetCache][key]) {
690
- Neo[getSetCache][key] = {
698
+ const publicDescriptor = {
691
699
  get() {
692
700
  let me = this,
693
- beforeGet = `beforeGet${key[0].toUpperCase() + key.slice(1)}`,
694
701
  hasNewKey = Object.hasOwn(me[configSymbol], key),
695
702
  newKey = me[configSymbol][key],
696
- value = hasNewKey ? newKey : me['_' + key];
703
+ value = hasNewKey ? newKey : me[_key];
697
704
 
698
705
  if (Array.isArray(value)) {
699
706
  if (key !== 'items') {
@@ -704,8 +711,8 @@ function autoGenerateGetSet(proto, key) {
704
711
  }
705
712
 
706
713
  if (hasNewKey) {
707
- me[key] = value; // we do want to trigger the setter => beforeSet, afterSet
708
- value = me['_' + key]; // return the value parsed by the setter
714
+ me[key] = value; // We do want to trigger the setter => beforeSet, afterSet
715
+ value = me[_key]; // Return the value parsed by the setter
709
716
  delete me[configSymbol][key]
710
717
  }
711
718
 
@@ -715,18 +722,14 @@ function autoGenerateGetSet(proto, key) {
715
722
 
716
723
  return value
717
724
  },
718
-
719
725
  set(value) {
720
- if (value === undefined) {
721
- return
722
- }
726
+ if (value === undefined) return;
727
+
728
+ const config = this.getConfig(key);
729
+ if (!config) return;
723
730
 
724
731
  let me = this,
725
- _key = '_' + key,
726
- uKey = key[0].toUpperCase() + key.slice(1),
727
- beforeSet = 'beforeSet' + uKey,
728
- afterSet = 'afterSet' + uKey,
729
- oldValue = me[_key];
732
+ oldValue = config.get(); // Get the old value from the Config instance
730
733
 
731
734
  // every set call has to delete the matching symbol
732
735
  delete me[configSymbol][key];
@@ -735,34 +738,39 @@ function autoGenerateGetSet(proto, key) {
735
738
  value = Neo.clone(value, true, true)
736
739
  }
737
740
 
738
- // we do want to store the value before the beforeSet modification as well,
739
- // since it could get pulled by other beforeSet methods of different configs
740
- me[_key] = value;
741
-
742
741
  if (typeof me[beforeSet] === 'function') {
743
742
  value = me[beforeSet](value, oldValue);
744
743
 
745
744
  // If they don't return a value, that means no change
746
745
  if (value === undefined) {
747
- me[_key] = oldValue;
748
746
  return
749
747
  }
750
-
751
- me[_key] = value;
752
748
  }
753
749
 
754
- if (
755
- (key === 'vnode' && value !== oldValue) || // vnode trees can be huge, avoid a deep comparison
756
- !Neo.isEqual(value, oldValue)
757
- ) {
750
+ // Set the new value into the Config instance
751
+ // The config.set() method will return true if the value actually changed.
752
+ if (config.set(value)) {
758
753
  me[afterSet]?.(value, oldValue);
759
754
  me.afterSetConfig?.(key, value, oldValue)
760
755
  }
761
756
  }
762
- }
757
+ };
758
+
759
+ const privateDescriptor = {
760
+ get() {
761
+ return this.getConfig(key)?.get();
762
+ },
763
+ set(value) {
764
+ this.getConfig(key)?.setRaw(value);
765
+ }
766
+ };
767
+
768
+ Neo[getSetCache][key] = publicDescriptor;
769
+ Neo[getSetCache][_key] = privateDescriptor;
763
770
  }
764
771
 
765
- Object.defineProperty(proto, key, Neo[getSetCache][key])
772
+ Object.defineProperty(proto, key, Neo[getSetCache][key]);
773
+ Object.defineProperty(proto, _key, Neo[getSetCache][_key])
766
774
  }
767
775
 
768
776
  /**
@@ -48,6 +48,11 @@ class Collection extends Base {
48
48
  * @member {Boolean} autoSort=true
49
49
  */
50
50
  autoSort: true,
51
+ /**
52
+ * Stores the items.length of the items array in use
53
+ * @member {Number} count_=0
54
+ */
55
+ count_: 0,
51
56
  /**
52
57
  * Use 'primitive' for default filters, use 'advanced' for filters using a filterBy method
53
58
  * which need to iterate over other collection items
@@ -140,6 +145,17 @@ class Collection extends Base {
140
145
  }
141
146
 
142
147
  /**
148
+ * Triggered after the badgePosition config got changed
149
+ * @param {Number} value
150
+ * @param {Number} oldValue
151
+ * @protected
152
+ */
153
+ afterSetCount(value, oldValue) {
154
+ this.fire('countChange', {oldValue, value})
155
+ }
156
+
157
+ /**
158
+ * Triggered after the filters config got changed
143
159
  * @param {Array} value
144
160
  * @param {Array} oldValue
145
161
  * @protected
@@ -158,6 +174,7 @@ class Collection extends Base {
158
174
  }
159
175
 
160
176
  /**
177
+ * Triggered after the items config got changed
161
178
  * @param {Array} value
162
179
  * @param {Array} oldValue
163
180
  * @protected
@@ -174,10 +191,13 @@ class Collection extends Base {
174
191
  item = value[i];
175
192
  me.map.set(item[keyProperty], item)
176
193
  }
194
+
195
+ me.count = len
177
196
  }
178
197
  }
179
198
 
180
199
  /**
200
+ * Triggered after the sorters config got changed
181
201
  * @param {Array} value
182
202
  * @param {Array} oldValue
183
203
  * @protected
@@ -198,6 +218,7 @@ class Collection extends Base {
198
218
  }
199
219
 
200
220
  /**
221
+ * Triggered after the sourceId config got changed
201
222
  * @param {Number|String} value
202
223
  * @param {Number|String} oldValue
203
224
  * @protected
@@ -673,6 +694,7 @@ class Collection extends Base {
673
694
 
674
695
  me.allItems = Neo.create(Collection, {
675
696
  ...Neo.clone(config, true, true),
697
+ id : me.id + '-all',
676
698
  keyProperty: me.keyProperty,
677
699
  sourceId : me.id
678
700
  })
@@ -727,6 +749,8 @@ class Collection extends Base {
727
749
  me.doSort(me.items, true)
728
750
  }
729
751
 
752
+ me.count = me.items.length;
753
+
730
754
  me.fire('filter', {
731
755
  isFiltered: me[isFiltered],
732
756
  items : me.items,
@@ -845,11 +869,12 @@ class Collection extends Base {
845
869
  }
846
870
 
847
871
  /**
848
- * Returns the length of the internal items array
872
+ * Returns the config value of this.count
849
873
  * @returns {Number}
874
+ * @deprecated Use `this.count` directly instead.
850
875
  */
851
876
  getCount() {
852
- return this._items.length
877
+ return this._count || 0 // skipping beforeGetCount() on purpose
853
878
  }
854
879
 
855
880
  /**
@@ -1238,6 +1263,8 @@ class Collection extends Base {
1238
1263
  }
1239
1264
 
1240
1265
  if (me[updatingIndex] === 0) {
1266
+ me.count = me._items.length;
1267
+
1241
1268
  me.fire('mutate', {
1242
1269
  addedItems : toAddArray,
1243
1270
  preventBubbleUp: me.preventBubbleUp,
@@ -10,6 +10,7 @@ import Rectangle from '../util/Rectangle.mjs';
10
10
  import Style from '../util/Style.mjs';
11
11
  import VDomUtil from '../util/VDom.mjs';
12
12
  import VNodeUtil from '../util/VNode.mjs';
13
+ import {isDescriptor} from '../core/ConfigSymbols.mjs';
13
14
 
14
15
  const
15
16
  addUnits = value => value == null ? value : isNaN(value) ? value : `${value}px`,
@@ -378,7 +379,11 @@ class Component extends Base {
378
379
  * @member {Object} vnode_=null
379
380
  * @protected
380
381
  */
381
- vnode_: null,
382
+ vnode_: {
383
+ [isDescriptor]: true,
384
+ value : null,
385
+ isEqual : (a, b) => a === b // vnode trees can be huge, and will get compared by the vdom worker.
386
+ },
382
387
  /**
383
388
  * Shortcut for style.width, defaults to px
384
389
  * @member {Number|String|null} width_=null
@@ -646,21 +651,6 @@ class Component extends Base {
646
651
  }
647
652
  }
648
653
 
649
- /**
650
- * Triggered after the flex config got changed
651
- * @param {Number|String|null} value
652
- * @param {Number|String|null} oldValue
653
- * @protected
654
- */
655
- afterSetFlex(value, oldValue) {
656
- if (!isNaN(value)) {
657
- value = `${value} ${value} 0%`
658
- }
659
-
660
- this.configuredFlex = value;
661
- this.changeVdomRootKey('flex', value)
662
- }
663
-
664
654
  /**
665
655
  * Triggered after the hasUnmountedVdomChanges config got changed
666
656
  * @param {Boolean} value