neo.mjs 10.0.0-beta.2 → 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 (52) hide show
  1. package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +41 -0
  2. package/ServiceWorker.mjs +2 -2
  3. package/apps/form/view/FormPageContainer.mjs +2 -3
  4. package/apps/portal/index.html +1 -1
  5. package/apps/portal/view/ViewportController.mjs +1 -1
  6. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  7. package/apps/portal/view/learn/ContentComponent.mjs +18 -11
  8. package/apps/portal/view/learn/MainContainerController.mjs +6 -6
  9. package/learn/README.md +9 -14
  10. package/learn/guides/datahandling/Collections.md +436 -0
  11. package/learn/guides/datahandling/Grids.md +621 -0
  12. package/learn/guides/datahandling/Records.md +287 -0
  13. package/learn/guides/{StateProviders.md → datahandling/StateProviders.md} +145 -1
  14. package/learn/guides/fundamentals/ExtendingNeoClasses.md +359 -0
  15. package/learn/guides/uibuildingblocks/CustomComponents.md +287 -0
  16. package/learn/guides/uibuildingblocks/Layouts.md +248 -0
  17. package/learn/guides/userinteraction/Forms.md +449 -0
  18. package/learn/guides/userinteraction/form_fields/ComboBox.md +241 -0
  19. package/learn/tree.json +63 -52
  20. package/package.json +2 -2
  21. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +9 -0
  22. package/src/DefaultConfig.mjs +2 -2
  23. package/src/Neo.mjs +37 -29
  24. package/src/collection/Base.mjs +29 -2
  25. package/src/component/Base.mjs +6 -16
  26. package/src/controller/Base.mjs +87 -63
  27. package/src/core/Base.mjs +72 -17
  28. package/src/core/Compare.mjs +3 -13
  29. package/src/core/Config.mjs +139 -0
  30. package/src/core/ConfigSymbols.mjs +3 -0
  31. package/src/core/Util.mjs +3 -18
  32. package/src/data/RecordFactory.mjs +22 -3
  33. package/src/form/field/ComboBox.mjs +6 -1
  34. package/src/util/Function.mjs +52 -5
  35. package/src/vdom/Helper.mjs +7 -5
  36. package/test/siesta/tests/ReactiveConfigs.mjs +112 -0
  37. package/learn/guides/CustomComponents.md +0 -45
  38. package/learn/guides/Forms.md +0 -1
  39. package/learn/guides/Layouts.md +0 -1
  40. /package/learn/guides/{Tables.md → datahandling/Tables.md} +0 -0
  41. /package/learn/guides/{ApplicationBootstrap.md → fundamentals/ApplicationBootstrap.md} +0 -0
  42. /package/learn/guides/{ConfigSystemDeepDive.md → fundamentals/ConfigSystemDeepDive.md} +0 -0
  43. /package/learn/guides/{DeclarativeComponentTreesVsImperativeVdom.md → fundamentals/DeclarativeComponentTreesVsImperativeVdom.md} +0 -0
  44. /package/learn/guides/{InstanceLifecycle.md → fundamentals/InstanceLifecycle.md} +0 -0
  45. /package/learn/guides/{MainThreadAddons.md → fundamentals/MainThreadAddons.md} +0 -0
  46. /package/learn/guides/{Mixins.md → specificfeatures/Mixins.md} +0 -0
  47. /package/learn/guides/{MultiWindow.md → specificfeatures/MultiWindow.md} +0 -0
  48. /package/learn/guides/{PortalApp.md → specificfeatures/PortalApp.md} +0 -0
  49. /package/learn/guides/{ComponentsAndContainers.md → uibuildingblocks/ComponentsAndContainers.md} +0 -0
  50. /package/learn/guides/{WorkingWithVDom.md → uibuildingblocks/WorkingWithVDom.md} +0 -0
  51. /package/learn/guides/{events → userinteraction/events}/CustomEvents.md +0 -0
  52. /package/learn/guides/{events → userinteraction/events}/DomEvents.md +0 -0
@@ -0,0 +1,241 @@
1
+ # ComboBox Field
2
+
3
+ The `Neo.form.field.ComboBox` is a powerful and flexible input component that provides a dropdown list for selecting one or multiple items. It combines the functionality of a text input field with a list, allowing users to either type to filter options or select directly from a predefined set of choices.
4
+
5
+ ## 1. Basic Usage
6
+
7
+ At its simplest, a `ComboBox` can be configured with a static array of data.
8
+
9
+ ```javascript live-preview
10
+ import ComboBox from '../../src/form/field/ComboBox.mjs';
11
+ import FormContainer from '../../src/form/Container.mjs';
12
+
13
+ class MainView extends FormContainer {
14
+ static config = {
15
+ className: 'MyComboBoxForm',
16
+ layout : {ntype: 'vbox', align: 'start'},
17
+ items : [{
18
+ module : ComboBox,
19
+ labelPosition: 'inline',
20
+ labelText : 'Select a Fruit',
21
+ name : 'selectedFruit',
22
+ width : 200,
23
+
24
+ store: { // Inline store configuration
25
+ data: [
26
+ {id: 'apple', name: 'Apple'},
27
+ {id: 'banana', name: 'Banana'},
28
+ {id: 'orange', name: 'Orange'},
29
+ {id: 'grape', name: 'Grape'}
30
+ ]
31
+ }
32
+ }]
33
+ }
34
+ }
35
+
36
+ MainView = Neo.setupClass(MainView);
37
+ ```
38
+
39
+ In this example:
40
+ * The `store` config is an inline object that gets automatically converted into a `Neo.data.Store`.
41
+ * The `id` property of each data item is used as the internal value, and `name` is used for display by default.
42
+ * **Note**: If your data uses different property names for the unique identifier or display value (e.g., `cityId` instead of `id`), you must explicitly define a `model` (even inline) within the `store` config and set the `keyProperty` to match your unique identifier.
43
+
44
+ ## 2. Data Integration with `Neo.data.Store`
45
+
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
+
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';
52
+
53
+ // Define a Model for your data
54
+ class CountryModel extends Model {
55
+ static config = {
56
+ className: 'CountryModel',
57
+ fields: [
58
+ {name: 'id', type: 'String'},
59
+ {name: 'name', type: 'String'}
60
+ ]
61
+ }
62
+ }
63
+
64
+ // Define a Store class
65
+ class CountriesStore extends Store {
66
+ static config = {
67
+ className: 'CountriesStore',
68
+ model : CountryModel,
69
+ autoLoad : true,
70
+ url : '/path/to/your/countries.json' // Example: fetch data from a URL
71
+ }
72
+ }
73
+
74
+ class CountryComboBoxForm extends Neo.form.Container {
75
+ static config = {
76
+ className: 'CountryComboBoxForm',
77
+ layout : {ntype: 'vbox', align: 'start'},
78
+ items : [{
79
+ module : ComboBox,
80
+ labelText: 'Select a Country',
81
+ name : 'selectedCountry',
82
+ store : CountriesStore // Pass the Store Class
83
+ }]
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Passing Models and Stores: Flexibility in Configuration
89
+
90
+ Neo.mjs offers significant flexibility in how you configure models and stores for your components. For both the `model` config within a `Store` and the `store` config within a data-bound component (like `ComboBox`), you can typically pass one of three types:
91
+
92
+ 1. **Configuration Object**: A plain JavaScript object containing the properties for the model or store. Neo.mjs will automatically create an instance from this object. This is convenient for inline, simple definitions.
93
+ ```javascript readonly
94
+ // Example: Inline Store config for ComboBox
95
+ store: {
96
+ model: { // Model config object
97
+ fields: [{name: 'id'}, {name: 'name'}]
98
+ },
99
+ data: [{id: 1, name: 'Item 1'}]
100
+ }
101
+ ```
102
+
103
+ 2. **Class Reference**: A direct reference to the class (e.g., `MyStoreClass`, `MyModelClass`). Neo.mjs will automatically instantiate this class when the component or store is created. This is the most common and recommended approach for reusable definitions.
104
+ ```javascript readonly
105
+ // Example: Passing a Store Class to ComboBox
106
+ import MyStoreClass from './MyStoreClass.mjs';
107
+
108
+ // ...
109
+ items: [{
110
+ module: ComboBox,
111
+ store : MyStoreClass // Pass the Store Class
112
+ }]
113
+ ```
114
+ ```javascript readonly
115
+ // Example: Passing a Model Class to a Store
116
+ import MyModelClass from './MyModelClass.mjs';
117
+
118
+ class MyStoreClass extends Neo.data.Store {
119
+ static config = {
120
+ model: MyModelClass // Pass the Model Class
121
+ }
122
+ }
123
+ ```
124
+
125
+ 3. **Instance**: A pre-created instance of the model or store. This is useful when you need a single, shared instance across multiple components (e.g., a singleton store for application-wide settings or a store that's managed externally).
126
+ ```javascript readonly
127
+ // Example: Passing a Store Instance to ComboBox
128
+ const mySharedStore = Neo.create({
129
+ module: Neo.data.Store,
130
+ // ... store configs
131
+ });
132
+
133
+ // ...
134
+ items: [{
135
+ module: ComboBox,
136
+ store : mySharedStore // Pass the Store Instance
137
+ }]
138
+ ```
139
+ While less common for `model` configs (as models are typically instantiated by stores), you could theoretically pass a `Model` instance if a custom scenario required it.
140
+
141
+ Choosing the appropriate method depends on your application's architecture, reusability needs, and whether you require shared or independent data instances.
142
+
143
+ ## 3. Key Configuration Options
144
+
145
+ The `ComboBox` offers several configurations to control its behavior and appearance:
146
+
147
+ * **`displayField`**: (Default: `'name'`) The name of the field in the store's records that will be displayed in the dropdown list and the input field.
148
+ * **`valueField`**: (Default: `'id'`) The name of the field in the store's records that will be used as the actual value of the `ComboBox` when selected. This is the value returned by `getSubmitValue()`.
149
+ * **`forceSelection`**: (Default: `true`) If `true`, the `ComboBox` will only allow values that match an existing record in its store. If the user types a value that doesn't match, the field will be cleared on blur.
150
+ * **`editable`**: (Default: `true`) If `true`, the user can type into the input field. If `false`, the input field is read-only, and selection can only be made from the dropdown list.
151
+ * **`filterDelay`**: (Default: `50`) The time in milliseconds to delay between user input and applying the filter to the dropdown list. Useful for performance with large stores.
152
+ * **`typeAhead`**: (Default: `true`) If `true`, as the user types, the `ComboBox` will attempt to complete the input with the first matching record from the store, displaying the suggestion as a hint.
153
+ * **`triggerAction`**: (Default: `'all'`) Controls what happens when the dropdown trigger is clicked.
154
+ * `'all'`: Shows all list items, regardless of current input.
155
+ * `'filtered'`: Shows only items that match the current input field's value.
156
+ * **`listConfig`**: An object that allows you to configure the underlying `Neo.list.Base` component used for the dropdown. For example, you can enable checkboxes for multi-selection (though `ComboBox` itself is typically single-select, `Chip` extends it for multi-select).
157
+
158
+ ```javascript live-preview
159
+ import ComboBox from '../../src/form/field/ComboBox.mjs';
160
+ import FormContainer from '../../src/form/Container.mjs';
161
+
162
+ class MainView extends FormContainer {
163
+ static config = {
164
+ className: 'AdvancedComboBox',
165
+ layout : {ntype: 'vbox', align: 'start'},
166
+ items : [{
167
+ module : ComboBox,
168
+ displayField : 'cityName',
169
+ editable : true,
170
+ filterDelay : 200,
171
+ forceSelection: false, // Allow custom input
172
+ labelText : 'Search for a City',
173
+ name : 'citySearch',
174
+ triggerAction : 'filtered',
175
+ typeAhead : true,
176
+ valueField : 'cityId',
177
+ width : 300,
178
+
179
+ store: {
180
+ keyProperty: 'cityId',
181
+ model : {fields: [{name: 'cityId', type: 'String'}, {name: 'cityName', type: 'String'}]},
182
+
183
+ data: [
184
+ {cityId: 'ny', cityName: 'New York'},
185
+ {cityId: 'la', cityName: 'Los Angeles'},
186
+ {cityId: 'chi', cityName: 'Chicago'},
187
+ {cityId: 'hou', cityName: 'Houston'}
188
+ ]
189
+ }
190
+ }]
191
+ }
192
+ }
193
+
194
+ MainView = Neo.setupClass(MainView);
195
+ ```
196
+
197
+ ## 4. Events
198
+
199
+ The `ComboBox` field emits several events that you can listen to for custom logic:
200
+
201
+ * **`change`**: Inherited from `Neo.form.field.Base`, fired when the field's `value` config changes.
202
+ * **`userChange`**: Inherited from `Neo.form.field.Base`, fired when the field's value changes due to direct user interaction (e.g., typing or selecting from the list).
203
+ * **`select`**: Specific to `ComboBox`, fired when an item is selected from the dropdown list. The event object contains the `record` that was selected.
204
+
205
+ ```javascript live-preview
206
+ import ComboBox from '../../src/form/field/ComboBox.mjs';
207
+ import FormContainer from '../../src/form/Container.mjs';
208
+
209
+ class MainView extends FormContainer {
210
+ static config = {
211
+ className: 'ComboBoxWithEvents',
212
+ layout : {ntype: 'vbox', align: 'start'},
213
+ items : [{
214
+ module : ComboBox,
215
+ labelPosition: 'top',
216
+ labelText : 'Choose an Option',
217
+ name : 'option',
218
+
219
+ store: {
220
+ data: [
221
+ {id: 1, name: 'Option A'},
222
+ {id: 2, name: 'Option B'}
223
+ ]
224
+ },
225
+ listeners: {
226
+ select: function(data) {
227
+ Neo.Main.log({value: `Selected record: ${data.value.name}`}); // data.value is the selected record
228
+ },
229
+ change: function(data) {
230
+ Neo.Main.log({value: `Field value changed (record or null): ${data.value?.name || data.value}`});
231
+ },
232
+ userChange: function(data) { // text input
233
+ Neo.Main.log({value: `User interacted, new value: ${data.value}`});
234
+ }
235
+ }
236
+ }]
237
+ }
238
+ }
239
+
240
+ MainView = Neo.setupClass(MainView);
241
+ ```
package/learn/tree.json CHANGED
@@ -1,54 +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": "Main Thread Addons: Interacting with the Browser", "parentId": "InDepth", "id": "guides.MainThreadAddons"},
32
- {"name": "User Input (Forms)", "parentId": "InDepth", "id": "guides.Forms", "hidden": true},
33
- {"name": "Component and Container Basics", "parentId": "InDepth", "id": "guides.ComponentsAndContainers"},
34
- {"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "guides.Layouts", "hidden": true},
35
- {"name": "Custom Components", "parentId": "InDepth", "id": "guides.CustomComponents", "hidden": true},
36
- {"name": "Events", "parentId": "InDepth", "isLeaf": false, "id": "GuideEvents"},
37
- {"name": "Custom Events", "parentId": "GuideEvents", "id": "guides.events.CustomEvents"},
38
- {"name": "DOM Events", "parentId": "GuideEvents", "id": "guides.events.DomEvents"},
39
- {"name": "Portal App", "parentId": "InDepth", "id": "guides.PortalApp"},
40
- {"name": "Tables (Stores)", "parentId": "InDepth", "id": "guides.Tables", "hidden": true},
41
- {"name": "Multi-Window Applications", "parentId": "InDepth", "id": "guides.MultiWindow", "hidden": true},
42
- {"name": "Mixins", "parentId": "InDepth", "id": "guides.Mixins", "hidden": true},
43
- {"name": "Tutorials", "parentId": null, "isLeaf": false, "id": "Tutorials", "collapsed": true},
44
- {"name": "Rock Scissors Paper", "parentId": "Tutorials", "id": "tutorials.RSP", "hidden": true},
45
- {"name": "Earthquakes", "parentId": "Tutorials", "id": "tutorials.Earthquakes"},
46
- {"name": "Todo List", "parentId": "Tutorials", "id": "tutorials.TodoList"},
47
- {"name": "JavaScript Classes", "parentId": null, "isLeaf": false, "id": "JavaScript", "hidden": true},
48
- {"name": "Classes, Properties, and Methods", "parentId": "JavaScript", "id": "javascript.Classes"},
49
- {"name": "Overriding Methods", "parentId": "JavaScript", "id": "javascript.Overrides"},
50
- {"name": "Other JavaScript Class Features", "parentId": "JavaScript", "id": "javascript.ClassFeatures"},
51
- {"name": "Super", "parentId": "JavaScript", "id": "javascript.Super"},
52
- {"name": "New Node", "parentId": "JavaScript", "id": "javascript.NewNode"},
53
- {"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"}
54
65
  ]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "neo.mjs",
3
- "version" : "10.0.0-beta.2",
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",
@@ -63,6 +63,15 @@
63
63
  color: #3E63DD;
64
64
  }
65
65
 
66
+ h2.neo-h2,
67
+ h3.neo-h3 {
68
+ code {
69
+ font-family: inherit !important;
70
+ font-size : 1em !important;
71
+ font-weight: inherit !important;
72
+ }
73
+ }
74
+
66
75
  .lab {
67
76
  box-shadow : 0 4px 8px 0 rgba(0, 0, 0, 0.2);
68
77
  font-size : 1em;
@@ -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.2'
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.2'
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
  /**