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.
- package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +41 -0
- package/ServiceWorker.mjs +2 -2
- package/apps/form/view/FormPageContainer.mjs +2 -3
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/ViewportController.mjs +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/learn/ContentComponent.mjs +18 -11
- package/apps/portal/view/learn/MainContainerController.mjs +6 -6
- package/learn/README.md +9 -14
- package/learn/guides/datahandling/Collections.md +436 -0
- package/learn/guides/datahandling/Grids.md +621 -0
- package/learn/guides/datahandling/Records.md +287 -0
- package/learn/guides/{StateProviders.md → datahandling/StateProviders.md} +145 -1
- package/learn/guides/fundamentals/ExtendingNeoClasses.md +359 -0
- package/learn/guides/uibuildingblocks/CustomComponents.md +287 -0
- package/learn/guides/uibuildingblocks/Layouts.md +248 -0
- package/learn/guides/userinteraction/Forms.md +449 -0
- package/learn/guides/userinteraction/form_fields/ComboBox.md +241 -0
- package/learn/tree.json +63 -52
- package/package.json +2 -2
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +9 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/Neo.mjs +37 -29
- package/src/collection/Base.mjs +29 -2
- package/src/component/Base.mjs +6 -16
- package/src/controller/Base.mjs +87 -63
- package/src/core/Base.mjs +72 -17
- package/src/core/Compare.mjs +3 -13
- package/src/core/Config.mjs +139 -0
- package/src/core/ConfigSymbols.mjs +3 -0
- package/src/core/Util.mjs +3 -18
- package/src/data/RecordFactory.mjs +22 -3
- package/src/form/field/ComboBox.mjs +6 -1
- package/src/util/Function.mjs +52 -5
- package/src/vdom/Helper.mjs +7 -5
- package/test/siesta/tests/ReactiveConfigs.mjs +112 -0
- package/learn/guides/CustomComponents.md +0 -45
- package/learn/guides/Forms.md +0 -1
- package/learn/guides/Layouts.md +0 -1
- /package/learn/guides/{Tables.md → datahandling/Tables.md} +0 -0
- /package/learn/guides/{ApplicationBootstrap.md → fundamentals/ApplicationBootstrap.md} +0 -0
- /package/learn/guides/{ConfigSystemDeepDive.md → fundamentals/ConfigSystemDeepDive.md} +0 -0
- /package/learn/guides/{DeclarativeComponentTreesVsImperativeVdom.md → fundamentals/DeclarativeComponentTreesVsImperativeVdom.md} +0 -0
- /package/learn/guides/{InstanceLifecycle.md → fundamentals/InstanceLifecycle.md} +0 -0
- /package/learn/guides/{MainThreadAddons.md → fundamentals/MainThreadAddons.md} +0 -0
- /package/learn/guides/{Mixins.md → specificfeatures/Mixins.md} +0 -0
- /package/learn/guides/{MultiWindow.md → specificfeatures/MultiWindow.md} +0 -0
- /package/learn/guides/{PortalApp.md → specificfeatures/PortalApp.md} +0 -0
- /package/learn/guides/{ComponentsAndContainers.md → uibuildingblocks/ComponentsAndContainers.md} +0 -0
- /package/learn/guides/{WorkingWithVDom.md → uibuildingblocks/WorkingWithVDom.md} +0 -0
- /package/learn/guides/{events → userinteraction/events}/CustomEvents.md +0 -0
- /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",
|
3
|
-
{"name": "Benefits",
|
4
|
-
{"name": "Introduction ",
|
5
|
-
{"name": "Off the Main Thread",
|
6
|
-
{"name": "4 Environments",
|
7
|
-
{"name": "Unified Config System",
|
8
|
-
{"name": "RPC Layer",
|
9
|
-
{"name": "Extreme Speed",
|
10
|
-
{"name": "Multi-Window Applications",
|
11
|
-
{"name": "Quick Application Development",
|
12
|
-
{"name": "Complexity and Effort",
|
13
|
-
{"name": "Forms Engine",
|
14
|
-
{"name": "Features and Benefits Summary",
|
15
|
-
{"name": "Getting Started",
|
16
|
-
{"name": "Setup",
|
17
|
-
{"name": "Workspaces and Applications",
|
18
|
-
{"name": "Describing a View",
|
19
|
-
{"name": "Events",
|
20
|
-
{"name": "Component References",
|
21
|
-
{"name": "Extending Classes",
|
22
|
-
{"name": "Config",
|
23
|
-
{"name": "Shared Bindable Data",
|
24
|
-
{"name": "Guides",
|
25
|
-
{"name": "
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
{"name": "
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
{"name": "
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
{"name": "
|
51
|
-
|
52
|
-
|
53
|
-
|
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.
|
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.
|
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;
|
package/src/DefaultConfig.mjs
CHANGED
@@ -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.
|
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.
|
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
|
-
|
518
|
-
//
|
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
|
-
|
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[
|
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;
|
708
|
-
value = me[
|
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
|
-
|
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
|
-
|
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
|
-
|
755
|
-
|
756
|
-
|
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,
|
772
|
+
Object.defineProperty(proto, key, Neo[getSetCache][key]);
|
773
|
+
Object.defineProperty(proto, _key, Neo[getSetCache][_key])
|
766
774
|
}
|
767
775
|
|
768
776
|
/**
|