neo.mjs 10.0.0-alpha.5 → 10.0.0-beta.2

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 (188) hide show
  1. package/ServiceWorker.mjs +2 -2
  2. package/apps/colors/view/GridContainer.mjs +1 -1
  3. package/apps/covid/view/AttributionComponent.mjs +1 -1
  4. package/apps/covid/view/HeaderContainer.mjs +6 -6
  5. package/apps/covid/view/MainContainerController.mjs +5 -5
  6. package/apps/covid/view/TableContainerController.mjs +1 -1
  7. package/apps/covid/view/country/Gallery.mjs +13 -13
  8. package/apps/covid/view/country/Helix.mjs +13 -13
  9. package/apps/covid/view/country/HistoricalDataTable.mjs +1 -1
  10. package/apps/email/view/Viewport.mjs +2 -2
  11. package/apps/form/view/SideNavList.mjs +1 -1
  12. package/apps/portal/index.html +1 -1
  13. package/apps/portal/resources/data/examples_devmode.json +26 -27
  14. package/apps/portal/resources/data/examples_dist_dev.json +26 -27
  15. package/apps/portal/resources/data/examples_dist_esm.json +25 -26
  16. package/apps/portal/resources/data/examples_dist_prod.json +26 -27
  17. package/apps/portal/view/HeaderToolbar.mjs +3 -3
  18. package/apps/portal/view/about/Container.mjs +2 -2
  19. package/apps/portal/view/about/MemberContainer.mjs +3 -3
  20. package/apps/portal/view/blog/List.mjs +7 -7
  21. package/apps/portal/view/examples/List.mjs +4 -4
  22. package/apps/portal/view/home/ContentBox.mjs +2 -2
  23. package/apps/portal/view/home/FeatureSection.mjs +3 -3
  24. package/apps/portal/view/home/FooterContainer.mjs +7 -7
  25. package/apps/portal/view/home/parts/AfterMath.mjs +3 -3
  26. package/apps/portal/view/home/parts/MainNeo.mjs +3 -3
  27. package/apps/portal/view/home/parts/References.mjs +6 -6
  28. package/apps/portal/view/learn/ContentComponent.mjs +102 -111
  29. package/apps/portal/view/learn/PageSectionsContainer.mjs +1 -1
  30. package/apps/portal/view/learn/PageSectionsList.mjs +2 -2
  31. package/apps/portal/view/services/Component.mjs +16 -16
  32. package/apps/realworld/view/FooterComponent.mjs +1 -1
  33. package/apps/realworld/view/HeaderComponent.mjs +8 -8
  34. package/apps/realworld/view/HomeComponent.mjs +6 -6
  35. package/apps/realworld/view/article/CommentComponent.mjs +4 -4
  36. package/apps/realworld/view/article/Component.mjs +14 -14
  37. package/apps/realworld/view/article/CreateCommentComponent.mjs +3 -3
  38. package/apps/realworld/view/article/CreateComponent.mjs +3 -3
  39. package/apps/realworld/view/article/PreviewComponent.mjs +1 -1
  40. package/apps/realworld/view/article/TagListComponent.mjs +2 -2
  41. package/apps/realworld/view/user/ProfileComponent.mjs +8 -8
  42. package/apps/realworld/view/user/SettingsComponent.mjs +4 -4
  43. package/apps/realworld/view/user/SignUpComponent.mjs +4 -4
  44. package/apps/realworld2/view/FooterComponent.mjs +1 -1
  45. package/apps/realworld2/view/HomeContainer.mjs +3 -3
  46. package/apps/realworld2/view/article/DetailsContainer.mjs +1 -1
  47. package/apps/realworld2/view/article/PreviewComponent.mjs +7 -7
  48. package/apps/realworld2/view/article/TagListComponent.mjs +2 -2
  49. package/apps/realworld2/view/user/ProfileContainer.mjs +1 -1
  50. package/apps/route/view/center/CardAdministration.mjs +2 -2
  51. package/apps/route/view/center/CardAdministrationDenied.mjs +1 -1
  52. package/apps/route/view/center/CardContact.mjs +2 -2
  53. package/apps/route/view/center/CardHome.mjs +1 -1
  54. package/apps/route/view/center/CardSection1.mjs +1 -1
  55. package/apps/route/view/center/CardSection2.mjs +1 -1
  56. package/apps/sharedcovid/view/AttributionComponent.mjs +1 -1
  57. package/apps/sharedcovid/view/HeaderContainer.mjs +6 -6
  58. package/apps/sharedcovid/view/MainContainerController.mjs +5 -5
  59. package/apps/sharedcovid/view/TableContainerController.mjs +1 -1
  60. package/apps/sharedcovid/view/country/Gallery.mjs +13 -13
  61. package/apps/sharedcovid/view/country/Helix.mjs +13 -13
  62. package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -1
  63. package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +1 -1
  64. package/apps/shareddialog/view/MainContainer.mjs +1 -1
  65. package/buildScripts/createApp.mjs +2 -2
  66. package/examples/table/cellEditing/MainContainer.mjs +1 -1
  67. package/examples/table/container/MainContainer.mjs +3 -3
  68. package/examples/table/nestedRecordFields/Viewport.mjs +6 -6
  69. package/examples/tableFiltering/MainContainer.mjs +1 -1
  70. package/examples/tablePerformance/MainContainer.mjs +1 -1
  71. package/examples/tablePerformance/MainContainer2.mjs +1 -1
  72. package/examples/tablePerformance/MainContainer3.mjs +2 -2
  73. package/examples/tableStore/MainContainer.mjs +2 -2
  74. package/learn/Glossary.md +261 -0
  75. package/learn/UsingTheseTopics.md +2 -2
  76. package/learn/benefits/ConfigSystem.md +538 -28
  77. package/learn/benefits/Effort.md +47 -2
  78. package/learn/benefits/Features.md +50 -32
  79. package/learn/benefits/FormsEngine.md +68 -38
  80. package/learn/benefits/MultiWindow.md +33 -7
  81. package/learn/benefits/OffTheMainThread.md +2 -2
  82. package/learn/benefits/Quick.md +45 -12
  83. package/learn/benefits/RPCLayer.md +75 -0
  84. package/learn/benefits/Speed.md +16 -11
  85. package/learn/gettingstarted/ComponentModels.md +4 -4
  86. package/learn/gettingstarted/Config.md +6 -6
  87. package/learn/gettingstarted/DescribingTheUI.md +4 -4
  88. package/learn/gettingstarted/Events.md +6 -6
  89. package/learn/gettingstarted/Extending.md +4 -4
  90. package/learn/gettingstarted/References.md +6 -6
  91. package/learn/gettingstarted/Workspaces.md +6 -6
  92. package/learn/guides/ApplicationBootstrap.md +26 -26
  93. package/learn/guides/ComponentsAndContainers.md +12 -12
  94. package/learn/guides/ConfigSystemDeepDive.md +280 -0
  95. package/learn/guides/CustomComponents.md +2 -2
  96. package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +17 -17
  97. package/learn/guides/InstanceLifecycle.md +295 -1
  98. package/learn/guides/MainThreadAddons.md +475 -0
  99. package/learn/guides/PortalApp.md +2 -2
  100. package/learn/guides/StateProviders.md +12 -12
  101. package/learn/guides/WorkingWithVDom.md +14 -14
  102. package/learn/guides/events/CustomEvents.md +16 -16
  103. package/learn/guides/events/DomEvents.md +12 -12
  104. package/learn/javascript/ClassFeatures.md +3 -2
  105. package/learn/javascript/Classes.md +8 -8
  106. package/learn/javascript/NewNode.md +4 -4
  107. package/learn/javascript/Overrides.md +8 -8
  108. package/learn/javascript/Super.md +10 -8
  109. package/learn/tree.json +52 -51
  110. package/learn/tutorials/Earthquakes.md +54 -57
  111. package/learn/tutorials/TodoList.md +4 -4
  112. package/package.json +2 -2
  113. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +12 -0
  114. package/resources/scss/src/table/{View.scss → Body.scss} +1 -1
  115. package/resources/scss/src/table/plugin/CellEditing.scss +1 -1
  116. package/resources/scss/theme-dark/table/{View.scss → Body.scss} +1 -1
  117. package/resources/scss/theme-light/table/{View.scss → Body.scss} +1 -1
  118. package/resources/scss/theme-neo-light/Global.scss +1 -2
  119. package/resources/scss/theme-neo-light/table/{View.scss → Body.scss} +1 -1
  120. package/src/DefaultConfig.mjs +2 -2
  121. package/src/Main.mjs +8 -7
  122. package/src/Neo.mjs +16 -2
  123. package/src/button/Base.mjs +2 -2
  124. package/src/calendar/view/SettingsContainer.mjs +2 -2
  125. package/src/calendar/view/YearComponent.mjs +9 -9
  126. package/src/calendar/view/calendars/ColorsList.mjs +1 -1
  127. package/src/calendar/view/calendars/List.mjs +1 -1
  128. package/src/calendar/view/month/Component.mjs +15 -15
  129. package/src/calendar/view/week/Component.mjs +12 -12
  130. package/src/calendar/view/week/EventDragZone.mjs +4 -4
  131. package/src/calendar/view/week/TimeAxisComponent.mjs +3 -3
  132. package/src/component/Base.mjs +17 -2
  133. package/src/component/Carousel.mjs +2 -2
  134. package/src/component/Chip.mjs +3 -3
  135. package/src/component/Circle.mjs +2 -2
  136. package/src/component/DateSelector.mjs +8 -8
  137. package/src/component/Helix.mjs +1 -1
  138. package/src/component/Label.mjs +3 -18
  139. package/src/component/Legend.mjs +3 -3
  140. package/src/component/MagicMoveText.mjs +6 -14
  141. package/src/component/Process.mjs +3 -3
  142. package/src/component/Progress.mjs +1 -1
  143. package/src/component/StatusBadge.mjs +2 -2
  144. package/src/component/Timer.mjs +2 -2
  145. package/src/component/Toast.mjs +5 -3
  146. package/src/container/AccordionItem.mjs +2 -2
  147. package/src/container/Base.mjs +1 -1
  148. package/src/core/Base.mjs +77 -14
  149. package/src/core/Util.mjs +14 -2
  150. package/src/date/DayViewComponent.mjs +2 -2
  151. package/src/date/SelectorContainer.mjs +1 -1
  152. package/src/draggable/grid/header/toolbar/SortZone.mjs +21 -21
  153. package/src/draggable/table/header/toolbar/SortZone.mjs +1 -1
  154. package/src/form/field/CheckBox.mjs +4 -4
  155. package/src/form/field/FileUpload.mjs +25 -39
  156. package/src/form/field/Range.mjs +1 -1
  157. package/src/form/field/Text.mjs +3 -3
  158. package/src/form/field/TextArea.mjs +2 -3
  159. package/src/grid/Body.mjs +8 -5
  160. package/src/grid/_export.mjs +1 -1
  161. package/src/list/Color.mjs +2 -2
  162. package/src/main/DeltaUpdates.mjs +157 -98
  163. package/src/main/addon/AmCharts.mjs +61 -84
  164. package/src/main/addon/Base.mjs +161 -42
  165. package/src/main/addon/GoogleMaps.mjs +9 -16
  166. package/src/main/addon/HighlightJS.mjs +2 -13
  167. package/src/main/addon/IntersectionObserver.mjs +21 -21
  168. package/src/main/addon/MonacoEditor.mjs +32 -64
  169. package/src/manager/ClassHierarchy.mjs +114 -0
  170. package/src/menu/List.mjs +1 -1
  171. package/src/plugin/Popover.mjs +2 -2
  172. package/src/sitemap/Component.mjs +1 -1
  173. package/src/table/{View.mjs → Body.mjs} +25 -22
  174. package/src/table/Container.mjs +43 -43
  175. package/src/table/_export.mjs +2 -2
  176. package/src/table/plugin/CellEditing.mjs +19 -19
  177. package/src/tooltip/Base.mjs +1 -6
  178. package/src/tree/Accordion.mjs +3 -3
  179. package/src/vdom/Helper.mjs +19 -22
  180. package/src/worker/App.mjs +1 -2
  181. package/src/worker/Base.mjs +7 -5
  182. package/src/worker/Canvas.mjs +2 -3
  183. package/src/worker/Data.mjs +5 -7
  184. package/src/worker/Task.mjs +2 -3
  185. package/src/worker/VDom.mjs +3 -4
  186. package/src/worker/mixin/RemoteMethodAccess.mjs +5 -2
  187. package/learn/guides/MainThreadAddonExample.md +0 -15
  188. package/learn/guides/MainThreadAddonIntro.md +0 -44
@@ -0,0 +1,280 @@
1
+
2
+ **Pre-requisite:** It is highly recommended to study [The Unified Class Config System](#/learn/benefits.ConfigSystem)
3
+ first to understand the foundational concepts and benefits.
4
+
5
+ The Neo.mjs class configuration system is a cornerstone of the framework, providing a powerful, declarative, and
6
+ reactive way to manage the state of your components and classes. Its internal mechanics are deeply intertwined with
7
+ the instance lifecycle, ensuring predictable and consistent behavior. This guide will take you on a deep dive into
8
+ how it achieves its remarkable consistency and power.
9
+
10
+ ## 1. Core Concepts Recap
11
+
12
+ At its heart, the config system is built on a few key principles:
13
+
14
+ * **`static config` Block:** All configurable properties of a class are declared in a `static config = {}` block.
15
+ This provides a single, clear source of truth for a class's API.
16
+ * **`_` Suffix Convention:** Config properties that require custom logic when they change are declared with a trailing
17
+ underscore (e.g., `myValue_`). This signals the framework to automatically create a native getter and setter on the
18
+ class's prototype for this property.
19
+ * **Lifecycle Hooks:** For a config like `myValue_`, the framework provides optional lifecycle hooks that you can
20
+ implement in your class:
21
+ * `beforeGetMyValue(value)`: Called before the getter returns the value.
22
+ * `beforeSetMyValue(value, oldValue)`: Called before the setter applies the new value.
23
+ * `afterSetMyValue(value, oldValue)`: Called after the setter has applied the new value.
24
+ * **Reactivity:** The `afterSet` hooks are the heart of the reactive system. They allow you to define logic that
25
+ automatically runs whenever a specific config property changes, ensuring your UI and application state are always
26
+ in sync.
27
+
28
+ ## 2. The Internal Mechanics: `set()`, `processConfigs()`, and `configSymbol`
29
+
30
+ To truly understand how Neo.mjs handles complex scenarios like simultaneous updates and inter-dependencies, we must
31
+ look at the internal machinery: the `set()` and `processConfigs()` methods in `Neo.core.Base`, and the special
32
+ `configSymbol` object.
33
+
34
+ ### The `set()` Method: Your Gateway to Updates
35
+
36
+ The `set()` method is the public interface for changing one or more config properties at once. When you call
37
+ `this.set({a: 1, b: 2})`, you kick off a carefully orchestrated sequence.
38
+
39
+ [[Source: core.Base.mjs](https://github.com/neomjs/neo/blob/dev/src/core/Base.mjs)]
40
+ ```javascript readonly
41
+ // Simplified for clarity
42
+ set(values={}) {
43
+ let me = this;
44
+
45
+ // If there are pending configs from a previous operation, process them first.
46
+ if (Object.keys(me[configSymbol]).length > 0) {
47
+ me.processConfigs();
48
+ }
49
+
50
+ // Stage the new values in the configSymbol object.
51
+ Object.assign(me[configSymbol], values); // (A)
52
+
53
+ // Start processing the newly staged values.
54
+ me.processConfigs(true); // (B)
55
+ }
56
+ ```
57
+
58
+ Here’s the breakdown:
59
+ 1. **Pre-processing (within `construct()`):** The method first checks if the internal `configSymbol` object has any
60
+ leftover configs from a previous, unfinished operation (e.g., from a parent class's `construct()` call). If so,
61
+ it processes them to ensure a clean state before new values are staged.
62
+ 2. **Staging (A):** `Object.assign(me[configSymbol], values)` is the critical first step. All new values from your
63
+ `set()` call are merged into the `configSymbol` object. This object acts as a **temporary staging area**. It
64
+ creates a snapshot of the intended end-state for all properties in this specific `set()` operation *before*
65
+ any individual setters or `afterSet` hooks are invoked.
66
+ 3. **Processing (B):** `me.processConfigs(true)` is called. This kicks off the process of applying the staged values
67
+ from `configSymbol` to the actual instance properties. The `true` argument (`forceAssign`) is crucial, as we'll
68
+ see next.
69
+
70
+ ### The `processConfigs()` Method: The Heart of the Operation
71
+
72
+ This internal method iteratively processes the configs stored in `configSymbol`. It's designed as a recursive
73
+ function to handle the dynamic nature of config processing, where one `afterSet` might trigger another `set()`.
74
+
75
+ [[Source: core.Base.mjs](https://github.com/neomjs/neo/blob/dev/src/core/Base.mjs)]
76
+ ```javascript readonly
77
+ // Simplified for clarity
78
+ processConfigs(forceAssign=false) {
79
+ let me = this,
80
+ keys = Object.keys(me[configSymbol]); // Get keys of pending configs
81
+
82
+ if (keys.length > 0) {
83
+ let key = keys[0];
84
+ let value = me[configSymbol][key];
85
+
86
+ // The auto-generated setter for the config is triggered here.
87
+ me[key] = value; // (C)
88
+
89
+ // The config is removed from the staging area after its setter is called.
90
+ delete me[configSymbol][key]; // (D)
91
+
92
+ // Recursively call to process the next config.
93
+ me.processConfigs(forceAssign); // (E)
94
+ }
95
+ }
96
+ ```
97
+
98
+ * **Iteration:** `processConfigs` takes the *first* key from `configSymbol`. It avoids a standard loop to prevent
99
+ issues if an `afterSet` hook modifies `configSymbol`.
100
+ * **Assignment (C):** `me[key] = value` is the most important step. This does **not** directly change a backing
101
+ field. Instead, it triggers the actual auto-generated **setter** for the config property (e.g., `set a(value)`).
102
+ This native setter is responsible for:
103
+ 1. Running the `beforeSet` hook (if it exists).
104
+ 2. Updating the internal backing property (e.g., `this._a = value`).
105
+ 3. Running the `afterSet` hook (if it exists and the value has changed).
106
+ * **Deletion (D):** `delete me[configSymbol][key]` removes the property from the staging area *after* its setter
107
+ has been invoked. This is vital to prevent reprocessing and to mark the config as handled.
108
+ * **Recursion (E):** The method calls itself to process the next item in `configSymbol` until it's empty.
109
+
110
+ ## 3. Solving the "Circular Reference" Problem
111
+
112
+ What happens when two `afterSet` methods depend on each other's properties?
113
+
114
+ Consider this common scenario:
115
+ ```javascript readonly
116
+ class MyComponent extends Component {
117
+ static config = {
118
+ a_: 1,
119
+ b_: 2
120
+ }
121
+
122
+ afterSetA(value, oldValue) {
123
+ // This depends on 'b'
124
+ console.log(`a changed to ${value}, b is ${this.b}`);
125
+ }
126
+
127
+ afterSetB(value, oldValue) {
128
+ // This depends on 'a'
129
+ console.log(`b changed to ${value}, a is ${this.a}`);
130
+ }
131
+
132
+ onConstructed() {
133
+ super.onConstructed();
134
+ this.set({
135
+ a: 10,
136
+ b: 20
137
+ });
138
+ }
139
+ }
140
+ ```
141
+ When `this.set({a: 10, b: 20})` is called, which `afterSet` runs first? And when it runs, what value will it see
142
+ for the *other* property?
143
+
144
+ **This is where the brilliance of the `configSymbol` shines.**
145
+
146
+ Here's the sequence:
147
+ 1. **`set()` called:** `this.set({a: 10, b: 20})` is executed.
148
+ 2. **Staging:** The `configSymbol` is immediately populated: `me[configSymbol] = {a: 10, b: 20}`. The internal
149
+ backing properties `_a` and `_b` have **not** been updated yet.
150
+ 3. **`processConfigs()` starts:**
151
+ * It picks `a`. The setter `setA(10)` is called.
152
+ * Inside `setA`, the internal `this._a` is updated to `10`.
153
+ * `afterSetA(10, 1)` is triggered.
154
+ 4. **Inside `afterSetA`:**
155
+ * The code encounters `this.b`. This calls the auto-generated getter for `b`.
156
+ * **Crucially, the getter for `b` is smart.** It first checks if `b` exists as a key in the `configSymbol`
157
+ staging area.
158
+ * It finds `b: 20` in `configSymbol` and immediately returns `20`, the **new, pending value**. It does *not*
159
+ return the old value from `this._b`.
160
+ * The console logs: `a changed to 10, b is 20`.
161
+ 5. **`processConfigs()` continues:**
162
+ * `a` is removed from `configSymbol`.
163
+ * The recursion continues, and it now picks `b`. The setter `setB(20)` is called.
164
+ * Inside `setB`, `this._b` is updated to `20`.
165
+ * `afterSetB(20, 2)` is triggered.
166
+ 6. **Inside `afterSetB`:**
167
+ * The code encounters `this.a`. The getter for `a` is called.
168
+ * It checks `configSymbol`, but `a` is no longer there (it was processed).
169
+ * It therefore returns the value from the internal backing property, `this._a`, which is now `10`.
170
+ * The console logs: `b changed to 20, a is 10`.
171
+
172
+ **Conclusion:** The `configSymbol` acts as a consistent, authoritative snapshot for the duration of a `set()`
173
+ operation. This guarantees that all `afterSet` handlers, regardless of their execution order, operate on the most
174
+ current and consistent state of all config properties involved in that operation.
175
+
176
+ ## 4. In-depth Example: A Reactive `MainContainer`
177
+
178
+ Let's analyze a practical example to see these concepts in action. The `Neo.examples.core.config.MainContainer`
179
+ demonstrates how to build a reactive UI declaratively.
180
+
181
+ **The Goal:** Create a container with two labels. The text of each label is calculated based on the values of two
182
+ config properties, `a` and `b`. A button allows the user to change `a` and `b` simultaneously.
183
+
184
+ **The Declarative Approach (`static config`)**
185
+
186
+ The entire UI structure, including child components and event handlers, is defined within the `static config` block.
187
+ This is the recommended approach as it makes the component's structure immediately clear.
188
+
189
+ ```javascript readonly
190
+ // From: Neo.examples.core.config.MainContainer
191
+ import Panel from '../../../src/container/Panel.mjs';
192
+ import Viewport from '../../../src/container/Viewport.mjs';
193
+
194
+ class MainContainer extends Viewport {
195
+ static config = {
196
+ className: 'Neo.examples.core.config.MainContainer',
197
+ a_: null,
198
+ b_: null,
199
+ style : { padding: '20px' },
200
+ items: [{
201
+ module: Panel,
202
+ // ... panel configs
203
+ headers: [{
204
+ dock : 'top',
205
+ items: [
206
+ { ntype: 'label', flag: 'label1' },
207
+ { ntype: 'label', flag: 'label2' },
208
+ { ntype: 'component', flex: 1 },
209
+ {
210
+ handler: 'up.changeConfig', // Declarative handler
211
+ iconCls: 'fa fa-user',
212
+ text : 'Change configs'
213
+ }
214
+ ]
215
+ }],
216
+ items: [{ ntype: 'label', text: 'Click the change configs button!' }]
217
+ }]
218
+ }
219
+
220
+ onConstructed() {
221
+ super.onConstructed();
222
+ this.set({ a: 5, b: 5 });
223
+ }
224
+
225
+ afterSetA(value, oldValue) {
226
+ if (oldValue !== undefined) {
227
+ this.down({flag: 'label1'}).text = value + this.b;
228
+ }
229
+ }
230
+
231
+ afterSetB(value, oldValue) {
232
+ if (oldValue !== undefined) {
233
+ this.down({flag: 'label2'}).text = value + this.a;
234
+ }
235
+ }
236
+
237
+ changeConfig(data) {
238
+ this.set({ a: 10, b: 10 });
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### Tracing the Data Flow
244
+
245
+ 1. **Initialization (`onConstructed`)**:
246
+ * `this.set({a: 5, b: 5})` is called. This happens within the `onConstructed()` lifecycle hook, which is guaranteed
247
+ to run *after* the instance's `construct()` method has fully processed its initial configuration.
248
+ * `configSymbol` becomes `{a: 5, b: 5}`.
249
+ * `afterSetA` runs. It calculates `label1.text` as `value (5) + this.b (reads 5 from configSymbol) = 10`.
250
+ * `afterSetB` runs. It calculates `label2.text` as `value (5) + this.a (reads 5 from _a) = 10`.
251
+ * **Initial State:** `label1` shows "10", `label2` shows "10".
252
+
253
+ 2. **Button Click (`changeConfig`)**:
254
+ * The button's `handler: 'up.changeConfig'` finds and calls the `changeConfig` method on the `MainContainer`.
255
+ * `this.set({a: 10, b: 10})` is called.
256
+ * `configSymbol` becomes `{a: 10, b: 10}`.
257
+ * `afterSetA` runs. It calculates `label1.text` as `value (10) + this.b (reads 10 from configSymbol) = 20`.
258
+ * `afterSetB` runs. It calculates `label2.text` as `value (10) + this.a (reads 10 from _a) = 20`.
259
+ * **New State:** `label1` shows "20", `label2` shows "20".
260
+
261
+ This example vividly demonstrates the dynamic and reactive nature of the system, where a single declarative state
262
+ change automatically propagates through the component logic.
263
+
264
+ ## 5. Best Practices
265
+
266
+ * **Embrace Declarativity:** Define your entire UI structure inside `static config` whenever possible. This improves
267
+ readability and maintainability.
268
+ * **Use the `_` Suffix Wisely:** Only add the trailing underscore to configs that need `afterSet`, `beforeSet` or
269
+ `beforeGet` based logic. For simple value properties, omit it to avoid unnecessary overhead.
270
+ * **Keep `afterSet` Handlers Pure:** An `afterSet` handler should ideally only react to the change of its own
271
+ property and update other parts of the application. Avoid triggering complex chains of `set()` calls from within
272
+ an `afterSet` if possible.
273
+ * **Batch Updates with `set()`:** When you need to change multiple properties at once, always use a single
274
+ `set({a: 1, b: 2})` call. This is more efficient and ensures consistency, as demonstrated above.
275
+ * **Use `onConstructed` for Post-Construction Logic:** Use the `onConstructed` lifecycle method to perform any setup
276
+ that depends on the instance's initial configuration being fully processed. This is the ideal place for logic that
277
+ requires all configs to be set and potentially other instances to be created (if set-driven).
278
+
279
+ By understanding these internal mechanics and following best practices, you can leverage the full power of Neo.mjs's
280
+ class config system to build highly complex, reactive, and maintainable applications with confidence.
@@ -9,7 +9,7 @@ Neo.mjs is class-based, which means you're free to extend any component (or any
9
9
 
10
10
  ## Lifecycle config properties
11
11
 
12
- <pre data-code-livepreview>
12
+ ```javascript live-preview
13
13
  import Button from '../button/Base.mjs';
14
14
  // In practice this would be some handy reusable component
15
15
  class MySpecialButton extends Button {
@@ -41,5 +41,5 @@ class MainView extends Container {
41
41
  }
42
42
 
43
43
  Neo.setupClass(MainView);
44
- </pre>
44
+ ```
45
45
 
@@ -14,7 +14,7 @@ Neo.mjs operates on two distinct abstraction layers:
14
14
  - **Component Tree Layer** (Application Development): Declarative, mutable, reactive component configurations
15
15
  - **VDom Tree Layer** (Framework Internals): Imperative virtual DOM operations for performance optimization
16
16
 
17
- ```
17
+ ```text
18
18
  Your Application Code → Component Tree (declarative, mutable, reactive)
19
19
 
20
20
  VDom Tree (imperative, optimized)
@@ -28,7 +28,7 @@ Your Application Code → Component Tree (declarative, mutable, reactive)
28
28
 
29
29
  In React, Vue, and Angular, you compose UIs by writing templates/JSX that mix HTML elements with custom components:
30
30
 
31
- ```jsx
31
+ ```javascript readonly
32
32
  // React/Vue/Angular pattern - mixing HTML with components
33
33
  function App() {
34
34
  return (
@@ -50,7 +50,7 @@ Your mental model:</br>
50
50
 
51
51
  In Neo.mjs, you work with **declarative component configurations** that create a component tree abstraction:
52
52
 
53
- ```javascript
53
+ ```javascript readonly
54
54
  // Neo.mjs pattern - component relationship configuration
55
55
  class Viewport extends Container {
56
56
  static config = {
@@ -91,7 +91,7 @@ Your new mental model:</br>
91
91
 
92
92
  Components are defined through static configuration objects that describe relationships and behavior:
93
93
 
94
- ```javascript
94
+ ```javascript readonly
95
95
  // Declarative component hierarchy
96
96
  class Viewport extends BaseViewport {
97
97
  static config = {
@@ -118,7 +118,7 @@ class Viewport extends BaseViewport {
118
118
 
119
119
  The component tree is **dynamic and mutable at runtime**:
120
120
 
121
- ```javascript
121
+ ```javascript readonly
122
122
  // Runtime mutations on the component tree
123
123
  container.add({module: NewComponent}); // Add component
124
124
  container.removeAt(0); // Remove component
@@ -134,7 +134,7 @@ targetContainer.add(sourceView);
134
134
 
135
135
  **Every component tree configuration change automatically triggers UI updates**:
136
136
 
137
- ```javascript
137
+ ```javascript readonly
138
138
  // These changes automatically update the UI
139
139
  button.text = 'New Text'; // Property change → UI update
140
140
  button.iconCls = 'fa fa-home'; // Config change → UI update
@@ -151,7 +151,7 @@ viewport.setState({size: 'large'}); // State change → UI update
151
151
 
152
152
  ### State Provider Integration
153
153
 
154
- ```javascript
154
+ ```javascript readonly
155
155
  // Portal.view.ViewportStateProvider
156
156
  class ViewportStateProvider extends StateProvider {
157
157
  static config = {
@@ -171,7 +171,7 @@ viewportStateProvider.setData({size: 'large'});
171
171
 
172
172
  Framework components define their internal DOM structure through `vdom` config:
173
173
 
174
- ```javascript
174
+ ```javascript readonly
175
175
  // Neo.button.Base
176
176
  class Button extends Component {
177
177
  static config = {
@@ -192,7 +192,7 @@ class Button extends Component {
192
192
 
193
193
  Framework code performs imperative operations on VDom node properties:
194
194
 
195
- ```javascript
195
+ ```javascript readonly
196
196
  // Neo.button.Base - internal framework code
197
197
  afterSetIconCls(value, oldValue) {
198
198
  let {iconNode} = this;
@@ -222,7 +222,7 @@ afterSetText(value, oldValue) {
222
222
 
223
223
  ### Performance Optimizations
224
224
 
225
- ```javascript
225
+ ```javascript readonly
226
226
  // Neo.button.Base - optimized animations
227
227
  async showRipple(data) {
228
228
  let rippleEl = this.rippleWrapper.cn[0];
@@ -253,7 +253,7 @@ async showRipple(data) {
253
253
 
254
254
  Understanding the value proposition of Neo.mjs's two-tier architecture:
255
255
 
256
- ```javascript
256
+ ```javascript readonly
257
257
  // What developers write - declarative configurations
258
258
  {
259
259
  module : Button,
@@ -281,7 +281,7 @@ This separation allows developers to focus on **what** they want to build rather
281
281
  Routing happens inside view controllers, instead of being tag-based.
282
282
  Developers are in full control to define what route-changes should do.
283
283
 
284
- ```javascript
284
+ ```javascript readonly
285
285
  // Portal.view.ViewportController
286
286
  // Declarative route configuration
287
287
  static config = {
@@ -319,7 +319,7 @@ async setMainContentIndex(index) {
319
319
 
320
320
  If needed, this can be done via JavaScript too (instead of purely focussing on CSS).
321
321
 
322
- ```javascript
322
+ ```javascript readonly
323
323
  // Portal.view.Viewport
324
324
  static sizes = ['large', 'medium', 'small', 'x-small', null];
325
325
 
@@ -340,7 +340,7 @@ afterSetSize(value, oldValue) {
340
340
 
341
341
  ### Dynamic Component Management
342
342
 
343
- ```javascript
343
+ ```javascript readonly
344
344
  // Portal.view.ViewportController
345
345
  async onAppConnect(data) {
346
346
  let app = Neo.apps[data.appName];
@@ -420,7 +420,7 @@ async onAppConnect(data) {
420
420
 
421
421
  ### Integration Patterns:
422
422
 
423
- ```javascript
423
+ ```javascript readonly
424
424
  // Wrapping existing components or custom elements
425
425
  {
426
426
  module: LegacyWrapper,
@@ -449,7 +449,7 @@ async onAppConnect(data) {
449
449
 
450
450
  ### Custom Component Development
451
451
 
452
- ```javascript
452
+ ```javascript readonly
453
453
  import Component from './src/component/Base.mjs';
454
454
  import VdomUtil from './src/util/Vdom.mjs';
455
455
 
@@ -481,7 +481,7 @@ Neo.mjs provides utilities such as `VdomUtil` for direct interaction with VDom n
481
481
 
482
482
  ### Performance Monitoring
483
483
 
484
- ```javascript
484
+ ```javascript readonly
485
485
  Neo.config.logDeltaUpdates = true; // Enable update timing logs
486
486
  ```
487
487